{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "date-picker",
  "title": "Date Picker",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-react/components/date-picker/date-picker.tsx",
      "content": "'use client'\n\nimport * as React from 'react'\nimport { Calendar as CalendarIcon, X } from 'lucide-react'\nimport { DayButton as RdpDayButton, type DateRange, type Matcher } from 'react-day-picker'\nimport { Button } from '@/components/ui/button'\nimport { Calendar } from '@/components/ui/calendar'\nimport { RangeCalendar } from '@/components/ui/range-calendar'\nimport { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'\nimport { TimeColumns } from '@/components/ui/time-picker'\nimport { cn } from '@/lib/utils'\nimport {\n  coerceDate,\n  coerceShape,\n  defaultRangePresets,\n  fmtDate,\n  fmtMonth,\n  fmtTime,\n  stripTime,\n  toISODate,\n  toISODateTime,\n  parseTimeShape,\n  weekNumber,\n  weekStart,\n  withTime,\n  type DatePickerLayout,\n  type DatePickerPicker,\n  type DatePickerPlacement,\n  type DatePickerPreset,\n  type DatePickerSize,\n  type DatePickerStatus,\n  type DatePickerType,\n  type DisabledTimeResult,\n  type FormatValue,\n  type InternalMultiple,\n  type InternalRange,\n  type InternalSingle,\n  type MultipleValue,\n  type RangeValue,\n  type SingleValue,\n  type TimeShape,\n} from './date-picker-utils'\n\nexport type {\n  DatePickerType,\n  DatePickerLayout,\n  DatePickerPicker,\n  DatePickerStatus,\n  DatePickerSize,\n  DatePickerPlacement,\n  FormatValue,\n  SingleValue,\n  MultipleValue,\n  RangeValue,\n  DatePickerPreset,\n  DisabledTimeResult,\n} from './date-picker-utils'\n\ntype InternalValue = InternalSingle | InternalMultiple | InternalRange | undefined\n\nexport interface DatePickerProps {\n  /** Controlled value. Shape depends on `type`. ISO `YYYY-MM-DD` (or `YYYY-MM-DDTHH:mm` when `showTime`). */\n  value?: SingleValue | MultipleValue | RangeValue\n  /** Uncontrolled initial value. */\n  defaultValue?: SingleValue | MultipleValue | RangeValue\n  onValueChange?: (value: SingleValue | MultipleValue | RangeValue) => void\n  type?: DatePickerType\n  placeholder?: string\n  disabled?: boolean\n  readOnly?: boolean\n  clearable?: boolean\n  /** Format for the trigger label. String presets or Intl.DateTimeFormatOptions. Ignored when `showTime`. */\n  format?: FormatValue\n  /** Backward-compat alias for `format`. */\n  dateFormat?: FormatValue\n  locale?: string\n  numberOfMonths?: number\n  weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6\n  fixedWeeks?: boolean\n  minValue?: string | Date\n  maxValue?: string | Date\n  /** Header layout: control which parts (month/year) become dropdowns. */\n  layout?: DatePickerLayout\n  /**\n   * Granularity of selection (single type only).\n   * - `day`: pick a day from the calendar grid.\n   * - `week`: pick a full week — value is the week's start date.\n   * - `month`: pick a whole month — value snaps to the 1st.\n   * - `quarter`: pick a quarter — value snaps to quarter start.\n   * - `year`: pick a whole year — value snaps to Jan 1.\n   */\n  picker?: DatePickerPicker\n  /** Show \"Today\" shortcut at popover top (single + day picker only). */\n  showCurrentDate?: boolean\n  /** Require clicking OK before applying the selected value. */\n  needConfirm?: boolean\n  /** Validation status — applies colored border to the trigger. */\n  status?: DatePickerStatus\n  /** Size variant of the trigger input. */\n  size?: DatePickerSize\n  /** Placement of the popover relative to the trigger. */\n  placement?: DatePickerPlacement\n  /** Pair the calendar with a time selector. Value becomes `YYYY-MM-DDTHH:mm`. */\n  showTime?: boolean\n  /** Show seconds column in time picker. Only used when `showTime`. */\n  showSeconds?: boolean\n  /** 24h vs AM/PM column. Only used when `showTime`. */\n  use24Hour?: boolean\n  /** Minute step for the time column. Only used when `showTime`. */\n  minuteStep?: number\n  /** Second step for the time column. Only used when `showTime` and `showSeconds`. */\n  secondStep?: number\n  /** Default time for newly picked dates when `showTime` and no prior selection. `HH:mm` or `HH:mm:ss`. */\n  defaultTime?: string\n  /** Preset shortcuts for quick selection. */\n  presets?: DatePickerPreset[]\n  /** Custom separator between range start and end dates. */\n  separator?: string\n  /** Function to determine if a specific date should be disabled. */\n  disabledDate?: (current: Date) => boolean\n  /** Function to determine if specific times should be disabled. Only used when `showTime`. */\n  disabledTime?: (current?: Date) => DisabledTimeResult\n  /** Custom cell renderer for day calendar cells. */\n  renderCell?: (day: Date) => React.ReactNode\n  triggerClassName?: string\n  className?: string\n}\n\nconst QUARTER_LABELS = ['Q1', 'Q2', 'Q3', 'Q4']\nconst QUARTER_MONTHS = [1, 4, 7, 10] as const\n\nfunction compareDates(a: Date, b: Date): number {\n  return stripTime(a).getTime() - stripTime(b).getTime()\n}\n\nfunction fmtDateTime(\n  d: Date,\n  locale: string,\n  format: FormatValue,\n  showSeconds: boolean,\n  use24Hour: boolean,\n) {\n  return `${fmtDate(d, locale, format)} ${fmtTime(d, showSeconds, use24Hour)}`.trim()\n}\n\nfunction quarterStart(year: number, quarterIdx: number): Date {\n  return new Date(year, QUARTER_MONTHS[quarterIdx]! - 1, 1)\n}\n\nfunction weekAnchorForMonth(year: number, month: number, weekStartsOn: number): Date {\n  return weekStart(new Date(year, month - 1, 1), weekStartsOn)\n}\n\nfunction layoutToCaptionLayout(\n  layout: DatePickerLayout,\n): 'dropdown' | 'dropdown-months' | 'dropdown-years' | undefined {\n  if (layout === 'month-and-year') return 'dropdown'\n  if (layout === 'month-only') return 'dropdown-months'\n  if (layout === 'year-only') return 'dropdown-years'\n  return undefined\n}\n\nfunction rangeFromCalendar(r: DateRange | undefined): InternalRange | undefined {\n  if (!r?.from) return undefined\n  return r.to ? { start: r.from, end: r.to } : { start: r.from }\n}\n\nfunction rangeToCalendar(r: InternalRange | undefined): DateRange | undefined {\n  if (!r?.start) return undefined\n  return { from: stripTime(r.start), to: r.end ? stripTime(r.end) : undefined }\n}\n\nfunction getLastTime(\n  internal: InternalValue,\n  type: DatePickerType,\n  defaultTime: string,\n): TimeShape {\n  if (type === 'single' && internal instanceof Date) {\n    if (internal.getHours() || internal.getMinutes() || internal.getSeconds()) {\n      return { h: internal.getHours(), m: internal.getMinutes(), s: internal.getSeconds() }\n    }\n  }\n  if (type === 'range') {\n    const r = internal as InternalRange\n    if (\n      r?.start &&\n      (r.start.getHours() || r.start.getMinutes() || r.start.getSeconds())\n    ) {\n      return { h: r.start.getHours(), m: r.start.getMinutes(), s: r.start.getSeconds() }\n    }\n  }\n  return parseTimeShape(defaultTime, { h: 12, m: 0, s: 0 })\n}\n\nconst DatePicker = React.forwardRef<HTMLButtonElement, DatePickerProps>(\n  (\n    {\n      value,\n      defaultValue = null,\n      onValueChange,\n      type = 'single',\n      placeholder = 'Pick a date',\n      disabled = false,\n      readOnly = false,\n      clearable = true,\n      format = 'medium',\n      dateFormat,\n      locale = 'en-US',\n      numberOfMonths,\n      weekStartsOn = 0,\n      fixedWeeks = false,\n      minValue,\n      maxValue,\n      layout = 'default',\n      picker = 'day',\n      showCurrentDate = false,\n      needConfirm = false,\n      status,\n      size = 'middle',\n      placement = 'bottomLeft',\n      showTime = false,\n      showSeconds = false,\n      use24Hour = false,\n      minuteStep = 5,\n      secondStep = 1,\n      defaultTime = '12:00',\n      presets,\n      separator = '~',\n      disabledDate,\n      disabledTime,\n      renderCell,\n      triggerClassName,\n      className,\n    },\n    ref,\n  ) => {\n    const [open, setOpen] = React.useState(false)\n    const [previewValue, setPreviewValue] = React.useState<InternalValue>(undefined)\n    const isControlled = value !== undefined\n    const [internalState, setInternalState] = React.useState<InternalValue>(() =>\n      coerceShape(type, defaultValue ?? null),\n    )\n\n    const internal = isControlled ? coerceShape(type, value) : internalState\n    const activeValue = needConfirm ? (previewValue ?? internal) : internal\n\n    const effectiveFormat = dateFormat ?? format\n    const effectiveNumberOfMonths = numberOfMonths ?? (type === 'range' ? 2 : 1)\n    const effectivePresets = React.useMemo(\n      () => presets ?? (type === 'range' ? defaultRangePresets() : undefined),\n      [presets, type],\n    )\n\n    const presetGroups = React.useMemo(() => {\n      const groups = new Map<string | undefined, DatePickerPreset[]>()\n      for (const p of effectivePresets ?? []) {\n        const cat = p.category\n        if (!groups.has(cat)) groups.set(cat, [])\n        groups.get(cat)!.push(p)\n      }\n      return Array.from(groups.entries()).map(([category, presetList]) => ({\n        category,\n        presets: presetList,\n      }))\n    }, [effectivePresets])\n\n    const minDate = React.useMemo(() => coerceDate(minValue) ?? undefined, [minValue])\n    const maxDate = React.useMemo(() => coerceDate(maxValue) ?? undefined, [maxValue])\n\n    const [monthYearAnchor, setMonthYearAnchor] = React.useState<Date>(() => {\n      const v = coerceShape(type, defaultValue ?? null)\n      if (v instanceof Date) return v\n      return stripTime(new Date())\n    })\n\n    React.useEffect(() => {\n      if (!open) setPreviewValue(undefined)\n    }, [open])\n\n    React.useEffect(() => {\n      if (open) {\n        const v = internal\n        if (v instanceof Date) setMonthYearAnchor(v)\n        else setMonthYearAnchor(stripTime(new Date()))\n      }\n    }, [open, internal])\n\n    function serializeDate(d: Date) {\n      return showTime ? toISODateTime(d, showSeconds) : toISODate(d)\n    }\n\n    function emitOut(v: InternalValue) {\n      if (type === 'multiple') {\n        const arr = (v as InternalMultiple) ?? []\n        onValueChange?.(arr.map(serializeDate))\n        return\n      }\n      if (type === 'range') {\n        const r = v as InternalRange | undefined\n        if (!r?.start || !r?.end) return\n        onValueChange?.({ start: serializeDate(r.start), end: serializeDate(r.end) })\n        return\n      }\n      const single = v as InternalSingle\n      onValueChange?.(single ? serializeDate(single) : null)\n    }\n\n    function handleUpdate(v: InternalValue) {\n      if (!isControlled) setInternalState(v)\n      emitOut(v)\n      if (needConfirm) return\n      if (type === 'single' && v && !showTime) setOpen(false)\n      if (type === 'range') {\n        const r = v as InternalRange | undefined\n        if (r?.start && r?.end && !showTime) setOpen(false)\n      }\n    }\n\n    function clear(event: React.SyntheticEvent) {\n      event.stopPropagation()\n      if (disabled || readOnly) return\n      if (type === 'multiple') handleUpdate([])\n      else handleUpdate(undefined)\n    }\n\n    function applyPreset(preset: DatePickerPreset) {\n      if (disabled || readOnly) return\n      const v = preset.value\n      if (!v) {\n        handleUpdate(undefined)\n        setOpen(false)\n        return\n      }\n      if (type === 'multiple') {\n        const arr = (v as MultipleValue) ?? []\n        handleUpdate(arr.map(coerceDate).filter((x): x is Date => x != null))\n        setOpen(false)\n        return\n      }\n      if (type === 'range') {\n        const r = v as RangeValue\n        if (!r?.start) {\n          handleUpdate(undefined)\n          setOpen(false)\n          return\n        }\n        const start = coerceDate(r.start)\n        const end = r.end ? coerceDate(r.end) : undefined\n        if (!start) {\n          handleUpdate(undefined)\n          setOpen(false)\n          return\n        }\n        handleUpdate(end ? { start, end } : { start })\n        setOpen(false)\n        return\n      }\n      handleUpdate(coerceDate(v as SingleValue) ?? undefined)\n      setOpen(false)\n    }\n\n    function commitPreview() {\n      if (needConfirm && previewValue !== undefined) {\n        handleUpdate(previewValue)\n        setPreviewValue(undefined)\n      }\n      setOpen(false)\n    }\n\n    function cancelPreview() {\n      setPreviewValue(undefined)\n      setOpen(false)\n    }\n\n    const display = React.useMemo(() => {\n      const v = internal\n      if (!v) return ''\n      if (type === 'multiple') {\n        const arr = v as InternalMultiple\n        if (!arr.length) return ''\n        if (arr.length === 1) return fmtDate(arr[0]!, locale, effectiveFormat)\n        if (arr.length <= 3) return arr.map((d) => fmtDate(d, locale, effectiveFormat)).join(', ')\n        return `${arr.length} dates selected`\n      }\n      if (type === 'range') {\n        const r = v as InternalRange\n        const fmt = showTime\n          ? (d: Date) => fmtDateTime(d, locale, effectiveFormat, showSeconds, use24Hour)\n          : (d: Date) => fmtDate(d, locale, effectiveFormat)\n        if (r.start && r.end) return `${fmt(r.start)} ${separator} ${fmt(r.end)}`\n        if (r.start) return `${fmt(r.start)} ${separator} …`\n        return ''\n      }\n      if (picker === 'week') {\n        const ws = weekStart(v as Date, weekStartsOn)\n        return `Week ${weekNumber(ws)}, ${ws.getFullYear()}`\n      }\n      if (picker === 'month') return fmtMonth(v as Date, locale)\n      if (picker === 'quarter') {\n        const d = v as Date\n        const q = Math.ceil((d.getMonth() + 1) / 3)\n        return `Q${q} ${d.getFullYear()}`\n      }\n      if (picker === 'year') return String((v as Date).getFullYear())\n      return showTime\n        ? fmtDateTime(v as Date, locale, effectiveFormat, showSeconds, use24Hour)\n        : fmtDate(v as Date, locale, effectiveFormat)\n    }, [\n      internal,\n      type,\n      locale,\n      effectiveFormat,\n      showTime,\n      showSeconds,\n      use24Hour,\n      separator,\n      picker,\n      weekStartsOn,\n    ])\n\n    const hasValue = React.useMemo(() => {\n      const v = internal\n      if (!v) return false\n      if (type === 'multiple') return (v as InternalMultiple).length > 0\n      if (type === 'range') return Boolean((v as InternalRange).start)\n      return true\n    }, [internal, type])\n\n    const lastTime = React.useMemo(\n      () => getLastTime(internal, type, defaultTime),\n      [internal, type, defaultTime],\n    )\n\n    function handleCalendarUpdate(v: unknown) {\n      if (readOnly) return\n      if (needConfirm) {\n        if (type === 'multiple') {\n          const arr = (v as Date[]) ?? []\n          setPreviewValue(arr.map((d) => (showTime ? withTime(d, lastTime) : d)))\n        } else if (type === 'range') {\n          const r = rangeFromCalendar(v as DateRange | undefined)\n          if (!r?.start) setPreviewValue(undefined)\n          else {\n            const start = showTime ? withTime(r.start, lastTime) : r.start\n            const end = r.end ? (showTime ? withTime(r.end, lastTime) : r.end) : undefined\n            setPreviewValue(end ? { start, end } : { start })\n          }\n        } else {\n          const d = v as Date | undefined\n          setPreviewValue(d ? (showTime ? withTime(d, lastTime) : d) : undefined)\n        }\n        return\n      }\n      if (!showTime) {\n        if (type === 'range') handleUpdate(rangeFromCalendar(v as DateRange | undefined))\n        else if (type === 'multiple') handleUpdate((v as Date[]) ?? [])\n        else handleUpdate(v as InternalSingle)\n        return\n      }\n      if (type === 'multiple') {\n        const arr = (v as Date[]) ?? []\n        handleUpdate(arr.map((d) => withTime(d, lastTime)))\n        return\n      }\n      if (type === 'range') {\n        const r = rangeFromCalendar(v as DateRange | undefined)\n        if (!r?.start) return handleUpdate(undefined)\n        const start = withTime(r.start, lastTime)\n        const end = r.end ? withTime(r.end, lastTime) : undefined\n        handleUpdate(end ? { start, end } : { start })\n        return\n      }\n      const d = v as Date | undefined\n      if (d) handleUpdate(withTime(d, lastTime))\n    }\n\n    function handleTimeUpdate(timeValue: string) {\n      if (!internal) return\n      const t = parseTimeShape(timeValue, lastTime)\n      if (type === 'single') {\n        handleUpdate(withTime(internal as Date, t))\n        return\n      }\n      if (type === 'range') {\n        const r = internal as InternalRange\n        if (!r.start) return\n        const start = withTime(r.start, t)\n        const end = r.end ? withTime(r.end, t) : undefined\n        handleUpdate(end ? { start, end } : { start })\n      }\n    }\n\n    const timeForColumns = showSeconds\n      ? `${String(lastTime.h).padStart(2, '0')}:${String(lastTime.m).padStart(2, '0')}:${String(lastTime.s).padStart(2, '0')}`\n      : `${String(lastTime.h).padStart(2, '0')}:${String(lastTime.m).padStart(2, '0')}`\n\n    const timeFormat = showSeconds ? 'HH:mm:ss' : 'HH:mm'\n\n    const disabledTimeConfig = React.useMemo(() => {\n      if (!disabledTime) return undefined\n      let current: Date | undefined\n      if (type === 'range') {\n        const r = activeValue as InternalRange | undefined\n        current = r?.start\n      } else if (activeValue instanceof Date) {\n        current = activeValue\n      } else if (Array.isArray(activeValue)) {\n        current = activeValue[0]\n      }\n      return disabledTime(current)\n    }, [disabledTime, activeValue, type])\n\n    const calendarDisabled = React.useMemo(() => {\n      const matchers: Matcher[] = []\n      if (minDate) matchers.push({ before: stripTime(minDate) })\n      if (maxDate) matchers.push({ after: stripTime(maxDate) })\n      if (disabledDate) matchers.push(disabledDate)\n      return matchers.length ? matchers : undefined\n    }, [minDate, maxDate, disabledDate])\n\n    const calendarValue = React.useMemo(() => {\n      const v = internal\n      if (type === 'multiple') return (v as InternalMultiple | undefined)?.map(stripTime)\n      if (type === 'range') return rangeToCalendar(v as InternalRange | undefined)\n      if (v instanceof Date) return stripTime(v)\n      return undefined\n    }, [internal, type])\n\n    const captionLayout = layoutToCaptionLayout(layout)\n\n    const calendarComponents = React.useMemo(() => {\n      if (!renderCell) return undefined\n      return {\n        DayButton: (props: React.ComponentProps<typeof RdpDayButton>) => {\n          const { day, children: _children, ...rest } = props\n          return (\n            <RdpDayButton day={day} modifiers={props.modifiers} {...rest}>\n              {renderCell(day.date)}\n            </RdpDayButton>\n          )\n        },\n      }\n    }, [renderCell])\n\n    const placementMap: Record<\n      DatePickerPlacement,\n      { side: 'top' | 'bottom' | 'left' | 'right'; align: 'start' | 'center' | 'end' }\n    > = {\n      top: { side: 'top', align: 'center' },\n      bottom: { side: 'bottom', align: 'center' },\n      left: { side: 'left', align: 'center' },\n      right: { side: 'right', align: 'center' },\n      topLeft: { side: 'top', align: 'start' },\n      topRight: { side: 'top', align: 'end' },\n      bottomLeft: { side: 'bottom', align: 'start' },\n      bottomRight: { side: 'bottom', align: 'end' },\n    }\n    const popoverPlacement = placementMap[placement] ?? { side: 'bottom', align: 'start' }\n\n    const buttonSize = size === 'small' ? 'sm' : size === 'large' ? 'lg' : 'default'\n\n    const triggerClasses = cn(\n      showTime ? 'min-w-[280px]' : 'min-w-[240px]',\n      'justify-start gap-2 text-left font-normal',\n      !hasValue && 'text-muted-foreground',\n      status === 'error' && 'border-destructive focus-visible:ring-destructive',\n      status === 'warning' && 'border-warning focus-visible:ring-warning',\n      triggerClassName,\n      className,\n    )\n\n    function pickToday() {\n      if (type !== 'single') return\n      const t = stripTime(new Date())\n      if (picker === 'week') {\n        handleUpdate(weekStart(t, weekStartsOn))\n        return\n      }\n      handleUpdate(t)\n    }\n\n    function shiftAnchor(dir: -1 | 1) {\n      const y = monthYearAnchor.getFullYear()\n      const m = monthYearAnchor.getMonth() + 1\n      if (picker === 'month' || picker === 'week') {\n        setMonthYearAnchor(new Date(y + dir, m - 1, 1))\n      } else if (picker === 'year' || picker === 'quarter') {\n        setMonthYearAnchor(new Date(y + dir, 0, 1))\n      }\n    }\n\n    const monthLabels = React.useMemo(\n      () =>\n        Array.from({ length: 12 }, (_, idx) =>\n          new Intl.DateTimeFormat(locale, { month: 'short' }).format(new Date(2024, idx, 1)),\n        ),\n      [locale],\n    )\n\n    const yearGrid = React.useMemo(() => {\n      const y = monthYearAnchor.getFullYear()\n      const start = y - (y % 12)\n      return Array.from({ length: 12 }, (_, i) => start + i)\n    }, [monthYearAnchor])\n\n    const weekGrid = React.useMemo(() => {\n      const y = monthYearAnchor.getFullYear()\n      const m = monthYearAnchor.getMonth() + 1\n      const start = weekAnchorForMonth(y, m, weekStartsOn)\n      return Array.from({ length: 6 }, (_, i) => {\n        const weekStartDate = new Date(start)\n        weekStartDate.setDate(weekStartDate.getDate() + i * 7)\n        const weekEndDate = new Date(weekStartDate)\n        weekEndDate.setDate(weekEndDate.getDate() + 6)\n        return {\n          start: weekStartDate,\n          end: weekEndDate,\n          weekNum: weekNumber(weekStartDate),\n        }\n      })\n    }, [monthYearAnchor, weekStartsOn])\n\n    function isWeekSelected(ws: Date) {\n      const v = activeValue\n      if (!(v instanceof Date)) return false\n      return compareDates(weekStart(v, weekStartsOn), ws) === 0\n    }\n\n    function isWeekDisabled(ws: Date) {\n      if (minDate && compareDates(ws, minDate) < 0) return true\n      const weekEnd = new Date(ws)\n      weekEnd.setDate(weekEnd.getDate() + 6)\n      if (maxDate && compareDates(weekEnd, maxDate) > 0) return true\n      if (disabledDate?.(ws)) return true\n      return false\n    }\n\n    function pickWeek(ws: Date) {\n      if (isWeekDisabled(ws) || readOnly) return\n      if (needConfirm) {\n        setPreviewValue(ws)\n        return\n      }\n      handleUpdate(ws)\n    }\n\n    function isQuarterSelected(qIdx: number) {\n      const v = activeValue\n      if (!(v instanceof Date)) return false\n      return (\n        v.getFullYear() === monthYearAnchor.getFullYear() &&\n        Math.ceil((v.getMonth() + 1) / 3) === qIdx + 1\n      )\n    }\n\n    function isQuarterDisabled(qIdx: number) {\n      const d = quarterStart(monthYearAnchor.getFullYear(), qIdx)\n      if (minDate && compareDates(d, minDate) < 0) return true\n      const lastDay = new Date(d.getFullYear(), d.getMonth() + 3, 0)\n      if (maxDate && compareDates(lastDay, maxDate) > 0) return true\n      if (disabledDate?.(d)) return true\n      return false\n    }\n\n    function pickQuarter(qIdx: number) {\n      const d = quarterStart(monthYearAnchor.getFullYear(), qIdx)\n      if (isQuarterDisabled(qIdx) || readOnly) return\n      if (needConfirm) {\n        setPreviewValue(d)\n        return\n      }\n      handleUpdate(d)\n    }\n\n    function isMonthSelected(monthIdx: number) {\n      const v = activeValue\n      if (!(v instanceof Date)) return false\n      return v.getFullYear() === monthYearAnchor.getFullYear() && v.getMonth() === monthIdx\n    }\n\n    function isMonthDisabled(monthIdx: number) {\n      const d = new Date(monthYearAnchor.getFullYear(), monthIdx, 1)\n      if (minDate && compareDates(d, minDate) < 0) return true\n      if (maxDate && compareDates(d, maxDate) > 0) return true\n      if (disabledDate?.(d)) return true\n      return false\n    }\n\n    function pickMonth(monthIdx: number) {\n      const d = new Date(monthYearAnchor.getFullYear(), monthIdx, 1)\n      if (isMonthDisabled(monthIdx) || readOnly) return\n      if (needConfirm) {\n        setPreviewValue(d)\n        return\n      }\n      handleUpdate(d)\n    }\n\n    function isYearSelected(year: number) {\n      const v = activeValue\n      return v instanceof Date && v.getFullYear() === year\n    }\n\n    function isYearDisabled(year: number) {\n      const d = new Date(year, 0, 1)\n      const lastDay = new Date(year, 11, 31)\n      if (minDate && compareDates(lastDay, minDate) < 0) return true\n      if (maxDate && compareDates(d, maxDate) > 0) return true\n      if (disabledDate?.(d)) return true\n      return false\n    }\n\n    function pickYear(year: number) {\n      const d = new Date(year, 0, 1)\n      if (isYearDisabled(year) || readOnly) return\n      if (picker === 'year') {\n        if (needConfirm) {\n          setPreviewValue(d)\n          return\n        }\n        handleUpdate(d)\n      } else {\n        setMonthYearAnchor(d)\n      }\n    }\n\n    const monthYearLabel =\n      picker === 'week'\n        ? new Intl.DateTimeFormat(locale, { month: 'short', year: 'numeric' }).format(monthYearAnchor)\n        : picker === 'quarter' || picker === 'month'\n          ? String(monthYearAnchor.getFullYear())\n          : `${yearGrid[0]} – ${yearGrid[yearGrid.length - 1]}`\n\n    const showAlternatePicker = picker !== 'day' && type === 'single'\n\n    return (\n      <Popover open={open} onOpenChange={setOpen}>\n        <PopoverTrigger asChild>\n          <Button\n            ref={ref}\n            type=\"button\"\n            variant=\"outline\"\n            size={buttonSize}\n            disabled={disabled}\n            className={triggerClasses}\n            data-uipkge=\"\"\n            data-slot=\"date-picker\"\n          >\n            <CalendarIcon className=\"size-4\" aria-hidden=\"true\" />\n            <span className=\"flex-1 truncate\">{display || placeholder}</span>\n            {clearable && hasValue && !disabled && !readOnly && (\n              <span\n                role=\"button\"\n                tabIndex={0}\n                className=\"text-muted-foreground hover:text-foreground focus-visible:ring-ring -mr-1 inline-flex size-9 cursor-pointer items-center justify-center rounded transition-colors focus-visible:ring-2 focus-visible:outline-none\"\n                aria-label=\"Clear date\"\n                onClick={(e) => {\n                  e.stopPropagation()\n                  clear(e)\n                }}\n                onKeyDown={(e) => {\n                  if (e.key === 'Enter' || e.key === ' ') {\n                    e.preventDefault()\n                    e.stopPropagation()\n                    clear(e)\n                  }\n                }}\n              >\n                <X className=\"size-3.5\" />\n              </span>\n            )}\n          </Button>\n        </PopoverTrigger>\n        <PopoverContent className=\"w-auto p-0\" side={popoverPlacement.side} align={popoverPlacement.align}>\n          {showCurrentDate && type === 'single' && (picker === 'day' || picker === 'week') && (\n            <div className=\"flex justify-end border-b px-3 py-2\">\n              <button\n                type=\"button\"\n                className=\"text-muted-foreground hover:text-foreground focus-visible:ring-ring rounded px-2 py-1.5 text-xs focus-visible:ring-2 focus-visible:outline-none\"\n                onClick={pickToday}\n              >\n                Today\n              </button>\n            </div>\n          )}\n\n          {showAlternatePicker ? (\n            <div className=\"w-[260px] p-3\" data-uipkge=\"\" data-slot=\"month-year-picker\">\n              <div className=\"mb-3 flex items-center justify-between\">\n                <button\n                  type=\"button\"\n                  className=\"hover:bg-accent focus-visible:ring-ring inline-flex size-9 items-center justify-center rounded transition-colors focus-visible:ring-2 focus-visible:outline-none\"\n                  aria-label=\"Previous\"\n                  onClick={() => shiftAnchor(-1)}\n                >\n                  <span className=\"text-muted-foreground text-sm\">‹</span>\n                </button>\n                <span className=\"text-sm font-medium\">{monthYearLabel}</span>\n                <button\n                  type=\"button\"\n                  className=\"hover:bg-accent focus-visible:ring-ring inline-flex size-9 items-center justify-center rounded transition-colors focus-visible:ring-2 focus-visible:outline-none\"\n                  aria-label=\"Next\"\n                  onClick={() => shiftAnchor(1)}\n                >\n                  <span className=\"text-muted-foreground text-sm\">›</span>\n                </button>\n              </div>\n\n              {picker === 'week' && (\n                <div className=\"flex flex-col gap-1\">\n                  {weekGrid.map((week, i) => (\n                    <button\n                      key={i}\n                      type=\"button\"\n                      data-uipkge=\"\"\n                      data-slot=\"week-picker-cell\"\n                      data-active={isWeekSelected(week.start) || undefined}\n                      disabled={isWeekDisabled(week.start)}\n                      aria-pressed={isWeekSelected(week.start)}\n                      aria-disabled={isWeekDisabled(week.start) || undefined}\n                      className={cn(\n                        'hover:bg-accent focus-visible:ring-ring flex items-center justify-between rounded px-3 py-2 text-sm transition-colors focus-visible:ring-2 focus-visible:outline-none disabled:opacity-30 disabled:hover:bg-transparent',\n                        isWeekSelected(week.start) && 'bg-primary text-primary-foreground hover:bg-primary',\n                      )}\n                      onClick={() => pickWeek(week.start)}\n                    >\n                      <span className=\"font-medium\">Week {week.weekNum}</span>\n                      <span\n                        className={cn(\n                          'text-muted-foreground text-xs',\n                          isWeekSelected(week.start) && 'text-primary-foreground',\n                        )}\n                      >\n                        {week.start.getDate()}{' '}\n                        {new Intl.DateTimeFormat(locale, { month: 'short' }).format(week.start)} –{' '}\n                        {week.end.getDate()}{' '}\n                        {new Intl.DateTimeFormat(locale, { month: 'short' }).format(week.end)}\n                      </span>\n                    </button>\n                  ))}\n                </div>\n              )}\n\n              {picker === 'quarter' && (\n                <div className=\"grid grid-cols-2 gap-2\">\n                  {QUARTER_LABELS.map((label, q) => (\n                    <button\n                      key={q}\n                      type=\"button\"\n                      data-uipkge=\"\"\n                      data-slot=\"quarter-picker-cell\"\n                      data-active={isQuarterSelected(q) || undefined}\n                      disabled={isQuarterDisabled(q)}\n                      aria-pressed={isQuarterSelected(q)}\n                      aria-disabled={isQuarterDisabled(q) || undefined}\n                      className={cn(\n                        'hover:bg-accent focus-visible:ring-ring rounded px-4 py-6 text-sm font-medium transition-colors focus-visible:ring-2 focus-visible:outline-none disabled:opacity-30 disabled:hover:bg-transparent',\n                        isQuarterSelected(q) && 'bg-primary text-primary-foreground hover:bg-primary',\n                      )}\n                      onClick={() => pickQuarter(q)}\n                    >\n                      {label}\n                    </button>\n                  ))}\n                </div>\n              )}\n\n              {picker === 'month' && (\n                <div className=\"grid grid-cols-3 gap-2\">\n                  {monthLabels.map((label, m) => (\n                    <button\n                      key={m}\n                      type=\"button\"\n                      data-uipkge=\"\"\n                      data-slot=\"month-picker-cell\"\n                      data-active={isMonthSelected(m) || undefined}\n                      disabled={isMonthDisabled(m)}\n                      aria-pressed={isMonthSelected(m)}\n                      aria-disabled={isMonthDisabled(m) || undefined}\n                      className={cn(\n                        'hover:bg-accent focus-visible:ring-ring rounded px-2 py-2 text-sm transition-colors focus-visible:ring-2 focus-visible:outline-none disabled:opacity-30 disabled:hover:bg-transparent',\n                        isMonthSelected(m) && 'bg-primary text-primary-foreground hover:bg-primary',\n                      )}\n                      onClick={() => pickMonth(m)}\n                    >\n                      {label}\n                    </button>\n                  ))}\n                </div>\n              )}\n\n              {picker === 'year' && (\n                <div className=\"grid grid-cols-3 gap-2\">\n                  {yearGrid.map((y) => (\n                    <button\n                      key={y}\n                      type=\"button\"\n                      data-uipkge=\"\"\n                      data-slot=\"year-picker-cell\"\n                      data-active={isYearSelected(y) || undefined}\n                      disabled={isYearDisabled(y)}\n                      aria-pressed={isYearSelected(y)}\n                      aria-disabled={isYearDisabled(y) || undefined}\n                      className={cn(\n                        'hover:bg-accent focus-visible:ring-ring rounded px-2 py-2 text-sm tabular-nums transition-colors focus-visible:ring-2 focus-visible:outline-none disabled:opacity-30 disabled:hover:bg-transparent',\n                        isYearSelected(y) && 'bg-primary text-primary-foreground hover:bg-primary',\n                      )}\n                      onClick={() => pickYear(y)}\n                    >\n                      {y}\n                    </button>\n                  ))}\n                </div>\n              )}\n            </div>\n          ) : (\n            <div className=\"flex\">\n              {presetGroups.length > 0 && (\n                <aside className=\"flex w-40 flex-col gap-1 border-r p-2\">\n                  {presetGroups.map((group, gIdx) => (\n                    <React.Fragment key={gIdx}>\n                      {group.category && (\n                        <div className=\"text-muted-foreground px-2 pt-1 text-xs font-semibold tracking-wider uppercase\">\n                          {group.category}\n                        </div>\n                      )}\n                      {group.presets.map((p) => (\n                        <button\n                          key={p.label}\n                          type=\"button\"\n                          className=\"hover:bg-accent focus-visible:ring-ring rounded-md px-2 py-1.5 text-left text-xs transition-colors focus-visible:ring-2 focus-visible:outline-none\"\n                          onClick={() => applyPreset(p)}\n                        >\n                          {p.label}\n                        </button>\n                      ))}\n                    </React.Fragment>\n                  ))}\n                </aside>\n              )}\n\n              {type === 'range' ? (\n                <RangeCalendar\n                  selected={calendarValue as DateRange | undefined}\n                  numberOfMonths={effectiveNumberOfMonths}\n                  weekStartsOn={weekStartsOn}\n                  fixedWeeks={fixedWeeks}\n                  disabled={calendarDisabled}\n                  startMonth={minDate ? stripTime(minDate) : undefined}\n                  endMonth={maxDate ? stripTime(maxDate) : undefined}\n                  onSelect={readOnly ? undefined : handleCalendarUpdate}\n                />\n              ) : (\n                <Calendar\n                  mode={type === 'multiple' ? 'multiple' : 'single'}\n                  selected={calendarValue as Date | Date[] | undefined}\n                  numberOfMonths={effectiveNumberOfMonths}\n                  weekStartsOn={weekStartsOn}\n                  fixedWeeks={fixedWeeks}\n                  disabled={calendarDisabled}\n                  captionLayout={captionLayout}\n                  startMonth={captionLayout && minDate ? stripTime(minDate) : undefined}\n                  endMonth={captionLayout && maxDate ? stripTime(maxDate) : undefined}\n                  components={calendarComponents}\n                  onSelect={readOnly ? undefined : handleCalendarUpdate}\n                />\n              )}\n\n              {showTime && (\n                <div className=\"flex flex-col border-l\">\n                  <div className=\"flex items-center justify-between border-b px-3 py-2\">\n                    <span className=\"text-muted-foreground text-xs tracking-widest uppercase\">Time</span>\n                    <button\n                      type=\"button\"\n                      className=\"text-primary focus-visible:ring-ring rounded px-2 py-1.5 text-xs font-medium focus-visible:ring-2 focus-visible:outline-none\"\n                      onClick={() => setOpen(false)}\n                    >\n                      Done\n                    </button>\n                  </div>\n                  <TimeColumns\n                    value={timeForColumns}\n                    format={timeFormat}\n                    use24Hour={use24Hour}\n                    minuteStep={minuteStep}\n                    secondStep={secondStep}\n                    visible={open}\n                    disabledHours={disabledTimeConfig?.disabledHours}\n                    disabledMinutes={disabledTimeConfig?.disabledMinutes}\n                    disabledSeconds={disabledTimeConfig?.disabledSeconds}\n                    onValueChange={handleTimeUpdate}\n                  />\n                </div>\n              )}\n            </div>\n          )}\n\n          {needConfirm && (\n            <div className=\"flex items-center justify-end gap-2 border-t px-3 py-2\">\n              <button\n                type=\"button\"\n                className=\"hover:bg-accent focus-visible:ring-ring rounded px-3 py-1.5 text-xs transition-colors focus-visible:ring-2 focus-visible:outline-none\"\n                onClick={cancelPreview}\n              >\n                Cancel\n              </button>\n              <button\n                type=\"button\"\n                className=\"bg-primary text-primary-foreground hover:bg-primary/90 focus-visible:ring-ring rounded px-3 py-1.5 text-xs transition-colors focus-visible:ring-2 focus-visible:outline-none\"\n                onClick={commitPreview}\n              >\n                OK\n              </button>\n            </div>\n          )}\n        </PopoverContent>\n      </Popover>\n    )\n  },\n)\nDatePicker.displayName = 'DatePicker'\n\nexport { DatePicker }",
      "type": "registry:ui",
      "target": "~/components/ui/date-picker/date-picker.tsx"
    },
    {
      "path": "packages/registry-react/components/date-picker/date-picker-utils.ts",
      "content": "export type DatePickerType = 'single' | 'multiple' | 'range'\nexport type DatePickerLayout = 'default' | 'month-and-year' | 'month-only' | 'year-only'\nexport type DatePickerPicker = 'day' | 'week' | 'month' | 'quarter' | 'year'\nexport type DatePickerStatus = 'error' | 'warning'\nexport type DatePickerSize = 'small' | 'middle' | 'large'\nexport type DatePickerPlacement =\n  | 'top'\n  | 'bottom'\n  | 'left'\n  | 'right'\n  | 'topLeft'\n  | 'topRight'\n  | 'bottomLeft'\n  | 'bottomRight'\n\nexport type FormatValue = 'short' | 'medium' | 'long' | 'full' | Intl.DateTimeFormatOptions\n\nexport type SingleValue = string | Date | null\nexport type MultipleValue = (string | Date)[] | null\nexport type RangeValue = { start: string | Date; end?: string | Date } | null\n\nexport interface DatePickerPreset {\n  label: string\n  value: SingleValue | MultipleValue | RangeValue\n  category?: string\n}\n\nexport interface DisabledTimeResult {\n  disabledHours?: () => number[]\n  disabledMinutes?: (selectedHour: number) => number[]\n  disabledSeconds?: (selectedHour: number, selectedMinute: number) => number[]\n}\n\nexport type TimeShape = { h: number; m: number; s: number }\n\nexport type InternalSingle = Date | undefined\nexport type InternalMultiple = Date[]\nexport type InternalRange = { start?: Date; end?: Date }\n\nexport function coerceDate(v: string | Date | null | undefined): Date | null {\n  if (!v) return null\n  if (v instanceof Date) return Number.isNaN(v.getTime()) ? null : v\n  const m = /^(\\d{4})-(\\d{2})-(\\d{2})/.exec(v)\n  if (m) {\n    const d = new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]))\n    return Number.isNaN(d.getTime()) ? null : d\n  }\n  const dt = /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2})(?::(\\d{2}))?/.exec(v)\n  if (dt) {\n    const d = new Date(\n      Number(dt[1]),\n      Number(dt[2]) - 1,\n      Number(dt[3]),\n      Number(dt[4]),\n      Number(dt[5]),\n      dt[6] ? Number(dt[6]) : 0,\n    )\n    return Number.isNaN(d.getTime()) ? null : d\n  }\n  const d = new Date(v)\n  return Number.isNaN(d.getTime()) ? null : d\n}\n\nexport function toISODate(d: Date): string {\n  const y = d.getFullYear()\n  const mo = String(d.getMonth() + 1).padStart(2, '0')\n  const da = String(d.getDate()).padStart(2, '0')\n  return `${y}-${mo}-${da}`\n}\n\nexport function toISODateTime(d: Date, showSeconds: boolean): string {\n  const date = toISODate(d)\n  const h = String(d.getHours()).padStart(2, '0')\n  const m = String(d.getMinutes()).padStart(2, '0')\n  if (showSeconds) {\n    const s = String(d.getSeconds()).padStart(2, '0')\n    return `${date}T${h}:${m}:${s}`\n  }\n  return `${date}T${h}:${m}`\n}\n\nexport function stripTime(d: Date): Date {\n  return new Date(d.getFullYear(), d.getMonth(), d.getDate())\n}\n\nexport function parseTimeShape(value: string, fallback: TimeShape): TimeShape {\n  const m = /^(\\d{1,2}):(\\d{2})(?::(\\d{2}))?$/.exec(value)\n  if (!m) return fallback\n  return { h: Number(m[1]), m: Number(m[2]), s: m[3] ? Number(m[3]) : 0 }\n}\n\nexport function withTime(d: Date, t: TimeShape): Date {\n  return new Date(d.getFullYear(), d.getMonth(), d.getDate(), t.h, t.m, t.s)\n}\n\nexport function weekStart(d: Date, weekStartsOn: number): Date {\n  const dayOfWeek = d.getDay()\n  const offset = (dayOfWeek - weekStartsOn + 7) % 7\n  const out = new Date(d)\n  out.setDate(out.getDate() - offset)\n  return stripTime(out)\n}\n\nexport function weekNumber(d: Date): number {\n  const jsDate = new Date(d)\n  const target = new Date(jsDate.valueOf())\n  const dayNr = (jsDate.getDay() + 6) % 7\n  target.setDate(target.getDate() - dayNr + 3)\n  const firstThursday = target.valueOf()\n  target.setMonth(0, 1)\n  if (target.getDay() !== 4) {\n    target.setMonth(0, 1 + ((4 - target.getDay() + 7) % 7))\n  }\n  return 1 + Math.ceil((firstThursday - target.valueOf()) / 604800000)\n}\n\nexport function defaultRangePresets(): DatePickerPreset[] {\n  const t = stripTime(new Date())\n  const yesterday = new Date(t)\n  yesterday.setDate(yesterday.getDate() - 1)\n  const last7 = new Date(t)\n  last7.setDate(last7.getDate() - 6)\n  const last30 = new Date(t)\n  last30.setDate(last30.getDate() - 29)\n  const monthStart = new Date(t.getFullYear(), t.getMonth(), 1)\n  const lastMonthEnd = new Date(t.getFullYear(), t.getMonth(), 0)\n  const lastMonthStart = new Date(lastMonthEnd.getFullYear(), lastMonthEnd.getMonth(), 1)\n  const ytd = new Date(t.getFullYear(), 0, 1)\n  return [\n    { label: 'Today', value: { start: t, end: t } },\n    { label: 'Yesterday', value: { start: yesterday, end: yesterday } },\n    { label: 'Last 7 days', value: { start: last7, end: t } },\n    { label: 'Last 30 days', value: { start: last30, end: t } },\n    { label: 'This month', value: { start: monthStart, end: t } },\n    { label: 'Last month', value: { start: lastMonthStart, end: lastMonthEnd } },\n    { label: 'Year to date', value: { start: ytd, end: t } },\n  ]\n}\n\nexport function coerceShape(\n  type: DatePickerType,\n  v: SingleValue | MultipleValue | RangeValue | undefined,\n): InternalSingle | InternalMultiple | InternalRange | undefined {\n  if (type === 'multiple') {\n    if (!Array.isArray(v)) return undefined\n    return v.map(coerceDate).filter((x): x is Date => x != null)\n  }\n  if (type === 'range') {\n    if (!v || Array.isArray(v) || typeof v === 'string' || !('start' in (v as object))) return undefined\n    const r = v as { start: string | Date; end?: string | Date }\n    const start = coerceDate(r.start)\n    const end = r.end ? coerceDate(r.end) : undefined\n    if (!start) return undefined\n    return end ? { start, end } : { start }\n  }\n  return coerceDate(v as SingleValue) ?? undefined\n}\n\nexport function fmtDate(d: Date, locale: string, format: FormatValue) {\n  const opts: Intl.DateTimeFormatOptions =\n    typeof format === 'object' ? format : { dateStyle: format }\n  return new Intl.DateTimeFormat(locale, opts).format(d)\n}\n\nexport function fmtMonth(d: Date, locale: string) {\n  return new Intl.DateTimeFormat(locale, { month: 'long', year: 'numeric' }).format(d)\n}\n\nexport function fmtTime(d: Date, showSeconds: boolean, use24Hour: boolean) {\n  const opts: Intl.DateTimeFormatOptions = showSeconds\n    ? { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: !use24Hour }\n    : { hour: '2-digit', minute: '2-digit', hour12: !use24Hour }\n  return new Intl.DateTimeFormat(undefined, opts).format(d)\n}",
      "type": "registry:ui",
      "target": "~/components/ui/date-picker/date-picker-utils.ts"
    },
    {
      "path": "packages/registry-react/components/date-picker/index.ts",
      "content": "export {\n  DatePicker,\n  type DatePickerProps,\n  type DatePickerType,\n  type DatePickerLayout,\n  type DatePickerPicker,\n  type DatePickerStatus,\n  type DatePickerSize,\n  type DatePickerPlacement,\n  type FormatValue,\n  type SingleValue,\n  type MultipleValue,\n  type RangeValue,\n  type DatePickerPreset,\n  type DisabledTimeResult,\n} from './date-picker'",
      "type": "registry:ui",
      "target": "~/components/ui/date-picker/index.ts"
    }
  ],
  "dependencies": [
    "lucide-react"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/react/button.json",
    "https://uipkge.dev/r/react/calendar.json",
    "https://uipkge.dev/r/react/popover.json",
    "https://uipkge.dev/r/react/range-calendar.json",
    "https://uipkge.dev/r/react/time-picker.json"
  ],
  "description": "Date input that opens a Calendar in a Popover. Handles parsing, formatting, min/max bounds, and disabled dates. Use the Range Calendar version for from/to selections.",
  "categories": [
    "date-time"
  ]
}