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;