{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "date-picker",
  "title": "Date Picker",
  "description": "An opinionated, responsive Shamsi/Gregorian date and date-time picker with SSR-safe today defaults, draft state, and Popover-to-Drawer presentation.",
  "dependencies": [
    "@base-ui/react",
    "lucide-react"
  ],
  "registryDependencies": [
    "utils",
    "@persianlabsui/button",
    "@persianlabsui/calendar",
    "@persianlabsui/popover",
    "@persianlabsui/drawer",
    "@persianlabsui/time-picker",
    "@persianlabsui/persian-date"
  ],
  "files": [
    {
      "path": "registry/base/ui/date-picker.tsx",
      "content": "\"use client\"\n\nimport { CalendarIcon, ClockIcon } from \"lucide-react\"\nimport * as React from \"react\"\n\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Calendar,\n  type CalendarProps,\n  type CalendarType,\n} from \"@/components/ui/calendar\"\nimport {\n  Drawer,\n  DrawerFooter,\n  DrawerPanel,\n  DrawerPopup,\n  DrawerTitle,\n  DrawerTrigger,\n} from \"@/components/ui/drawer\"\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n} from \"@/components/ui/popover\"\nimport {\n  formatTimePickerValue,\n  ResponsiveTimePicker,\n  ResponsiveTimePickerContent,\n  ResponsiveTimePickerTrigger,\n  type TimePickerDigits,\n  type TimePickerHourFormat,\n  type TimePickerValue,\n} from \"@/components/ui/time-picker\"\nimport { useMediaQuery } from \"@/hooks/use-media-query\"\nimport { formatDate } from \"@/lib/persian-date\"\nimport { cn } from \"@/lib/utils\"\n\nexport type DatePickerPresentation = \"auto\" | \"popover\" | \"drawer\"\nexport type DatePickerConfirmMode = \"auto\" | \"immediate\" | \"explicit\"\nexport type DatePickerDefaultValue = Date | \"today\" | null\n\nexport interface DateTimePickerValue {\n  date: Date | null\n  time: TimePickerValue\n}\n\nexport type DatePickerCalendarProps = Omit<\n  CalendarProps,\n  | \"mode\"\n  | \"selected\"\n  | \"onSelect\"\n  | \"month\"\n  | \"defaultMonth\"\n  | \"onMonthChange\"\n  | \"calendarType\"\n>\n\ninterface PickerShellProps {\n  open?: boolean\n  defaultOpen?: boolean\n  onOpenChange?: (open: boolean) => void\n  presentation?: DatePickerPresentation\n  /** Width below which auto presentation uses a Drawer. @default 800 */\n  mobileBreakpoint?: number\n  disabled?: boolean\n  className?: string\n  contentClassName?: string\n  drawerTitle?: string\n}\n\nexport interface DatePickerProps extends PickerShellProps {\n  value?: Date | null\n  defaultValue?: DatePickerDefaultValue\n  onValueChange?: (value: Date | null) => void\n  /** @default \"shamsi\" */\n  calendarType?: CalendarType\n  /** @default \"auto\" — immediate in Popover, explicit in Drawer. */\n  confirmMode?: DatePickerConfirmMode\n  placeholder?: string\n  format?: string\n  confirmLabel?: string\n  cancelLabel?: string\n  calendarProps?: DatePickerCalendarProps\n  renderTrigger?: (state: {\n    value: Date | null\n    formattedValue: string | null\n    open: boolean\n  }) => React.ReactElement\n}\n\nexport interface DateTimePickerProps extends PickerShellProps {\n  value?: DateTimePickerValue\n  defaultValue?: {\n    date?: DatePickerDefaultValue\n    time?: TimePickerValue\n  }\n  onValueChange?: (value: DateTimePickerValue) => void\n  /** @default \"shamsi\" */\n  calendarType?: CalendarType\n  dateFormat?: string\n  hourFormat?: TimePickerHourFormat\n  digits?: TimePickerDigits\n  showSeconds?: boolean\n  showTimeLabels?: boolean\n  placeholder?: string\n  timeLabel?: string\n  confirmLabel?: string\n  cancelLabel?: string\n  calendarProps?: DatePickerCalendarProps\n  renderTrigger?: (state: {\n    value: DateTimePickerValue\n    formattedValue: string | null\n    open: boolean\n  }) => React.ReactElement\n}\n\nconst DEFAULT_TIME: TimePickerValue = { hour: 0, minute: 0 }\n\nfunction resolveStaticDate(value: DatePickerDefaultValue | undefined) {\n  return value instanceof Date ? value : null\n}\n\nfunction DatePicker({\n  value: valueProp,\n  defaultValue = null,\n  onValueChange,\n  open: openProp,\n  defaultOpen = false,\n  onOpenChange,\n  presentation = \"auto\",\n  mobileBreakpoint = 800,\n  calendarType = \"shamsi\",\n  confirmMode = \"auto\",\n  placeholder = \"انتخاب تاریخ\",\n  format = \"yyyy/MM/dd\",\n  confirmLabel = \"تایید\",\n  cancelLabel = \"انصراف\",\n  disabled,\n  className,\n  contentClassName,\n  drawerTitle,\n  calendarProps,\n  renderTrigger,\n}: DatePickerProps) {\n  const isControlled = valueProp !== undefined\n  const [uncontrolledValue, setUncontrolledValue] = React.useState<Date | null>(\n    resolveStaticDate(defaultValue)\n  )\n  const value = isControlled ? valueProp : uncontrolledValue\n  const [draft, setDraft] = React.useState<Date | null>(value)\n  const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen)\n  const open = openProp ?? uncontrolledOpen\n\n  React.useEffect(() => {\n    if (defaultValue !== \"today\" || isControlled) return\n    const init = () => setUncontrolledValue((current) => current ?? new Date())\n    init()\n  }, [defaultValue, isControlled])\n\n  React.useEffect(() => {\n    if (open) return\n    const sync = () => setDraft(value)\n    sync()\n  }, [open, value])\n\n  const shell = usePickerShell({\n    open,\n    setOpen: (next) => {\n      if (openProp === undefined) setUncontrolledOpen(next)\n      onOpenChange?.(next)\n      if (next) setDraft(value)\n    },\n    presentation,\n    mobileBreakpoint,\n  })\n  const resolvedConfirmMode =\n    confirmMode === \"auto\"\n      ? shell.isDrawer\n        ? \"explicit\"\n        : \"immediate\"\n      : confirmMode\n\n  const commit = (next: Date | null) => {\n    if (!isControlled) setUncontrolledValue(next)\n    onValueChange?.(next)\n  }\n  const select = (next: Date | undefined) => {\n    if (!next) return\n    setDraft(next)\n    if (resolvedConfirmMode === \"immediate\") {\n      commit(next)\n      shell.setOpen(false)\n    }\n  }\n  const apply = () => {\n    commit(draft)\n    shell.setOpen(false)\n  }\n  const cancel = () => {\n    setDraft(value)\n    shell.setOpen(false)\n  }\n  const formattedValue = value\n    ? formatDate(value, format, { calendarType })\n    : null\n  const trigger = renderTrigger?.({ value, formattedValue, open }) ?? (\n    <Button\n      variant=\"outline\"\n      disabled={disabled}\n      className={cn(\"w-56 justify-start font-normal\", className)}\n    >\n      <CalendarIcon className=\"size-4\" />\n      {formattedValue ?? placeholder}\n    </Button>\n  )\n  const calendar = (\n    <Calendar\n      {...calendarProps}\n      mode=\"single\"\n      calendarType={calendarType}\n      selected={draft ?? undefined}\n      onSelect={select}\n      defaultMonth={draft ?? value ?? undefined}\n      className={cn(\n        \"p-2\",\n        shell.isDrawer && \"rounded-xl border bg-transparent\",\n        calendarProps?.className\n      )}\n    />\n  )\n  const actions = resolvedConfirmMode === \"explicit\" && (\n    <PickerActions\n      confirmLabel={confirmLabel}\n      cancelLabel={cancelLabel}\n      onConfirm={apply}\n      onCancel={cancel}\n      confirmDisabled={!draft}\n      showCancel={!shell.isDrawer}\n    />\n  )\n\n  return shell.render({\n    trigger,\n    content: calendar,\n    actions,\n    contentClassName,\n    title: drawerTitle ?? placeholder,\n  })\n}\n\nfunction DateTimePicker({\n  value: valueProp,\n  defaultValue,\n  onValueChange,\n  open: openProp,\n  defaultOpen = false,\n  onOpenChange,\n  presentation = \"auto\",\n  mobileBreakpoint = 800,\n  calendarType = \"shamsi\",\n  dateFormat = \"yyyy/MM/dd\",\n  hourFormat = \"24\",\n  digits = \"fa\",\n  showSeconds = false,\n  showTimeLabels = false,\n  placeholder = \"انتخاب تاریخ و زمان\",\n  timeLabel = \"انتخاب زمان\",\n  confirmLabel = \"تایید\",\n  cancelLabel = \"انصراف\",\n  disabled,\n  className,\n  contentClassName,\n  drawerTitle,\n  calendarProps,\n  renderTrigger,\n}: DateTimePickerProps) {\n  const isControlled = valueProp !== undefined\n  const initialValue: DateTimePickerValue = {\n    date: resolveStaticDate(defaultValue?.date),\n    time: defaultValue?.time ?? DEFAULT_TIME,\n  }\n  const [uncontrolledValue, setUncontrolledValue] =\n    React.useState<DateTimePickerValue>(initialValue)\n  const value = isControlled ? valueProp : uncontrolledValue\n  const [draft, setDraft] = React.useState<DateTimePickerValue>(value)\n  const [timeOpen, setTimeOpen] = React.useState(false)\n  const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen)\n  const open = openProp ?? uncontrolledOpen\n\n  React.useEffect(() => {\n    if (defaultValue?.date !== \"today\" || isControlled) return\n    const init = () =>\n      setUncontrolledValue((current) =>\n        current.date ? current : { ...current, date: new Date() }\n      )\n    init()\n  }, [defaultValue?.date, isControlled])\n\n  React.useEffect(() => {\n    if (open) return\n    const sync = () => setDraft(value)\n    sync()\n  }, [open, value])\n\n  const shell = usePickerShell({\n    open,\n    setOpen: (next) => {\n      if (openProp === undefined) setUncontrolledOpen(next)\n      onOpenChange?.(next)\n      if (next) setDraft(value)\n    },\n    presentation,\n    mobileBreakpoint,\n  })\n  const apply = () => {\n    if (!isControlled) setUncontrolledValue(draft)\n    onValueChange?.(draft)\n    shell.setOpen(false)\n  }\n  const cancel = () => {\n    setDraft(value)\n    shell.setOpen(false)\n  }\n  const formattedValue = value.date\n    ? `${formatDate(value.date, dateFormat, { calendarType })} - ${formatTimePickerValue(value.time, { hourFormat, showSeconds, digits })}`\n    : null\n  const trigger = renderTrigger?.({ value, formattedValue, open }) ?? (\n    <Button\n      variant=\"outline\"\n      disabled={disabled}\n      className={cn(\"w-64 justify-start font-normal\", className)}\n    >\n      <CalendarIcon className=\"size-4\" />\n      {formattedValue ?? placeholder}\n    </Button>\n  )\n  const content = (\n    <div className={cn(\"flex w-fit flex-col\", shell.isDrawer && \"gap-4\")}>\n      <Calendar\n        {...calendarProps}\n        mode=\"single\"\n        calendarType={calendarType}\n        selected={draft.date ?? undefined}\n        onSelect={(date) =>\n          date && setDraft((current) => ({ ...current, date }))\n        }\n        defaultMonth={draft.date ?? value.date ?? undefined}\n        className={cn(\n          \"p-2\",\n          shell.isDrawer && \"rounded-xl border bg-transparent\",\n          calendarProps?.className\n        )}\n      />\n      <div className={cn(\"w-full\", shell.isDrawer ? \"px-0\" : \"p-3 pt-1\")}>\n        <ResponsiveTimePicker open={timeOpen} onOpenChange={setTimeOpen}>\n          <ResponsiveTimePickerTrigger\n            render={\n              <Button variant=\"outline\" className=\"w-full justify-start\">\n                <ClockIcon className=\"size-4\" />\n                {timeLabel}\n                <span className=\"ms-auto font-mono tabular-nums\">\n                  {formatTimePickerValue(draft.time, {\n                    hourFormat,\n                    showSeconds,\n                    digits,\n                  })}\n                </span>\n              </Button>\n            }\n          />\n          <ResponsiveTimePickerContent\n            value={draft.time}\n            onValueChange={(time) =>\n              setDraft((current) => ({ ...current, time }))\n            }\n            hourFormat={hourFormat}\n            digits={digits}\n            showSeconds={showSeconds}\n            showLabels={showTimeLabels}\n            drawerTitle={timeLabel}\n            drawerConfirmLabel={confirmLabel}\n            onDrawerConfirm={() => setTimeOpen(false)}\n          />\n        </ResponsiveTimePicker>\n      </div>\n    </div>\n  )\n  const actions = (\n    <PickerActions\n      confirmLabel={confirmLabel}\n      cancelLabel={cancelLabel}\n      onConfirm={apply}\n      onCancel={cancel}\n      confirmDisabled={!draft.date}\n      showCancel={!shell.isDrawer}\n    />\n  )\n\n  return shell.render({\n    trigger,\n    content,\n    actions,\n    contentClassName,\n    title: drawerTitle ?? placeholder,\n  })\n}\n\nfunction PickerActions({\n  confirmLabel,\n  cancelLabel,\n  onConfirm,\n  onCancel,\n  confirmDisabled,\n  showCancel = true,\n}: {\n  confirmLabel: string\n  cancelLabel: string\n  onConfirm: () => void\n  onCancel: () => void\n  confirmDisabled?: boolean\n  showCancel?: boolean\n}) {\n  return (\n    <>\n      {showCancel && (\n        <Button variant=\"outline\" onClick={onCancel}>\n          {cancelLabel}\n        </Button>\n      )}\n      <Button disabled={confirmDisabled} onClick={onConfirm}>\n        {confirmLabel}\n      </Button>\n    </>\n  )\n}\n\nfunction usePickerShell({\n  open,\n  setOpen,\n  presentation,\n  mobileBreakpoint,\n}: {\n  open: boolean\n  setOpen: (open: boolean) => void\n  presentation: DatePickerPresentation\n  mobileBreakpoint: number\n}) {\n  const matchesMobile = useMediaQuery({ max: mobileBreakpoint })\n  const isDrawer =\n    presentation === \"drawer\" || (presentation === \"auto\" && matchesMobile)\n\n  return {\n    isDrawer,\n    setOpen,\n    render({\n      trigger,\n      content,\n      actions,\n      contentClassName,\n      title,\n    }: {\n      trigger: React.ReactElement\n      content: React.ReactNode\n      actions: React.ReactNode\n      contentClassName?: string\n      title: string\n    }) {\n      if (isDrawer) {\n        return (\n          <Drawer open={open} onOpenChange={setOpen}>\n            <DrawerTrigger\n              render={\n                trigger as React.ReactElement<\n                  React.ComponentProps<typeof DrawerTrigger>\n                >\n              }\n            />\n            <DrawerPopup showBar>\n              <DrawerTitle className=\"sr-only\">{title}</DrawerTitle>\n              <DrawerPanel\n                scrollable\n                className={cn(\"flex flex-col items-center\", contentClassName)}\n              >\n                {content}\n              </DrawerPanel>\n              {actions && <DrawerFooter>{actions}</DrawerFooter>}\n            </DrawerPopup>\n          </Drawer>\n        )\n      }\n\n      return (\n        <Popover open={open} onOpenChange={setOpen}>\n          <PopoverTrigger render={trigger} />\n          <PopoverContent className={cn(\"w-auto p-0\", contentClassName)}>\n            {content}\n            {actions && (\n              <div className=\"flex justify-end gap-2 border-t border-border p-3\">\n                {actions}\n              </div>\n            )}\n          </PopoverContent>\n        </Popover>\n      )\n    },\n  }\n}\n\nexport { DatePicker, DateTimePicker }\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}