{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "date-picker",
  "title": "Date picker",
  "description": "A button that opens a calendar in a popover, or a bottom sheet on phones, for single dates and ranges.",
  "dependencies": [
    "@tabler/icons-react",
    "cn",
    "react-day-picker@^10.0.2"
  ],
  "registryDependencies": [
    "https://hextaui.com/r/theme.json",
    "https://hextaui.com/r/button.json",
    "https://hextaui.com/r/calendar.json",
    "https://hextaui.com/r/popover.json",
    "https://hextaui.com/r/sheet.json"
  ],
  "files": [
    {
      "path": "components/ui/date-picker.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { IconCalendar } from \"@tabler/icons-react\"\nimport { cn } from \"cn\"\nimport type { DateRange } from \"react-day-picker\"\n\nimport { Button } from \"@/components/ui/button\"\nimport { Calendar, type CalendarProps } from \"@/components/ui/calendar\"\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n} from \"@/components/ui/popover\"\nimport {\n  Sheet,\n  SheetBody,\n  SheetContent,\n  SheetFooter,\n  SheetHeader,\n  SheetTitle,\n  SheetTrigger,\n} from \"@/components/ui/sheet\"\n\nconst phoneQuery = \"(max-width: 639px)\"\n\nfunction subscribePhone(onChange: () => void) {\n  if (typeof window.matchMedia !== \"function\") {\n    return () => {}\n  }\n  const media = window.matchMedia(phoneQuery)\n  media.addEventListener(\"change\", onChange)\n  return () => media.removeEventListener(\"change\", onChange)\n}\n\nfunction getPhone() {\n  return typeof window.matchMedia === \"function\"\n    ? window.matchMedia(phoneQuery).matches\n    : false\n}\n\nfunction useIsPhone() {\n  return React.useSyncExternalStore(subscribePhone, getPhone, () => false)\n}\n\nfunction toIsoDate(date: Date) {\n  const month = String(date.getMonth() + 1).padStart(2, \"0\")\n  const day = String(date.getDate()).padStart(2, \"0\")\n  return `${date.getFullYear()}-${month}-${day}`\n}\n\nfunction normalizeSpaces(text: string) {\n  return text.replace(/[\\u2009\\u202f\\u00a0]/g, \" \")\n}\n\nfunction isSameDay(a: Date, b: Date) {\n  return (\n    a.getFullYear() === b.getFullYear() &&\n    a.getMonth() === b.getMonth() &&\n    a.getDate() === b.getDate()\n  )\n}\n\ntype CalendarPassthrough = Omit<\n  CalendarProps,\n  \"mode\" | \"selected\" | \"onSelect\" | \"required\" | \"numberOfMonths\" | \"autoFocus\"\n>\n\ntype TriggerProps = Omit<\n  React.ComponentProps<typeof Button>,\n  \"value\" | \"defaultValue\" | \"onChange\" | \"children\" | \"render\"\n>\n\ntype DatePickerSharedProps = TriggerProps & {\n  open?: boolean\n  defaultOpen?: boolean\n  onOpenChange?: (open: boolean) => void\n  placeholder?: string\n  title?: string\n  locale?: string\n  clearable?: boolean\n  clearLabel?: string\n  name?: string\n  calendarProps?: CalendarPassthrough\n}\n\ntype DatePickerShellProps = Omit<\n  DatePickerSharedProps,\n  \"locale\" | \"calendarProps\"\n> & {\n  label: string | undefined\n  hasValue: boolean\n  hiddenValue: string | undefined\n  onClear: () => void\n  children: (close: () => void, phone: boolean) => React.ReactNode\n}\n\nfunction DatePickerShell({\n  open: openProp,\n  defaultOpen = false,\n  onOpenChange,\n  placeholder,\n  title,\n  label,\n  hasValue,\n  hiddenValue,\n  clearable = false,\n  clearLabel = \"Clear\",\n  name,\n  onClear,\n  children,\n  className,\n  variant = \"outline\",\n  disabled,\n  ...props\n}: DatePickerShellProps) {\n  const phone = useIsPhone()\n  const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen)\n  const open = openProp ?? uncontrolledOpen\n\n  const setOpen = (next: boolean) => {\n    if (openProp === undefined) {\n      setUncontrolledOpen(next)\n    }\n    onOpenChange?.(next)\n  }\n  const close = () => setOpen(false)\n\n  const trigger = (\n    <Button\n      variant={variant}\n      disabled={disabled}\n      data-slot=\"date-picker-trigger\"\n      data-empty={hasValue ? undefined : \"\"}\n      className={cn(\n        \"w-60 max-w-full justify-start gap-2 px-3 text-start font-normal tabular-nums data-empty:text-muted-foreground\",\n        className\n      )}\n      {...props}\n    />\n  )\n\n  const triggerContent = (\n    <>\n      <IconCalendar aria-hidden className=\"text-muted-foreground\" />\n      <span className=\"min-w-0 truncate\">{label ?? placeholder}</span>\n    </>\n  )\n\n  const clearButton =\n    clearable && hasValue ? (\n      <Button\n        type=\"button\"\n        variant=\"ghost\"\n        size=\"sm\"\n        data-slot=\"date-picker-clear\"\n        onClick={() => {\n          onClear()\n          close()\n        }}\n      >\n        {clearLabel}\n      </Button>\n    ) : null\n\n  const hidden =\n    name !== undefined ? (\n      <input type=\"hidden\" name={name} value={hiddenValue ?? \"\"} />\n    ) : null\n\n  if (phone) {\n    return (\n      <Sheet open={open} onOpenChange={setOpen}>\n        <SheetTrigger render={trigger}>{triggerContent}</SheetTrigger>\n        {hidden}\n        <SheetContent side=\"bottom\">\n          <SheetHeader>\n            <SheetTitle>{title}</SheetTitle>\n          </SheetHeader>\n          <SheetBody>\n            <div\n              data-slot=\"date-picker-content\"\n              className=\"flex justify-center\"\n            >\n              {children(close, true)}\n            </div>\n          </SheetBody>\n          {clearButton ? <SheetFooter>{clearButton}</SheetFooter> : null}\n        </SheetContent>\n      </Sheet>\n    )\n  }\n\n  return (\n    <Popover open={open} onOpenChange={setOpen}>\n      <PopoverTrigger render={trigger}>{triggerContent}</PopoverTrigger>\n      {hidden}\n      <PopoverContent\n        align=\"start\"\n        aria-label={title}\n        className=\"w-auto gap-0 p-0\"\n      >\n        <div data-slot=\"date-picker-content\">{children(close, false)}</div>\n        {clearButton ? (\n          <div className=\"flex justify-end border-t px-2 py-1.5\">\n            {clearButton}\n          </div>\n        ) : null}\n      </PopoverContent>\n    </Popover>\n  )\n}\n\ntype DatePickerProps = DatePickerSharedProps & {\n  value?: Date | null\n  defaultValue?: Date | null\n  onValueChange?: (value: Date | null) => void\n}\n\nfunction DatePicker({\n  value: valueProp,\n  defaultValue = null,\n  onValueChange,\n  placeholder = \"Pick a date\",\n  title = \"Select a date\",\n  locale = \"en-US\",\n  calendarProps,\n  ...props\n}: DatePickerProps) {\n  const [uncontrolledValue, setUncontrolledValue] = React.useState(defaultValue)\n  const value = valueProp !== undefined ? valueProp : uncontrolledValue\n  const formatter = React.useMemo(\n    () => new Intl.DateTimeFormat(locale, { dateStyle: \"medium\" }),\n    [locale]\n  )\n\n  const setValue = (next: Date | null) => {\n    if (valueProp === undefined) {\n      setUncontrolledValue(next)\n    }\n    onValueChange?.(next)\n  }\n\n  return (\n    <DatePickerShell\n      {...props}\n      placeholder={placeholder}\n      title={title}\n      label={value ? normalizeSpaces(formatter.format(value)) : undefined}\n      hasValue={Boolean(value)}\n      hiddenValue={value ? toIsoDate(value) : undefined}\n      onClear={() => setValue(null)}\n    >\n      {(close) => (\n        <Calendar\n          {...calendarProps}\n          mode=\"single\"\n          required\n          autoFocus\n          selected={value ?? undefined}\n          defaultMonth={value ?? calendarProps?.defaultMonth}\n          onSelect={(date) => {\n            setValue(date)\n            close()\n          }}\n        />\n      )}\n    </DatePickerShell>\n  )\n}\n\ntype DateRangePickerProps = DatePickerSharedProps & {\n  value?: DateRange | null\n  defaultValue?: DateRange | null\n  onValueChange?: (value: DateRange | null) => void\n}\n\nfunction DateRangeCalendar({\n  value,\n  phone,\n  calendarProps,\n  onComplete,\n}: {\n  value: DateRange | null\n  phone: boolean\n  calendarProps?: CalendarPassthrough\n  onComplete: (range: DateRange) => void\n}) {\n  const [draft, setDraft] = React.useState<DateRange | undefined>(undefined)\n  const selected = draft ?? value ?? undefined\n\n  return (\n    <Calendar\n      {...calendarProps}\n      mode=\"range\"\n      autoFocus\n      numberOfMonths={phone ? 1 : 2}\n      defaultMonth={value?.from ?? calendarProps?.defaultMonth}\n      selected={selected}\n      onSelect={(_, day) => {\n        if (!draft) {\n          setDraft({ from: day, to: day })\n          return\n        }\n        const start = draft.from ?? day\n        const range =\n          day < start ? { from: day, to: start } : { from: start, to: day }\n        setDraft(undefined)\n        onComplete(range)\n      }}\n    />\n  )\n}\n\nfunction DateRangePicker({\n  value: valueProp,\n  defaultValue = null,\n  onValueChange,\n  placeholder = \"Pick a date range\",\n  title = \"Select dates\",\n  locale = \"en-US\",\n  calendarProps,\n  ...props\n}: DateRangePickerProps) {\n  const [uncontrolledValue, setUncontrolledValue] = React.useState(defaultValue)\n  const value = valueProp !== undefined ? valueProp : uncontrolledValue\n  const formatter = React.useMemo(\n    () => new Intl.DateTimeFormat(locale, { dateStyle: \"medium\" }),\n    [locale]\n  )\n\n  const setValue = (next: DateRange | null) => {\n    if (valueProp === undefined) {\n      setUncontrolledValue(next)\n    }\n    onValueChange?.(next)\n  }\n\n  const from = value?.from\n  const to = value?.to ?? value?.from\n  const label =\n    from && to\n      ? isSameDay(from, to)\n        ? normalizeSpaces(formatter.format(from))\n        : normalizeSpaces(formatter.formatRange(from, to))\n      : undefined\n\n  return (\n    <DatePickerShell\n      {...props}\n      placeholder={placeholder}\n      title={title}\n      label={label}\n      hasValue={Boolean(from)}\n      hiddenValue={\n        from && to ? `${toIsoDate(from)}/${toIsoDate(to)}` : undefined\n      }\n      onClear={() => setValue(null)}\n    >\n      {(close, phone) => (\n        <DateRangeCalendar\n          value={value}\n          phone={phone}\n          calendarProps={calendarProps}\n          onComplete={(range) => {\n            setValue(range)\n            close()\n          }}\n        />\n      )}\n    </DatePickerShell>\n  )\n}\n\nexport { DatePicker, DateRangePicker }\nexport type { DatePickerProps, DateRangePickerProps }\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "forms"
  ],
  "type": "registry:ui"
}