"use client"; import * as React from "react"; import { parseDate } from "chrono-node"; import { CalendarIcon } from "lucide-react"; import { Button } from "~/components/ui/button"; import { Calendar } from "~/components/ui/calendar"; import { Input } from "~/components/ui/input"; import { Popover, PopoverContent, PopoverTrigger, } from "~/components/ui/popover"; import { cn } from "~/lib/utils"; const DATE_FORMAT_OPTIONS: Intl.DateTimeFormatOptions = { day: "2-digit", month: "long", year: "numeric", }; function formatDate(date: Date | undefined) { if (!date) { return ""; } return date.toLocaleDateString("en-US", DATE_FORMAT_OPTIONS); } // Longest month name in en-US long format (September 30, 2026). const LONGEST_FORMATTED_DATE = formatDate(new Date(2026, 8, 30)); interface DatePickerProps { date?: Date; onDateChange: (date: Date | undefined) => void; placeholder?: string; className?: string; disabled?: boolean; id?: string; size?: "sm" | "md" | "lg"; inputClassName?: string; } export function DatePicker({ date, onDateChange, placeholder = "Tomorrow or next week", className, inputClassName, disabled = false, id, size = "md", }: DatePickerProps) { const [open, setOpen] = React.useState(false); const [value, setValue] = React.useState(formatDate(date)); const [month, setMonth] = React.useState(date); const sizeClasses = { sm: "h-9 text-xs", md: "h-9 text-sm", lg: "h-10 text-sm", }; const wantsFullWidth = className?.includes("w-full"); React.useEffect(() => { // eslint-disable-next-line react-hooks/set-state-in-effect -- Keep text input and calendar month synchronized with the controlled date prop. setValue(formatDate(date)); setMonth(date); }, [date]); return (
{LONGEST_FORMATTED_DATE} { setValue(e.target.value); const parsedDate = parseDate(e.target.value); if (parsedDate) { onDateChange(parsedDate); setMonth(parsedDate); } }} onKeyDown={(e) => { if (e.key === "ArrowDown") { e.preventDefault(); setOpen(true); } }} /> { onDateChange(selectedDate); setValue(formatDate(selectedDate)); setOpen(false); }} />
); }