{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "date-picker",
  "type": "registry:ui",
  "title": "Date Picker",
  "description": "A ruled field that opens a desk calendar.",
  "categories": [
    "core"
  ],
  "files": [
    {
      "path": "ui/date-picker.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { Popover as PopoverPrimitive } from \"radix-ui\";\nimport { Calendar } from \"./calendar\";\n\n// Popups render in a layer on <body> that copies the paper type of the nearest [data-paper]\n// element (and follows it if it changes). That keeps their theme without trapping them inside the\n// container's stacking context, so sticky headers can't cover them.\nfunction usePaperContainer() {\n  const anchor = React.useRef<HTMLSpanElement>(null);\n  const [container, setContainer] = React.useState<HTMLElement | undefined>();\n  React.useEffect(() => {\n    const source = anchor.current?.closest<HTMLElement>(\"[data-paper]\");\n    if (!source || source === document.documentElement) return;\n    const layer = document.createElement(\"div\");\n    layer.setAttribute(\"data-paper-layer\", \"\");\n    const sync = () => layer.setAttribute(\"data-paper\", source.getAttribute(\"data-paper\") ?? \"\");\n    sync();\n    document.body.appendChild(layer);\n    const observer = new MutationObserver(sync);\n    observer.observe(source, { attributes: true, attributeFilter: [\"data-paper\"] });\n    setContainer(layer);\n    return () => {\n      observer.disconnect();\n      layer.remove();\n    };\n  }, []);\n  return [anchor, container] as const;\n}\n\nexport interface DatePickerProps {\n  value?: Date;\n  defaultValue?: Date;\n  onChange?: (date: Date) => void;\n  placeholder?: string;\n  locale?: string;\n  disabled?: (date: Date) => boolean;\n  id?: string;\n  className?: string;\n}\n\n/** A ruled-line field that opens a desk calendar. */\nexport function DatePicker({\n  value,\n  defaultValue,\n  onChange,\n  placeholder = \"Pick a date\",\n  locale = \"en-US\",\n  disabled,\n  id,\n  className = \"\",\n}: DatePickerProps) {\n  const [inner, setInner] = React.useState(defaultValue);\n  const [open, setOpen] = React.useState(false);\n  const [anchor, container] = usePaperContainer();\n  const date = value ?? inner;\n\n  return (\n    <PopoverPrimitive.Root open={open} onOpenChange={setOpen}>\n      <span ref={anchor} hidden />\n      <PopoverPrimitive.Trigger\n        id={id}\n        className={`flex h-10 w-full cursor-pointer items-center justify-between gap-2 border-0 border-b-[1.5px] border-(--rule) bg-transparent px-0.5 pb-1 pt-2 text-left text-[15px] outline-none transition-colors focus-visible:border-(--pen) data-[state=open]:border-(--pen) ${\n          date ? \"text-(--ink)\" : \"text-(--ink-faint)\"\n        } ${className}`}\n        style={{ fontFamily: \"var(--font-type, 'Courier New', ui-monospace, monospace)\" }}\n      >\n        {date ? new Intl.DateTimeFormat(locale, { day: \"numeric\", month: \"short\", year: \"numeric\" }).format(date) : placeholder}\n        <svg viewBox=\"0 0 24 24\" className=\"size-4 shrink-0 text-(--ink-muted)\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" aria-hidden>\n          <rect x=\"3.5\" y=\"5\" width=\"17\" height=\"15\" rx=\"1.5\" />\n          <path d=\"M3.5 10h17M8 3v4M16 3v4\" strokeLinecap=\"round\" />\n        </svg>\n      </PopoverPrimitive.Trigger>\n      <PopoverPrimitive.Portal container={container}>\n        <PopoverPrimitive.Content align=\"start\" sideOffset={8} data-paper-motion className=\"z-50 outline-none data-[state=open]:animate-[paper-pop-in_.16s_ease-out]\">\n          <Calendar\n            selected={date}\n            locale={locale}\n            disabled={disabled}\n            onSelect={(d) => {\n              setInner(d);\n              onChange?.(d);\n              setOpen(false);\n            }}\n          />\n        </PopoverPrimitive.Content>\n      </PopoverPrimitive.Portal>\n    </PopoverPrimitive.Root>\n  );\n}\n"
    },
    {
      "path": "ui/calendar.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nexport interface CalendarProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onSelect\" | \"defaultValue\"> {\n  selected?: Date;\n  onSelect?: (date: Date) => void;\n  /** Month shown first. Defaults to the selected date or today. */\n  defaultMonth?: Date;\n  /** 0 = Sunday, 1 = Monday */\n  weekStartsOn?: 0 | 1;\n  disabled?: (date: Date) => boolean;\n  /** BCP 47 locale for month and weekday names. Fixed by default so server and client render the same. */\n  locale?: string;\n}\n\nconst type = { fontFamily: \"var(--font-type, 'Courier New', ui-monospace, monospace)\" };\n\nconst sameDay = (a?: Date, b?: Date) =>\n  !!a && !!b && a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();\nconst addDays = (d: Date, n: number) => new Date(d.getFullYear(), d.getMonth(), d.getDate() + n);\nconst startOfMonth = (d: Date) => new Date(d.getFullYear(), d.getMonth(), 1);\n\n/** A desk calendar page. The chosen day gets circled in pen. */\nexport function Calendar({\n  selected,\n  onSelect,\n  defaultMonth,\n  weekStartsOn = 1,\n  disabled,\n  locale = \"en-US\",\n  className = \"\",\n  ...props\n}: CalendarProps) {\n  // No `new Date()` during render: it breaks prerendering and server/client markup can disagree.\n  // Without a selected/default date, the current month fills in once mounted.\n  const initial = defaultMonth ?? selected;\n  const [month, setMonth] = React.useState<Date | undefined>(initial && startOfMonth(initial));\n  const [focus, setFocus] = React.useState<Date | undefined>(selected ?? defaultMonth);\n  const [today, setToday] = React.useState<Date>();\n  const grid = React.useRef<HTMLDivElement>(null);\n  const moved = React.useRef(false);\n\n  React.useEffect(() => {\n    const now = new Date();\n    setToday(now);\n    setMonth((m) => m ?? startOfMonth(now));\n    setFocus((f) => f ?? now);\n  }, []);\n\n  React.useEffect(() => {\n    if (!moved.current) return;\n    moved.current = false;\n    grid.current?.querySelector<HTMLButtonElement>('[tabindex=\"0\"]')?.focus();\n  }, [focus]);\n\n  const first = month && startOfMonth(month);\n  const offset = first ? (first.getDay() - weekStartsOn + 7) % 7 : 0;\n  const days = first ? Array.from({ length: 42 }, (_, i) => addDays(first, i - offset)) : [];\n  const weeks = Array.from({ length: 6 }, (_, w) => days.slice(w * 7, w * 7 + 7));\n  const weekdays = Array.from({ length: 7 }, (_, i) =>\n    new Intl.DateTimeFormat(locale, { weekday: \"narrow\" }).format(new Date(2024, 0, 7 + weekStartsOn + i)),\n  );\n  const focusInMonth = !!focus && !!month && focus.getMonth() === month.getMonth() && focus.getFullYear() === month.getFullYear();\n  const tabbable = focusInMonth ? focus : first;\n\n  const go = (d: Date) => {\n    moved.current = true;\n    setFocus(d);\n    if (!month || d.getMonth() !== month.getMonth() || d.getFullYear() !== month.getFullYear()) setMonth(startOfMonth(d));\n  };\n\n  const onKeyDown = (e: React.KeyboardEvent, d: Date) => {\n    const map: Record<string, number> = { ArrowLeft: -1, ArrowRight: 1, ArrowUp: -7, ArrowDown: 7 };\n    if (e.key in map) {\n      e.preventDefault();\n      go(addDays(d, map[e.key]));\n    } else if (e.key === \"PageUp\" || e.key === \"PageDown\") {\n      e.preventDefault();\n      go(new Date(d.getFullYear(), d.getMonth() + (e.key === \"PageUp\" ? -1 : 1), d.getDate()));\n    }\n  };\n\n  const nav = (delta: number) => month && setMonth(new Date(month.getFullYear(), month.getMonth() + delta, 1));\n  const navBtn =\n    \"flex size-8 cursor-pointer items-center justify-center rounded-[3px] text-[18px] text-(--ink-muted) outline-none hover:bg-(--paper-sunk) hover:text-(--ink) focus-visible:ring-2 focus-visible:ring-(--pen)\";\n\n  return (\n    <div\n      className={`paper relative inline-block w-[300px] max-w-full rounded-[4px] border border-(--edge) bg-(--paper-raised) text-(--ink) shadow-[0_10px_24px_-12px_rgb(0_0_0/.4)] ${className}`}\n      {...props}\n    >\n      {/* binding with two rings */}\n      <div className=\"relative h-4 rounded-t-[4px] bg-(--pen)\">\n        {[28, 72].map((x) => (\n          <span\n            key={x}\n            aria-hidden\n            className=\"absolute -top-2.5 h-5 w-2 rounded-full border-2 border-(--ink-muted) bg-(--paper-sunk)\"\n            style={{ left: `${x}%` }}\n          />\n        ))}\n      </div>\n      <div className=\"flex items-center justify-between px-3 pb-1 pt-3\">\n        <button type=\"button\" className={navBtn} onClick={() => nav(-1)} aria-label=\"Previous month\">\n          ‹\n        </button>\n        <div className=\"text-center\" aria-live=\"polite\">\n          <div className=\"text-[22px] font-semibold leading-none tracking-tight\" style={{ fontFamily: \"var(--font-display, Georgia, serif)\" }}>\n            {month ? new Intl.DateTimeFormat(locale, { month: \"long\" }).format(month) : \"\\u00a0\"}\n          </div>\n          <div className=\"mt-1 text-[11px] tracking-[0.3em] text-(--ink-muted)\" style={type}>\n            {month?.getFullYear() ?? \"\\u00a0\"}\n          </div>\n        </div>\n        <button type=\"button\" className={navBtn} onClick={() => nav(1)} aria-label=\"Next month\">\n          ›\n        </button>\n      </div>\n      <div ref={grid} role=\"grid\" className=\"px-3 pb-3\" style={type}>\n        <div role=\"row\" className=\"grid grid-cols-7 border-b border-(--rule) pb-1\">\n          {weekdays.map((w, i) => (\n            <span key={i} role=\"columnheader\" className=\"text-center text-[10.5px] font-bold text-(--ink-faint)\">\n              {w}\n            </span>\n          ))}\n        </div>\n        {weeks.map((week, w) => (\n          <div key={w} role=\"row\" className=\"grid min-h-9 grid-cols-7\">\n            {week.map((d) => {\n              const outside = d.getMonth() !== month?.getMonth();\n              const isSel = sameDay(d, selected);\n              const isToday = sameDay(d, today);\n              const isDisabled = disabled?.(d) ?? false;\n              const sunday = d.getDay() === 0;\n              return (\n                <div key={d.toISOString()} role=\"gridcell\" aria-selected={isSel || undefined}>\n                  <button\n                    type=\"button\"\n                    tabIndex={sameDay(d, tabbable) ? 0 : -1}\n                    disabled={isDisabled}\n                    onClick={() => {\n                      setFocus(d);\n                      onSelect?.(d);\n                    }}\n                    onKeyDown={(e) => onKeyDown(e, d)}\n                    aria-label={new Intl.DateTimeFormat(locale, { dateStyle: \"full\" }).format(d)}\n                    aria-current={isToday ? \"date\" : undefined}\n                    className={`relative mx-auto flex size-9 cursor-pointer items-center justify-center rounded-full text-[14px] tabular-nums outline-none transition-colors hover:bg-(--paper-sunk) focus-visible:ring-2 focus-visible:ring-(--pen) disabled:cursor-not-allowed disabled:line-through disabled:opacity-40 ${\n                      outside ? \"text-(--ink-faint)\" : sunday ? \"text-(--danger)\" : \"\"\n                    } ${isToday ? \"font-bold underline decoration-(--pen) decoration-2 underline-offset-4\" : \"\"}`}\n                  >\n                    {d.getDate()}\n                    {isSel && (\n                      <svg viewBox=\"0 0 40 40\" aria-hidden className=\"pointer-events-none absolute -inset-1 overflow-visible text-(--pen)\">\n                        <path\n                          d=\"M 22 5 C 9 4, 3 13, 5 23 C 7 33, 20 37, 29 33 C 37 29, 38 17, 32 10 C 27 5, 18 4, 12 8\"\n                          fill=\"none\"\n                          stroke=\"currentColor\"\n                          strokeWidth=\"2.2\"\n                          strokeLinecap=\"round\"\n                          strokeDasharray=\"120\"\n                          data-paper-motion\n                          style={{ animation: \"paper-draw .35s ease-out both\" }}\n                        />\n                      </svg>\n                    )}\n                  </button>\n                </div>\n              );\n            })}\n          </div>\n        ))}\n      </div>\n    </div>\n  );\n}\n"
    }
  ],
  "dependencies": [
    "radix-ui"
  ]
}