import { addMonths, eachDayOfInterval, endOfMonth, endOfWeek, format, isSameDay, isToday, startOfMonth, startOfWeek, subMonths, } from "date-fns"; import { useMemo, useState } from "react"; import { HiChevronLeft, HiChevronRight } from "react-icons/hi2"; import { twMerge } from "tailwind-merge"; interface DateSelectorProps { selectedDate?: Date | null; onDateSelect?: (date: Date | undefined) => void; } const DateSelector = ({ selectedDate, onDateSelect }: DateSelectorProps) => { const [currentMonth, setCurrentMonth] = useState(() => { return selectedDate ? startOfMonth(selectedDate) : startOfMonth(new Date()); }); const monthName = format(currentMonth, "MMMM"); const year = format(currentMonth, "yyyy"); const dayHeaders = useMemo(() => { const weekStart = startOfWeek(new Date(), { weekStartsOn: 1 }); // Monday return eachDayOfInterval({ start: weekStart, end: new Date(weekStart.getTime() + 6 * 24 * 60 * 60 * 1000), }).map((date) => format(date, "EEEEEE")); // Shortest localized day name }, []); const days = useMemo(() => { const monthStart = startOfMonth(currentMonth); const monthEnd = endOfMonth(currentMonth); const calendarStart = startOfWeek(monthStart, { weekStartsOn: 1 }); // Monday const calendarEnd = endOfWeek(monthEnd, { weekStartsOn: 1 }); // Monday return eachDayOfInterval({ start: calendarStart, end: calendarEnd }).map( (date) => { const dateString = format(date, "yyyy-MM-dd"); return { date: dateString, isToday: isToday(date), isSelected: selectedDate ? isSameDay(date, selectedDate) : false, isCurrentMonth: date >= monthStart && date <= monthEnd, dateObj: date, }; }, ); }, [currentMonth, selectedDate]); const handlePreviousMonth = () => { setCurrentMonth(subMonths(currentMonth, 1)); }; const handleNextMonth = () => { setCurrentMonth(addMonths(currentMonth, 1)); }; const handleDateClick = (date: Date, e: React.MouseEvent) => { e.stopPropagation(); // If clicking the same date that's already selected, unselect it if (selectedDate && isSameDay(date, selectedDate)) { onDateSelect?.(undefined); } else { onDateSelect?.(date); } }; return (
{monthName} {year}
{dayHeaders.map((day, index) => (
{day}
))}
{days.map((day) => ( ))}
); }; export default DateSelector;