import { getDate, endOfMonth, sub, format, startOfMonth, set, add, isAfter, isBefore } from "date-fns"; const weekDays: WeekDays = { sunday: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ], monday: [ "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" ] }; /** * Using date-fns, this function checks if currDate is within the month of selectedDate or not. * @param {Date} selectedDate The current month. * @param {Date} currDate The date to be compared to the selected month. * @returns True if currDate is outside of the month of selectedDate, false if otherwise. */ const isOverflow = ( selectedDate: Date, currDate: Date ): { isOverflow: boolean; overflowDirection: "prev" | "next" | null; } => { let flag = false; let direction: "next" | "prev" | null = null; const start = startOfMonth(selectedDate); const end = endOfMonth(selectedDate); if (isBefore(currDate, start)) { flag = true; direction = "prev"; } if (isAfter(currDate, end)) { flag = true; direction = "next"; } return { isOverflow: flag, overflowDirection: direction }; }; /** * A function that will return a month layout when given a date. It produces * an object with 6 weeks that include overflow from the previous and next month * with all dates aligned with the day of the week. * @param selectedDate The date of the month to generate a month layout for. * @returns The month layout object for the provided month. */ const populateMonth = (selectedDate: Date): MonthLayout => { const endLastMonth = getDate(endOfMonth(sub(selectedDate, { months: 1 }))); const startOfSelectedMonth = format(startOfMonth(selectedDate), "iii"); const ISOToIndex = { sunday: { Sun: 0, Mon: 1, Tue: 2, Wed: 3, Thu: 4, Fri: 5, Sat: 6 }, monday: { Mon: -1, Tue: 0, Wed: 1, Thu: 2, Fri: 3, Sat: 4, Sun: 5 } }; const sundays = { week1: new Array(7).fill(null), week2: new Array(7).fill(null), week3: new Array(7).fill(null), week4: new Array(7).fill(null), week5: new Array(7).fill(null), week6: new Array(7).fill(null) }; const sunStartDay = endLastMonth - (ISOToIndex.sunday[startOfSelectedMonth] - 1); let sunCurrDate = set(sub(selectedDate, { months: 1 }), { date: sunStartDay }); for (const week in sundays) { const thisWeek = sundays[week]; thisWeek.forEach((e, i) => { const overflowInfo = isOverflow(selectedDate, sunCurrDate); const day: MonthDay = { ...overflowInfo, date: sunCurrDate.toJSON() }; sunCurrDate = add(sunCurrDate, { days: 1 }); sundays[week][i] = day; }); } const mondays = { week1: new Array(7).fill(null), week2: new Array(7).fill(null), week3: new Array(7).fill(null), week4: new Array(7).fill(null), week5: new Array(7).fill(null), week6: new Array(7).fill(null) }; const monStartDay = endLastMonth - ISOToIndex.monday[startOfSelectedMonth]; let monCurrDate = set(sub(selectedDate, { months: 1 }), { date: monStartDay }); for (const week in mondays) { const thisWeek = mondays[week]; thisWeek.forEach((e, i) => { const overflowInfo = isOverflow(selectedDate, monCurrDate); const day: MonthDay = { ...overflowInfo, date: monCurrDate.toJSON() }; monCurrDate = add(monCurrDate, { days: 1 }); mondays[week][i] = day; }); } const output = { sunday: { weekdays: weekDays.sunday, month: sundays }, monday: { weekdays: weekDays.monday, month: mondays } }; return output; }; export default populateMonth;