import React, { ReactElement, MouseEvent, useState, useCallback } from 'react'; import getDate from 'date-fns/fp/getDate'; import getMonth from 'date-fns/fp/getMonth'; import getYear from 'date-fns/fp/getYear'; import addMonths from 'date-fns/fp/addMonths'; import addYears from 'date-fns/fp/addYears'; import subMonths from 'date-fns/fp/subMonths'; import subYears from 'date-fns/fp/subYears'; import setMonth from 'date-fns/fp/setMonth'; import setYear from 'date-fns/fp/setYear'; import Select from '../../Select'; import Divider from '../../Divider'; import { Container, Navigation, DoubleCalendarContainer, CalendarContainer, CalendarWrapper, CalendarRow, DayWrapper, Day, } from './StyledCalendar'; import { DAYS, getMonthMatrix, getDoubleCalendarDayState, generateMonthOptions, generateYearOptions, } from './utils'; export interface DoubleCalendarProps { dateClickCount: number; endDate?: Date; isSettingEndDate: boolean; isSettingStartDate: boolean; maxDate?: Date; minDate?: Date; onSelectDate?: (date: Date) => void; startDate?: Date; } const DoubleCalendar = ({ startDate, endDate, minDate, maxDate, onSelectDate, isSettingStartDate, isSettingEndDate, dateClickCount, }: DoubleCalendarProps): ReactElement => { const currentDate = new Date(); const initialDate = startDate !== undefined ? startDate : currentDate; const [date, setDate] = useState(initialDate); const [tempDate, setTempDate] = useState(); const month = getMonth(date); const year = getYear(date); const dateInNextMonth = addMonths(1, date); const nextMonth = getMonth(dateInNextMonth); const nextYear = getYear(dateInNextMonth); const caldendarMinDate = minDate !== undefined ? minDate : subYears(100, currentDate); const calendarMaxDate = maxDate !== undefined ? maxDate : addYears(100, currentDate); const yearOptions = generateYearOptions({ minDate: caldendarMinDate, maxDate: calendarMaxDate, }); const monthOptions = generateMonthOptions({ minDate: caldendarMinDate, maxDate: calendarMaxDate, year, }); const nextMonthOptions = generateMonthOptions({ minDate: caldendarMinDate, maxDate: calendarMaxDate, year: nextYear, }); const onChangeMonth = useCallback( newMonth => { setDate(setMonth(newMonth, date)); }, [date, setDate] ); const onChangeYear = useCallback( newYear => { setDate(setYear(newYear, date)); }, [date, setDate] ); const onChangeNextMonth = useCallback( newMonth => { setDate(subMonths(1, setMonth(newMonth, dateInNextMonth))); }, [dateInNextMonth, setDate] ); const onChangeNextYear = useCallback( newYear => { setDate(subMonths(1, setYear(newYear, dateInNextMonth))); }, [dateInNextMonth, setDate] ); const onMouseOverDay = useCallback( (hoveredDate: Date): void => { setTempDate(hoveredDate); }, [setTempDate] ); const onDateClick = useCallback( (e: MouseEvent, clickedDate: Date): void => { if (onSelectDate !== undefined) { onSelectDate(clickedDate); } e.preventDefault(); }, [onSelectDate] ); const renderCalendar = useCallback( (monthMatrix: { date: Date; disabled: boolean }[][]): ReactElement => ( {DAYS.map(day => ( {day} ))} {monthMatrix.map((week, weekNumber) => ( // eslint-disable-next-line react/no-array-index-key {week.map(dateOfWeek => { const day = getDate(dateOfWeek.date); const dayState = getDoubleCalendarDayState({ dateOfWeek, isSettingStartDate, isSettingEndDate, dateClickCount, startDate, endDate, tempDate, }); return ( // eslint-disable-next-line react/no-array-index-key onDateClick(e, dateOfWeek.date) : undefined } onMouseOver={(): void => onMouseOverDay(dateOfWeek.date)} onFocus={(): void => onMouseOverDay(dateOfWeek.date)} > {day} ); })} ))} ), [ isSettingStartDate, isSettingEndDate, dateClickCount, startDate, endDate, tempDate, onDateClick, onMouseOverDay, ] ); return ( {renderCalendar(getMonthMatrix({ month, year, minDate, maxDate }))} {renderCalendar( getMonthMatrix({ month: nextMonth, year: nextYear, minDate, maxDate, }) )} ); }; export default DoubleCalendar;