{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "date-picker",
  "title": "Date Picker",
  "description": "A date field with a calendar popover on Base UI and calendar-core. Typed dates parse in the field's locale, months slide the way you page, the selection glides between days, and ranges preview as you hover.",
  "dependencies": [
    "@base-ui/react",
    "motion"
  ],
  "registryDependencies": [
    "https://eased.dev/r/eased-tokens.json",
    "https://eased.dev/r/eased-motion.json",
    "https://eased.dev/r/eased-field.json",
    "https://eased.dev/r/eased-floating.json",
    "https://eased.dev/r/eased-text-swap.json",
    "https://eased.dev/r/eased-calendar-core.json"
  ],
  "files": [
    {
      "path": "src/registry/eased/date-picker/date-picker.tsx",
      "content": "\"use client\";\n\nimport { Input as BaseInput } from \"@base-ui/react/input\";\nimport { Popover } from \"@base-ui/react/popover\";\nimport { useMemo, useRef, useState, type ComponentProps } from \"react\";\nimport { addDays, startOfDay, type WeekStart } from \"../_primitives/calendar-core/calendar-core\";\nimport { InputGroup, InputGroupAddon, controlClass, controlPadClass, type ControlSize } from \"../_primitives/field/field\";\nimport { FloatingLayer, floatingSurface } from \"../_primitives/floating/floating\";\nimport { pressIcon } from \"../lib/press\";\nimport { Calendar, useToday, type DateRange } from \"./calendar\";\n\nexport { Calendar, useToday, type CalendarProps, type CalendarRangeProps, type CalendarSingleProps, type DateRange } from \"./calendar\";\n\ntype InputProps = Omit<ComponentProps<typeof BaseInput>, \"size\" | \"className\" | \"value\" | \"defaultValue\" | \"onValueChange\" | \"type\" | \"min\" | \"max\">;\n\ntype DatePickerBaseProps = {\n  /** Locale for parsing, formatting and the calendar. Default: the runtime's. */\n  locale?: string;\n  /** How the value is written in the field. Default `{ day: \"numeric\", month: \"short\", year: \"numeric\" }`. */\n  formatOptions?: Intl.DateTimeFormatOptions;\n  /** 0 Sunday … 6 Saturday. Default: the locale's. */\n  weekStartsOn?: WeekStart;\n  min?: Date;\n  max?: Date;\n  /** Days that can't be picked or typed. */\n  isDateDisabled?: (date: Date) => boolean;\n  /** \"Today\", for the today mark and for typed words like \"tomorrow\". Default: now. */\n  today?: Date;\n  /** sm 32px, md 36px (default), lg 40px. */\n  size?: ControlSize;\n  placeholder?: string;\n  disabled?: boolean;\n  /** Controlled popover state. */\n  open?: boolean;\n  defaultOpen?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  /** Close the calendar once a day (or a range's second end) is picked. Default true. */\n  closeOnSelect?: boolean;\n  /** Popover alignment against the field. Default \"start\". */\n  align?: \"start\" | \"center\" | \"end\";\n  /** Classes for the bordered box. */\n  className?: string;\n  /** Stacking order of the popover. Default 50; raise it inside a dialog. */\n  zIndex?: number;\n};\n\nexport type DatePickerSingleProps = DatePickerBaseProps &\n  Omit<InputProps, keyof DatePickerBaseProps> & {\n    mode?: \"single\";\n    value?: Date | null;\n    defaultValue?: Date | null;\n    onValueChange?: (date: Date | null) => void;\n    /** Accessible name for the calendar button. Default \"Choose date\". */\n    calendarLabel?: string;\n    /** Called with the text when a typed date can't be read or isn't allowed. */\n    onInvalidInput?: (text: string) => void;\n  };\n\nexport type DatePickerRangeProps = DatePickerBaseProps & {\n  mode: \"range\";\n  value?: DateRange | null;\n  defaultValue?: DateRange | null;\n  onValueChange?: (range: DateRange | null) => void;\n  minLength?: number;\n  maxLength?: number;\n  id?: string;\n  name?: string;\n  \"aria-label\"?: string;\n  \"aria-labelledby\"?: string;\n};\n\nexport type DatePickerProps = DatePickerSingleProps | DatePickerRangeProps;\n\nconst DEFAULT_FORMAT: Intl.DateTimeFormatOptions = { day: \"numeric\", month: \"short\", year: \"numeric\" };\n\n/**\n * A date field with a calendar in a popover (Base UI Popover; the grid is calendar-core).\n * Single mode is a text input that reads typed dates in the field's locale (\"24 Oct\", \"10/24\",\n * \"2026-10-24\", \"tomorrow\") with a calendar button beside it; range mode is a button showing\n * the span. The popover grows from the field; the calendar inside pages with a directional\n * slide and its selection springs between days.\n */\nexport function DatePicker(props: DatePickerProps) {\n  return props.mode === \"range\" ? <RangePicker {...props} /> : <SinglePicker {...props} />;\n}\n\nfunction usePopoverState(open: boolean | undefined, defaultOpen: boolean | undefined, onOpenChange?: (open: boolean) => void) {\n  const [own, setOwn] = useState(defaultOpen ?? false);\n  const isOpen = open ?? own;\n  const setOpen = (next: boolean) => {\n    if (open === undefined) setOwn(next);\n    onOpenChange?.(next);\n  };\n  return [isOpen, setOpen] as const;\n}\n\nfunction SinglePicker({\n  // eslint-disable-next-line @typescript-eslint/no-unused-vars -- kept off the input\n  mode: _mode,\n  value: valueProp,\n  defaultValue = null,\n  onValueChange,\n  locale,\n  formatOptions = DEFAULT_FORMAT,\n  weekStartsOn,\n  min,\n  max,\n  isDateDisabled,\n  today: todayProp,\n  size = \"md\",\n  placeholder,\n  disabled,\n  open,\n  defaultOpen,\n  onOpenChange,\n  closeOnSelect = true,\n  align = \"start\",\n  className = \"\",\n  zIndex,\n  calendarLabel = \"Choose date\",\n  onInvalidInput,\n  onBlur,\n  onKeyDown,\n  ...inputProps\n}: DatePickerSingleProps) {\n  const [own, setOwn] = useState<Date | null>(defaultValue);\n  const value = valueProp !== undefined ? valueProp : own;\n  const [isOpen, setOpen] = usePopoverState(open, defaultOpen, onOpenChange);\n  const now = useToday();\n  const today = todayProp ?? now ?? undefined;\n  const anchor = useRef<HTMLDivElement>(null);\n  const inputRef = useRef<HTMLInputElement>(null);\n\n  const fmt = useMemo(() => new Intl.DateTimeFormat(locale, formatOptions), [locale, formatOptions]);\n  const shown = value ? plainSpaces(fmt.format(value)) : \"\";\n  // What the person is typing; null while the field shows the formatted value.\n  const [draft, setDraft] = useState<string | null>(null);\n\n  const allowed = (d: Date) => !((min && d < startOfDay(min)) || (max && d > startOfDay(max)) || isDateDisabled?.(d));\n\n  const commit = (next: Date | null) => {\n    if (valueProp === undefined) setOwn(next);\n    if ((next?.getTime() ?? null) !== (value?.getTime() ?? null)) onValueChange?.(next);\n  };\n\n  /** Reads the draft; a date that can't be read or picked reverts to the last good value. */\n  const commitDraft = () => {\n    if (draft === null) return;\n    const text = draft.trim();\n    setDraft(null);\n    if (!text) return commit(null);\n    const parsed = parseDate(text, { locale, today });\n    if (parsed && allowed(parsed)) commit(parsed);\n    else onInvalidInput?.(text);\n  };\n\n  return (\n    <Popover.Root\n      open={isOpen}\n      onOpenChange={(next) => {\n        if (next) commitDraft();\n        setOpen(next);\n      }}\n    >\n      <div ref={anchor} className=\"flex w-full min-w-0\">\n        <InputGroup size={size} className={className}>\n          <BaseInput\n            {...inputProps}\n            ref={inputRef}\n            value={draft ?? shown}\n            onValueChange={(v) => setDraft(v)}\n            placeholder={placeholder ?? (today ? plainSpaces(fmt.format(today)) : undefined)}\n            disabled={disabled}\n            autoComplete=\"off\"\n            spellCheck={false}\n            onBlur={(e) => {\n              commitDraft();\n              onBlur?.(e);\n            }}\n            onKeyDown={(e) => {\n              onKeyDown?.(e);\n              if (e.defaultPrevented) return;\n              if (e.key === \"Enter\") commitDraft();\n              else if (e.key === \"Escape\" && draft !== null) {\n                e.preventDefault();\n                setDraft(null);\n              } else if (e.key === \"ArrowDown\" && (e.altKey || !draft)) {\n                e.preventDefault();\n                commitDraft();\n                setOpen(true);\n              }\n            }}\n            className={controlClass}\n          />\n          <Popover.Trigger\n            aria-label={calendarLabel}\n            disabled={disabled}\n            className={`-me-1.5 inline-flex size-7 shrink-0 cursor-pointer items-center justify-center rounded-control-xs text-text-muted hover-fine:not-disabled:bg-surface-hover hover-fine:not-disabled:text-text active:not-disabled:bg-surface-active data-popup-open:bg-surface-active data-popup-open:text-text disabled:cursor-not-allowed disabled:text-text-disabled ${pressIcon} active:scale-100`}\n          >\n            <CalendarIcon />\n          </Popover.Trigger>\n        </InputGroup>\n      </div>\n      <CalendarPopover anchor={anchor} align={align} zIndex={zIndex}>\n        <Calendar\n          value={value}\n          onValueChange={commit}\n          onSelectComplete={() => {\n            if (!closeOnSelect) return;\n            setOpen(false);\n          }}\n          locale={locale}\n          weekStartsOn={weekStartsOn}\n          min={min}\n          max={max}\n          isDateDisabled={isDateDisabled}\n          today={today}\n        />\n      </CalendarPopover>\n    </Popover.Root>\n  );\n}\n\nfunction RangePicker({\n  value: valueProp,\n  defaultValue = null,\n  onValueChange,\n  locale,\n  formatOptions = DEFAULT_FORMAT,\n  weekStartsOn,\n  min,\n  max,\n  isDateDisabled,\n  minLength,\n  maxLength,\n  today,\n  size = \"md\",\n  placeholder = \"Pick dates\",\n  disabled,\n  open,\n  defaultOpen,\n  onOpenChange,\n  closeOnSelect = true,\n  align = \"start\",\n  className = \"\",\n  zIndex,\n  id,\n  name,\n  \"aria-label\": ariaLabel,\n  \"aria-labelledby\": ariaLabelledBy,\n}: DatePickerRangeProps) {\n  const [own, setOwn] = useState<DateRange | null>(defaultValue);\n  const value = valueProp !== undefined ? valueProp : own;\n  const [isOpen, setOpen] = usePopoverState(open, defaultOpen, onOpenChange);\n  const anchor = useRef<HTMLDivElement>(null);\n  // Escape while a range is half-picked drops the half-pick and keeps the calendar open.\n  const fmt = useMemo(() => new Intl.DateTimeFormat(locale, formatOptions), [locale, formatOptions]);\n  const label = value ? plainSpaces(fmt.formatRange(value.start, value.end)) : \"\";\n\n  const commit = (r: DateRange | null) => {\n    if (valueProp === undefined) setOwn(r);\n    onValueChange?.(r);\n  };\n\n  return (\n    <Popover.Root\n      open={isOpen}\n      onOpenChange={(next, details) => {\n        if (!next && details.reason === \"escape-key\" && details.event.defaultPrevented) return details.cancel();\n        setOpen(next);\n      }}\n    >\n      <div ref={anchor} className=\"flex w-full min-w-0\">\n        <InputGroup size={size} className={`px-0! ${className}`}>\n          <Popover.Trigger\n            id={id}\n            disabled={disabled}\n            aria-label={ariaLabel}\n            aria-labelledby={ariaLabelledBy}\n            className={`flex min-w-0 flex-1 cursor-pointer items-center gap-2 self-stretch rounded-[inherit] text-left outline-none disabled:cursor-not-allowed ${controlPadClass[size]}`}\n          >\n            <span className={`min-w-0 flex-1 truncate tabular-nums ${label ? \"text-text\" : \"text-text-muted\"} in-disabled:text-text-disabled`}>\n              {label || placeholder}\n            </span>\n            <InputGroupAddon>\n              <CalendarIcon />\n            </InputGroupAddon>\n          </Popover.Trigger>\n          {name && <input type=\"hidden\" name={name} value={value ? `${toIso(value.start)}/${toIso(value.end)}` : \"\"} />}\n        </InputGroup>\n      </div>\n      <CalendarPopover anchor={anchor} align={align} zIndex={zIndex}>\n        <Calendar\n          mode=\"range\"\n          value={value}\n          onValueChange={commit}\n          onSelectComplete={() => {\n            if (closeOnSelect) setOpen(false);\n          }}\n          locale={locale}\n          weekStartsOn={weekStartsOn}\n          min={min}\n          max={max}\n          isDateDisabled={isDateDisabled}\n          minLength={minLength}\n          maxLength={maxLength}\n          today={today}\n        />\n      </CalendarPopover>\n    </Popover.Root>\n  );\n}\n\nfunction CalendarPopover({\n  anchor,\n  align,\n  zIndex,\n  children,\n}: {\n  anchor: React.RefObject<HTMLDivElement | null>;\n  align: \"start\" | \"center\" | \"end\";\n  zIndex?: number;\n  children: React.ReactNode;\n}) {\n  const body = useRef<HTMLDivElement>(null);\n  return (\n    <FloatingLayer\n      parts={Popover}\n      role=\"popover\"\n      side=\"bottom\"\n      align={align}\n      zIndex={zIndex}\n      positionerProps={{ anchor }}\n      // Open on the active day (the selection, else today), the grid's roving tab stop.\n      popupProps={{ initialFocus: () => body.current?.querySelector<HTMLElement>('[role=\"gridcell\"][tabindex=\"0\"]') ?? true }}\n      className={`${floatingSurface} rounded-panel p-3 [--pad:12px] [--radius-outer:var(--eased-radius-panel)]`}\n    >\n      <div ref={body}>{children}</div>\n    </FloatingLayer>\n  );\n}\n\n/** Intl's thin and narrow no-break spaces differ between Node's ICU and the browser's; plain ones hydrate. */\nconst plainSpaces = (s: string) => s.replace(/[\\u2009\\u202f]/g, \" \");\n\nconst toIso = (d: Date) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, \"0\")}-${String(d.getDate()).padStart(2, \"0\")}`;\n\n/* ---------- Typed dates ----------------------------------------------------- */\n\nconst WORDS: Record<string, number> = { today: 0, tod: 0, tomorrow: 1, tmr: 1, tom: 1, yesterday: -1 };\n\n/**\n * Reads a typed date in the locale's order: \"2026-10-24\", \"24/10\" or \"10/24\" (the locale\n * decides), \"24 Oct\", \"Oct 24 2026\", \"October 24\", and today / tomorrow / yesterday. A missing\n * year is the year of `today`; two-digit years are 20xx. Null when it can't be read or the day\n * doesn't exist (31 Feb).\n */\nexport function parseDate(text: string, { locale, today = new Date() }: { locale?: string; today?: Date } = {}): Date | null {\n  const t = text.trim().toLowerCase();\n  if (!t) return null;\n  if (t in WORDS) return addDays(startOfDay(today), WORDS[t]);\n\n  const make = (y: number, m: number, d: number) => {\n    if (y < 100) y += 2000;\n    const date = new Date(y, m, d);\n    return date.getFullYear() === y && date.getMonth() === m && date.getDate() === d ? date : null;\n  };\n\n  const iso = /^(\\d{4})-(\\d{1,2})-(\\d{1,2})$/.exec(t);\n  if (iso) return make(+iso[1], +iso[2] - 1, +iso[3]);\n\n  const year = today.getFullYear();\n  const tokens = t.split(/[\\s,./-]+/).filter(Boolean);\n\n  // A month written as a word.\n  const names = monthNames(locale);\n  const wordIndex = tokens.findIndex((tok) => /\\p{L}/u.test(tok));\n  if (wordIndex >= 0) {\n    const word = tokens[wordIndex].replace(/\\.$/, \"\");\n    const m = names.findIndex((n) => n.some((name) => word.length >= 3 && name.startsWith(word)));\n    if (m < 0) return null;\n    const nums = tokens.filter((_, i) => i !== wordIndex).map(Number);\n    if (nums.some(Number.isNaN) || nums.length === 0 || nums.length > 2) return null;\n    const [a, b] = nums;\n    // \"Oct 24 2026\", \"24 Oct 2026\", \"2026 Oct 24\".\n    if (nums.length === 1) return make(year, m, a);\n    return a > 31 ? make(a, m, b) : make(b, m, a);\n  }\n\n  // All numbers, in the locale's day/month/year order.\n  const nums = tokens.map(Number);\n  if (nums.some(Number.isNaN) || nums.length < 2 || nums.length > 3) return null;\n  const order = numericOrder(locale);\n  const parts: Record<\"day\" | \"month\" | \"year\", number> = { day: NaN, month: NaN, year };\n  const fields = nums.length === 3 ? order : order.filter((f) => f !== \"year\");\n  fields.forEach((f, i) => (parts[f] = nums[i]));\n  return make(parts.year, parts.month - 1, parts.day);\n}\n\nconst monthCache = new Map<string, string[][]>();\nfunction monthNames(locale?: string): string[][] {\n  const key = locale ?? \"\";\n  const hit = monthCache.get(key);\n  if (hit) return hit;\n  const long = new Intl.DateTimeFormat(locale, { month: \"long\" });\n  const short = new Intl.DateTimeFormat(locale, { month: \"short\" });\n  const en = new Intl.DateTimeFormat(\"en\", { month: \"long\" });\n  const names = Array.from({ length: 12 }, (_, m) => {\n    const d = new Date(2000, m, 1);\n    return [long.format(d), short.format(d), en.format(d)].map((s) => s.toLowerCase().replace(/\\.$/, \"\"));\n  });\n  monthCache.set(key, names);\n  return names;\n}\n\nfunction numericOrder(locale?: string): (\"day\" | \"month\" | \"year\")[] {\n  const parts = new Intl.DateTimeFormat(locale, { day: \"numeric\", month: \"numeric\", year: \"numeric\" }).formatToParts(new Date(2000, 10, 22));\n  const order = parts.map((p) => p.type).filter((t): t is \"day\" | \"month\" | \"year\" => t === \"day\" || t === \"month\" || t === \"year\");\n  return order.length === 3 ? order : [\"month\", \"day\", \"year\"];\n}\n\nfunction CalendarIcon() {\n  return (\n    <svg viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden className=\"size-4\">\n      <rect x=\"2.25\" y=\"3.25\" width=\"11.5\" height=\"10.5\" rx=\"2.25\" stroke=\"currentColor\" strokeWidth=\"1.5\" />\n      <path d=\"M2.5 6.75h11M5.5 1.75v2.5M10.5 1.75v2.5\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" />\n    </svg>\n  );\n}\n",
      "type": "registry:ui",
      "target": "@components/eased/date-picker/date-picker.tsx"
    },
    {
      "path": "src/registry/eased/date-picker/calendar.tsx",
      "content": "\"use client\";\n\nimport { AnimatePresence, motion } from \"motion/react\";\nimport { useEffect, useId, useMemo, useRef, useState, useSyncExternalStore, type KeyboardEvent } from \"react\";\nimport {\n  addMonths,\n  isSameDay,\n  localeWeekStart,\n  monthGrid,\n  nextFocusDate,\n  startOfDay,\n  startOfMonth,\n  toDayKey,\n  useRangeSelection,\n  type CalendarDay,\n  type DateRange,\n  type WeekStart,\n} from \"../_primitives/calendar-core/calendar-core\";\nimport { TextSwap } from \"../_primitives/text-swap/text-swap\";\nimport { enter, exitFast, reducedFade, smooth, snappy, useSafeReducedMotion } from \"../lib/motion\";\nimport { pressIcon } from \"../lib/press\";\n\nexport type { DateRange };\n\nexport type CalendarBaseProps = {\n  /** Locale for the month title, weekday names and day labels. Default: the runtime's. */\n  locale?: string;\n  /** 0 Sunday … 6 Saturday. Default: the locale's first day of the week. */\n  weekStartsOn?: WeekStart;\n  /** Earliest selectable day. */\n  min?: Date;\n  /** Latest selectable day. */\n  max?: Date;\n  /** Days that can't be picked: weekends, booked nights, holidays. A range never spans one. */\n  isDateDisabled?: (date: Date) => boolean;\n  /** \"Today\", for the today mark. Pass a fixed date for deterministic demos. Default: now. */\n  today?: Date;\n  /** The month shown first. Default: the selected day's month, else today's. */\n  defaultMonth?: Date;\n  /** Fires when the shown month changes (the first of that month). */\n  onMonthChange?: (month: Date) => void;\n  /** Focus the active day on mount (a popover opening). Default false. */\n  autoFocus?: boolean;\n  /** Called after a pick that finishes a selection: one day, or a range's second end. */\n  onSelectComplete?: () => void;\n  className?: string;\n};\n\nexport type CalendarSingleProps = CalendarBaseProps & {\n  mode?: \"single\";\n  value?: Date | null;\n  defaultValue?: Date | null;\n  onValueChange?: (date: Date | null) => void;\n};\n\nexport type CalendarRangeProps = CalendarBaseProps & {\n  mode: \"range\";\n  value?: DateRange | null;\n  defaultValue?: DateRange | null;\n  onValueChange?: (range: DateRange | null) => void;\n  /** Fewest days in a range. Default 1. */\n  minLength?: number;\n  /** Most days in a range. */\n  maxLength?: number;\n};\n\nexport type CalendarProps = CalendarSingleProps | CalendarRangeProps;\n\nconst SERVER_DAY = new Date(2000, 0, 1);\n\nlet todayCache: Date | null = null;\nconst readToday = () => {\n  const d = startOfDay(Date.now());\n  if (!todayCache || +todayCache !== +d) todayCache = d;\n  return todayCache;\n};\nconst noSubscribe = () => () => {};\n\n/** Local midnight today, read on the client only (null on the server and during hydration). */\nexport function useToday(): Date | null {\n  return useSyncExternalStore(noSubscribe, readToday, () => null);\n}\n\nconst CELL = \"h-9\"; // 36px rows: 7 × 36 = 252px wide, so the popover fits a 320px screen.\nconst monthKey = (d: Date) => `${d.getFullYear()}-${d.getMonth()}`;\n\n/**\n * A month grid on calendar-core: one day or a range, arrow keys, Home/End and PageUp/PageDown\n * (Shift for years). Paging slides the days the way you moved while the title swaps; the\n * selected day's fill glides between days on the snappy spring, and a range's ends pop in.\n */\nexport function Calendar(props: CalendarProps) {\n  const {\n    locale,\n    weekStartsOn: weekStartsOnProp,\n    min,\n    max,\n    isDateDisabled,\n    today: todayProp,\n    defaultMonth,\n    onMonthChange,\n    autoFocus = false,\n    onSelectComplete,\n    className = \"\",\n  } = props;\n  const range = props.mode === \"range\";\n  const reduced = useSafeReducedMotion();\n  const uid = useId();\n\n  const now = useToday();\n  // Before hydration there is no clock to read; a fixed day keeps the server render stable.\n  const today = todayProp ?? now ?? SERVER_DAY;\n  const weekStartsOn = weekStartsOnProp ?? localeWeekStart(locale);\n\n  const isDisabled = (d: Date) => (min !== undefined && d < startOfDay(min)) || (max !== undefined && d > startOfDay(max)) || !!isDateDisabled?.(d);\n\n  // Single selection.\n  const [singleOwn, setSingleOwn] = useState<Date | null>(!range ? (props.defaultValue ?? null) : null);\n  const single = !range ? (props.value !== undefined ? props.value : singleOwn) : null;\n  const pickSingle = (d: Date) => {\n    if (range) return;\n    if (props.value === undefined) setSingleOwn(d);\n    props.onValueChange?.(d);\n    onSelectComplete?.();\n  };\n\n  // Range selection: calendar-core owns click-click, drag, preview and the constraints.\n  const sel = useRangeSelection({\n    value: range ? props.value : undefined,\n    defaultValue: range ? props.defaultValue : null,\n    onChange: (r) => {\n      if (!range) return;\n      props.onValueChange?.(r);\n      if (r) onSelectComplete?.();\n    },\n    isDisabled,\n    minLength: range ? props.minLength : undefined,\n    maxLength: range ? props.maxLength : undefined,\n  });\n\n  const anchorDate = range ? (sel.value?.start ?? null) : single;\n  const [month, setMonthState] = useState(() => startOfMonth(defaultMonth ?? anchorDate ?? today));\n  const [dir, setDir] = useState<1 | -1>(1);\n  const [focusKey, setFocusKey] = useState(() => toDayKey(anchorDate ?? today));\n  const pendingFocus = useRef(autoFocus);\n  const gridRef = useRef<HTMLDivElement>(null);\n\n  const grid = useMemo(() => monthGrid(month, { weekStartsOn, today, locale }), [month, weekStartsOn, today, locale]);\n  const dayLabel = useMemo(() => new Intl.DateTimeFormat(locale, { weekday: \"long\", day: \"numeric\", month: \"long\", year: \"numeric\" }), [locale]);\n\n  const goTo = (next: Date) => {\n    const m = startOfMonth(next);\n    if (+m === +month) return;\n    setDir(+m > +month ? 1 : -1);\n    setMonthState(m);\n    onMonthChange?.(m);\n  };\n\n  const prevDisabled = min !== undefined && +addMonths(month, -1) < +startOfMonth(min);\n  const nextDisabled = max !== undefined && +addMonths(month, 1) > +startOfMonth(max);\n\n  // The roving tab stop stays on a day of the shown month.\n  const focusInMonth = grid.days.some((d) => d.inMonth && d.key === focusKey);\n  const tabKey = focusInMonth ? focusKey : (grid.days.find((d) => d.inMonth && !isDisabled(d.date))?.key ?? grid.days.find((d) => d.inMonth)!.key);\n\n  useEffect(() => {\n    if (!pendingFocus.current) return;\n    pendingFocus.current = false;\n    gridRef.current?.querySelector<HTMLElement>(`[data-month=\"${monthKey(month)}\"] [data-date=\"${tabKey}\"][data-in-month]`)?.focus({ preventScroll: true });\n  }, [month, tabKey]);\n\n  const moveFocus = (date: Date) => {\n    pendingFocus.current = true;\n    setFocusKey(toDayKey(date));\n    goTo(date);\n    if (range) sel.hover(date);\n  };\n\n  const onCellKey = (e: KeyboardEvent<HTMLElement>, day: CalendarDay, rangeKey?: (e: KeyboardEvent<HTMLElement>) => void) => {\n    const next = nextFocusDate(day.date, e.key, { weekStartsOn, shift: e.shiftKey });\n    if (next) {\n      e.preventDefault();\n      if (min && next < startOfDay(min)) return moveFocus(startOfDay(min));\n      if (max && next > startOfDay(max)) return moveFocus(startOfDay(max));\n      return moveFocus(next);\n    }\n    if (rangeKey) return rangeKey(e);\n    if ((e.key === \"Enter\" || e.key === \" \") && !isDisabled(day.date)) {\n      e.preventDefault();\n      pickSingle(day.date);\n    }\n  };\n\n  const slide = reduced ? 0 : 28;\n\n  return (\n    <div className={`flex w-63 flex-col gap-2 select-none ${className}`}>\n      <div className=\"flex h-8 items-center justify-between\">\n        <TextSwap direction={dir} className=\"text-ui font-medium text-text\" aria-live=\"polite\">\n          {grid.title}\n        </TextSwap>\n        <div className=\"-me-2 flex items-center\">\n          <NavButton label=\"Previous month\" disabled={prevDisabled} onClick={() => goTo(addMonths(month, -1))} dir=\"left\" />\n          <NavButton label=\"Next month\" disabled={nextDisabled} onClick={() => goTo(addMonths(month, 1))} dir=\"right\" />\n        </div>\n      </div>\n\n      <div\n        ref={gridRef}\n        role=\"grid\"\n        aria-label={grid.title}\n        aria-multiselectable={range || undefined}\n        className=\"flex flex-col\"\n        onPointerLeave={range ? () => sel.hover(null) : undefined}\n      >\n        <div role=\"row\" className=\"grid grid-cols-7 pb-1\">\n          {grid.weekdays.map((w) => (\n            <span key={w.weekday} role=\"columnheader\" aria-label={w.long} className=\"flex h-6 items-center justify-center text-ui-xs text-text-muted\">\n              {w.short}\n            </span>\n          ))}\n        </div>\n\n        <div className=\"relative overflow-hidden\" style={{ height: `calc(6 * var(--spacing) * 9 + 5 * var(--spacing) * 0.5)` }}>\n          <AnimatePresence initial={false} mode=\"popLayout\" custom={dir}>\n            <motion.div\n              key={monthKey(month)}\n              role=\"rowgroup\"\n              data-month={monthKey(month)}\n              custom={dir}\n              variants={{\n                enter: (d: number) => ({ x: d * slide, opacity: 0 }),\n                center: { x: 0, opacity: 1 },\n                exit: (d: number) => ({ x: d * -slide * 0.5, opacity: 0, transition: reduced ? reducedFade : { x: smooth, opacity: exitFast } }),\n              }}\n              initial=\"enter\"\n              animate=\"center\"\n              exit=\"exit\"\n              transition={reduced ? reducedFade : { x: smooth, opacity: enter }}\n              className={`flex w-full flex-col gap-0.5 ${range ? \"touch-none\" : \"\"}`}\n            >\n              {grid.weeks.map((week) => (\n                <div key={week[0].key} role=\"row\" className=\"grid grid-cols-7\">\n                  {week.map((day, col) => {\n                    const disabled = isDisabled(day.date);\n                    const label = dayLabel.format(day.date);\n                    const common = {\n                      role: \"gridcell\",\n                      \"aria-label\": label,\n                      \"aria-current\": day.isToday ? (\"date\" as const) : undefined,\n                      \"data-date\": day.key,\n                      \"data-in-month\": day.inMonth ? \"\" : undefined,\n                      tabIndex: day.inMonth && day.key === tabKey ? 0 : -1,\n                    };\n                    if (range) {\n                      const cell = sel.cellProps(day.date);\n                      const s = sel.stateOf(day.date);\n                      return (\n                        <div\n                          key={day.key}\n                          {...common}\n                          {...cell}\n                          onFocus={() => {\n                            setFocusKey(day.key);\n                            cell.onFocus();\n                          }}\n                          onKeyDown={(e) => onCellKey(e, day, cell.onKeyDown)}\n                          className={`group/day relative flex ${CELL} items-center justify-center outline-none ${disabled ? \"cursor-not-allowed\" : \"cursor-pointer\"}`}\n                        >\n                          <RangeBand position={s.position} col={col} preview={s.previewing && !s.selected} />\n                          <DayFace\n                            day={day}\n                            disabled={disabled}\n                            filled={s.position !== null && s.position !== \"middle\"}\n                            preview={s.previewing}\n                            reduced={reduced}\n                            pop\n                          />\n                        </div>\n                      );\n                    }\n                    const selected = !!single && isSameDay(single, day.date);\n                    return (\n                      <div\n                        key={day.key}\n                        {...common}\n                        aria-selected={selected}\n                        aria-disabled={disabled || undefined}\n                        onClick={() => !disabled && pickSingle(day.date)}\n                        onFocus={() => setFocusKey(day.key)}\n                        onKeyDown={(e) => onCellKey(e, day)}\n                        className={`group/day relative flex ${CELL} items-center justify-center outline-none ${disabled ? \"cursor-not-allowed\" : \"cursor-pointer\"}`}\n                      >\n                        {selected && day.inMonth && (\n                          <motion.span\n                            layoutId={`${uid}-selected-${monthKey(month)}`}\n                            transition={reduced ? { duration: 0 } : snappy}\n                            className=\"absolute inset-y-0 left-1/2 w-9 -translate-x-1/2 rounded-control-sm bg-brand\"\n                          />\n                        )}\n                        <DayFace day={day} disabled={disabled} filled={selected && day.inMonth} reduced={reduced} />\n                      </div>\n                    );\n                  })}\n                </div>\n              ))}\n            </motion.div>\n          </AnimatePresence>\n        </div>\n      </div>\n    </div>\n  );\n}\n\n/** The light band behind a range: square in the middle, open towards the inside at each end, rounded at row edges. */\nfunction RangeBand({ position, col, preview }: { position: \"single\" | \"start\" | \"middle\" | \"end\" | null; col: number; preview: boolean }) {\n  if (position === null || position === \"single\") return null;\n  const left = position === \"start\" ? \"left-1/2\" : \"left-0\";\n  const right = position === \"end\" ? \"right-1/2\" : \"right-0\";\n  const roundL = col === 0 && position !== \"start\" ? \"rounded-l-control-sm\" : \"\";\n  const roundR = col === 6 && position !== \"end\" ? \"rounded-r-control-sm\" : \"\";\n  return (\n    <span\n      aria-hidden\n      className={`absolute inset-y-0 ${left} ${right} ${roundL} ${roundR} bg-brand-subtle transition-opacity duration-(--eased-duration-fast) ease-standard ${preview ? \"opacity-60\" : \"opacity-100\"}`}\n    />\n  );\n}\n\nfunction DayFace({\n  day,\n  disabled,\n  filled,\n  preview = false,\n  reduced,\n  pop = false,\n}: {\n  day: CalendarDay;\n  disabled: boolean;\n  filled: boolean;\n  preview?: boolean;\n  reduced: boolean;\n  pop?: boolean;\n}) {\n  const tone = filled\n    ? \"text-text-on-brand\"\n    : disabled\n      ? \"text-text-disabled\"\n      : day.isToday\n        ? \"text-brand-text font-medium\"\n        : day.inMonth\n          ? \"text-text\"\n          : \"text-text-muted\";\n  return (\n    <span\n      className={`relative flex size-9 items-center justify-center rounded-control-sm text-ui-sm tabular-nums [transition:color_150ms_var(--eased-ease-standard),background-color_150ms_var(--eased-ease-standard),box-shadow_var(--eased-duration-focus)_var(--eased-ease-standard)] group-focus-visible/day:z-10 group-focus-visible/day:shadow-[0_0_0_2px_var(--eased-bg),0_0_0_4px_var(--eased-ring)] ${tone} ${\n        !filled && !disabled ? \"group-hover-fine/day:bg-surface-hover group-active/day:bg-surface-active\" : \"\"\n      }`}\n    >\n      {pop && filled && (\n        <motion.span\n          key={day.key}\n          aria-hidden\n          initial={reduced ? { opacity: 0 } : { scale: 0.8, opacity: 0 }}\n          animate={{ scale: 1, opacity: preview ? 0.75 : 1 }}\n          transition={reduced ? reducedFade : { scale: snappy, opacity: enter }}\n          className=\"absolute inset-0 rounded-[inherit] bg-brand\"\n        />\n      )}\n      <span className=\"relative\">{day.day}</span>\n      {day.isToday && <span aria-hidden className=\"absolute bottom-1 left-1/2 size-1 -translate-x-1/2 rounded-pill bg-current\" />}\n    </span>\n  );\n}\n\nfunction NavButton({ label, onClick, disabled, dir }: { label: string; onClick: () => void; disabled: boolean; dir: \"left\" | \"right\" }) {\n  return (\n    <button\n      type=\"button\"\n      aria-label={label}\n      disabled={disabled}\n      onClick={onClick}\n      className={`inline-flex size-8 cursor-pointer items-center justify-center rounded-control-sm text-text-muted hover-fine:not-disabled:bg-surface-hover hover-fine:not-disabled:text-text active:not-disabled:bg-surface-active disabled:cursor-not-allowed disabled:text-text-disabled ${pressIcon}`}\n    >\n      <svg viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" strokeWidth={1.5} strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden className=\"size-4\">\n        <path d={dir === \"left\" ? \"M9.5 3.5 5 8l4.5 4.5\" : \"M6.5 3.5 11 8l-4.5 4.5\"} />\n      </svg>\n    </button>\n  );\n}\n",
      "type": "registry:ui",
      "target": "@components/eased/date-picker/calendar.tsx"
    }
  ],
  "docs": "Import DatePicker (or Calendar for an inline grid) from components/eased/date-picker/date-picker; wrap it in Field for a label and messages. The Eased tokens were merged into your global CSS; nothing to import.",
  "type": "registry:ui"
}