import { ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, ReloadIcon, } from '@radix-ui/react-icons'; import { Button, ChevronDownIcon, Flex, Grid, IconButton, Link, Popover, Text, } from '@radix-ui/themes'; import { times } from 'lodash-es'; import { useState } from 'react'; const DAY_TITLES = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; const YEAR_OPTIONS = 7; interface DatePickerProps { onDateChange?: (date: Date) => void; defaultDate?: Date; } export function DatePicker({ onDateChange, defaultDate = new Date(), }: DatePickerProps) { const [year, setYear] = useState(defaultDate.getFullYear()); const [month, setMonth] = useState(defaultDate.getMonth()); const [day, setDay] = useState(defaultDate.getDate()); const [yearScroll, setYearScroll] = useState(0); const [yearOffset, setYearOffset] = useState(0); const [monthOffset, setMonthOffset] = useState(0); const date = new Date(year + yearOffset, month + monthOffset, day); const daysInLastMonth = new Date( year + yearOffset, month + monthOffset, 0, ).getDate(); const daysInMonth = new Date( year + yearOffset, month + monthOffset + 1, 0, ).getDate(); const firstDayOfMonth = new Date( year + yearOffset, month + monthOffset, 1, ).getDay(); const lastDayOfMonth = new Date( year + yearOffset, month + monthOffset, daysInMonth, ).getDay(); return ( { if (monthOffset === 0) { setYearOffset((state) => state - 1); setMonthOffset(11); } else { setMonthOffset((state) => state - 1); } }} ml="2" > { setYear(defaultDate.getFullYear()); setMonth(defaultDate.getMonth()); setDay(defaultDate.getDate()); setYearOffset(0); setMonthOffset(0); }} > {date.toLocaleString(undefined, { month: 'long', })} setYearScroll(0)}> {date.getFullYear()} setYearScroll((state) => state + 1)} mb="2" > {times(YEAR_OPTIONS, (index) => { const thisYear = year + yearOffset - index + Math.floor(YEAR_OPTIONS / 2) + yearScroll; const selected = year + yearOffset === thisYear; return ( ); })} setYearScroll((state) => state - 1)} mt="2" > { if (monthOffset === 11) { setYearOffset((state) => state + 1); setMonthOffset(0); } else { setMonthOffset((state) => state + 1); } }} mr="2" > {times(7, (index) => ( {DAY_TITLES[index]} ))} {times(firstDayOfMonth, (index) => ( {daysInLastMonth - firstDayOfMonth + index + 1} ))} {times(daysInMonth, (index) => { const selected = index === day - 1 && monthOffset === 0 && yearOffset === 0; return ( { const newYear = year + yearOffset + Math.floor((month + monthOffset) / 12); const newMonth = (month + monthOffset) % 12; const newDay = index + 1; setYear(newYear); setMonth(newMonth); setDay(newDay); setYearOffset(0); setMonthOffset(0); onDateChange?.(new Date(newYear, newMonth, newDay)); }} > {index + 1} ); })} {times(6 - lastDayOfMonth, (index) => ( {index + 1} ))} ); }