{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "date-picker",
  "title": "Date Picker",
  "description": "Date, range, time and date-time pickers behind a field or icon trigger.",
  "dependencies": ["lucide-react"],
  "registryDependencies": [
    "https://indiacn.in/r/theme.json",
    "https://indiacn.in/r/typography.json",
    "https://indiacn.in/r/button.json",
    "https://indiacn.in/r/popover.json",
    "https://indiacn.in/r/calendar.json",
    "https://indiacn.in/r/time-picker.json"
  ],
  "files": [
    {
      "path": "components/ui/date-picker.tsx",
      "content": "'use client';\n\nimport { CalendarDays, Clock } from 'lucide-react';\nimport { ReactNode, useCallback, useState } from 'react';\n\nimport { Button } from '@/components/ui/button';\nimport { Calendar, IDateRange, MONTH_LABELS, startOfDay } from '@/components/ui/calendar';\nimport { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';\nimport {\n  ITimeValue,\n  THourCycle,\n  TimeColumns,\n  formatTime,\n  toTimeValue,\n} from '@/components/ui/time-picker';\nimport { Label1 } from '@/components/ui/typography';\nimport { cn } from '@/lib/utils';\n\n/*\n * UX4G picker triggers: an input-shaped field with a trailing icon, or a bare\n * 32px icon button. Both open the same surface.\n *\n * The field is a button rather than a text input. The kit draws it as a field,\n * and it looks identical here, but a real input invites typing and then has to\n * guess what \"03/04\" means. The button says what it is — aria-haspopup dialog,\n * the chosen date as its label — and the grid stays the one way in.\n */\nconst TRIGGER_FIELD =\n  'border-neutral-200 bg-neutral-0 hover:border-primary focus-visible:border-primary focus-visible:shadow-focus-primary flex h-11 w-full cursor-pointer items-center gap-2 rounded-md border px-3 text-left text-sm transition-colors focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50 [&>svg]:size-[18px] [&>svg]:shrink-0 [&>svg]:text-neutral-600';\n\nconst TRIGGER_ICON =\n  'border-neutral-200 bg-neutral-0 hover:border-primary hover:text-primary focus-visible:shadow-focus-primary flex size-8 cursor-pointer items-center justify-center rounded-md border text-neutral-600 transition-colors focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50 [&>svg]:size-[18px]';\n\n/*\n * Radix reports how much room it has below the trigger. Without the cap the\n * stacked date-time surface came to 703px in an 844px viewport and hung 307px\n * above the top edge, where it could not be reached.\n */\nconst PANEL =\n  'w-auto max-h-[var(--radix-popover-content-available-height)] overflow-y-auto border-0 bg-transparent p-0 shadow-none';\n\n/*\n * `modal` on every picker. Radix popovers are non-modal by default, and Tab\n * from the last day cell closed the surface and dropped focus on a bare span.\n * The kit asks that focus stay in the picker until the choice is confirmed or\n * cancelled, and a calendar with 42 cells is a place you can get lost.\n */\nconst MODAL = true;\n\ntype TPickerTrigger = 'field' | 'icon';\n\n/** \"21 Oct 2021\" — spelled out so the day and month can never be read the wrong way round. */\nfunction formatDate(date: Date) {\n  return `${date.getDate()} ${MONTH_LABELS[date.getMonth()]} ${date.getFullYear()}`;\n}\n\n/** \"21 Oct 2021 – 30 Oct 2021\", or just the start while the span is open. */\nfunction formatRange(range: IDateRange) {\n  if (!range.from) return '';\n\n  return range.to ? `${formatDate(range.from)} – ${formatDate(range.to)}` : formatDate(range.from);\n}\n\ninterface IPickerTriggerProps {\n  trigger: TPickerTrigger;\n  text: string;\n  placeholder: string;\n  icon: ReactNode;\n  triggerLabel: string;\n  disabled?: boolean;\n  className?: string;\n}\n\n/** Shared trigger for every picker on this page. */\nfunction PickerTrigger({\n  trigger,\n  text,\n  placeholder,\n  icon,\n  triggerLabel,\n  disabled,\n  className,\n}: IPickerTriggerProps) {\n  if (trigger === 'icon') {\n    return (\n      <PopoverTrigger\n        aria-label={text ? `${triggerLabel}: ${text}` : triggerLabel}\n        disabled={disabled}\n        className={cn(TRIGGER_ICON, className)}\n      >\n        {icon}\n      </PopoverTrigger>\n    );\n  }\n\n  return (\n    <PopoverTrigger\n      aria-label={text ? `${triggerLabel}: ${text}` : triggerLabel}\n      disabled={disabled}\n      className={cn(TRIGGER_FIELD, className)}\n    >\n      <Label1\n        className={cn('flex-1 truncate font-normal', text ? 'text-neutral' : 'text-neutral-500')}\n      >\n        {text || placeholder}\n      </Label1>\n      {icon}\n    </PopoverTrigger>\n  );\n}\n\ninterface IDatePickerProps {\n  value: Date | null;\n  onValueChange: (date: Date) => void;\n  trigger?: TPickerTrigger;\n  placeholder?: string;\n  /** Overrides the trigger text. Useful for a locale-aware format. */\n  format?: (date: Date) => string;\n  showViewControls?: boolean;\n  min?: Date;\n  max?: Date;\n  disabled?: boolean;\n  className?: string;\n}\n\n/** Single date. Picking a day commits it and closes the surface. */\nfunction DatePicker({\n  value,\n  onValueChange,\n  trigger = 'field',\n  placeholder = 'Select Date',\n  format = formatDate,\n  showViewControls = true,\n  min,\n  max,\n  disabled,\n  className,\n}: IDatePickerProps) {\n  const [open, setOpen] = useState(false);\n\n  const handleSelect = useCallback(\n    (date: Date) => {\n      onValueChange(date);\n      setOpen(false);\n    },\n    [onValueChange],\n  );\n\n  return (\n    <Popover open={open} onOpenChange={setOpen} modal={MODAL}>\n      <PickerTrigger\n        trigger={trigger}\n        text={value ? format(value) : ''}\n        placeholder={placeholder}\n        icon={<CalendarDays aria-hidden />}\n        triggerLabel='Choose date'\n        disabled={disabled}\n        className={className}\n      />\n      <PopoverContent align='start' className={PANEL}>\n        <Calendar\n          value={value}\n          onValueChange={handleSelect}\n          showViewControls={showViewControls}\n          min={min}\n          max={max}\n        />\n      </PopoverContent>\n    </Popover>\n  );\n}\n\ninterface IDateRangePickerProps {\n  value: IDateRange;\n  onValueChange: (range: IDateRange) => void;\n  trigger?: TPickerTrigger;\n  placeholder?: string;\n  min?: Date;\n  max?: Date;\n  disabled?: boolean;\n  className?: string;\n}\n\n/**\n * Date span across two months.\n *\n * The surface stays open until both ends are set, since closing on the first\n * click would make the second one impossible.\n */\nfunction DateRangePicker({\n  value,\n  onValueChange,\n  trigger = 'field',\n  placeholder = 'Select Dates',\n  min,\n  max,\n  disabled,\n  className,\n}: IDateRangePickerProps) {\n  const [open, setOpen] = useState(false);\n\n  const handleChange = useCallback(\n    (range: IDateRange) => {\n      onValueChange(range);\n      if (range.from && range.to) setOpen(false);\n    },\n    [onValueChange],\n  );\n\n  return (\n    <Popover open={open} onOpenChange={setOpen} modal={MODAL}>\n      <PickerTrigger\n        trigger={trigger}\n        text={formatRange(value)}\n        placeholder={placeholder}\n        icon={<CalendarDays aria-hidden />}\n        triggerLabel='Choose dates'\n        disabled={disabled}\n        className={className}\n      />\n      <PopoverContent align='start' className={PANEL}>\n        <Calendar\n          months={2}\n          range={value}\n          onRangeChange={handleChange}\n          min={min}\n          max={max}\n          weekStartsOn={1}\n        />\n      </PopoverContent>\n    </Popover>\n  );\n}\n\ninterface ITimePickerFieldProps {\n  value: ITimeValue | null;\n  onValueChange: (value: ITimeValue) => void;\n  trigger?: TPickerTrigger;\n  placeholder?: string;\n  showSeconds?: boolean;\n  /** 12 adds an AM/PM column and shows hours as 12, 1, 2 … */\n  hourCycle?: THourCycle;\n  disabled?: boolean;\n  className?: string;\n}\n\n/** Time of day. Ok commits the draft, so scrolling past a value costs nothing. */\nfunction TimePickerField({\n  value,\n  onValueChange,\n  trigger = 'field',\n  placeholder = 'Select Time',\n  showSeconds = true,\n  hourCycle = 24,\n  disabled,\n  className,\n}: ITimePickerFieldProps) {\n  const [open, setOpen] = useState(false);\n  const [draft, setDraft] = useState<ITimeValue>(value ?? { hours: 0, minutes: 0, seconds: 0 });\n\n  const handleOpenChange = useCallback(\n    (next: boolean) => {\n      if (next && value) setDraft(value);\n      setOpen(next);\n    },\n    [value],\n  );\n\n  const handleNow = useCallback(() => setDraft(toTimeValue(new Date())), []);\n  const handleConfirm = useCallback(() => {\n    onValueChange(draft);\n    setOpen(false);\n  }, [onValueChange, draft]);\n\n  return (\n    <Popover open={open} onOpenChange={handleOpenChange} modal={MODAL}>\n      <PickerTrigger\n        trigger={trigger}\n        text={value ? formatTime(value, showSeconds, hourCycle) : ''}\n        placeholder={placeholder}\n        icon={<Clock aria-hidden />}\n        triggerLabel='Choose time'\n        disabled={disabled}\n        className={className}\n      />\n      <PopoverContent align='start' className={PANEL}>\n        <div className='bg-neutral-0 inline-flex flex-col divide-y divide-neutral-100 rounded-lg shadow-md'>\n          <TimeColumns\n            value={draft}\n            onValueChange={setDraft}\n            showSeconds={showSeconds}\n            hourCycle={hourCycle}\n            className='py-2'\n          />\n          <PickerFooter onReset={handleNow} resetLabel='Now' onConfirm={handleConfirm} />\n        </div>\n      </PopoverContent>\n    </Popover>\n  );\n}\n\ninterface IDateTimePickerProps {\n  value: Date | null;\n  onValueChange: (date: Date) => void;\n  trigger?: TPickerTrigger;\n  placeholder?: string;\n  showSeconds?: boolean;\n  /** 12 adds an AM/PM column and shows hours as 12, 1, 2 … */\n  hourCycle?: THourCycle;\n  min?: Date;\n  max?: Date;\n  disabled?: boolean;\n  className?: string;\n}\n\n/** Calendar and clock on one surface. Ok commits both halves at once. */\nfunction DateTimePicker({\n  value,\n  onValueChange,\n  trigger = 'field',\n  placeholder = 'Select Date & Time',\n  showSeconds = true,\n  hourCycle = 24,\n  min,\n  max,\n  disabled,\n  className,\n}: IDateTimePickerProps) {\n  const [open, setOpen] = useState(false);\n  const [draftDate, setDraftDate] = useState<Date | null>(value);\n  const [draftTime, setDraftTime] = useState<ITimeValue>(\n    value ? toTimeValue(value) : { hours: 0, minutes: 0, seconds: 0 },\n  );\n\n  const handleOpenChange = useCallback(\n    (next: boolean) => {\n      if (next && value) {\n        setDraftDate(value);\n        setDraftTime(toTimeValue(value));\n      }\n      setOpen(next);\n    },\n    [value],\n  );\n\n  const handleToday = useCallback(() => setDraftDate(startOfDay(new Date())), []);\n  const handleConfirm = useCallback(() => {\n    const day = draftDate ?? startOfDay(new Date());\n    onValueChange(\n      new Date(\n        day.getFullYear(),\n        day.getMonth(),\n        day.getDate(),\n        draftTime.hours,\n        draftTime.minutes,\n        draftTime.seconds,\n      ),\n    );\n    setOpen(false);\n  }, [draftDate, draftTime, onValueChange]);\n\n  const text = value\n    ? `${formatDate(value)} ${formatTime(toTimeValue(value), showSeconds, hourCycle)}`\n    : '';\n\n  return (\n    <Popover open={open} onOpenChange={handleOpenChange} modal={MODAL}>\n      <PickerTrigger\n        trigger={trigger}\n        text={text}\n        placeholder={placeholder}\n        icon={<CalendarDays aria-hidden />}\n        triggerLabel='Choose date and time'\n        disabled={disabled}\n        className={className}\n      />\n      <PopoverContent align='start' className={PANEL}>\n        <div className='bg-neutral-0 inline-flex flex-col overflow-hidden rounded-lg shadow-md'>\n          {/*\n            One grid for the body and the footer, so Today sits under the middle\n            of the calendar and Ok under the clock. With the footer as its own\n            row, Today centred on the whole card and landed 38px off.\n          */}\n          <div className='grid grid-cols-2 sm:grid-cols-[auto_auto]'>\n            <Calendar\n              value={draftDate}\n              onValueChange={setDraftDate}\n              min={min}\n              max={max}\n              className='col-span-2 rounded-none bg-transparent p-4 shadow-none sm:col-span-1'\n            />\n            {/* pt-4 + h-8 puts the clock readout on the same band as the\n                calendar's month label, which sits under the same 16px pad. */}\n            <div className='col-span-2 flex flex-col border-t border-neutral-100 pt-4 sm:col-span-1 sm:border-t-0 sm:border-l'>\n              <Label1 className='text-neutral flex h-8 items-center justify-center border-b border-neutral-100 px-4 text-center tabular-nums'>\n                {formatTime(draftTime, showSeconds, hourCycle)}\n              </Label1>\n              <TimeColumns\n                value={draftTime}\n                onValueChange={setDraftTime}\n                showSeconds={showSeconds}\n                hourCycle={hourCycle}\n                className='py-2'\n              />\n            </div>\n            <div className='flex items-center justify-center border-t border-neutral-100 p-2.5'>\n              <Button variant='text' size='sm' onClick={handleToday}>\n                Today\n              </Button>\n            </div>\n            <div className='flex items-center justify-end border-t border-neutral-100 p-2.5 sm:border-l'>\n              <Button size='sm' onClick={handleConfirm}>\n                Ok\n              </Button>\n            </div>\n          </div>\n        </div>\n      </PopoverContent>\n    </Popover>\n  );\n}\n\n/** The reset-and-confirm row under a draft surface. */\nfunction PickerFooter({\n  onReset,\n  resetLabel,\n  onConfirm,\n}: {\n  onReset: () => void;\n  resetLabel: string;\n  onConfirm: () => void;\n}) {\n  return (\n    <div className='flex items-center justify-between gap-3 p-2.5'>\n      <div className='flex-1 text-center'>\n        <Button variant='text' size='sm' onClick={onReset}>\n          {resetLabel}\n        </Button>\n      </div>\n      <Button size='sm' onClick={onConfirm}>\n        Ok\n      </Button>\n    </div>\n  );\n}\n\nexport type { TPickerTrigger };\n\nexport { DatePicker, DateRangePicker, DateTimePicker, TimePickerField, formatDate, formatRange };\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}
