{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "date-picker-dialog",
  "title": "Date Picker Dialog",
  "description": "The modal date picker, with the calendar and text-entry modes.",
  "registryDependencies": [
    "https://guillermo-rebolledo.github.io/materialcn/r/button.json",
    "https://guillermo-rebolledo.github.io/materialcn/r/calendar.json",
    "https://guillermo-rebolledo.github.io/materialcn/r/calendar-utils.json",
    "https://guillermo-rebolledo.github.io/materialcn/r/dialog.json",
    "https://guillermo-rebolledo.github.io/materialcn/r/field.json",
    "https://guillermo-rebolledo.github.io/materialcn/r/input.json",
    "https://guillermo-rebolledo.github.io/materialcn/r/materialcn-theme.json",
    "https://guillermo-rebolledo.github.io/materialcn/r/utils.json"
  ],
  "files": [
    {
      "path": "src/components/ui/date-picker-dialog.tsx",
      "content": "import { useId, useState } from \"react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"./button\"\nimport { Calendar, type CalendarProps } from \"./calendar\"\nimport { isDateSelectable } from \"./calendar-utils\"\nimport {\n  Dialog,\n  DialogClose,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle,\n  DialogTrigger,\n} from \"./dialog\"\nimport { Field, FieldError, FieldLabel } from \"./field\"\nimport { Input } from \"./input\"\n\ntype DateRange = { start: Date | null; end: Date | null }\n\ntype SharedModalProps = Pick<\n  CalendarProps,\n  \"defaultMonth\" | \"isDateUnavailable\" | \"locale\" | \"max\" | \"min\"\n> & {\n  disabled?: boolean\n  error?: string\n  invalid?: boolean\n  label: string\n}\n\ntype DatePickerDialogProps = SharedModalProps & {\n  formatDate?: (date: Date, locale: string) => string\n  mode?: \"calendar\" | \"input\"\n  onValueChange: (value: Date | null) => void\n  parseDate?: (text: string, locale: string) => Date | null\n  value: Date | null\n}\n\nfunction defaultFormatDate(date: Date, locale = \"en-US\") {\n  return new Intl.DateTimeFormat(locale, {\n    year: \"numeric\",\n    month: \"short\",\n    day: \"numeric\",\n  }).format(date)\n}\n\nfunction defaultParseDate(text: string) {\n  const timestamp = Date.parse(text)\n  return Number.isNaN(timestamp) ? null : new Date(timestamp)\n}\n\nfunction DatePickerDialog({\n  defaultMonth,\n  disabled,\n  error,\n  formatDate = defaultFormatDate,\n  invalid,\n  isDateUnavailable,\n  label,\n  locale = \"en-US\",\n  max,\n  min,\n  mode = \"calendar\",\n  onValueChange,\n  parseDate = defaultParseDate,\n  value,\n}: DatePickerDialogProps) {\n  const [open, setOpen] = useState(false)\n  const [draft, setDraft] = useState<Date | null>(value)\n  const [text, setText] = useState(() => value ? formatDate(value, locale) : \"\")\n  const id = useId()\n  const inputId = `${id}-input`\n  const errorId = `${id}-error`\n  const parsedText = text ? parseDate(text, locale) : null\n  const inputInvalid = mode === \"input\" && Boolean(\n    text &&\n      (!parsedText || !isDateSelectable(parsedText, { isDateUnavailable, max, min })),\n  )\n\n  return (\n    <Dialog\n      open={open}\n      onOpenChange={(nextOpen) => {\n        setOpen(nextOpen)\n        if (nextOpen) {\n          setDraft(value)\n          setText(value ? formatDate(value, locale) : \"\")\n        }\n      }}\n    >\n      <DialogTrigger\n        disabled={disabled}\n        render={<Button variant=\"outline\" />}\n      >\n        {value ? formatDate(value, locale) : `Choose ${label}`}\n      </DialogTrigger>\n      {/* Kit: the day picker is 360dp wide, the input picker 328dp. */}\n      <DialogContent showCloseButton={false} className={cn(\"max-w-[calc(100%-2rem)] gap-3 p-0\", mode === \"input\" ? \"w-[328px]\" : \"w-[360px]\")}>\n        {/* Kit modal header: 120dp, label-medium supporting text over a\n            headline-large selected date, closed by a 1dp Outline Variant rule. */}\n        <DialogHeader className=\"min-h-30 gap-9 border-b border-m3-outline-variant px-6 pt-4 pb-3\">\n          <DialogTitle className=\"text-m3-label-md text-m3-on-surface-variant\">{label}</DialogTitle>\n          <DialogDescription className=\"text-m3-headline-lg text-foreground\">\n            {draft ? formatDate(draft, locale) : mode === \"input\" ? \"Enter a date\" : \"Select a date\"}\n          </DialogDescription>\n        </DialogHeader>\n        {mode === \"calendar\" ? (\n          <Calendar\n            className=\"w-full rounded-none p-3 shadow-none\"\n            selected={draft}\n            onSelect={setDraft}\n            defaultMonth={defaultMonth ?? value ?? undefined}\n            locale={locale}\n            min={min}\n            max={max}\n            isDateUnavailable={isDateUnavailable}\n          />\n        ) : (\n          <Field data-invalid={invalid || inputInvalid || undefined} className=\"px-6 py-4\">\n            <FieldLabel htmlFor={inputId}>Date</FieldLabel>\n            <Input\n              id={inputId}\n              value={text}\n              aria-invalid={invalid || inputInvalid || undefined}\n              aria-describedby={error || inputInvalid ? errorId : undefined}\n              onChange={(event) => {\n                setText(event.target.value)\n                const parsed = parseDate(event.target.value, locale)\n                setDraft(\n                  parsed && isDateSelectable(parsed, { isDateUnavailable, max, min })\n                    ? parsed\n                    : null,\n                )\n              }}\n            />\n            {(error || inputInvalid) && (\n              <FieldError id={errorId}>\n                {error ?? \"Enter an available date within the allowed range\"}\n              </FieldError>\n            )}\n          </Field>\n        )}\n        <DialogFooter className=\"px-3 pt-1 pb-2\">\n          <DialogClose render={<Button variant=\"ghost\">Cancel</Button>} />\n          <DialogClose\n            render={<Button />}\n            disabled={!draft || invalid || inputInvalid}\n            onClick={() => {\n              onValueChange(draft)\n            }}\n          >\n            Confirm date\n          </DialogClose>\n        </DialogFooter>\n      </DialogContent>\n    </Dialog>\n  )\n}\n\ntype DateRangePickerProps = SharedModalProps & {\n  onValueChange: (value: DateRange) => void\n  value: DateRange\n}\n\nfunction DateRangePicker({\n  defaultMonth,\n  disabled,\n  error,\n  invalid,\n  isDateUnavailable,\n  label,\n  locale = \"en-US\",\n  max,\n  min,\n  onValueChange,\n  value,\n}: DateRangePickerProps) {\n  const [open, setOpen] = useState(false)\n  const [draft, setDraft] = useState<DateRange>(value)\n  const summary = value.start\n    ? `${defaultFormatDate(value.start, locale)}${value.end ? ` – ${defaultFormatDate(value.end, locale)}` : \" – …\"}`\n    : `Choose ${label}`\n\n  const select = (date: Date) => {\n    if (!draft.start || draft.end) return setDraft({ start: date, end: null })\n    if (date < draft.start) return setDraft({ start: date, end: draft.start })\n    setDraft({ start: draft.start, end: date })\n  }\n\n  return (\n    <Dialog\n      open={open}\n      onOpenChange={(nextOpen) => {\n        setOpen(nextOpen)\n        if (nextOpen) setDraft(value)\n      }}\n    >\n      <DialogTrigger disabled={disabled} render={<Button variant=\"outline\" />}>\n        {summary}\n      </DialogTrigger>\n      <DialogContent showCloseButton={false} className=\"w-[360px] max-w-[calc(100%-2rem)] gap-3 p-0\">\n        <DialogHeader className=\"min-h-30 gap-9 border-b border-m3-outline-variant px-6 pt-4 pb-3\">\n          <DialogTitle className=\"text-m3-label-md text-m3-on-surface-variant\">{label}</DialogTitle>\n          <DialogDescription className=\"text-m3-headline-lg text-foreground\">\n            {draft.start ? `${defaultFormatDate(draft.start, locale)} – ${draft.end ? defaultFormatDate(draft.end, locale) : \"…\"}` : \"Select dates\"}\n          </DialogDescription>\n        </DialogHeader>\n        <Calendar\n          className=\"w-full rounded-none p-3 shadow-none\"\n          range={draft}\n          onSelect={select}\n          defaultMonth={defaultMonth ?? value.start ?? undefined}\n          locale={locale}\n          min={min}\n          max={max}\n          isDateUnavailable={isDateUnavailable}\n        />\n        {error && <FieldError className=\"px-6\">{error}</FieldError>}\n        {/* Kit local actions: Clear leading, Cancel / OK trailing, 4 / 12 / 8 / 12 padding. */}\n        <DialogFooter className=\"px-3 pt-1 pb-2\">\n          <Button\n            variant=\"ghost\"\n            className=\"mr-auto\"\n            onClick={() => {\n              onValueChange({ start: null, end: null })\n              setOpen(false)\n            }}\n          >\n            Clear dates\n          </Button>\n          <DialogClose render={<Button variant=\"ghost\">Cancel</Button>} />\n          <DialogClose\n            render={<Button />}\n            aria-label=\"Confirm dates\"\n            disabled={!draft.start || invalid}\n            onClick={() => {\n              onValueChange(draft)\n            }}\n          >\n            Confirm\n          </DialogClose>\n        </DialogFooter>\n      </DialogContent>\n    </Dialog>\n  )\n}\n\nexport {\n  DatePickerDialog,\n  DateRangePicker,\n  type DatePickerDialogProps,\n  type DateRange,\n  type DateRangePickerProps,\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "forms"
  ],
  "type": "registry:ui"
}