import { useState } from "react"; import { Calendar as CalendarIcon, X } from "lucide-react"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Calendar } from "@/components/ui/calendar"; import { Button } from "@/components/ui/button"; import { useMoney } from "@/hooks/useMoney"; import { toDateString } from "@/lib/format"; import { parseLocalDate, cn } from "@/lib/utils"; interface Props { /** "YYYY-MM-DD", or null/"" for no date picked. */ value: string | null; onChange: (value: string | null) => void; /** No date after this one is selectable. */ maxDate?: Date; /** No date before this one is selectable. */ minDate?: Date; placeholder?: string; /** Shows a small clear button once a date is picked - for optional dates * (a todo's due date, an asset's purchase date). Omit for a date that's * always required (a report range's start/end). */ clearable?: boolean; className?: string; "aria-label"?: string; } /** * The app's own calendar popover in place of the browser's native * ``, whose chrome (icon, dropdown styling, light/dark * behavior) is entirely OS/browser-drawn and can't be made to match the * rest of the UI. Every other date-pickable field already used this * Popover+Calendar pattern inline (AddTransactionDialog, Reports' own * previous picker, GoalsTracker, the investment dialogs); this pulls it * into one place instead of a sixth copy. */ export function DatePicker({ value, onChange, maxDate, minDate, placeholder = "Pick a date", clearable, className, "aria-label": ariaLabel, }: Props) { const [open, setOpen] = useState(false); const { date: fmt } = useMoney(); const selected = value ? parseLocalDate(value) : undefined; return ( { if (d) onChange(toDateString(d)); setOpen(false); }} disabled={(d) => (maxDate != null && d > maxDate) || (minDate != null && d < minDate)} defaultMonth={selected ?? maxDate} initialFocus /> ); }