import React, { ReactElement, MouseEvent, useState, useCallback, useEffect, } from 'react'; import getDate from 'date-fns/fp/getDate'; import getWeek from 'date-fns/fp/getWeek'; import getMonth from 'date-fns/fp/getMonth'; import getYear from 'date-fns/fp/getYear'; import addYears from 'date-fns/fp/addYears'; 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, CalendarWrapper, CalendarRow, DayWrapper, Day, Week, WeekWrapper, WeekState, DayState, } from './StyledCalendar'; import { DAYS, getMonthMatrix, generateYearOptions, generateMonthOptions, getWeekCalendarDayState, getWeekCalendarState, } from './utils'; type SelectMonthAndYearProps = { currentDate?: Date; month: number; onChangeMonth: (month?: string | number) => void; onChangeYear: (year?: string | number) => void; year: number; }; export const SelectMonthAndYear = ({ currentDate = new Date(), year, month, onChangeMonth, onChangeYear, }: SelectMonthAndYearProps): ReactElement => { const caldendarMinDate = subYears(100, currentDate); const calendarMaxDate = addYears(100, currentDate); const yearOptions = generateYearOptions({ minDate: caldendarMinDate, maxDate: calendarMaxDate, }); const monthOptions = generateMonthOptions({ minDate: caldendarMinDate, maxDate: calendarMaxDate, year, }); return ( ); }; export const FirstRowOfWeekCalendar = () => { return ( Week {DAYS.map(day => ( {day} ))} ); }; type WeekComponentProps = { firstDateState: WeekState; onMouseOver: () => void; showedWeekNumber: number; }; export const WeekComponent = ({ onMouseOver, firstDateState, showedWeekNumber, }: WeekComponentProps): ReactElement => { return ( {showedWeekNumber} ); }; type DayComponent = { dateState: DayState; day: number; onMouseOver: () => void; }; export const DayComponent = ({ day, dateState, onMouseOver, }: DayComponent): ReactElement => { return ( {day} ); }; type DayProps = { date: Date; disabled: boolean; }; type WeekProps = DayProps[]; type CalendarComponentProps = { monthMatrix: WeekProps[]; onSelectFirstDateOfWeek?: (date: Date) => void; selectedFirstDateOfWeek?: Date; setTempDate: (date: Date) => void; tempDate?: Date; }; const CalendarMatrix = ({ monthMatrix, onSelectFirstDateOfWeek, selectedFirstDateOfWeek, setTempDate, tempDate, }: CalendarComponentProps): ReactElement => { return ( <> {monthMatrix.map((week: WeekProps, weekNumber: number) => { const firstDate = week[0] !== undefined ? week[0].date : new Date(); const onDateClick = (e: MouseEvent): void => { if (onSelectFirstDateOfWeek !== undefined) { onSelectFirstDateOfWeek(firstDate); } e.preventDefault(); }; const onMouseOverDay = (hoveredDate: Date): void => { setTempDate(hoveredDate); }; const firstDateObject = { date: week[0] !== undefined ? week[0].date : new Date(), disabled: week[0] !== undefined ? week[0]?.disabled : false, }; const showedWeekNumber = getWeek(firstDate); const firstDateState = getWeekCalendarState({ dateOfWeek: firstDateObject, selectedDate: selectedFirstDateOfWeek, tempDate, }); return ( // eslint-disable-next-line react/no-array-index-key onDateClick(e)}> onMouseOverDay(firstDate)} firstDateState={firstDateState} showedWeekNumber={showedWeekNumber} /> {week.map(dateOfWeek => { const day = getDate(dateOfWeek.date); const dateState = getWeekCalendarDayState({ dateOfWeek, selectedDate: selectedFirstDateOfWeek, tempDate, }); return ( onMouseOverDay(dateOfWeek.date)} /> ); })} ); })} ); }; export interface WeekCalendarProps { onSelectFirstDateOfWeek?: (date: Date) => void; selectedFirstDateOfWeek?: Date; } const WeekCalendar = ({ onSelectFirstDateOfWeek, selectedFirstDateOfWeek, }: WeekCalendarProps): ReactElement => { const currentDate = new Date(); const initialDate = selectedFirstDateOfWeek !== undefined ? selectedFirstDateOfWeek : currentDate; const [date, setDate] = useState(initialDate); const [tempDate, setTempDate] = useState(); const month = getMonth(date); const year = getYear(date); const onChangeMonth = useCallback( newMonth => { setDate(setMonth(newMonth, date)); }, [date, setDate] ); const onChangeYear = useCallback( newYear => { setDate(setYear(newYear, date)); }, [date, setDate] ); useEffect(() => { setDate( selectedFirstDateOfWeek !== undefined ? selectedFirstDateOfWeek : new Date() ); }, [selectedFirstDateOfWeek, setDate]); const monthMatrix = getMonthMatrix({ month, year }); return ( ); }; export default WeekCalendar;