/** * PeriodPicker - dual-month range calendar (vertically stacked). * * Pure presentation; receives a `range` and emits `onPickDate`. Constraint * checks (min/max/direction) are delegated to `isDateDisabled`. */ import React, { useMemo } from 'react'; import { useLanguage } from '../../providers/language/LanguageContext'; import { addMonthsToDate, buildMonthCells, isDateDisabled, _isSameDay, } from './PeriodPickerLogic'; import type { PeriodConstraints } from './PeriodPickerTypes'; import styles from './PeriodPicker.module.css'; interface CalendarRange { from: Date | null; to: Date | null; } interface PeriodPickerCalendarProps { anchor: Date; onAnchorChange: (next: Date) => void; range: CalendarRange; onPickDate: (d: Date) => void; constraints: PeriodConstraints; } function _monthLabel(d: Date, t: (k: string) => string): string { switch (d.getMonth()) { case 0: return `${t('Januar')} ${d.getFullYear()}`; case 1: return `${t('Februar')} ${d.getFullYear()}`; case 2: return `${t('März')} ${d.getFullYear()}`; case 3: return `${t('April')} ${d.getFullYear()}`; case 4: return `${t('Mai')} ${d.getFullYear()}`; case 5: return `${t('Juni')} ${d.getFullYear()}`; case 6: return `${t('Juli')} ${d.getFullYear()}`; case 7: return `${t('August')} ${d.getFullYear()}`; case 8: return `${t('September')} ${d.getFullYear()}`; case 9: return `${t('Oktober')} ${d.getFullYear()}`; case 10: return `${t('November')} ${d.getFullYear()}`; case 11: return `${t('Dezember')} ${d.getFullYear()}`; default: return `${d.getFullYear()}`; } } function _dayOfWeekLabel(idx: number, t: (k: string) => string): string { switch (idx) { case 0: return t('Mo'); case 1: return t('Di'); case 2: return t('Mi'); case 3: return t('Do'); case 4: return t('Fr'); case 5: return t('Sa'); case 6: return t('So'); default: return ''; } } const PeriodPickerCalendar: React.FC = (props) => { const { anchor, onAnchorChange, range, onPickDate, constraints } = props; const { t } = useLanguage(); const monthsToShow = useMemo(() => [anchor, addMonthsToDate(anchor, 1)], [anchor]); return (
{`${_monthLabel(monthsToShow[0], t)} – ${_monthLabel(monthsToShow[1], t)}`}
{monthsToShow.map((monthAnchor) => (
{_monthLabel(monthAnchor, t)}
{[0, 1, 2, 3, 4, 5, 6].map((i) => (
{_dayOfWeekLabel(i, t)}
))} {buildMonthCells(monthAnchor).map((cell) => { const disabled = isDateDisabled(cell.date, constraints); const cls: string[] = [styles.dayCell]; if (!cell.inMonth) cls.push(styles.muted); if (disabled) cls.push(styles.disabled); if (cell.isToday) cls.push(styles.today); if (range.from && range.to && cell.date >= range.from && cell.date <= range.to) { cls.push(styles.inRange); } if (range.from && _isSameDay(cell.date, range.from)) cls.push(styles.rangeStart); if (range.to && _isSameDay(cell.date, range.to)) cls.push(styles.rangeEnd); return ( ); })}
))}
); }; export default PeriodPickerCalendar;