import { addWeeks, eachDayOfInterval, endOfMonth, endOfWeek, format, getISODay, getISOWeek, getMonth, getYear, isSameMonth, startOfMonth, startOfWeek } from 'date-fns'; import { chunk, isFunction, range } from 'lodash'; import React, { useContext } from 'react'; import { buildClassName } from '../../../shared/utils/class-util'; import { getLocale } from '../../../shared/utils/localization-util'; import SettingsContext from '../../settings-context'; import CalendarDay from './calendar-day'; import Icon from '../../../shared/components/icon'; interface CalendarProps { year?: number; month?: number; hasPreviousButton?: boolean; hasNextButton?: boolean; hasFixedHeight?: boolean; weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6; disabledDaysFunction?: (date: Date) => boolean; selectedDaysFunction?: (date: Date) => boolean; extraClassNamesFunction?: (date: Date) => string[]; dayContentFunction?: (date: Date) => JSX.Element | null | undefined; onDayClick?: (date: Date) => void; onDayMouseOver?: (date: Date) => void; onNextClick?: (year: number, month: number) => void; onPreviousClick?: (year: number, month: number) => void; } const Calendar: React.FC = ({ year = getYear(new Date()), month = getMonth(new Date()), hasPreviousButton = true, hasNextButton = true, hasFixedHeight = true, weekStartsOn = 1, disabledDaysFunction, selectedDaysFunction, extraClassNamesFunction, dayContentFunction, onDayClick, onDayMouseOver, onNextClick, onPreviousClick }) => { const { language } = useContext(SettingsContext); const locale = getLocale(language); const handleDayClick = (day: Date) => { if (onDayClick) { onDayClick(day); } }; const handleDayMouseOver = (day: Date) => { if (onDayMouseOver) { onDayMouseOver(day); } }; const handleNextClick = () => { const nextMonth = (month + 1) % 12; const nextMonthsYear = nextMonth < month ? year + 1 : year; if (onNextClick) { onNextClick(nextMonthsYear, nextMonth); } }; const handlePreviousClick = () => { const previousMonth = (month - 1) % 12; const previousMonthsYear = previousMonth > month ? year - 1 : year; if (onPreviousClick) { onPreviousClick(previousMonthsYear, previousMonth); } }; const focusDate = new Date(year, month); const firstDay = startOfWeek(startOfMonth(focusDate), { weekStartsOn }); const lastDay = hasFixedHeight ? endOfWeek(addWeeks(firstDay, 5), { weekStartsOn }) : endOfWeek(endOfMonth(focusDate), { weekStartsOn }); const calendarDays = eachDayOfInterval({ start: firstDay, end: lastDay }); const mapDay = (day: Date) => { const isoDay = getISODay(day); const isDisabled = isFunction(disabledDaysFunction) && disabledDaysFunction(day); const isSelected = isFunction(selectedDaysFunction) && selectedDaysFunction(day); const isOutsideMonth = !isSameMonth(day, focusDate); const extraClassNames = isFunction(extraClassNamesFunction) ? extraClassNamesFunction(day) : undefined; return ( {isFunction(dayContentFunction) ? dayContentFunction(day) : undefined} ); }; const mapWeek = (weekDays: Date[]) => { if (weekDays.length === 0) { return null; } const isoWeek = getISOWeek(weekDays[0]); return (
{weekDays.map(mapDay)}
); }; return (
{format(focusDate, 'MMMM yyyy', { locale })}
{range(0, 7).map((i) => (
{format(calendarDays[i], 'EEEEEE', { locale: locale })}
))}
{chunk(calendarDays, 7).map(mapWeek)}
); }; export default Calendar;