WIP Do not merge.

This commit is contained in:
Bryan Mauger 2023-03-21 14:36:01 -04:00
parent d2e5fd0f9b
commit f35f530e4a

View file

@ -3,59 +3,62 @@
<fieldset v-if="months"> <fieldset v-if="months">
<legend class="sr-only">Date Picker</legend> <legend class="sr-only">Date Picker</legend>
<div <div
v-for="month in months" class="test-div"
:key="`${month.monthLabel}-${month.yearNum.toString()}-${months.length}`"> :class="[isActive ? '' : 'active']"
<div v-for="month in months"
class="calendar-grid-container" :key="`${month.monthLabel}-${month.yearNum.toString()}-${months.length}`">
:class="[isInitialView === true ? 'initial-view' : '', month.monthClass]"> <div
<div class="month-year body-small d-flex align-items-center ps-3 small"> class="calendar-grid-container"
{{ month.monthLabel }} {{ month.yearNum.toString() }} :class="[isInitialView === true ? 'initial-view' : '', month.monthClass, isAddingNewMonth === true ? 'is-adding-new-month' : '']">
</div> <div class="month-year body-small d-flex align-items-center ps-3 small">
<div {{ month.monthLabel }} {{ month.yearNum.toString() }}
v-if="calendarViewDirection === 'future'"
class="legend caption d-flex align-items-center justify-content-end">
<span class="legend-circle me-1"></span> &equals; Available
</div>
<div class="nav-back ps-3"><button></button></div>
<div class="nav-forward pe-3"><button></button></div>
<!-- Do the days of the weeek need to be read? -->
<div class="grid-item caption"><span class="sr-only">Sunday</span>S</div>
<div class="grid-item caption"><span class="sr-only">Monday</span>M</div>
<div class="grid-item caption"><span class="sr-only">Tuesday</span>T</div>
<div class="grid-item caption"><span class="sr-only">Wednesday</span>W</div>
<div class="grid-item caption"><span class="sr-only">Thursday</span>T</div>
<div class="grid-item caption"><span class="sr-only">Friday</span>F</div>
<div class="grid-item caption"><span class="sr-only">Saturday</span>S</div>
<div
v-for="date in month.dates"
:key="`${month.monthLabel}-${date.dateNum.toString()}-${months.length}`"
class="grid-item radio-wrapper"
:class="[
date.dateNum === 1 ? 'first-day-' + month.startDateDayIndex : '',
date.dayClasses,
]">
<input
:disabled="!isSelectableDate(date.inputValue)"
type="radio"
name="day-of-month"
v-model="selectedDate"
:value="date.inputValue"
:id="`${month.monthLabel}-${date.dateNum.toString()}`" />
<label :for="`${month.monthLabel}-${date.dateNum.toString()}`">
<span>{{ date.dateNum.toString() }}</span>
</label>
</div>
</div>
</div> </div>
</fieldset> <div
<button
v-if="calendarViewDirection === 'future'" v-if="calendarViewDirection === 'future'"
type="button" class="legend caption d-flex align-items-center justify-content-end">
class="btn-link" <span class="legend-circle me-1"></span> &equals; Available
@click="goForward"> </div>
View more dates <div class="nav-back ps-3"><button></button></div>
</button> <div class="nav-forward pe-3"><button></button></div>
<!-- Do the days of the weeek need to be read? -->
<div class="grid-item caption"><span class="sr-only">Sunday</span>S</div>
<div class="grid-item caption"><span class="sr-only">Monday</span>M</div>
<div class="grid-item caption"><span class="sr-only">Tuesday</span>T</div>
<div class="grid-item caption"><span class="sr-only">Wednesday</span>W</div>
<div class="grid-item caption"><span class="sr-only">Thursday</span>T</div>
<div class="grid-item caption"><span class="sr-only">Friday</span>F</div>
<div class="grid-item caption"><span class="sr-only">Saturday</span>S</div>
<div
v-for="date in month.dates"
:key="`${month.monthLabel}-${date.dateNum.toString()}-${months.length}`"
class="grid-item radio-wrapper"
:class="[
date.dateNum === 1 ? 'first-day-' + month.startDateDayIndex : '',
date.dayClasses,
]">
<input
:disabled="!isSelectableDate(date.inputValue)"
type="radio"
name="day-of-month"
v-model="selectedDate"
:value="date.inputValue"
:id="`${month.monthLabel}-${date.dateNum.toString()}`" />
<label :for="`${month.monthLabel}-${date.dateNum.toString()}`">
<span>{{ date.dateNum.toString() }}</span>
</label>
</div> </div>
</div>
</div>
</fieldset>
</div>
<button
v-if="calendarViewDirection === 'future'"
type="button"
class="btn-link"
@click="goForward(); toggleClass()">
View more dates
</button>
</template> </template>
<script> <script>
@ -71,6 +74,7 @@ export default {
name: "datePicker", name: "datePicker",
data() { data() {
return { return {
isActive: false,
calendarData: [], calendarData: [],
monthsBeforeToLoadOffset: 0, monthsBeforeToLoadOffset: 0,
monthsAfterToLoadOffset: 1, monthsAfterToLoadOffset: 1,
@ -78,6 +82,7 @@ export default {
isInitialView: true, isInitialView: true,
initialViewRowsToShow: 5, initialViewRowsToShow: 5,
initialViewRowsTally: 0, initialViewRowsTally: 0,
isAddingNewMonth: false,
}; };
}, },
props: { props: {
@ -106,8 +111,8 @@ export default {
computed: { computed: {
todayDate() { todayDate() {
return this.todayOverrideDateString return this.todayOverrideDateString
? new Date(this.todayOverrideDateString) ? new Date(this.todayOverrideDateString)
: new Date(); : new Date();
}, },
months() { months() {
if (this.calendarData?.length < 1) { if (this.calendarData?.length < 1) {
@ -130,6 +135,9 @@ export default {
}, },
}, },
methods: { methods: {
toggleClass: function (event) {
this.isActive = !this.isActive;
},
goForward() { goForward() {
if (this.isInitialView) { if (this.isInitialView) {
this.isInitialView = false; // 1st click removes hidden styling this.isInitialView = false; // 1st click removes hidden styling
@ -218,23 +226,23 @@ export default {
const todayDayIndex = this.todayDate.getDay(); // get day of week index of today (0-6) const todayDayIndex = this.todayDate.getDay(); // get day of week index of today (0-6)
currentWeekStartDateNum = currentWeekStartDateNum =
todayDayIndex > todayDateNum ? 1 : todayDateNum - todayDayIndex; // FUTURE todayDayIndex > todayDateNum ? 1 : todayDateNum - todayDayIndex; // FUTURE
const monthEndDate = new Date(yearNum, monthIndex + 1, 0); // BOTH const monthEndDate = new Date(yearNum, monthIndex + 1, 0); // BOTH
let monthEndDateNum = monthEndDate.getDate(); // BOTH let monthEndDateNum = monthEndDate.getDate(); // BOTH
currentWeekEndDateNum = todayDateNum + 6 - todayDayIndex; // PAST, aka Sat. (ok if it's larger than the month end?) currentWeekEndDateNum = todayDateNum + 6 - todayDayIndex; // PAST, aka Sat. (ok if it's larger than the month end?)
if (offset === 0 && direction === "past" && monthEndDateNum > currentWeekEndDateNum) if (offset === 0 && direction === "past" && monthEndDateNum > currentWeekEndDateNum)
monthEndDateNum = currentWeekEndDateNum; // PAST monthEndDateNum = currentWeekEndDateNum; // PAST
const monthStartDateNum = const monthStartDateNum =
offset === 0 && direction === "future" ? currentWeekStartDateNum : 1; // FUTURE offset === 0 && direction === "future" ? currentWeekStartDateNum : 1; // FUTURE
const monthStartDate = new Date(yearNum, monthIndex, monthStartDateNum); // BOTH const monthStartDate = new Date(yearNum, monthIndex, monthStartDateNum); // BOTH
const startDateDayIndex = monthStartDate.getDay(); // FUTURE const startDateDayIndex = monthStartDate.getDay(); // FUTURE
const endDateDayIndex = monthEndDate.getDay(); // PAST const endDateDayIndex = monthEndDate.getDay(); // PAST
if (typeof offset !== "undefined") { if (typeof offset !== "undefined") {
if (offset === 0 && direction === "future") if (offset === 0 && direction === "future")
firstMonthDayTally = monthStartDateNum - startDateDayIndex; // FUTURE (starts low, counts up) firstMonthDayTally = monthStartDateNum - startDateDayIndex; // FUTURE (starts low, counts up)
if (offset === 0 && direction === "past") if (offset === 0 && direction === "past")
firstMonthDayTally = currentWeekEndDateNum; // PAST (starts high, counts down) firstMonthDayTally = currentWeekEndDateNum; // PAST (starts high, counts down)
while ( while (
direction === "future" && direction === "future" &&
@ -271,6 +279,9 @@ export default {
this.initialViewRowsTally++; this.initialViewRowsTally++;
} }
} }
} else {
debugger; // eslint-disable-line no-debugger
monthClass = monthClass + ' new-month';
} }
if (this.selectableDates === "custom") { if (this.selectableDates === "custom") {
@ -335,6 +346,10 @@ export default {
return monthToAdd; return monthToAdd;
}, },
addMonthData() { addMonthData() {
//this.isAddingNewMonth = true;
const oldMonth = document.querySelector(".new-month");
oldMonth?.classList.remove("new-month");
debugger; // eslint-disable-line no-debugger
const monthToAdd = this.getMonthData(); const monthToAdd = this.getMonthData();
if (this.calendarViewDirection === "future") { if (this.calendarViewDirection === "future") {
this.calendarData.push(monthToAdd); this.calendarData.push(monthToAdd);
@ -342,6 +357,15 @@ export default {
if (this.calendarViewDirection === "past") { if (this.calendarViewDirection === "past") {
this.calendarData.unshift(monthToAdd); this.calendarData.unshift(monthToAdd);
} }
let newMonth;
this.$nextTick(() => {
debugger; // eslint-disable-line no-debugger
newMonth = document.querySelector(".new-month");
newMonth.classList.add("is-adding-new-month");
});
setTimeout(() => {
newMonth.classList.remove("is-adding-new-month");
}, .025);
}, },
isSelectableDate(thisDate) { isSelectableDate(thisDate) {
const testForDate = (dateInArray) => { const testForDate = (dateInArray) => {
@ -352,16 +376,16 @@ export default {
); );
}; };
const isSelectable = const isSelectable =
this.selectableDatesData.findIndex(testForDate) > -1 ? true : false; this.selectableDatesData.findIndex(testForDate) > -1 ? true : false;
return isSelectable; return isSelectable;
}, },
updateSelectableDates(monthStart, monthEnd) { updateSelectableDates(monthStart, monthEnd) {
this.customSelectableDatesCallback(monthStart, monthEnd)?.forEach((newObj) => { this.customSelectableDatesCallback(monthStart, monthEnd)?.forEach((newObj) => {
const index = this.selectableDatesData.findIndex( const index = this.selectableDatesData.findIndex(
(obj) => (obj) =>
obj.year === newObj.year && obj.year === newObj.year &&
obj.month === newObj.month && obj.month === newObj.month &&
obj.date === newObj.date obj.date === newObj.date
); );
if (index === -1) this.selectableDatesData.push(newObj); if (index === -1) this.selectableDatesData.push(newObj);
}); });
@ -371,92 +395,110 @@ export default {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.calendar-grid-container { .date-picker {
margin: 0 auto; .calendar-grid-container {
max-width: 414px; margin: 0 auto;
display: grid; max-width: 414px;
grid-template-columns: repeat(7, 1fr); max-height: 500px;
justify-content: center; display: grid;
align-items: center; grid-template-columns: repeat(7, 1fr);
padding: 4px;
.grid-item {
text-align: center;
margin: 10%;
&.first-day-,
&.first-day-0 {
grid-column-start: 1;
}
&.first-day-1 {
grid-column-start: 2;
}
&.first-day-2 {
grid-column-start: 3;
}
&.first-day-3 {
grid-column-start: 4;
}
&.first-day-4 {
grid-column-start: 5;
}
&.first-day-5 {
grid-column-start: 6;
}
&.first-day-6 {
grid-column-start: 7;
}
}
.month-year {
grid-area: 1 / 1 / 2 / 5;
text-transform: uppercase;
font-weight: 300;
letter-spacing: 0.75px;
}
.legend {
grid-area: 1 / 5 / 2 / 8;
.legend-circle {
border-radius: 50%;
width: 16px;
height: 16px;
background-color: $blue-100;
border: 1px solid $blue;
}
}
.nav-back,
.nav-forward {
display: none;
}
.radio-wrapper {
position: relative;
display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
outline: none; padding: 4px;
input[type="radio"] { .grid-item {
position: absolute; //override bootstrap text-align: center;
height: 0; margin: 10%;
opacity: 0;
&:focus-visible + label { &.first-day-,
box-shadow: 0 0 0 2.5px $blue; &.first-day-0 {
grid-column-start: 1;
} }
&.first-day-1 {
grid-column-start: 2;
}
&.first-day-2 {
grid-column-start: 3;
}
&.first-day-3 {
grid-column-start: 4;
}
&.first-day-4 {
grid-column-start: 5;
}
&.first-day-5 {
grid-column-start: 6;
}
&.first-day-6 {
grid-column-start: 7;
}
}
&:focus + label, .month-year {
&:checked:focus + label { grid-area: 1 / 1 / 2 / 5;
box-shadow: 0 0 0 3px #fff, 0 0 0 5.5px #1574a1; text-transform: uppercase;
background-color: $blue; font-weight: 300;
color: $white; letter-spacing: 0.75px;
&.past-day { }
box-shadow: none; .legend {
background-color: transparent; grid-area: 1 / 5 / 2 / 8;
color: $gray-500; .legend-circle {
font-weight: normal; border-radius: 50%;
width: 16px;
height: 16px;
background-color: $blue-100;
border: 1px solid $blue;
}
}
.nav-back,
.nav-forward {
display: none;
}
.radio-wrapper {
position: relative;
display: flex;
justify-content: center;
align-items: center;
outline: none;
height: 2rem;
opacity: 1;
transition: ease all 250ms;
input[type="radio"] {
position: absolute; //override bootstrap
height: 0;
opacity: 0;
&:focus-visible + label {
box-shadow: 0 0 0 2.5px $blue;
} }
&.current-day {
&:focus + label,
&:checked:focus + label {
box-shadow: 0 0 0 3px #fff, 0 0 0 5.5px #1574a1;
background-color: $blue;
color: $white;
&.past-day {
box-shadow: none;
background-color: transparent;
color: $gray-500;
font-weight: normal;
}
&.current-day {
&:after {
background-color: $white;
}
.first-day {
color: $white;
}
}
}
&:checked + label {
color: $white;
background: $blue;
box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $blue;
&:after { &:after {
background-color: $white; background-color: $white;
} }
@ -466,169 +508,178 @@ export default {
} }
} }
&:checked + label { .first-day {
color: $white; position: absolute;
background: $blue; font-size: 9px;
box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $blue; font-weight: 500;
&:after { color: $gray-600;
background-color: $white; top: 0;
} z-index: 1;
.first-day {
color: $white;
}
}
}
.first-day {
position: absolute;
font-size: 9px;
font-weight: 500;
color: $gray-600;
top: 0;
z-index: 1;
}
label {
position: relative;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
width: 40px;
height: 40px;
min-width: 40px;
border-radius: 50%;
span {
&.small {
font-size: 0.75rem;
color: $gray-550;
}
} }
&:hover,
&:checked {
@include media-breakpoint-up(sm) {
box-shadow: 0 0 0 4px transparent;
background-color: $blue;
color: $white;
&:after {
background-color: $white;
}
}
cursor: pointer;
&.current-day {
+ .first-day {
color: $white;
}
}
.first-day {
color: $white;
}
}
+ p {
display: none;
}
}
&.selectable-day {
label { label {
color: $blue;
background-color: $blue-100;
border: 1px solid $blue;
}
}
&.past-day,
&.future-day,
&.unavailable-day {
label {
color: $gray-500;
background-color: transparent;
border: 1px solid transparent;
pointer-events: none;
}
}
&.current-day {
label {
&:after {
content: "";
width: 0.25rem;
height: 0.25rem;
border-radius: 50%;
background-color: $blue;
position: absolute;
top: 28px;
}
.first-day {
color: $blue;
}
}
}
}
&.initial-view {
&.month-hidden {
display: none;
}
.day-hidden {
display: none;
}
}
}
.past {
.calendar-grid-container {
.month-year {
grid-area: 1 / 1 / 2 / 6;
}
.nav-forward {
grid-area: 1 / 7 / 2 / 8;
display: flex;
justify-content: flex-end;
button {
&:after {
content: "";
position: absolute;
width: 7px;
height: 12px;
background-image: url("data:image/svg+xml,%3Csvg width='7' height='12' viewBox='0 0 7 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6.66831 5.99879C6.66955 6.17554 6.60074 6.34558 6.47695 6.47168L1.15501 11.8017C1.02812 11.9287 0.85603 12 0.676587 12C0.497145 12 0.325053 11.9287 0.198168 11.8017C0.0712831 11.6748 7.268e-09 11.5026 8.07183e-09 11.3231C8.87567e-09 11.1436 0.0712831 10.9714 0.198168 10.8445L5.05126 5.99879L0.198168 1.14736C0.0725521 1.02042 0.00248585 0.848755 0.00338306 0.670131C0.00428028 0.491506 0.0760674 0.320554 0.202952 0.194881C0.329837 0.0692091 0.501426 -0.000888818 0.679971 9.54485e-06C0.858515 0.000906955 1.02939 0.0727263 1.15501 0.199668L6.47312 5.52399C6.59843 5.65017 6.66862 5.82092 6.66831 5.99879Z' fill='%231574A1'/%3E%3C/svg%3E%0A");
}
}
}
.nav-back {
grid-area: 1 / 6 / 2 / 7;
display: flex;
justify-content: flex-end;
button {
&:after {
content: "";
position: absolute;
width: 7px;
height: 12px;
background-image: url("data:image/svg+xml,%3Csvg width='7' height='12' viewBox='0 0 7 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0.331685 6.00121C0.330445 5.82446 0.399256 5.65442 0.523053 5.52832L5.84499 0.198256C5.97188 0.0713149 6.14397 -8.63821e-08 6.32341 -6.72174e-08C6.50285 -4.80527e-08 6.67495 0.071315 6.80183 0.198256C6.92872 0.325198 7 0.497368 7 0.67689C7 0.856412 6.92872 1.02858 6.80183 1.15552L1.94874 6.00121L6.80183 10.8526C6.92745 10.9796 6.99751 11.1512 6.99662 11.3299C6.99572 11.5085 6.92393 11.6794 6.79705 11.8051C6.67016 11.9308 6.49857 12.0009 6.32003 12C6.14148 11.9991 5.97061 11.9273 5.84499 11.8003L0.526881 6.47601C0.401568 6.34983 0.331375 6.17908 0.331685 6.00121Z' fill='%231574A1'/%3E%3C/svg%3E%0A");
}
}
}
.nav-back,
.nav-forward {
button {
position: relative; position: relative;
border-radius: 50%; cursor: pointer;
width: 28px;
height: 28px;
background-color: $blue-100;
border: 1px solid $blue;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
&:focus, width: 40px;
&:focus-visible { height: 40px;
border: 2.5px solid $blue; min-width: 40px;
box-shadow: none; border-radius: 50%;
span {
&.small {
font-size: 0.75rem;
color: $gray-550;
}
}
&:hover,
&:checked {
@include media-breakpoint-up(sm) {
box-shadow: 0 0 0 4px transparent;
background-color: $blue;
color: $white;
&:after {
background-color: $white;
}
}
cursor: pointer;
&.current-day {
+ .first-day {
color: $white;
}
}
.first-day {
color: $white;
}
}
+ p {
display: none;
}
}
&.selectable-day {
label {
color: $blue;
background-color: $blue-100; background-color: $blue-100;
color: $white; border: 1px solid $blue;
}
}
&.past-day,
&.future-day,
&.unavailable-day {
label {
color: $gray-500;
background-color: transparent;
border: 1px solid transparent;
pointer-events: none;
}
}
&.current-day {
label {
&:after {
content: "";
width: 0.25rem;
height: 0.25rem;
border-radius: 50%;
background-color: $blue;
position: absolute;
top: 28px;
}
.first-day {
color: $blue;
}
}
}
}
&.initial-view {
&.month-hidden {
display: none;
}
.day-hidden {
opacity: 0;
height: 0;
overflow: hidden;
display: flex;
margin: 0;
}
}
&.is-adding-new-month {
&.new-month {
max-height: 0;
overflow: hidden;
transition: all ease 2s;
}
}
}
.past {
.calendar-grid-container {
.month-year {
grid-area: 1 / 1 / 2 / 6;
}
.nav-forward {
grid-area: 1 / 7 / 2 / 8;
display: flex;
justify-content: flex-end;
button {
&:after {
content: "";
position: absolute;
width: 7px;
height: 12px;
background-image: url("data:image/svg+xml,%3Csvg width='7' height='12' viewBox='0 0 7 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6.66831 5.99879C6.66955 6.17554 6.60074 6.34558 6.47695 6.47168L1.15501 11.8017C1.02812 11.9287 0.85603 12 0.676587 12C0.497145 12 0.325053 11.9287 0.198168 11.8017C0.0712831 11.6748 7.268e-09 11.5026 8.07183e-09 11.3231C8.87567e-09 11.1436 0.0712831 10.9714 0.198168 10.8445L5.05126 5.99879L0.198168 1.14736C0.0725521 1.02042 0.00248585 0.848755 0.00338306 0.670131C0.00428028 0.491506 0.0760674 0.320554 0.202952 0.194881C0.329837 0.0692091 0.501426 -0.000888818 0.679971 9.54485e-06C0.858515 0.000906955 1.02939 0.0727263 1.15501 0.199668L6.47312 5.52399C6.59843 5.65017 6.66862 5.82092 6.66831 5.99879Z' fill='%231574A1'/%3E%3C/svg%3E%0A");
}
}
}
.nav-back {
grid-area: 1 / 6 / 2 / 7;
display: flex;
justify-content: flex-end;
button {
&:after {
content: "";
position: absolute;
width: 7px;
height: 12px;
background-image: url("data:image/svg+xml,%3Csvg width='7' height='12' viewBox='0 0 7 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0.331685 6.00121C0.330445 5.82446 0.399256 5.65442 0.523053 5.52832L5.84499 0.198256C5.97188 0.0713149 6.14397 -8.63821e-08 6.32341 -6.72174e-08C6.50285 -4.80527e-08 6.67495 0.071315 6.80183 0.198256C6.92872 0.325198 7 0.497368 7 0.67689C7 0.856412 6.92872 1.02858 6.80183 1.15552L1.94874 6.00121L6.80183 10.8526C6.92745 10.9796 6.99751 11.1512 6.99662 11.3299C6.99572 11.5085 6.92393 11.6794 6.79705 11.8051C6.67016 11.9308 6.49857 12.0009 6.32003 12C6.14148 11.9991 5.97061 11.9273 5.84499 11.8003L0.526881 6.47601C0.401568 6.34983 0.331375 6.17908 0.331685 6.00121Z' fill='%231574A1'/%3E%3C/svg%3E%0A");
}
}
}
.nav-back,
.nav-forward {
button {
position: relative;
border-radius: 50%;
width: 28px;
height: 28px;
background-color: $blue-100;
border: 1px solid $blue;
display: flex;
justify-content: center;
align-items: center;
&:focus,
&:focus-visible {
border: 2.5px solid $blue;
box-shadow: none;
background-color: $blue-100;
color: $white;
}
} }
} }
} }
} }
.btn-link {
background: transparent;
border: none;
margin-top: 2rem;
}
.new-month {
max-height: 500px;
overflow: hidden;
transition: all ease 10s;
}
} }
</style> </style>