import { noop } from '../../utils/functions'; import { SUNDAY_INDEX, TOTAL_DATES_ITEMS, WEEK_INDEX_OFFSET, } from './constants'; import type { CalendarProps, ParsedMaskedDate } from './types'; export const initArray = (length: number, func: (value: number) => T): T[] => Array.from(Array(length)).map((_, index) => func(index)); export const isEqDate = (dateA?: Date, dateB?: Date) => dateA?.toDateString() === dateB?.toDateString(); export const isDateInRange = ({ date, range, }: { date: Date; range?: { startDate?: Date; endDate?: Date; }; }) => { if (!range || range.startDate === undefined || range.endDate === undefined) { return false; } return date >= range.startDate && date <= range.endDate; }; export const getValidDate = ( date: Date, minDate?: Date, maxDate?: Date ): Date | undefined => { if (minDate === undefined && maxDate === undefined) { return date; } if (minDate !== undefined && maxDate === undefined) { return date >= minDate ? date : undefined; } if (minDate === undefined && maxDate !== undefined) { return date <= maxDate ? date : undefined; } if (minDate !== undefined && maxDate !== undefined) { return date >= minDate && date <= maxDate ? date : undefined; } return undefined; }; export const setStartOrEndDate = ({ date, startDate, endDate, }: { date: Date; startDate?: Date; endDate?: Date; }) => { // If both dates are set, selecting start or end date will set the same date if ( startDate && endDate && (isEqDate(date, startDate) || isEqDate(date, endDate)) ) { return { startDate: date, endDate: date, }; } // No start date yet - set as start if (!startDate) { return { startDate: date, endDate: undefined, }; } // Have start but no end - set as end and reorder if needed if (!endDate) { return date < startDate ? { startDate: date, endDate: startDate, } : { startDate, endDate: date, }; } // Clicking within range - start new selection if (date >= startDate && date <= endDate) { return { startDate: date, endDate: undefined, }; } // Clicking outside range - reset range return { startDate: date, endDate: undefined, }; }; export const shouldUseMonthPicker = ( onMonthChange: CalendarProps['onMonthChange'] ) => onMonthChange !== noop; export const getCalendarDate = ({ visibleDate, markedDates = [], minDate, maxDate, }: CalendarProps) => { const currentMonth = visibleDate.getMonth(); const currentYear = visibleDate.getFullYear(); const parsedMaskedDate: ParsedMaskedDate = markedDates.reduce( (current, markedDate) => ({ ...current, [markedDate.toDateString()]: true, }), {} ); const firstDateOfMonth = new Date(currentYear, currentMonth, 1); const lastDateOfMonth = new Date(currentYear, currentMonth + 1, 0); const lastDateOfPreviousMonth = new Date(currentYear, currentMonth, 0); // Index of day in week is shifted by 1 due to Sunday is the last column const firstDayWeekIndexOfMonth = firstDateOfMonth.getDay() === 0 ? SUNDAY_INDEX : firstDateOfMonth.getDay() - WEEK_INDEX_OFFSET; const lastDayIndexOfCurrentMonth = lastDateOfMonth.getDate(); const lastDayIndexOfPreviousMonth = lastDateOfPreviousMonth.getDate(); const daysOfPreviousMonth = initArray(firstDayWeekIndexOfMonth, (index) => { const reversedIndex = firstDayWeekIndexOfMonth - index - 1; const count = lastDayIndexOfPreviousMonth - reversedIndex; return getValidDate( new Date(currentYear, currentMonth - 1, count), minDate, maxDate ); }); const daysOfCurrentMonth = initArray(lastDayIndexOfCurrentMonth, (index) => getValidDate( new Date(currentYear, currentMonth, index + 1), minDate, maxDate ) ); const daysOfNextMonth = initArray( TOTAL_DATES_ITEMS - (daysOfPreviousMonth.length + daysOfCurrentMonth.length), (index) => getValidDate( new Date(currentYear, currentMonth + 1, index + 1), minDate, maxDate ) ); return { firstDateOfMonth, lastDateOfMonth, parsedMaskedDate, daysOfPreviousMonth, daysOfCurrentMonth, daysOfNextMonth, }; }; export const getCalendarButtonState = ({ visibleDate, markedDates, minDate, maxDate, }: CalendarProps) => { const { daysOfPreviousMonth, daysOfNextMonth, firstDateOfMonth, lastDateOfMonth, } = getCalendarDate({ visibleDate, markedDates, minDate, maxDate }); const disablePrevButton = minDate === undefined ? false : !daysOfPreviousMonth.some((date) => date !== undefined) && minDate >= firstDateOfMonth; const disableNextButton = maxDate === undefined ? false : !daysOfNextMonth.some((date) => date !== undefined) || maxDate <= lastDateOfMonth; return { disablePrevButton, disableNextButton, }; };