CSR-886: first version of date-picker along with demo page
This commit is contained in:
parent
603068f98d
commit
081bbfd161
6 changed files with 494 additions and 241 deletions
|
|
@ -15,7 +15,7 @@ module.exports = {
|
|||
"!src/layouts/vehicle-damage/windshield-options/windshield-options.vue",
|
||||
"!src/layouts/reveal/**/*.vue",
|
||||
"!src/ux-components/text-link/**/*.vue",
|
||||
"!src/common-components/date-picker/**/*.vue",
|
||||
"!src/common-components/date-picker/**/*.vue", // Temp until unit tests completed
|
||||
"!src/layouts/vin-lookup/**/*.vue", //Temporary for Quote page testing
|
||||
"!src/common-components/funnel-header/menu-modal/**/*.vue",
|
||||
// END
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
test.todo("some test to be written in the future");
|
||||
|
|
@ -86,12 +86,12 @@ describe("date-picker.vue", () => {
|
|||
|
||||
wrapper.unmount();
|
||||
});
|
||||
describe("for each month in calendarData, each should have keys monthLabel, yearNum, dates, startDayIndex", () => {
|
||||
describe("each month should have keys monthLabel, yearNum, dates, firstDateDayIndex", () => {
|
||||
const testScenarios = [
|
||||
["monthLabel", true, "string"],
|
||||
["yearNum", true, "number"],
|
||||
["dates", true, "object"],
|
||||
["startDayIndex", true, "number"],
|
||||
["firstDateDayIndex", true, "number"],
|
||||
["monthClass", true, "string"],
|
||||
];
|
||||
|
||||
|
|
@ -106,7 +106,7 @@ describe("date-picker.vue", () => {
|
|||
|
||||
// Act
|
||||
const calendarData = await wrapper.vm.calendarData;
|
||||
console.log("calendarData: ", calendarData)
|
||||
// console.log("calendarData: ", calendarData);
|
||||
|
||||
// Assert
|
||||
expect(calendarData.every(containsMonthLabel)).toBe(true);
|
||||
|
|
@ -118,7 +118,64 @@ describe("date-picker.vue", () => {
|
|||
);
|
||||
});
|
||||
|
||||
test("for each month in calendarData, the dates array should have the 1st of the month", async () => {
|
||||
describe("the current month's week which includes today", () => {
|
||||
const testScenarios = [
|
||||
["2022-02-05T03:00:00", 1, 2, true],
|
||||
["2024-02-05T03:00:00", 4, 0, true],
|
||||
["2020-01-15T03:00:00", 12, 0, true],
|
||||
];
|
||||
|
||||
test.each(testScenarios)(
|
||||
"the first date of the week including the date %s should be the date number %s and be day index %s",
|
||||
async (todayDateString, startDate, dayOfWeekIndex) => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({
|
||||
propsData: {
|
||||
todayOverrideDateString: todayDateString,
|
||||
},
|
||||
});
|
||||
|
||||
// Act
|
||||
const calendarData = await wrapper.vm.calendarData;
|
||||
const currentMonthIndex = calendarData.findIndex((month) => {
|
||||
console.log("month: ", month);
|
||||
return month.monthClass === "currentMonth";
|
||||
});
|
||||
console.log("calendarData ", calendarData);
|
||||
console.log("currentMonthIndex ", currentMonthIndex);
|
||||
|
||||
// Assert
|
||||
expect(calendarData[currentMonthIndex].dates[0].dateNum === startDate).toBe(
|
||||
true
|
||||
);
|
||||
expect(
|
||||
calendarData[currentMonthIndex].dates[0].firstDateDayIndex ===
|
||||
dayOfWeekIndex
|
||||
).toBe(true);
|
||||
|
||||
wrapper.unmount();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test("the current month's dates array should start with the first day of the week which includes today", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
const hasAFirstInDates = (obj) => obj?.dates[0].dateNum === 1;
|
||||
|
||||
// Act
|
||||
const calendarData = await wrapper.vm.calendarData;
|
||||
const currentMonthIndex = calendarData.findIndex((month) => {
|
||||
return month.monthClass === "currentMonth";
|
||||
});
|
||||
|
||||
// Assert
|
||||
expect(calendarData[currentMonthIndex].dates[0].dateNum === 1).toBe(true);
|
||||
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test("for each month (other than current month), the dates array should have the 1st of the month", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
const hasAFirstInDates = (obj) => obj?.dates[0].dateNum === 1;
|
||||
|
|
@ -132,7 +189,7 @@ describe("date-picker.vue", () => {
|
|||
wrapper.unmount();
|
||||
});
|
||||
|
||||
describe("for each month in calendarData, the dates array should have correct days of month", () => {
|
||||
describe("for the current month in calendarData, the dates array should have correct days of month", () => {
|
||||
const testScenarios = [
|
||||
["2022-02-05T03:00:00", 28, true],
|
||||
["2024-02-05T03:00:00", 29, true],
|
||||
|
|
|
|||
|
|
@ -1,21 +1,17 @@
|
|||
<template>
|
||||
<div id="app">
|
||||
formattedTodayDate is {{ formatDate(todayDateObj) }}
|
||||
<br />
|
||||
<span v-if="calendarData.length > 0"
|
||||
>calendarData[0].monthLabel is {{ calendarData[0].monthLabel }}</span
|
||||
>
|
||||
<br />
|
||||
<fieldset v-if="calendarData">
|
||||
<div class="date-picker" :class="calendarViewDirection">
|
||||
<fieldset v-if="months">
|
||||
<legend class="sr-only">Date Picker</legend>
|
||||
<span
|
||||
v-for="month in calendarData"
|
||||
:key="`${month.monthLabel}-${month.yearNum.toString()}`">
|
||||
<div class="calendar-grid-container">
|
||||
v-for="month in months"
|
||||
:key="`${month.monthLabel}-${month.yearNum.toString()}-${months.length}`">
|
||||
<div
|
||||
class="calendar-grid-container"
|
||||
:class="[isInitialView === true ? 'initial-view' : '', month.monthClass]">
|
||||
<div class="month-year body-small d-flex align-items-center ps-3 small">
|
||||
{{ month.monthLabel }} {{ month.yearNum.toString() }}
|
||||
</div>
|
||||
<div class="legend caption d-flex align-items-center justify-content-end">
|
||||
<div v-if="calendarViewDirection === 'future'" class="legend caption d-flex align-items-center justify-content-end">
|
||||
<span class="legend-circle me-1"></span> = Available
|
||||
</div>
|
||||
<div class="nav-back ps-3"><button></button></div>
|
||||
|
|
@ -29,185 +25,326 @@
|
|||
<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
|
||||
class="grid-item radio-wrapper"
|
||||
:class="[date.dateNum === 1 ? 'first-day-' + month.startDayIndex : '']"
|
||||
v-for="date in month.dates"
|
||||
:key="`${month.monthLabel}-${date.dateNum.toString()}`">
|
||||
<!-- TODO: maybe make date.dateNum a string in the data. Any reason not to? -->
|
||||
: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
|
||||
:disabled="!isSelectableDate(date.inputValue)"
|
||||
type="radio"
|
||||
name="day-of-month"
|
||||
id="`${month.monthLabel}-${date.dateNum.toString()}`" />
|
||||
<label for="`${month.monthLabel}-${date.dateNum.toString()}`"
|
||||
><span>{{ date.dateNum.toString() }}</span></label
|
||||
>
|
||||
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>
|
||||
</span>
|
||||
</fieldset>
|
||||
<button v-if="calendarViewDirection === 'future'" type="button" class="btn-link" @click="goForward">View more dates</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// Supporting files
|
||||
import { monthsOfYear } from "@/constants/scheduling.js";
|
||||
|
||||
const SelectableDaysOptions = Object.freeze({
|
||||
CUSTOM: "custom",
|
||||
PAST: "past",
|
||||
});
|
||||
|
||||
export default {
|
||||
name: "datePicker",
|
||||
data() {
|
||||
return {
|
||||
currentMonth: "February",
|
||||
currentYear: "2023",
|
||||
isFirstDayOfMonth: true,
|
||||
isCurrentDay: true,
|
||||
// calendarData: [],
|
||||
monthsBeforeToLoadOffset: -1,
|
||||
calendarData: [],
|
||||
monthsBeforeToLoadOffset: 0,
|
||||
monthsAfterToLoadOffset: 1,
|
||||
selectableDatesData: [], // NOTE: uses monthNum (1-based), NOT monthIndex (0-based)
|
||||
isInitialView: true,
|
||||
initialViewRowsToShow: 5,
|
||||
initialViewRowsTally: 0,
|
||||
};
|
||||
},
|
||||
props: {
|
||||
todayOverrideDateString: { // only used for unit tests to override today's date
|
||||
selectableDates: {
|
||||
type: String,
|
||||
validator(value) {
|
||||
return Object.values(SelectableDaysOptions).includes(value);
|
||||
},
|
||||
default: SelectableDaysOptions.PAST,
|
||||
},
|
||||
modelValue: {
|
||||
type: Object,
|
||||
},
|
||||
todayOverrideDateString: { // only used for unit tests to override today's date
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
customSelectableDatesCallback: {
|
||||
type: Function,
|
||||
default() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
todayDateObj() {
|
||||
return this.todayOverrideDateString ? new Date(this.todayOverrideDateString) : new Date();
|
||||
todayDate() {
|
||||
return this.todayOverrideDateString
|
||||
? new Date(this.todayOverrideDateString)
|
||||
: new Date();
|
||||
},
|
||||
calendarData() {
|
||||
return this.setCalendarData();
|
||||
},
|
||||
// initialFirstDate() {
|
||||
// return startOfWeek(this.todayDateObj);
|
||||
// },
|
||||
},
|
||||
// mounted() {
|
||||
// this.setCalendarData();
|
||||
// },
|
||||
methods: {
|
||||
formatDate(date) {
|
||||
// return format(date, "MM/dd/yyyy");
|
||||
// console.log("date: ", date)
|
||||
const dateArr = date.toDateString().split(" ");
|
||||
// console.log("dateArr: ", dateArr)
|
||||
const dayIndex = date.getDay();
|
||||
// console.log("dayIndex: ", dayIndex)
|
||||
const dateIndex = date.getDate();
|
||||
// console.log("dateIndex: ", dateIndex)
|
||||
const yearIndex = date.getFullYear();
|
||||
// console.log("yearIndex: ", yearIndex)
|
||||
const monthIndex = date.getMonth();
|
||||
// console.log("monthIndex: ", monthIndex)
|
||||
const monthIndexStr = this.formatNumberToSetDigits(monthIndex + 1, 2);
|
||||
const dateIndexStr = this.formatNumberToSetDigits(dateIndex, 2);
|
||||
|
||||
return monthIndexStr + "/" + dateIndexStr + "/" + yearIndex.toString();
|
||||
},
|
||||
formatNumberToSetDigits(num, digits) {
|
||||
let numStr = num.toString();
|
||||
// const strLength = numStr.length;
|
||||
while (numStr.length < digits) {
|
||||
numStr = "0" + numStr;
|
||||
months() {
|
||||
if (this.calendarData?.length < 1) {
|
||||
this.setCalendarData();
|
||||
}
|
||||
return this.calendarData;
|
||||
},
|
||||
calendarViewDirection() {
|
||||
if (this.selectableDates === "past") return "past";
|
||||
if (this.selectableDates === "custom") return "future";
|
||||
return "none";
|
||||
},
|
||||
selectedDate: {
|
||||
get() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set(newSelectedDate) {
|
||||
this.$emit("update:modelValue", newSelectedDate);
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
goForward() {
|
||||
if (this.isInitialView) {
|
||||
this.isInitialView = false; // 1st click removes hidden styling
|
||||
} else {
|
||||
this.addMonthData(); // 2nd click adds 1 month data
|
||||
}
|
||||
return numStr;
|
||||
},
|
||||
setCalendarData() {
|
||||
const monthsOfYear = [ // TODO: MOVE THIS TO A CONSTANT
|
||||
"January", // 0 // -12 // 12
|
||||
"February", // 1 // -11 // 13
|
||||
"March", // 2 // -10 // 14
|
||||
"April", // 3 // -9 // 15
|
||||
"May", // 4 // -8 // 16
|
||||
"June", // 5 // -7 // 17
|
||||
"July", // 6 // -6 // 18
|
||||
"August", // 7 // -5 // 19
|
||||
"September", // 8 // -4 // 20
|
||||
"October", // 9 // -3 // 21
|
||||
"November", // 10 // -2 // 22
|
||||
"December", // 11 // -1 // 23
|
||||
];
|
||||
const todayDateObj = this.todayDateObj;
|
||||
const todayMonth = todayDateObj.getMonth(); // returns zero index of months
|
||||
const todayYear = todayDateObj.getFullYear(); // returns # of year
|
||||
// GENERATE MONTHS AND PUSH THEM INTO ARRAY
|
||||
const months = [];
|
||||
if (this.calendarViewDirection === "future") {
|
||||
// first 0, then 1
|
||||
for (let i = 0; i <= this.monthsAfterToLoadOffset; i++) {
|
||||
months.push(this.getMonthData(i));
|
||||
}
|
||||
} else if (this.calendarViewDirection === "past") {
|
||||
// first 0, then -1
|
||||
for (let i = 0; i >= (0 - this.monthsAfterToLoadOffset); i--) {
|
||||
months.unshift(this.getMonthData(i));
|
||||
}
|
||||
} else {
|
||||
// TK - IF A CALENDAR WITH BOTH PAST AND FUTURE WAS EVER NEEDED
|
||||
// for (let i = this.monthsAfterToLoadOffset; i >= this.monthsBeforeToLoadOffset; i--) {
|
||||
// months.push(this.getMonthDataPAST(i));
|
||||
// }
|
||||
}
|
||||
this.calendarData = months;
|
||||
},
|
||||
getMonthData(offset) {
|
||||
const direction = this.calendarViewDirection; // "past" or "future"
|
||||
let yearNum;
|
||||
let monthIndex;
|
||||
let monthClass = "";
|
||||
let initialViewEndDate; // only used for setCalendarData FUTURE
|
||||
let initialViewStartDate; // only used for setCalendarData PAST
|
||||
let currentWeekEndDateNum; // only used for setCalendarData PAST
|
||||
let currentWeekStartDateNum; // only used for setCalendarData FUTURE
|
||||
let firstMonthDayTally; // only used for setCalendarData on 1st month generated
|
||||
const datesArray = [];
|
||||
const todayDateNum = this.todayDate.getDate();
|
||||
|
||||
// const todayDate = todayDateObj.getDate(); // returns # of date
|
||||
// const todayMonthFirstDate = new Date(todayYear, todayMonth, 1); // returns date object
|
||||
// const todayMonthLastDate = new Date(todayYear, todayMonth + 1, 0); // returns date object
|
||||
// const todayMonthLastDateNum = todayMonthLastDate.getDate(); // returns # of date
|
||||
// const todayStartDayIndex = todayMonthFirstDate.getDay(); // returns zero index of weekdays
|
||||
if (typeof offset !== 'undefined') { // offset only passed during initial setup with setCalendarData
|
||||
yearNum = this.todayDate.getFullYear();
|
||||
monthIndex = this.todayDate.getMonth() + offset;
|
||||
|
||||
function getMonthData(offset) {
|
||||
let monthClass = "";
|
||||
let yearNum = todayYear;
|
||||
let adjustedMonthIndex = todayMonth + offset;
|
||||
|
||||
// console.log("111... todayMonth: ", todayMonth, " / yearNum: ", yearNum, " / offset: ", offset);
|
||||
|
||||
if (offset === 0) {
|
||||
monthClass = "currentMonth";
|
||||
} else if (offset > 0) {
|
||||
monthClass = "futureMonth";
|
||||
while (adjustedMonthIndex > 11) {
|
||||
adjustedMonthIndex = adjustedMonthIndex - 12;
|
||||
if (direction === "future" && offset > 0) {
|
||||
while (monthIndex > 11) {
|
||||
monthIndex = monthIndex - 12;
|
||||
yearNum++;
|
||||
}
|
||||
} else {
|
||||
monthClass = "pastMonth";
|
||||
while (adjustedMonthIndex < 0) {
|
||||
adjustedMonthIndex = 12 + adjustedMonthIndex;
|
||||
} else if (direction === "past" && offset < 0) {
|
||||
while (monthIndex < 0) {
|
||||
monthIndex = 12 + monthIndex;
|
||||
yearNum--;
|
||||
}
|
||||
}
|
||||
|
||||
// console.log("adjustedMonthIndex: ", adjustedMonthIndex, " / yearNum: ", yearNum);
|
||||
} else { // used only when adding an additional month
|
||||
|
||||
const monthEndDate = new Date(yearNum, adjustedMonthIndex + 1, 0);
|
||||
const datesArray = [];
|
||||
for (let i = 1; i <= monthEndDate.getDate(); i++) {
|
||||
datesArray.push({ dateNum: i });
|
||||
if (direction === "future") {
|
||||
// get last day of current calendar data
|
||||
const lastMonthInCalendarData = this.calendarData[this.calendarData.length - 1];
|
||||
|
||||
if (lastMonthInCalendarData.monthIndex === 11) {
|
||||
monthIndex = 0;
|
||||
yearNum = lastMonthInCalendarData.yearNum + 1;
|
||||
} else {
|
||||
monthIndex = lastMonthInCalendarData.monthIndex + 1;
|
||||
yearNum = lastMonthInCalendarData.yearNum;
|
||||
}
|
||||
}
|
||||
if (direction === "past") {
|
||||
// get last day of current calendar data
|
||||
const firstMonthInCalendarData = this.calendarData[0];
|
||||
|
||||
if (firstMonthInCalendarData.monthIndex === 0) {
|
||||
monthIndex = 11;
|
||||
yearNum = firstMonthInCalendarData.yearNum - 1;
|
||||
} else {
|
||||
monthIndex = firstMonthInCalendarData.monthIndex - 1;
|
||||
yearNum = firstMonthInCalendarData.yearNum;
|
||||
}
|
||||
}
|
||||
const monthFirstDate = new Date(yearNum, adjustedMonthIndex, 1); // returns date object
|
||||
const startDayIndex = monthFirstDate.getDay();
|
||||
console.log("startDayIndex: ", startDayIndex)
|
||||
|
||||
const monthToAdd = {
|
||||
monthLabel: monthsOfYear[adjustedMonthIndex],
|
||||
yearNum: yearNum,
|
||||
dates: datesArray,
|
||||
startDayIndex: startDayIndex,
|
||||
monthClass: monthClass,
|
||||
};
|
||||
// console.log("monthToAdd: ", monthToAdd)
|
||||
return monthToAdd;
|
||||
}
|
||||
|
||||
const todayDayIndex = this.todayDate.getDay(); // get day of week index of today (0-6)
|
||||
currentWeekStartDateNum = todayDayIndex > todayDateNum ? 1 : todayDateNum - todayDayIndex; // FUTURE
|
||||
const monthEndDate = new Date(yearNum, monthIndex + 1, 0); // BOTH
|
||||
let monthEndDateNum = monthEndDate.getDate(); // BOTH
|
||||
currentWeekEndDateNum = todayDateNum + 6 - todayDayIndex; // PAST, aka Sat. (ok if it's larger than the month end?)
|
||||
if (offset === 0 && direction === "past" && monthEndDateNum > currentWeekEndDateNum) monthEndDateNum = currentWeekEndDateNum; // PAST
|
||||
const monthStartDateNum = (offset === 0 && direction === "future") ? currentWeekStartDateNum : 1; // FUTURE
|
||||
const monthStartDate = new Date(yearNum, monthIndex, monthStartDateNum); // BOTH
|
||||
const startDateDayIndex = monthStartDate.getDay(); // FUTURE
|
||||
const endDateDayIndex = monthEndDate.getDay(); // PAST
|
||||
|
||||
if (typeof offset !== 'undefined') {
|
||||
|
||||
if (offset === 0 && direction === "future") firstMonthDayTally = monthStartDateNum - startDateDayIndex; // FUTURE (starts low, counts up)
|
||||
if (offset === 0 && direction === "past") firstMonthDayTally = currentWeekEndDateNum; // PAST (starts high, counts down)
|
||||
|
||||
while (direction === "future" && offset === 0 && firstMonthDayTally < monthEndDateNum) { // FUTURE
|
||||
firstMonthDayTally = firstMonthDayTally + 7;
|
||||
this.initialViewRowsTally++;
|
||||
}
|
||||
|
||||
while (direction === "past" && offset === 0 && firstMonthDayTally > monthStartDateNum) { // PAST
|
||||
firstMonthDayTally = firstMonthDayTally - 7;
|
||||
this.initialViewRowsTally++;
|
||||
}
|
||||
|
||||
if (Math.abs(offset) === 1) {
|
||||
|
||||
if (this.initialViewRowsTally < this.initialViewRowsToShow) {
|
||||
initialViewEndDate = 6 - startDateDayIndex + monthStartDateNum; // FUTURE
|
||||
initialViewStartDate = monthEndDateNum - endDateDayIndex; // PAST
|
||||
this.initialViewRowsTally++;
|
||||
} else {
|
||||
monthClass = monthClass + " month-hidden";
|
||||
}
|
||||
|
||||
while (this.initialViewRowsTally < this.initialViewRowsToShow) {
|
||||
initialViewEndDate = initialViewEndDate + 7;
|
||||
initialViewStartDate = initialViewStartDate - 7;
|
||||
this.initialViewRowsTally++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// this.monthsBeforeToLoadOffset, this.monthsAfterToLoadOffset (DATA VARS)
|
||||
const months = [];
|
||||
for (let i = this.monthsBeforeToLoadOffset; i <= this.monthsAfterToLoadOffset; i++) {
|
||||
months.push(getMonthData(i));
|
||||
if (this.selectableDates === "custom") {
|
||||
const monthStart = {
|
||||
year: yearNum,
|
||||
month: monthIndex + 1,
|
||||
date: (offset === 0) ? todayDateNum : monthStartDateNum,
|
||||
};
|
||||
const monthEnd = {
|
||||
year: monthEndDate.getFullYear(),
|
||||
month: monthEndDate.getMonth() + 1,
|
||||
date: monthEndDateNum,
|
||||
};
|
||||
// get available dates
|
||||
this.updateSelectableDates(monthStart, monthEnd);
|
||||
}
|
||||
return months;
|
||||
|
||||
// populate datesArray
|
||||
for (let i = monthStartDateNum; i <= monthEndDateNum; i++) {
|
||||
let dayClasses = "";
|
||||
const thisDate = { // NOTE: uses monthNum (1-based), NOT monthIndex (0-based)
|
||||
year: yearNum,
|
||||
month: monthIndex + 1,
|
||||
date: i,
|
||||
};
|
||||
if (offset === 0 && i === todayDateNum) {
|
||||
dayClasses += "current-day";
|
||||
}
|
||||
if (offset === 0 && i < todayDateNum && direction === "future") {
|
||||
dayClasses += "unavailable-day";
|
||||
}
|
||||
if (offset === 0 && i > todayDateNum && direction === "past") {
|
||||
dayClasses += "unavailable-day";
|
||||
}
|
||||
if (Math.abs(offset) === 1&& direction === "future" && i > initialViewEndDate) {
|
||||
dayClasses += "day-hidden";
|
||||
}
|
||||
if (Math.abs(offset) === 1&& direction === "past" && i < initialViewStartDate) {
|
||||
dayClasses += "day-hidden";
|
||||
}
|
||||
if (this.selectableDates === "custom" && this.isSelectableDate(thisDate)) {
|
||||
dayClasses += " selectable-day";
|
||||
}
|
||||
const dateObject = {
|
||||
dateNum: i,
|
||||
dayClasses: dayClasses,
|
||||
inputValue: thisDate,
|
||||
};
|
||||
datesArray.push(dateObject);
|
||||
}
|
||||
|
||||
const monthToAdd = {
|
||||
monthLabel: monthsOfYear[monthIndex],
|
||||
monthIndex: monthIndex,
|
||||
yearNum: yearNum,
|
||||
dates: datesArray,
|
||||
startDateDayIndex: startDateDayIndex,
|
||||
monthClass: monthClass,
|
||||
};
|
||||
|
||||
return monthToAdd;
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
getTodayDate() {
|
||||
const todayDate = new Date();
|
||||
return todayDate;
|
||||
addMonthData() {
|
||||
const monthToAdd = this.getMonthData();
|
||||
if (this.calendarViewDirection === "future") {
|
||||
this.calendarData.push(monthToAdd);
|
||||
}
|
||||
if (this.calendarViewDirection === "past") {
|
||||
this.calendarData.unshift(monthToAdd);
|
||||
}
|
||||
},
|
||||
isSelectableDate(thisDate) {
|
||||
const testForDate = (dateInArray) => {
|
||||
return (
|
||||
dateInArray.date === thisDate.date &&
|
||||
dateInArray.month === thisDate.month &&
|
||||
dateInArray.year === thisDate.year
|
||||
);
|
||||
};
|
||||
const isSelectable =
|
||||
this.selectableDatesData.findIndex(testForDate) > -1 ? true : false;
|
||||
return isSelectable;
|
||||
},
|
||||
updateSelectableDates(monthStart, monthEnd) {
|
||||
this.customSelectableDatesCallback(monthStart, monthEnd)?.forEach(newObj => {
|
||||
const index = this.selectableDatesData.findIndex(obj => obj.year === newObj.year && obj.month === newObj.month && obj.date === newObj.date);
|
||||
if (index === -1) this.selectableDatesData.push(newObj);
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
hr {
|
||||
margin: 90px;
|
||||
}
|
||||
|
||||
.calendar-grid-container {
|
||||
margin: 0 auto;
|
||||
max-width: 414px;
|
||||
|
|
@ -246,13 +383,13 @@ hr {
|
|||
}
|
||||
|
||||
.month-year {
|
||||
grid-area: 1 / 1 / 2 / 4;
|
||||
grid-area: 1 / 1 / 2 / 5;
|
||||
text-transform: uppercase;
|
||||
font-weight: 300;
|
||||
letter-spacing: 0.75px;
|
||||
}
|
||||
.legend {
|
||||
grid-area: 1 / 4 / 2 / 6;
|
||||
grid-area: 1 / 5 / 2 / 8;
|
||||
.legend-circle {
|
||||
border-radius: 50%;
|
||||
width: 16px;
|
||||
|
|
@ -261,54 +398,9 @@ hr {
|
|||
border: 1px solid $blue;
|
||||
}
|
||||
}
|
||||
.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;
|
||||
}
|
||||
}
|
||||
display: none;
|
||||
}
|
||||
|
||||
.radio-wrapper {
|
||||
|
|
@ -381,40 +473,6 @@ hr {
|
|||
min-width: 40px;
|
||||
border-radius: 50%;
|
||||
|
||||
&.past-day {
|
||||
color: $gray-500;
|
||||
background-color: transparent;
|
||||
border: 1px solid transparent;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&.selectable-day {
|
||||
color: $blue;
|
||||
background-color: $blue-100;
|
||||
border: 1px solid $blue;
|
||||
}
|
||||
|
||||
&.current-day {
|
||||
&:after {
|
||||
content: "";
|
||||
width: 0.25rem;
|
||||
height: 0.25rem;
|
||||
border-radius: 50%;
|
||||
background-color: $blue;
|
||||
position: absolute;
|
||||
top: 28px;
|
||||
}
|
||||
.first-day {
|
||||
color: $blue;
|
||||
}
|
||||
}
|
||||
|
||||
&.future-day {
|
||||
color: $gray-600;
|
||||
background-color: transparent;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
span {
|
||||
&.small {
|
||||
font-size: 0.75rem;
|
||||
|
|
@ -446,6 +504,103 @@ hr {
|
|||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.selectable-day {
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
16
src/constants/scheduling.js
Normal file
16
src/constants/scheduling.js
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
const monthsOfYear = [
|
||||
"January",
|
||||
"February",
|
||||
"March",
|
||||
"April",
|
||||
"May",
|
||||
"June",
|
||||
"July",
|
||||
"August",
|
||||
"September",
|
||||
"October",
|
||||
"November",
|
||||
"December",
|
||||
];
|
||||
|
||||
export { monthsOfYear };
|
||||
|
|
@ -3,11 +3,17 @@
|
|||
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
||||
<div class="select-car">
|
||||
<div class="select-car-form rounded text-center">
|
||||
<vehicleBanner cmsWidgetName="VehicleBannerWidget" displayGenericVehicleImage />
|
||||
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
|
||||
<div class="fade-on-route-transition">
|
||||
<date-picker />
|
||||
<!-- <date-picker todayOverrideDateString="2024-02-25T03:00:00" /> -->
|
||||
<date-picker
|
||||
selectableDates="custom"
|
||||
v-model="selectedDate"
|
||||
:customSelectableDatesCallback="getAvailableDates" />
|
||||
<!-- EXAMPLE THAT OVERRIDES TODAY BY PASSING STRING --
|
||||
<date-picker
|
||||
selectableDates="custom"
|
||||
v-model="selectedDate"
|
||||
:customSelectableDatesCallback="getAvailableDates"
|
||||
todayOverrideDateString="2022-12-30T03:00:00" /> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -33,11 +39,27 @@ import baseMixin from "@/mixins/base-mixin";
|
|||
|
||||
export default {
|
||||
name: "demo-date-picker",
|
||||
// data() {
|
||||
// return {
|
||||
// todayDate: new Date(),
|
||||
// };
|
||||
// },
|
||||
data() {
|
||||
return {
|
||||
selectedDate: null,
|
||||
// selectedDate: { // USE THIS FORMAT FOR A PRE-SELECTED DATE ON LOAD
|
||||
// year: 2023,
|
||||
// month: 3, // use 1-based index for months
|
||||
// date: 21,
|
||||
// },
|
||||
mockSelectableDatesData: [
|
||||
{ year: 2023, month: 3, date: 4 },
|
||||
{ year: 2023, month: 3, date: 5 },
|
||||
{ year: 2023, month: 3, date: 22 },
|
||||
{ year: 2023, month: 4, date: 13 },
|
||||
{ year: 2023, month: 4, date: 14 },
|
||||
{ year: 2023, month: 4, date: 26 },
|
||||
{ year: 2023, month: 5, date: 21 },
|
||||
{ year: 2023, month: 5, date: 23 },
|
||||
{ year: 2023, month: 5, date: 25 },
|
||||
],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
todayDate() {
|
||||
const today = new Date();
|
||||
|
|
@ -48,12 +70,16 @@ export default {
|
|||
arePagePrerequisitesValid() {
|
||||
return true;
|
||||
},
|
||||
getAvailableDates(startDate, endDate) {
|
||||
this.mockSelectableDatesData.push(endDate);
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
// temporary test method that adds endDate to list of selectable dates
|
||||
return this.mockSelectableDatesData;
|
||||
},
|
||||
},
|
||||
components: {
|
||||
datePicker,
|
||||
funnelHeader,
|
||||
vehicleBanner,
|
||||
funnelSubHeader,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Reference in a new issue