{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "date-picker",
  "title": "Date Picker",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-svelte/components/date-picker/DatePicker.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { Snippet } from 'svelte'\n  import type {\n    DatePickerLayout,\n    DatePickerPicker,\n    DatePickerPlacement,\n    DatePickerPreset,\n    DatePickerSize,\n    DatePickerStatus,\n    DatePickerType,\n    DisabledTimeResult,\n    FormatValue,\n    MultipleValue,\n    RangeValue,\n    SingleValue,\n  } from './date-picker-utils'\n\n  export 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\n  export interface DatePickerProps {\n    /** Controlled value (`bind: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. Receives the cell's date. */\n    renderCell?: Snippet<[Date]>\n    triggerClassName?: string\n    className?: string\n    /** Svelte-idiomatic alias of `className` (merged after it). */\n    class?: string\n    /** The trigger <button>, via `bind:ref` (React's forwarded ref). */\n    ref?: HTMLButtonElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { CalendarDate, type DateValue } from '@internationalized/date'\n  import { Calendar as CalendarIcon, ChevronLeft, ChevronRight, X } from '@lucide/svelte'\n  import { Button } from '$lib/components/ui/button'\n  import { Calendar, type CalendarRange } from '$lib/components/ui/calendar'\n  import { RangeCalendar, type DateRange } from '$lib/components/ui/range-calendar'\n  import { Popover, PopoverContent, PopoverTrigger } from '$lib/components/ui/popover'\n  import { TimeColumns } from '$lib/components/ui/time-picker'\n  import { cn } from '$lib/utils'\n  import {\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 InternalMultiple,\n    type InternalRange,\n    type InternalSingle,\n    type TimeShape,\n  } from './date-picker-utils'\n\n  type InternalValue = InternalSingle | InternalMultiple | InternalRange | undefined\n\n  let {\n    value = $bindable(),\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    class: classProp,\n    ref = $bindable(null),\n  }: DatePickerProps = $props()\n\n  const QUARTER_LABELS = ['Q1', 'Q2', 'Q3', 'Q4']\n  const QUARTER_MONTHS = [1, 4, 7, 10] as const\n\n  function compareDates(a: Date, b: Date): number {\n    return stripTime(a).getTime() - stripTime(b).getTime()\n  }\n\n  function fmtDateTime(d: Date, loc: string, fmt: FormatValue, secs: boolean, h24: boolean) {\n    return `${fmtDate(d, loc, fmt)} ${fmtTime(d, secs, h24)}`.trim()\n  }\n\n  function quarterStart(year: number, quarterIdx: number): Date {\n    return new Date(year, QUARTER_MONTHS[quarterIdx]! - 1, 1)\n  }\n\n  function weekAnchorForMonth(year: number, month: number, wso: number): Date {\n    return weekStart(new Date(year, month - 1, 1), wso)\n  }\n\n  /** React maps `layout` to react-day-picker's captionLayout; the Svelte Calendar takes the layout name directly. */\n  function layoutToCalendarLayout(l: DatePickerLayout): 'month-and-year' | 'month-only' | 'year-only' | undefined {\n    return l === 'default' ? undefined : l\n  }\n\n  function rangeFromCalendar(r: DateRange | undefined): InternalRange | undefined {\n    if (!r?.start) return undefined\n    return r.end ? { start: r.start, end: r.end } : { start: r.start }\n  }\n\n  function rangeToCalendar(r: InternalRange | undefined): DateRange | undefined {\n    if (!r?.start) return undefined\n    return { start: stripTime(r.start), end: r.end ? stripTime(r.end) : undefined }\n  }\n\n  // The Svelte Calendar speaks @internationalized/date; the public API stays on JS Dates (React parity).\n  function toCalendarDate(d: Date): CalendarDate {\n    return new CalendarDate(d.getFullYear(), d.getMonth() + 1, d.getDate())\n  }\n\n  function fromDateValue(d: DateValue): Date {\n    return new Date(d.year, d.month - 1, d.day)\n  }\n\n  function getLastTime(v: InternalValue, t: DatePickerType, dt: string): TimeShape {\n    if (t === 'single' && v instanceof Date) {\n      if (v.getHours() || v.getMinutes() || v.getSeconds()) {\n        return { h: v.getHours(), m: v.getMinutes(), s: v.getSeconds() }\n      }\n    }\n    if (t === 'range') {\n      const r = v as InternalRange\n      if (r?.start && (r.start.getHours() || r.start.getMinutes() || r.start.getSeconds())) {\n        return { h: r.start.getHours(), m: r.start.getMinutes(), s: r.start.getSeconds() }\n      }\n    }\n    return parseTimeShape(dt, { h: 12, m: 0, s: 0 })\n  }\n\n  let open = $state(false)\n  let previewValue = $state<InternalValue>(undefined)\n  const isControlled = $derived(value !== undefined)\n  // Uncontrolled seed: intentionally the initial `type` / `defaultValue` only (React useState parity).\n  // svelte-ignore state_referenced_locally\n  let internalState = $state<InternalValue>(coerceShape(type, defaultValue ?? null))\n\n  const internal = $derived<InternalValue>(isControlled ? coerceShape(type, value) : internalState)\n  const activeValue = $derived<InternalValue>(needConfirm ? (previewValue ?? internal) : internal)\n\n  const effectiveFormat = $derived(dateFormat ?? format)\n  const effectiveNumberOfMonths = $derived(numberOfMonths ?? (type === 'range' ? 2 : 1))\n  const effectivePresets = $derived(presets ?? (type === 'range' ? defaultRangePresets() : undefined))\n\n  const presetGroups = $derived.by(() => {\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  })\n\n  const minDate = $derived(coerceDate(minValue) ?? undefined)\n  const maxDate = $derived(coerceDate(maxValue) ?? undefined)\n\n  // svelte-ignore state_referenced_locally\n  let monthYearAnchor = $state<Date>(\n    (() => {\n      const v = coerceShape(type, defaultValue ?? null)\n      if (v instanceof Date) return v\n      return stripTime(new Date())\n    })(),\n  )\n\n  $effect(() => {\n    if (!open) previewValue = undefined\n  })\n\n  $effect(() => {\n    if (open) {\n      const v = internal\n      if (v instanceof Date) monthYearAnchor = v\n      else monthYearAnchor = stripTime(new Date())\n    }\n  })\n\n  function serializeDate(d: Date) {\n    return showTime ? toISODateTime(d, showSeconds) : toISODate(d)\n  }\n\n  function emit(out: SingleValue | MultipleValue | RangeValue) {\n    value = out\n    onValueChange?.(out)\n  }\n\n  function emitOut(v: InternalValue) {\n    if (type === 'multiple') {\n      const arr = (v as InternalMultiple) ?? []\n      emit(arr.map(serializeDate))\n      return\n    }\n    if (type === 'range') {\n      // Clear must emit null. Incomplete ranges (start only) stay local until both ends exist.\n      if (!v) {\n        emit(null)\n        return\n      }\n      const r = v as InternalRange\n      if (!r.start || !r.end) return\n      emit({ start: serializeDate(r.start), end: serializeDate(r.end) })\n      return\n    }\n    const single = v as InternalSingle\n    emit(single ? serializeDate(single) : null)\n  }\n\n  function handleUpdate(v: InternalValue) {\n    if (!isControlled) internalState = v\n    emitOut(v)\n    if (needConfirm) return\n    if (type === 'single' && v && !showTime) open = false\n    if (type === 'range') {\n      const r = v as InternalRange | undefined\n      if (r?.start && r?.end && !showTime) open = false\n    }\n  }\n\n  function clear(event: Event) {\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      open = 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      open = false\n      return\n    }\n    if (type === 'range') {\n      const r = v as RangeValue\n      if (!r?.start) {\n        handleUpdate(undefined)\n        open = 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        open = false\n        return\n      }\n      handleUpdate(end ? { start, end } : { start })\n      open = false\n      return\n    }\n    handleUpdate(coerceDate(v as SingleValue) ?? undefined)\n    open = false\n  }\n\n  function commitPreview() {\n    if (needConfirm && previewValue !== undefined) {\n      handleUpdate(previewValue)\n      previewValue = undefined\n    }\n    open = false\n  }\n\n  function cancelPreview() {\n    previewValue = undefined\n    open = false\n  }\n\n  const display = $derived.by(() => {\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\n  const hasValue = $derived.by(() => {\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  })\n\n  // Prefer preview (confirm mode) so time columns track the uncommitted selection.\n  const lastTime = $derived(getLastTime(activeValue, type, defaultTime))\n\n  function handleCalendarUpdate(v: unknown) {\n    if (readOnly) return\n    if (needConfirm) {\n      if (type === 'multiple') {\n        const arr = (v as Date[]) ?? []\n        previewValue = 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) previewValue = 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          previewValue = end ? { start, end } : { start }\n        }\n      } else {\n        const d = v as Date | undefined\n        previewValue = 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  /** Svelte Calendar emits DateValue(s); convert to JS Dates before the shared React logic. */\n  function handleDayCalendarUpdate(v: DateValue | DateValue[] | CalendarRange | undefined) {\n    if (v != null && typeof v === 'object' && 'start' in v) return // range never occurs here (single/multiple only)\n    if (Array.isArray(v)) handleCalendarUpdate(v.map(fromDateValue))\n    else handleCalendarUpdate(v ? fromDateValue(v) : undefined)\n  }\n\n  function handleTimeUpdate(timeValue: string) {\n    const base = needConfirm ? (previewValue ?? internal) : internal\n    if (!base) return\n    const t = parseTimeShape(timeValue, lastTime)\n    if (type === 'single') {\n      const next = withTime(base as Date, t)\n      if (needConfirm) {\n        previewValue = next\n        return\n      }\n      handleUpdate(next)\n      return\n    }\n    if (type === 'range') {\n      const r = base 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      const next = end ? { start, end } : { start }\n      if (needConfirm) {\n        previewValue = next\n        return\n      }\n      handleUpdate(next)\n    }\n  }\n\n  const timeForColumns = $derived(\n    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\n  const timeFormat = $derived(showSeconds ? 'HH:mm:ss' : 'HH:mm')\n\n  const disabledTimeConfig = $derived.by(() => {\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  })\n\n  // React feeds react-day-picker `disabled` matchers (before min / after max / disabledDate);\n  // the Svelte Calendar takes one predicate over DateValue.\n  const calendarDisabled = $derived.by(() => {\n    if (!minDate && !maxDate && !disabledDate) return undefined\n    const min = minDate ? stripTime(minDate) : undefined\n    const max = maxDate ? stripTime(maxDate) : undefined\n    return (dv: DateValue) => {\n      const d = fromDateValue(dv)\n      if (min && d < min) return true\n      if (max && d > max) return true\n      return disabledDate?.(d) ?? false\n    }\n  })\n\n  const calendarValue = $derived.by(() => {\n    // Use activeValue so needConfirm preview highlights on the grid (not only internal).\n    const v = activeValue\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  })\n\n  const dayCalendarValue = $derived.by((): DateValue | DateValue[] | undefined => {\n    const v = calendarValue\n    if (Array.isArray(v)) return v.map(toCalendarDate)\n    if (v instanceof Date) return toCalendarDate(v)\n    return undefined\n  })\n\n  const calendarLayout = $derived(layoutToCalendarLayout(layout))\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 = $derived(placementMap[placement] ?? { side: 'bottom', align: 'start' })\n\n  const buttonSize = $derived(size === 'small' ? 'sm' : size === 'large' ? 'lg' : 'default')\n\n  const triggerClasses = $derived(\n    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      classProp,\n    ),\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      monthYearAnchor = new Date(y + dir, m - 1, 1)\n    } else if (picker === 'year' || picker === 'quarter') {\n      monthYearAnchor = new Date(y + dir, 0, 1)\n    }\n  }\n\n  const monthLabels = $derived(\n    Array.from({ length: 12 }, (_, idx) =>\n      new Intl.DateTimeFormat(locale, { month: 'short' }).format(new Date(2024, idx, 1)),\n    ),\n  )\n\n  const yearGrid = $derived.by(() => {\n    const y = monthYearAnchor.getFullYear()\n    const start = y - (y % 12)\n    return Array.from({ length: 12 }, (_, i) => start + i)\n  })\n\n  const weekGrid = $derived.by(() => {\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  })\n\n  const shortMonth = $derived(new Intl.DateTimeFormat(locale, { month: 'short' }))\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      previewValue = 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 v.getFullYear() === monthYearAnchor.getFullYear() && Math.ceil((v.getMonth() + 1) / 3) === qIdx + 1\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      previewValue = 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      previewValue = 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        previewValue = d\n        return\n      }\n      handleUpdate(d)\n    } else {\n      monthYearAnchor = d\n    }\n  }\n\n  const monthYearLabel = $derived(\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\n  const showAlternatePicker = $derived(picker !== 'day' && type === 'single')\n\n  function onClearClick(e: MouseEvent) {\n    e.stopPropagation()\n    // Also mark handled: PopoverTrigger skips its toggle on defaultPrevented clicks.\n    e.preventDefault()\n    clear(e)\n  }\n\n  function onClearKeydown(e: KeyboardEvent) {\n    if (e.key === 'Enter' || e.key === ' ') {\n      e.preventDefault()\n      e.stopPropagation()\n      clear(e)\n    }\n  }\n\n  function onDone() {\n    // In confirm mode, Done must commit preview — closing alone discards it.\n    if (needConfirm) commitPreview()\n    else open = false\n  }\n</script>\n\n{#snippet dayCell({ day }: { day: CalendarDate; month: CalendarDate })}\n  {@render renderCell?.(fromDateValue(day))}\n{/snippet}\n\n<Popover bind:open>\n  <PopoverTrigger>\n    {#snippet child({ props })}\n      <Button\n        {...props}\n        bind:ref\n        type=\"button\"\n        variant=\"outline\"\n        size={buttonSize}\n        {disabled}\n        class={triggerClasses}\n        data-uipkge=\"\"\n        data-slot=\"date-picker\"\n      >\n        <CalendarIcon class=\"size-4\" aria-hidden=\"true\" />\n        <span class=\"flex-1 truncate\">{display || placeholder}</span>\n        {#if clearable && hasValue && !disabled && !readOnly}\n          <!-- span + role=button: a nested <button> inside the trigger Button is invalid HTML -->\n          <span\n            role=\"button\"\n            tabindex={0}\n            class=\"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={onClearClick}\n            onkeydown={onClearKeydown}\n          >\n            <X class=\"size-3.5\" aria-hidden=\"true\" />\n          </span>\n        {/if}\n      </Button>\n    {/snippet}\n  </PopoverTrigger>\n  <PopoverContent class=\"w-auto p-0\" side={popoverPlacement.side} align={popoverPlacement.align}>\n    {#if showCurrentDate && type === 'single' && (picker === 'day' || picker === 'week')}\n      <div class=\"flex justify-end border-b px-3 py-2\">\n        <button\n          type=\"button\"\n          class=\"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    {/if}\n\n    {#if showAlternatePicker}\n      <div class=\"w-[260px] p-3\" data-uipkge=\"\" data-slot=\"month-year-picker\">\n        <div class=\"mb-3 flex items-center justify-between\">\n          <button\n            type=\"button\"\n            class=\"border-input hover:bg-accent focus-visible:ring-ring inline-flex size-7 items-center justify-center rounded-md border bg-transparent transition-colors focus-visible:ring-1 focus-visible:outline-none\"\n            aria-label=\"Previous\"\n            onclick={() => shiftAnchor(-1)}\n          >\n            <ChevronLeft class=\"text-muted-foreground size-4\" aria-hidden=\"true\" />\n          </button>\n          <span class=\"text-sm font-medium\">{monthYearLabel}</span>\n          <button\n            type=\"button\"\n            class=\"border-input hover:bg-accent focus-visible:ring-ring inline-flex size-7 items-center justify-center rounded-md border bg-transparent transition-colors focus-visible:ring-1 focus-visible:outline-none\"\n            aria-label=\"Next\"\n            onclick={() => shiftAnchor(1)}\n          >\n            <ChevronRight class=\"text-muted-foreground size-4\" aria-hidden=\"true\" />\n          </button>\n        </div>\n\n        {#if picker === 'week'}\n          <div class=\"flex flex-col gap-1\">\n            {#each weekGrid as week, i (i)}\n              <button\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                class={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 class=\"font-medium\">Week {week.weekNum}</span>\n                <span class={cn('text-muted-foreground text-xs', isWeekSelected(week.start) && 'text-primary-foreground')}>\n                  {week.start.getDate()}\n                  {shortMonth.format(week.start)} – {week.end.getDate()}\n                  {shortMonth.format(week.end)}\n                </span>\n              </button>\n            {/each}\n          </div>\n        {/if}\n\n        {#if picker === 'quarter'}\n          <div class=\"grid grid-cols-2 gap-2\">\n            {#each QUARTER_LABELS as label, q (q)}\n              <button\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                class={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            {/each}\n          </div>\n        {/if}\n\n        {#if picker === 'month'}\n          <div class=\"grid grid-cols-3 gap-2\">\n            {#each monthLabels as label, m (m)}\n              <button\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                class={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            {/each}\n          </div>\n        {/if}\n\n        {#if picker === 'year'}\n          <div class=\"grid grid-cols-3 gap-2\">\n            {#each yearGrid as y (y)}\n              <button\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                class={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            {/each}\n          </div>\n        {/if}\n      </div>\n    {:else}\n      <div class=\"flex\">\n        {#if presetGroups.length > 0}\n          <aside class=\"flex w-40 flex-col gap-1 border-r p-2\">\n            {#each presetGroups as group, gIdx (gIdx)}\n              {#if group.category}\n                <div class=\"text-muted-foreground px-2 pt-1 text-xs font-semibold tracking-wider uppercase\">\n                  {group.category}\n                </div>\n              {/if}\n              {#each group.presets as p (p.label)}\n                <button\n                  type=\"button\"\n                  class=\"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              {/each}\n            {/each}\n          </aside>\n        {/if}\n\n        {#if type === 'range'}\n          <!-- defaultValue seeds the calendar's own state: its `value ?? internal` fallback can't be cleared through `value`. -->\n          <RangeCalendar\n            value={calendarValue as DateRange | undefined}\n            defaultValue={calendarValue as DateRange | undefined}\n            numberOfMonths={effectiveNumberOfMonths}\n            {weekStartsOn}\n            {fixedWeeks}\n            minValue={minDate ? stripTime(minDate) : undefined}\n            maxValue={maxDate ? stripTime(maxDate) : undefined}\n            isDateDisabled={disabledDate}\n            readonly={readOnly}\n            onValueChange={handleCalendarUpdate}\n          />\n        {:else}\n          <Calendar\n            type={type === 'multiple' ? 'multiple' : 'single'}\n            value={dayCalendarValue}\n            numberOfMonths={effectiveNumberOfMonths}\n            {weekStartsOn}\n            {fixedWeeks}\n            isDateDisabled={calendarDisabled}\n            layout={calendarLayout}\n            minValue={calendarLayout && minDate ? toCalendarDate(minDate) : undefined}\n            maxValue={calendarLayout && maxDate ? toCalendarDate(maxDate) : undefined}\n            cell={renderCell ? dayCell : undefined}\n            readonly={readOnly}\n            onValueChange={handleDayCalendarUpdate}\n          />\n        {/if}\n\n        {#if showTime}\n          <div class=\"flex flex-col border-l\">\n            <div class=\"flex items-center justify-between border-b px-3 py-2\">\n              <span class=\"text-muted-foreground text-xs tracking-widest uppercase\">Time</span>\n              <button\n                type=\"button\"\n                class=\"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={onDone}\n              >\n                Done\n              </button>\n            </div>\n            <TimeColumns\n              value={timeForColumns}\n              format={timeFormat}\n              {use24Hour}\n              {minuteStep}\n              {secondStep}\n              visible={open}\n              disabledHours={disabledTimeConfig?.disabledHours}\n              disabledMinutes={disabledTimeConfig?.disabledMinutes}\n              disabledSeconds={disabledTimeConfig?.disabledSeconds}\n              onValueChange={handleTimeUpdate}\n            />\n          </div>\n        {/if}\n      </div>\n    {/if}\n\n    {#if needConfirm}\n      <div class=\"flex items-center justify-end gap-2 border-t px-3 py-2\">\n        <button\n          type=\"button\"\n          class=\"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          class=\"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    {/if}\n  </PopoverContent>\n</Popover>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/date-picker/DatePicker.svelte"
    },
    {
      "path": "packages/registry-svelte/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' | 'bottom' | 'left' | 'right' | 'topLeft' | 'topRight' | 'bottomLeft' | '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  // Date-time first: the date-only pattern also matches `YYYY-MM-DDTHH:mm` and would drop the time.\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 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 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 = 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}\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/date-picker/date-picker-utils.ts"
    },
    {
      "path": "packages/registry-svelte/components/date-picker/index.ts",
      "content": "export {\n  default as 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 './DatePicker.svelte'\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/date-picker/index.ts"
    }
  ],
  "dependencies": [
    "@internationalized/date",
    "@lucide/svelte"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/svelte/button.json",
    "https://uipkge.dev/r/svelte/calendar.json",
    "https://uipkge.dev/r/svelte/popover.json",
    "https://uipkge.dev/r/svelte/range-calendar.json",
    "https://uipkge.dev/r/svelte/time-picker.json"
  ],
  "description": "Form date control: trigger + popover + Calendar/RangeCalendar + optional time. Single/multiple/range, week/month/quarter/year pickers, presets, confirm mode, min/max and disabled dates. For an always-visible grid, use Calendar instead.",
  "categories": [
    "date-time"
  ]
}