{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "date-picker",
  "title": "Date Picker",
  "author": "Zubeyr <https://zubeyr.dev>",
  "description": "Date, date range, date + time, time and month pickers that share the Date Range Picker’s calendar and panel: typed entry in plain words, dayjs-powered locale formatting, translatable labels, min and max limits, Today, Now and This month shortcuts, clear, keyboard navigation and a panel that flips to stay on screen.",
  "dependencies": [
    "lucide-react",
    "dayjs"
  ],
  "registryDependencies": [
    "https://tomei.zubeyr.dev/ui/r/date-range-picker.json"
  ],
  "files": [
    {
      "path": "registry/dashboard/components/date-picker/date-picker.tsx",
      "content": "'use client'\n\nimport { useEffect, useRef, useState } from 'react'\nimport { Calendar } from 'lucide-react'\n\nimport {\n  CalendarMonth,\n  monthTitle,\n} from '@/registry/dashboard/components/date-range-picker/calendar-month'\nimport {\n  addDays,\n  fromInputValue,\n  sameMonth,\n  startOfDay,\n  startOfMonth,\n  toInputValue,\n} from '@/registry/dashboard/components/date-range-picker/dates'\n\nimport {\n  FooterButton,\n  NavButton,\n  Picker,\n  PickerFooter,\n  PickerInput,\n  type PickerBaseProps,\n  usePickerClose,\n} from './picker'\nimport { formatDay, label, parseDay, type PickerLabels } from './locale'\n\ntype DatePickerProps = PickerBaseProps & {\n  value: string\n  onChange: (value: string) => void\n  min?: string\n  max?: string\n}\n\nconst keyOffsets: Record<string, number> = {\n  ArrowLeft: -1,\n  ArrowRight: 1,\n  ArrowUp: -7,\n  ArrowDown: 7,\n}\n\nexport function parseDate(text: string, locale?: string) {\n  return parseDay(text, locale)\n}\n\nexport function DatePicker({\n  value,\n  onChange,\n  min,\n  max,\n  placeholder,\n  locale,\n  labels,\n  ...props\n}: DatePickerProps) {\n  const selected = fromInputValue(value)\n  return (\n    <Picker\n      icon={<Calendar />}\n      text={selected ? formatDay(selected, locale) : ''}\n      label={label(labels, 'chooseDate')}\n      placeholder={placeholder ?? label(labels, 'pickDate')}\n      labels={labels}\n      panelClassName=\"flex flex-col gap-3 p-3\"\n      onClear={() => onChange('')}\n      {...props}\n    >\n      <DatePanel\n        locale={locale}\n        labels={labels}\n        value={value}\n        min={min}\n        max={max}\n        onPick={onChange}\n      />\n    </Picker>\n  )\n}\n\nfunction DatePanel({\n  locale,\n  labels,\n  value,\n  min,\n  max,\n  onPick,\n}: {\n  value: string\n  min?: string\n  max?: string\n  onPick: (value: string) => void\n  locale?: string\n  labels?: Partial<PickerLabels>\n}) {\n  const close = usePickerClose()\n  const done = (v: string) => {\n    onPick(v)\n    close()\n  }\n  const [today] = useState(() => startOfDay(new Date()))\n  const selected = fromInputValue(value)\n  const minDate = (min && fromInputValue(min)) || undefined\n  const maxDate = (max && fromInputValue(max)) || undefined\n  const allowed = (d: Date) => (!minDate || d >= minDate) && (!maxDate || d <= maxDate)\n  const start = selected ?? (allowed(today) ? today : (minDate ?? maxDate ?? today))\n\n  const [view, setView] = useState(() => startOfMonth(start))\n  const [focused, setFocused] = useState(start)\n  const [focusTick, setFocusTick] = useState(1)\n  const [typed, setTyped] = useState(selected ? toInputValue(selected) : '')\n  const [typedError, setTypedError] = useState(false)\n  const grid = useRef<HTMLDivElement>(null)\n\n  useEffect(() => {\n    if (!focusTick) return\n    grid.current?.querySelector<HTMLButtonElement>(`[data-date=\"${focused.getTime()}\"]`)?.focus()\n  }, [focusTick, focused, view])\n\n  const pick = (date: Date) => allowed(date) && done(toInputValue(date))\n\n  const commitTyped = () => {\n    const date = parseDate(typed, locale)\n    if (!date || !allowed(date)) return setTypedError(!!typed.trim())\n    pick(date)\n  }\n\n  return (\n    <>\n      <PickerInput\n        value={typed}\n        placeholder={label(labels, 'typeDate', { example: formatDay(today, locale) })}\n        label={label(labels, 'chooseDate')}\n        invalid={typedError}\n        onChange={(v) => {\n          setTyped(v)\n          setTypedError(false)\n          setFocusTick(0)\n          const date = parseDate(v, locale)\n          if (date && !sameMonth(date, view)) setView(startOfMonth(date))\n        }}\n        onCommit={commitTyped}\n        onKeyDown={(e) => {\n          if (e.key !== 'ArrowDown') return\n          e.preventDefault()\n          setFocusTick(focusTick + 1)\n        }}\n      />\n      <div ref={grid}>\n        <CalendarMonth\n          month={view}\n          start={selected}\n          end={selected}\n          preview={null}\n          focused={focused}\n          today={today}\n          minDate={minDate}\n          maxDate={maxDate}\n          onSelect={pick}\n          locale={locale}\n          onHover={() => {}}\n          onKeyDown={(event, date) => {\n            const page = event.key === 'PageUp' ? -1 : event.key === 'PageDown' ? 1 : 0\n            const offset = keyOffsets[event.key]\n            if (offset === undefined && !page) return\n            event.preventDefault()\n            const next = page\n              ? new Date(date.getFullYear(), date.getMonth() + page, date.getDate())\n              : addDays(date, offset)\n            setFocused(next)\n            setFocusTick(focusTick + 1)\n            if (!sameMonth(next, view)) setView(startOfMonth(next))\n          }}\n          header={\n            <>\n              <NavButton\n                direction={-1}\n                label={label(labels, 'previousMonth')}\n                onClick={() => setView(startOfMonth(view, -1))}\n              />\n              <span className=\"flex-1 text-center text-sm font-semibold\">\n                {monthTitle(view, locale)}\n              </span>\n              <NavButton\n                direction={1}\n                label={label(labels, 'nextMonth')}\n                onClick={() => setView(startOfMonth(view, 1))}\n              />\n            </>\n          }\n        />\n      </div>\n      <PickerFooter>\n        <FooterButton onClick={() => pick(today)} disabled={!allowed(today)}>\n          {label(labels, 'today')}\n        </FooterButton>\n        <FooterButton onClick={() => done('')} disabled={!value}>\n          {label(labels, 'clear')}\n        </FooterButton>\n      </PickerFooter>\n    </>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/dashboard/date-picker/date-picker.tsx"
    },
    {
      "path": "registry/dashboard/components/date-picker/date-range-field.tsx",
      "content": "'use client'\n\nimport { useEffect, useRef, useState } from 'react'\nimport { CalendarRange } from 'lucide-react'\n\nimport { cn } from '@/lib/utils'\nimport {\n  CalendarMonth,\n  monthTitle,\n} from '@/registry/dashboard/components/date-range-picker/calendar-month'\nimport {\n  addDays,\n  fromInputValue,\n  sameMonth,\n  startOfDay,\n  startOfMonth,\n  toInputValue,\n  type DatePreset,\n} from '@/registry/dashboard/components/date-range-picker/dates'\n\nimport { parseDate } from './date-picker'\nimport { formatDay, label, type PickerLabels } from './locale'\nimport {\n  FooterButton,\n  NavButton,\n  Picker,\n  PickerFooter,\n  PickerInput,\n  usePickerClose,\n  type PickerBaseProps,\n} from './picker'\n\ntype DateRangeFieldProps = PickerBaseProps & {\n  value: string\n  onChange: (value: string) => void\n  min?: string\n  max?: string\n  presets?: DatePreset[]\n}\n\nconst keyOffsets: Record<string, number> = {\n  ArrowLeft: -1,\n  ArrowRight: 1,\n  ArrowUp: -7,\n  ArrowDown: 7,\n}\n\nconst rangeText = (start: Date, end: Date, locale?: string) =>\n  `${formatDay(start, locale)} – ${formatDay(end, locale)}`\n\nexport const splitRange = (value: string) => {\n  const [a = '', b = ''] = value.split('/')\n  return [fromInputValue(a), fromInputValue(b)] as const\n}\n\nexport function DateRangeField({\n  value,\n  onChange,\n  min,\n  max,\n  presets,\n  placeholder,\n  locale,\n  labels,\n  ...props\n}: DateRangeFieldProps) {\n  const [start, end] = splitRange(value)\n  return (\n    <Picker\n      icon={<CalendarRange />}\n      text={start && end ? rangeText(start, end, locale) : ''}\n      label={label(labels, 'chooseRange')}\n      placeholder={placeholder ?? label(labels, 'pickDates')}\n      labels={labels}\n      panelClassName=\"flex\"\n      onClear={() => onChange('')}\n      {...props}\n    >\n      <RangePanel\n        locale={locale}\n        labels={labels}\n        value={value}\n        min={min}\n        max={max}\n        presets={presets}\n        onPick={onChange}\n      />\n    </Picker>\n  )\n}\n\nfunction RangePanel({\n  locale,\n  labels,\n  value,\n  min,\n  max,\n  presets,\n  onPick,\n}: {\n  value: string\n  min?: string\n  max?: string\n  presets?: DatePreset[]\n  onPick: (value: string) => void\n  locale?: string\n  labels?: Partial<PickerLabels>\n}) {\n  const close = usePickerClose()\n  const [today] = useState(() => startOfDay(new Date()))\n  const [initialStart, initialEnd] = splitRange(value)\n  const minDate = (min && fromInputValue(min)) || undefined\n  const maxDate = (max && fromInputValue(max)) || undefined\n  const allowed = (d: Date) => (!minDate || d >= minDate) && (!maxDate || d <= maxDate)\n\n  const [start, setStart] = useState<Date | null>(initialStart)\n  const [end, setEnd] = useState<Date | null>(initialEnd)\n  const [preview, setPreview] = useState<Date | null>(null)\n  const anchor = initialStart ?? (allowed(today) ? today : (minDate ?? today))\n  const [view, setView] = useState(() => startOfMonth(anchor))\n  const [focused, setFocused] = useState(anchor)\n  const [focusTick, setFocusTick] = useState(1)\n  const [inputs, setInputs] = useState({\n    start: initialStart ? toInputValue(initialStart) : '',\n    end: initialEnd ? toInputValue(initialEnd) : '',\n  })\n  const grid = useRef<HTMLDivElement>(null)\n\n  useEffect(() => {\n    if (!focusTick) return\n    grid.current?.querySelector<HTMLButtonElement>(`[data-date=\"${focused.getTime()}\"]`)?.focus()\n  }, [focusTick, focused, view])\n\n  const commit = (a: Date, b: Date) => {\n    onPick(`${toInputValue(a)}/${toInputValue(b)}`)\n    close()\n  }\n\n  const show = (a: Date | null, b: Date | null) => {\n    setStart(a)\n    setEnd(b)\n    setInputs({ start: a ? toInputValue(a) : '', end: b ? toInputValue(b) : '' })\n  }\n\n  const select = (date: Date) => {\n    if (!allowed(date)) return\n    setFocused(date)\n    if (!start || end) return show(date, null)\n    const [a, b] = date < start ? [date, start] : [start, date]\n    show(a, b)\n    commit(a, b)\n  }\n\n  const commitInput = (key: 'start' | 'end') => {\n    const date = parseDate(inputs[key], locale)\n    if (!date || !allowed(date)) return show(start, end)\n    const a = key === 'start' ? date : (start ?? date)\n    const b = key === 'end' ? date : (end ?? date)\n    const [s, e] = a <= b ? [a, b] : [b, a]\n    show(s, e)\n    if (!sameMonth(date, view)) setView(startOfMonth(date))\n  }\n\n  const month = (offset: number, header: React.ReactNode) => (\n    <CalendarMonth\n      month={startOfMonth(view, offset)}\n      start={start}\n      end={end}\n      preview={start && !end ? preview : null}\n      focused={focused}\n      today={today}\n      minDate={minDate}\n      maxDate={maxDate}\n      onSelect={select}\n      onHover={setPreview}\n      locale={locale}\n      onKeyDown={(event, date) => {\n        const offsetDays = keyOffsets[event.key]\n        if (offsetDays === undefined) return\n        event.preventDefault()\n        const next = addDays(date, offsetDays)\n        setFocused(next)\n        setFocusTick(focusTick + 1)\n        if (start && !end) setPreview(next)\n        if (!sameMonth(next, view) && !sameMonth(next, startOfMonth(view, 1)))\n          setView(startOfMonth(next, next < view ? 0 : -1))\n      }}\n      header={header}\n    />\n  )\n\n  const title = (offset: number) => (\n    <span className=\"flex-1 text-center text-sm font-semibold\">\n      {monthTitle(startOfMonth(view, offset), locale)}\n    </span>\n  )\n\n  return (\n    <>\n      {presets?.length ? (\n        <ul className=\"hidden w-40 shrink-0 flex-col gap-px overflow-y-auto border-r p-2 md:flex\">\n          {presets.map((preset) => (\n            <li key={preset.label}>\n              <button\n                type=\"button\"\n                onClick={() => {\n                  const r = preset.range(today)\n                  commit(r.start, r.end)\n                }}\n                className=\"flex h-8 w-full items-center rounded-lg px-2.5 text-left text-sm hover:bg-muted\"\n              >\n                {preset.label}\n              </button>\n            </li>\n          ))}\n        </ul>\n      ) : null}\n      <div className=\"flex flex-col gap-3 p-3\">\n        <div className=\"flex items-center gap-2\">\n          <PickerInput\n            value={inputs.start}\n            placeholder={label(labels, 'start')}\n            label={label(labels, 'startDate')}\n            onChange={(v) => {\n              setInputs({ ...inputs, start: v })\n              setFocusTick(0)\n            }}\n            onCommit={() => commitInput('start')}\n          />\n          <span className=\"text-muted-foreground\">→</span>\n          <PickerInput\n            value={inputs.end}\n            placeholder={label(labels, 'end')}\n            label={label(labels, 'endDate')}\n            onChange={(v) => {\n              setInputs({ ...inputs, end: v })\n              setFocusTick(0)\n            }}\n            onCommit={() => commitInput('end')}\n          />\n        </div>\n        <div ref={grid} className=\"flex gap-6\">\n          {month(\n            0,\n            <>\n              <NavButton\n                direction={-1}\n                label={label(labels, 'previousMonth')}\n                onClick={() => setView(startOfMonth(view, -1))}\n              />\n              {title(0)}\n              <span className=\"size-8 max-md:hidden\" />\n              <span className=\"md:hidden\">\n                <NavButton\n                  direction={1}\n                  label={label(labels, 'nextMonth')}\n                  onClick={() => setView(startOfMonth(view, 1))}\n                />\n              </span>\n            </>,\n          )}\n          <div className=\"max-md:hidden\">\n            {month(\n              1,\n              <>\n                <span className=\"size-8\" />\n                {title(1)}\n                <NavButton\n                  direction={1}\n                  label={label(labels, 'nextMonth')}\n                  onClick={() => setView(startOfMonth(view, 1))}\n                />\n              </>,\n            )}\n          </div>\n        </div>\n        <PickerFooter>\n          <span\n            className={cn('text-xs', start && end ? 'text-foreground' : 'text-muted-foreground')}\n          >\n            {start && end\n              ? rangeText(start, end, locale)\n              : start\n                ? label(labels, 'pickEnd')\n                : label(labels, 'pickStart')}\n          </span>\n          <div className=\"flex gap-1\">\n            {start && end && (\n              <FooterButton onClick={() => commit(start, end)}>\n                {label(labels, 'apply')}\n              </FooterButton>\n            )}\n            <FooterButton\n              onClick={() => {\n                onPick('')\n                close()\n              }}\n              disabled={!value}\n            >\n              {label(labels, 'clear')}\n            </FooterButton>\n          </div>\n        </PickerFooter>\n      </div>\n    </>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/dashboard/date-picker/date-range-field.tsx"
    },
    {
      "path": "registry/dashboard/components/date-picker/date-time-picker.tsx",
      "content": "'use client'\n\nimport { useEffect, useRef, useState } from 'react'\nimport { CalendarClock } from 'lucide-react'\n\nimport { cn } from '@/lib/utils'\nimport {\n  CalendarMonth,\n  monthTitle,\n} from '@/registry/dashboard/components/date-range-picker/calendar-month'\nimport {\n  addDays,\n  fromInputValue,\n  sameMonth,\n  startOfDay,\n  startOfMonth,\n  toInputValue,\n} from '@/registry/dashboard/components/date-range-picker/dates'\n\nimport {\n  FooterButton,\n  NavButton,\n  Picker,\n  PickerFooter,\n  usePickerClose,\n  type PickerBaseProps,\n} from './picker'\nimport { formatDay, label, type PickerLabels } from './locale'\nimport { formatTime } from './time-picker'\n\ntype DateTimePickerProps = PickerBaseProps & {\n  value: string\n  onChange: (value: string) => void\n  step?: number\n  min?: string\n  max?: string\n}\n\nconst pad = (n: number) => String(n).padStart(2, '0')\nconst keyOffsets: Record<string, number> = {\n  ArrowLeft: -1,\n  ArrowRight: 1,\n  ArrowUp: -7,\n  ArrowDown: 7,\n}\n\nexport function formatDateTime(value: string, locale?: string) {\n  const [date, time] = value.split('T')\n  const d = fromInputValue(date ?? '')\n  if (!d) return value\n  return time ? `${formatDay(d, locale)}, ${formatTime(time, locale)}` : formatDay(d, locale)\n}\n\nexport function DateTimePicker({\n  value,\n  onChange,\n  step = 15,\n  min,\n  max,\n  placeholder,\n  locale,\n  labels,\n  ...props\n}: DateTimePickerProps) {\n  return (\n    <Picker\n      icon={<CalendarClock />}\n      text={value ? formatDateTime(value, locale) : ''}\n      label={label(labels, 'chooseDateTime')}\n      placeholder={placeholder ?? label(labels, 'pickDateTime')}\n      labels={labels}\n      panelClassName=\"flex flex-col gap-3 p-3\"\n      onClear={() => onChange('')}\n      {...props}\n    >\n      <DateTimePanel\n        locale={locale}\n        labels={labels}\n        value={value}\n        step={step}\n        min={min}\n        max={max}\n        onPick={onChange}\n      />\n    </Picker>\n  )\n}\n\nfunction DateTimePanel({\n  locale,\n  labels,\n  value,\n  step,\n  min,\n  max,\n  onPick,\n}: {\n  value: string\n  step: number\n  min?: string\n  max?: string\n  onPick: (value: string) => void\n  locale?: string\n  labels?: Partial<PickerLabels>\n}) {\n  const close = usePickerClose()\n  const [today] = useState(() => startOfDay(new Date()))\n  const [initialDate, initialTime = ''] = value.split('T')\n  const minDay = min ? fromInputValue(min.slice(0, 10)) : null\n  const maxDay = max ? fromInputValue(max.slice(0, 10)) : null\n  const dayAllowed = (d: Date) => (!minDay || d >= minDay) && (!maxDay || d <= maxDay)\n\n  const startDay =\n    fromInputValue(initialDate ?? '') ?? (dayAllowed(today) ? today : (minDay ?? today))\n  const [day, setDay] = useState<Date | null>(fromInputValue(initialDate ?? ''))\n  const [time, setTime] = useState(initialTime)\n  const [view, setView] = useState(() => startOfMonth(startDay))\n  const [focused, setFocused] = useState(startDay)\n  const [focusTick, setFocusTick] = useState(1)\n  const grid = useRef<HTMLDivElement>(null)\n  const list = useRef<HTMLUListElement>(null)\n\n  const slots = Array.from({ length: Math.ceil(1440 / step) }, (_, i) => {\n    const m = i * step\n    return `${pad(Math.floor(m / 60))}:${pad(m % 60)}`\n  })\n  const stamp = (d: Date, t: string) => `${toInputValue(d)}T${t}`\n  const allowed = (d: Date, t: string) =>\n    (!min || stamp(d, t) >= min) && (!max || stamp(d, t) <= max)\n\n  useEffect(() => {\n    if (!focusTick) return\n    grid.current?.querySelector<HTMLButtonElement>(`[data-date=\"${focused.getTime()}\"]`)?.focus()\n  }, [focusTick, focused, view])\n\n  useEffect(() => {\n    const target = initialTime || slots[Math.floor((9 * 60) / step)]\n    list.current\n      ?.querySelector<HTMLElement>(`[data-time=\"${target}\"]`)\n      ?.scrollIntoView({ block: 'center' })\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [])\n\n  const finish = (d: Date, t: string) => {\n    onPick(stamp(d, t))\n    close()\n  }\n\n  return (\n    <>\n      <div className=\"flex gap-3\">\n        <div ref={grid}>\n          <CalendarMonth\n            month={view}\n            start={day}\n            end={day}\n            preview={null}\n            focused={focused}\n            today={today}\n            minDate={minDay ?? undefined}\n            maxDate={maxDay ?? undefined}\n            onSelect={(d) => {\n              setDay(d)\n              setFocused(d)\n              setFocusTick(0)\n              if (time && allowed(d, time)) finish(d, time)\n            }}\n            onHover={() => {}}\n            locale={locale}\n            onKeyDown={(event, date) => {\n              const offset = keyOffsets[event.key]\n              if (offset === undefined) return\n              event.preventDefault()\n              const next = addDays(date, offset)\n              setFocused(next)\n              setFocusTick(focusTick + 1)\n              if (!sameMonth(next, view)) setView(startOfMonth(next))\n            }}\n            header={\n              <>\n                <NavButton\n                  direction={-1}\n                  label={label(labels, 'previousMonth')}\n                  onClick={() => setView(startOfMonth(view, -1))}\n                />\n                <span className=\"flex-1 text-center text-sm font-semibold\">\n                  {monthTitle(view, locale)}\n                </span>\n                <NavButton\n                  direction={1}\n                  label={label(labels, 'nextMonth')}\n                  onClick={() => setView(startOfMonth(view, 1))}\n                />\n              </>\n            }\n          />\n        </div>\n        <div className=\"flex w-32 flex-col gap-2 border-l pl-3\">\n          <span className=\"flex h-8 items-center text-sm font-semibold\">\n            {label(labels, 'time')}\n          </span>\n          <ul\n            ref={list}\n            role=\"listbox\"\n            aria-label=\"Times\"\n            className=\"-mr-1 flex max-h-[17rem] [scrollbar-width:thin] [scrollbar-gutter:stable] flex-col gap-px overflow-y-auto py-0.5 pr-1.5\"\n          >\n            {slots.map((t) => {\n              const ok = !day || allowed(day, t)\n              return (\n                <li key={t}>\n                  <button\n                    type=\"button\"\n                    role=\"option\"\n                    data-time={t}\n                    aria-selected={t === time}\n                    disabled={!ok}\n                    onClick={() => {\n                      setTime(t)\n                      if (day) finish(day, t)\n                    }}\n                    className={cn(\n                      'flex h-8 w-full items-center rounded-lg px-2 text-sm tabular-nums outline-none focus-visible:ring-2 focus-visible:ring-ring/60 disabled:cursor-not-allowed disabled:opacity-30',\n                      t === time ? 'bg-primary text-primary-foreground' : 'hover:bg-muted',\n                    )}\n                  >\n                    {formatTime(t, locale)}\n                  </button>\n                </li>\n              )\n            })}\n          </ul>\n        </div>\n      </div>\n      <PickerFooter>\n        <span className=\"text-xs text-muted-foreground\">\n          {day && time\n            ? formatDateTime(stamp(day, time), locale)\n            : day\n              ? label(labels, 'pickTimeNext')\n              : time\n                ? label(labels, 'pickDayNext')\n                : label(labels, 'pickDayTime')}\n        </span>\n        <div className=\"flex gap-1\">\n          <FooterButton\n            onClick={() => {\n              const now = new Date()\n              const n = Math.ceil((now.getHours() * 60 + now.getMinutes()) / step) * step\n              const t = `${pad(Math.floor(Math.min(n, 1439) / 60))}:${pad(Math.min(n, 1439) % 60)}`\n              finish(startOfDay(now), t)\n            }}\n          >\n            {label(labels, 'now')}\n          </FooterButton>\n          <FooterButton\n            onClick={() => {\n              onPick('')\n              close()\n            }}\n            disabled={!value}\n          >\n            {label(labels, 'clear')}\n          </FooterButton>\n        </div>\n      </PickerFooter>\n    </>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/dashboard/date-picker/date-time-picker.tsx"
    },
    {
      "path": "registry/dashboard/components/date-picker/locale.ts",
      "content": "import dayjs from 'dayjs'\nimport customParseFormat from 'dayjs/plugin/customParseFormat'\nimport localizedFormat from 'dayjs/plugin/localizedFormat'\n\ndayjs.extend(customParseFormat)\ndayjs.extend(localizedFormat)\n\nexport { dayjs }\n\nexport const pickerLabels = {\n  today: 'Today',\n  now: 'Now',\n  thisMonth: 'This month',\n  clear: 'Clear',\n  apply: 'Apply',\n  time: 'Time',\n  start: 'Start',\n  end: 'End',\n  startDate: 'Start date',\n  endDate: 'End date',\n  typeDate: 'Type a date, e.g. {example}',\n  typeTime: 'Type a time, e.g. {example}',\n  pickDate: 'Pick a date',\n  pickTime: 'Pick a time',\n  pickMonth: 'Pick a month',\n  pickDates: 'Pick dates',\n  pickDateTime: 'Pick a date and time',\n  pickStart: 'Pick the start date',\n  pickEnd: 'Now pick the end date',\n  pickDayTime: 'Pick a day and a time',\n  pickTimeNext: 'Now pick a time',\n  pickDayNext: 'Now pick a day',\n  use: 'Use {time}',\n  noTimes: 'No matching times',\n  previousMonth: 'Previous month',\n  nextMonth: 'Next month',\n  previousYear: 'Previous year',\n  nextYear: 'Next year',\n  chooseDate: 'Choose a date',\n  chooseTime: 'Choose a time',\n  chooseMonth: 'Choose a month',\n  chooseRange: 'Choose a date range',\n  chooseDateTime: 'Choose a date and time',\n  clearValue: 'Clear',\n}\n\nexport type PickerLabels = typeof pickerLabels\n\nexport const label = (\n  labels: Partial<PickerLabels> | undefined,\n  key: keyof PickerLabels,\n  vars: Record<string, string> = {},\n) => (labels?.[key] ?? pickerLabels[key]).replace(/\\{(\\w+)\\}/g, (_, k: string) => vars[k] ?? '')\n\nconst DATE_INPUTS = [\n  'YYYY-MM-DD',\n  'D/M/YYYY',\n  'DD/MM/YYYY',\n  'M/D/YYYY',\n  'MM/DD/YYYY',\n  'D.M.YYYY',\n  'D MMM YYYY',\n  'D MMMM YYYY',\n  'MMM D YYYY',\n  'MMM D, YYYY',\n  'MMMM D YYYY',\n  'MMMM D, YYYY',\n]\n\nconst TIME_INPUTS = [\n  'H:mm',\n  'HH:mm',\n  'h:mm a',\n  'h:mma',\n  'h a',\n  'ha',\n  'hmm a',\n  'hmma',\n  'HHmm',\n  'Hmm',\n  'H',\n]\n\nexport function parseDay(text: string, locale?: string): Date | null {\n  const t = text.trim()\n  if (!t) return null\n  for (const format of DATE_INPUTS) {\n    const d = locale ? dayjs(t, format, locale, true) : dayjs(t, format, true)\n    if (d.isValid()) return d.startOf('day').toDate()\n  }\n  return null\n}\n\nexport function parseClock(text: string, locale?: string): string | null {\n  const t = text\n    .trim()\n    .toLowerCase()\n    .replace(/\\.(?=m)/g, '')\n    .replace(/([ap])$/, '$1m')\n  if (!t) return null\n  for (const format of TIME_INPUTS) {\n    const d = locale ? dayjs(t, format, locale, true) : dayjs(t, format, true)\n    if (d.isValid()) return d.format('HH:mm')\n  }\n  return null\n}\n\nexport const formatDay = (date: Date, locale?: string) =>\n  (locale ? dayjs(date).locale(locale) : dayjs(date)).format('ll')\n\nexport const formatClock = (hhmm: string, locale?: string) => {\n  const d = dayjs(hhmm, 'HH:mm', true)\n  return d.isValid() ? (locale ? d.locale(locale) : d).format('LT') : hhmm\n}\n\nexport const formatMonthYear = (date: Date, locale?: string) =>\n  (locale ? dayjs(date).locale(locale) : dayjs(date)).format('MMM YYYY')\n\nexport const monthName = (date: Date, locale?: string) =>\n  (locale ? dayjs(date).locale(locale) : dayjs(date)).format('MMMM YYYY')\n\nexport const shortMonths = (locale?: string) =>\n  Array.from({ length: 12 }, (_, i) =>\n    (locale ? dayjs().locale(locale) : dayjs()).month(i).format('MMM'),\n  )\n\nexport const fullMonth = (year: number, month: number, locale?: string) =>\n  (locale ? dayjs().locale(locale) : dayjs()).year(year).month(month).format('MMMM YYYY')\n",
      "type": "registry:component",
      "target": "components/dashboard/date-picker/locale.ts"
    },
    {
      "path": "registry/dashboard/components/date-picker/month-picker.tsx",
      "content": "'use client'\n\nimport { useEffect, useRef, useState } from 'react'\nimport { CalendarDays } from 'lucide-react'\n\nimport { cn } from '@/lib/utils'\n\nimport {\n  FooterButton,\n  NavButton,\n  Picker,\n  PickerFooter,\n  type PickerBaseProps,\n  usePickerClose,\n} from './picker'\nimport { formatMonthYear, fullMonth, label, shortMonths, type PickerLabels } from './locale'\n\ntype MonthPickerProps = PickerBaseProps & {\n  value: string\n  onChange: (value: string) => void\n  min?: string\n  max?: string\n}\n\nconst pad = (n: number) => String(n).padStart(2, '0')\nconst key = (year: number, month: number) => `${year}-${pad(month + 1)}`\n\nexport function formatMonth(value: string, locale?: string) {\n  const [y, m] = value.split('-').map(Number)\n  return y && m ? formatMonthYear(new Date(y, m - 1, 1), locale) : value\n}\n\nexport function MonthPicker({\n  value,\n  onChange,\n  min,\n  max,\n  placeholder,\n  locale,\n  labels,\n  ...props\n}: MonthPickerProps) {\n  return (\n    <Picker\n      icon={<CalendarDays />}\n      text={value ? formatMonth(value, locale) : ''}\n      label={label(labels, 'chooseMonth')}\n      placeholder={placeholder ?? label(labels, 'pickMonth')}\n      labels={labels}\n      panelClassName=\"flex w-64 flex-col gap-3 p-3\"\n      onClear={() => onChange('')}\n      {...props}\n    >\n      <MonthPanel\n        locale={locale}\n        labels={labels}\n        value={value}\n        min={min}\n        max={max}\n        onPick={onChange}\n      />\n    </Picker>\n  )\n}\n\nfunction MonthPanel({\n  locale,\n  labels,\n  value,\n  min,\n  max,\n  onPick,\n}: {\n  value: string\n  min?: string\n  max?: string\n  onPick: (value: string) => void\n  locale?: string\n  labels?: Partial<PickerLabels>\n}) {\n  const close = usePickerClose()\n  const months = shortMonths(locale)\n  const done = (v: string) => {\n    onPick(v)\n    close()\n  }\n  const [now] = useState(() => new Date())\n  const thisMonth = key(now.getFullYear(), now.getMonth())\n  const [y, m] = value.split('-').map(Number)\n  const [year, setYear] = useState(y || now.getFullYear())\n  const [focused, setFocused] = useState(m ? m - 1 : now.getMonth())\n  const grid = useRef<HTMLDivElement>(null)\n\n  useEffect(() => {\n    grid.current?.querySelector<HTMLButtonElement>(`[data-month=\"${focused}\"]`)?.focus()\n  }, [focused, year])\n\n  const allowed = (k: string) => (!min || k >= min) && (!max || k <= max)\n  const pick = (k: string) => allowed(k) && done(k)\n\n  return (\n    <>\n      <div className=\"flex h-8 items-center justify-between\">\n        <NavButton\n          direction={-1}\n          label={label(labels, 'previousYear')}\n          onClick={() => setYear(year - 1)}\n        />\n        <span className=\"text-sm font-semibold tabular-nums\">{year}</span>\n        <NavButton\n          direction={1}\n          label={label(labels, 'nextYear')}\n          onClick={() => setYear(year + 1)}\n        />\n      </div>\n      <div ref={grid} role=\"grid\" aria-label={String(year)} className=\"grid grid-cols-3 gap-1\">\n        {months.map((name, i) => {\n          const k = key(year, i)\n          const on = k === value\n          return (\n            <button\n              key={name}\n              type=\"button\"\n              data-month={i}\n              tabIndex={i === focused ? 0 : -1}\n              disabled={!allowed(k)}\n              aria-pressed={on}\n              aria-label={fullMonth(year, i, locale)}\n              onClick={() => pick(k)}\n              onKeyDown={(e) => {\n                const moves: Record<string, number> = {\n                  ArrowLeft: -1,\n                  ArrowRight: 1,\n                  ArrowUp: -3,\n                  ArrowDown: 3,\n                }\n                if (!(e.key in moves)) return\n                e.preventDefault()\n                const next = i + moves[e.key]\n                if (next < 0) setYear(year - 1)\n                if (next > 11) setYear(year + 1)\n                setFocused((next + 12) % 12)\n              }}\n              className={cn(\n                'h-9 rounded-full text-sm outline-none focus-visible:ring-2 focus-visible:ring-ring/60 disabled:cursor-not-allowed disabled:opacity-30',\n                on\n                  ? 'bg-primary text-primary-foreground'\n                  : 'hover:bg-muted hover:ring-1 hover:ring-border',\n                k === thisMonth && 'font-bold',\n              )}\n            >\n              {name}\n            </button>\n          )\n        })}\n      </div>\n      <PickerFooter>\n        <FooterButton onClick={() => pick(thisMonth)} disabled={!allowed(thisMonth)}>\n          {label(labels, 'thisMonth')}\n        </FooterButton>\n        <FooterButton onClick={() => done('')} disabled={!value}>\n          {label(labels, 'clear')}\n        </FooterButton>\n      </PickerFooter>\n    </>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/dashboard/date-picker/month-picker.tsx"
    },
    {
      "path": "registry/dashboard/components/date-picker/picker.tsx",
      "content": "'use client'\n\nimport { createContext, useContext, useEffect, useLayoutEffect, useRef, useState } from 'react'\nimport { X } from 'lucide-react'\n\nimport { cn } from '@/lib/utils'\n\nimport { label as labelOf, type PickerLabels } from './locale'\n\nexport type PickerBaseProps = {\n  id?: string\n  placeholder?: string\n  disabled?: boolean\n  invalid?: boolean\n  describedBy?: string\n  clearable?: boolean\n  className?: string\n  locale?: string\n  labels?: Partial<PickerLabels>\n}\n\nconst CloseContext = createContext<() => void>(() => {})\n\nexport const usePickerClose = () => useContext(CloseContext)\n\ntype PickerProps = PickerBaseProps & {\n  icon: React.ReactNode\n  text?: string\n  label: string\n  panelClassName?: string\n  onClear?: () => void\n  children: React.ReactNode\n}\n\nexport function Picker({\n  id,\n  icon,\n  text,\n  label,\n  placeholder,\n  disabled,\n  invalid,\n  describedBy,\n  clearable = true,\n  className,\n  panelClassName,\n  onClear,\n  children,\n  labels,\n}: PickerProps) {\n  const [open, setOpen] = useState(false)\n  const [place, setPlace] = useState<{ top: number; left: number } | null>(null)\n  const root = useRef<HTMLDivElement>(null)\n  const trigger = useRef<HTMLButtonElement>(null)\n  const panel = useRef<HTMLDivElement>(null)\n\n  const close = () => {\n    setOpen(false)\n    trigger.current?.focus()\n  }\n\n  useLayoutEffect(() => {\n    if (!open) return\n    const update = () => {\n      if (!panel.current || !trigger.current) return\n      const t = trigger.current.getBoundingClientRect()\n      const p = panel.current.getBoundingClientRect()\n      const gap = 6\n      const below = window.innerHeight - t.bottom - gap\n      const above = t.top - gap\n      const up = below < p.height && above > below\n      const left = Math.max(8, Math.min(t.left, window.innerWidth - p.width - 8))\n      setPlace({ top: Math.max(8, up ? t.top - gap - p.height : t.bottom + gap), left })\n    }\n    update()\n    window.addEventListener('scroll', update, true)\n    window.addEventListener('resize', update)\n    return () => {\n      window.removeEventListener('scroll', update, true)\n      window.removeEventListener('resize', update)\n    }\n  }, [open])\n\n  useEffect(() => {\n    if (!open) return\n    const onPointer = (event: PointerEvent) => {\n      if (!root.current?.contains(event.target as Node)) setOpen(false)\n    }\n    document.addEventListener('pointerdown', onPointer)\n    return () => document.removeEventListener('pointerdown', onPointer)\n  }, [open])\n\n  const showClear = clearable && !!text && !disabled && !!onClear\n\n  return (\n    <div\n      ref={root}\n      className={cn('relative w-full min-w-0', className)}\n      onKeyDown={(event) => {\n        if (event.key !== 'Escape' || !open) return\n        event.stopPropagation()\n        setOpen(false)\n        trigger.current?.focus()\n      }}\n    >\n      <button\n        ref={trigger}\n        id={id}\n        type=\"button\"\n        aria-haspopup=\"dialog\"\n        aria-expanded={open}\n        aria-describedby={describedBy}\n        data-invalid={invalid || undefined}\n        disabled={disabled}\n        onClick={() => setOpen(!open)}\n        onKeyDown={(e) => {\n          if (e.key === 'ArrowDown' && !open) {\n            e.preventDefault()\n            setOpen(true)\n          }\n        }}\n        className={cn(\n          'flex h-8 w-full min-w-0 items-center gap-2 rounded-lg border border-input bg-card pl-2.5 text-left text-sm whitespace-nowrap text-foreground transition-[border-color] duration-150 outline-none hover:border-ring focus-visible:border-ring disabled:cursor-default disabled:bg-muted/40 disabled:hover:border-input data-invalid:border-destructive [&_svg]:size-3.5 [&_svg]:shrink-0',\n          showClear ? 'pr-8' : 'pr-2.5',\n          open && 'border-ring',\n        )}\n      >\n        <span className=\"flex text-muted-foreground\">{icon}</span>\n        <span className={cn('min-w-0 flex-1 truncate', !text && 'text-muted-foreground')}>\n          {text || placeholder}\n        </span>\n      </button>\n      {showClear && (\n        <button\n          type=\"button\"\n          aria-label={labelOf(labels, 'clearValue')}\n          onClick={() => {\n            onClear()\n            trigger.current?.focus()\n          }}\n          className=\"absolute top-1/2 right-1.5 flex size-5 -translate-y-1/2 items-center justify-center rounded-md text-muted-foreground hover:bg-muted hover:text-foreground [&_svg]:size-3\"\n        >\n          <X />\n        </button>\n      )}\n      {open && (\n        <div\n          ref={panel}\n          style={{\n            top: place?.top ?? 0,\n            left: place?.left ?? 0,\n            visibility: place ? 'visible' : 'hidden',\n          }}\n          role=\"dialog\"\n          aria-label={label}\n          className={cn(\n            'fixed z-[60] rounded-xl bg-popover text-popover-foreground shadow-(--shadow-overlay)',\n            panelClassName,\n          )}\n        >\n          <CloseContext value={close}>{children}</CloseContext>\n        </div>\n      )}\n    </div>\n  )\n}\n\nexport function NavButton({\n  direction,\n  label,\n  onClick,\n}: {\n  direction: -1 | 1\n  label: string\n  onClick: () => void\n}) {\n  return (\n    <button\n      type=\"button\"\n      aria-label={label}\n      onClick={onClick}\n      className=\"flex size-8 items-center justify-center rounded-lg text-muted-foreground hover:bg-muted hover:text-foreground\"\n    >\n      <svg viewBox=\"0 0 16 16\" fill=\"none\" className=\"size-4\" aria-hidden>\n        <path\n          d={direction < 0 ? 'M10 3 5 8l5 5' : 'm6 3 5 5-5 5'}\n          stroke=\"currentColor\"\n          strokeWidth=\"1.6\"\n          strokeLinecap=\"round\"\n          strokeLinejoin=\"round\"\n        />\n      </svg>\n    </button>\n  )\n}\n\nexport function PickerInput({\n  value,\n  placeholder,\n  label,\n  invalid,\n  autoFocus,\n  onChange,\n  onCommit,\n  onKeyDown,\n  inputRef,\n}: {\n  value: string\n  placeholder: string\n  label: string\n  invalid?: boolean\n  autoFocus?: boolean\n  onChange: (value: string) => void\n  onCommit: () => void\n  onKeyDown?: (event: React.KeyboardEvent<HTMLInputElement>) => void\n  inputRef?: React.Ref<HTMLInputElement>\n}) {\n  return (\n    <input\n      ref={inputRef}\n      value={value}\n      placeholder={placeholder}\n      aria-label={label}\n      aria-invalid={invalid || undefined}\n      autoFocus={autoFocus}\n      onChange={(e) => onChange(e.target.value)}\n      onKeyDown={(e) => {\n        if (e.key === 'Enter') {\n          e.preventDefault()\n          onCommit()\n        }\n        onKeyDown?.(e)\n      }}\n      className=\"h-8 w-full min-w-0 rounded-lg border border-input bg-card px-2.5 text-sm tabular-nums outline-none placeholder:text-muted-foreground focus:border-ring aria-invalid:border-destructive\"\n    />\n  )\n}\n\nexport function PickerFooter({ children }: { children: React.ReactNode }) {\n  return <div className=\"flex items-center justify-between gap-2 border-t pt-3\">{children}</div>\n}\n\nexport function FooterButton({\n  children,\n  onClick,\n  disabled,\n}: {\n  children: React.ReactNode\n  onClick: () => void\n  disabled?: boolean\n}) {\n  return (\n    <button\n      type=\"button\"\n      onClick={onClick}\n      disabled={disabled}\n      className=\"h-7 rounded-lg px-2.5 text-xs font-medium text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-40 disabled:hover:bg-transparent\"\n    >\n      {children}\n    </button>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/dashboard/date-picker/picker.tsx"
    },
    {
      "path": "registry/dashboard/components/date-picker/time-picker.tsx",
      "content": "'use client'\n\nimport { useEffect, useRef, useState } from 'react'\nimport { Clock } from 'lucide-react'\n\nimport { cn } from '@/lib/utils'\n\nimport {\n  FooterButton,\n  Picker,\n  PickerFooter,\n  PickerInput,\n  type PickerBaseProps,\n  usePickerClose,\n} from './picker'\nimport { formatClock, label, parseClock, type PickerLabels } from './locale'\n\ntype TimePickerProps = PickerBaseProps & {\n  value: string\n  onChange: (value: string) => void\n  step?: number\n  min?: string\n  max?: string\n}\n\nconst pad = (n: number) => String(n).padStart(2, '0')\nconst minutesOf = (t: string) => {\n  const [h, m] = t.split(':').map(Number)\n  return h * 60 + m\n}\nconst fromMinutes = (n: number) => `${pad(Math.floor(n / 60))}:${pad(n % 60)}`\n\nexport function formatTime(hhmm: string, locale?: string) {\n  return formatClock(hhmm, locale)\n}\n\nexport function parseTime(text: string, locale?: string) {\n  const viaDayjs = parseClock(text, locale)\n  if (viaDayjs) return viaDayjs\n  const t = text.trim().toLowerCase().replace(/\\s+/g, '').replace(/\\./g, '')\n  if (!t) return null\n  const match =\n    /^(\\d{1,2})(?::(\\d{2}))?(a|p|am|pm)?$/.exec(t) ?? /^(\\d{1,2})(\\d{2})(a|p|am|pm)?$/.exec(t)\n  if (!match) return null\n  let h = Number(match[1])\n  const m = Number(match[2] ?? 0)\n  const meridiem = match[3]?.[0]\n  if (meridiem) {\n    if (h < 1 || h > 12) return null\n    h = (h % 12) + (meridiem === 'p' ? 12 : 0)\n  }\n  if (h > 23 || m > 59) return null\n  return `${pad(h)}:${pad(m)}`\n}\n\nexport function TimePicker({\n  value,\n  onChange,\n  step = 15,\n  min,\n  max,\n  placeholder,\n  locale,\n  labels,\n  ...props\n}: TimePickerProps) {\n  return (\n    <Picker\n      icon={<Clock />}\n      text={value ? formatTime(value, locale) : ''}\n      label={label(labels, 'chooseTime')}\n      placeholder={placeholder ?? label(labels, 'pickTime')}\n      labels={labels}\n      panelClassName=\"flex w-56 flex-col gap-2 p-2\"\n      onClear={() => onChange('')}\n      {...props}\n    >\n      <TimePanel\n        locale={locale}\n        labels={labels}\n        value={value}\n        step={step}\n        min={min}\n        max={max}\n        onPick={onChange}\n      />\n    </Picker>\n  )\n}\n\nfunction TimePanel({\n  locale,\n  labels,\n  value,\n  step,\n  min,\n  max,\n  onPick,\n}: {\n  value: string\n  step: number\n  min?: string\n  max?: string\n  onPick: (value: string) => void\n  locale?: string\n  labels?: Partial<PickerLabels>\n}) {\n  const close = usePickerClose()\n  const done = (v: string) => {\n    onPick(v)\n    close()\n  }\n  const [typed, setTyped] = useState('')\n  const [typedError, setTypedError] = useState(false)\n  const [now] = useState(() => {\n    const d = new Date()\n    const n = Math.round((d.getHours() * 60 + d.getMinutes()) / step) * step\n    return fromMinutes(Math.min(n, 1440 - step))\n  })\n  const list = useRef<HTMLUListElement>(null)\n  const input = useRef<HTMLInputElement>(null)\n\n  const allowed = (t: string) => (!min || t >= min) && (!max || t <= max)\n  const slots = Array.from({ length: Math.ceil(1440 / step) }, (_, i) =>\n    fromMinutes(i * step),\n  ).filter(allowed)\n\n  const q = typed.trim().toLowerCase().replace(/\\s/g, '')\n  const exact = parseTime(typed, locale)\n  const shown = q\n    ? slots.filter(\n        (s) =>\n          formatTime(s, locale).toLowerCase().replace(/\\s/g, '').startsWith(q) || s.startsWith(q),\n      )\n    : slots\n\n  const [anchor] = useState(() => {\n    const target = minutesOf(value || now)\n    return slots.reduce(\n      (best, s) =>\n        Math.abs(minutesOf(s) - target) < Math.abs(minutesOf(best) - target) ? s : best,\n      slots[0],\n    )\n  })\n\n  useEffect(() => {\n    list.current\n      ?.querySelector<HTMLElement>(`[data-time=\"${anchor}\"]`)\n      ?.scrollIntoView({ block: 'center' })\n  }, [anchor])\n\n  const pick = (t: string) => (allowed(t) ? done(t) : setTypedError(true))\n\n  const focusSlot = (index: number) => {\n    const buttons = list.current?.querySelectorAll<HTMLButtonElement>('button')\n    if (!buttons?.length) return\n    buttons[Math.max(0, Math.min(buttons.length - 1, index))].focus()\n  }\n\n  return (\n    <>\n      <PickerInput\n        inputRef={input}\n        autoFocus\n        value={typed}\n        placeholder={label(labels, 'typeTime', { example: formatTime('14:30', locale) })}\n        label={label(labels, 'chooseTime')}\n        invalid={typedError}\n        onChange={(v) => {\n          setTyped(v)\n          setTypedError(false)\n        }}\n        onCommit={() => {\n          const t = exact ?? shown[0]\n          if (t) pick(t)\n          else setTypedError(true)\n        }}\n        onKeyDown={(e) => {\n          if (e.key !== 'ArrowDown') return\n          e.preventDefault()\n          const index = shown.indexOf(value || anchor)\n          focusSlot(index >= 0 ? index : 0)\n        }}\n      />\n      {exact && !shown.includes(exact) && allowed(exact) && (\n        <button\n          type=\"button\"\n          onClick={() => pick(exact)}\n          className=\"flex h-8 items-center rounded-lg px-2.5 text-left text-sm hover:bg-muted\"\n        >\n          {label(labels, 'use', { time: formatTime(exact, locale) })}\n        </button>\n      )}\n      <ul\n        ref={list}\n        role=\"listbox\"\n        aria-label=\"Times\"\n        className=\"flex max-h-56 [scrollbar-width:thin] [scrollbar-gutter:stable] flex-col gap-px overflow-y-auto py-0.5 pr-1.5\"\n        onKeyDown={(e) => {\n          const moves: Record<string, number> = {\n            ArrowDown: 1,\n            ArrowUp: -1,\n            PageDown: 8,\n            PageUp: -8,\n          }\n          if (!(e.key in moves)) return\n          e.preventDefault()\n          const buttons = [...(list.current?.querySelectorAll<HTMLButtonElement>('button') ?? [])]\n          const index = buttons.indexOf(e.target as HTMLButtonElement)\n          if (index <= 0 && moves[e.key] < 0) return input.current?.focus()\n          focusSlot(index + moves[e.key])\n        }}\n      >\n        {shown.map((t) => (\n          <li key={t}>\n            <button\n              type=\"button\"\n              role=\"option\"\n              data-time={t}\n              aria-selected={t === value}\n              onClick={() => pick(t)}\n              className={cn(\n                'flex h-8 w-full items-center justify-between rounded-lg px-2.5 text-sm tabular-nums outline-none focus-visible:ring-2 focus-visible:ring-ring/60',\n                t === value ? 'bg-primary text-primary-foreground' : 'hover:bg-muted',\n              )}\n            >\n              {formatTime(t, locale)}\n              {t === now && t !== value && (\n                <span className=\"text-xs text-muted-foreground\">Now</span>\n              )}\n            </button>\n          </li>\n        ))}\n        {!shown.length && !exact && (\n          <li className=\"px-2.5 py-2 text-sm text-muted-foreground\">{label(labels, 'noTimes')}</li>\n        )}\n      </ul>\n      <PickerFooter>\n        <FooterButton onClick={() => pick(now)} disabled={!allowed(now)}>\n          {label(labels, 'now')}\n        </FooterButton>\n        <FooterButton onClick={() => done('')} disabled={!value}>\n          {label(labels, 'clear')}\n        </FooterButton>\n      </PickerFooter>\n    </>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/dashboard/date-picker/time-picker.tsx"
    }
  ],
  "cssVars": {
    "light": {
      "shadow-overlay": "0 16px 40px rgb(0 0 0 / 0.12), 0 0 0 1px rgb(0 0 0 / 0.08)"
    },
    "dark": {
      "shadow-overlay": "0 16px 40px rgb(0 0 0 / 0.5), 0 0 0 1px #0e0e0e"
    }
  },
  "meta": {
    "scenario": "dashboard",
    "kind": "components"
  },
  "categories": [
    "dashboard",
    "inputs",
    "date"
  ],
  "type": "registry:component"
}