{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "date-picker",
  "title": "Date picker",
  "description": "Single-date and range popovers. Commits on Apply.",
  "dependencies": [
    "@internationalized/date@^3.12.3",
    "lucide-react@^1.33.0"
  ],
  "registryDependencies": [
    "https://components.exe.xyz/r/button.json",
    "https://components.exe.xyz/r/icon-button.json",
    "https://components.exe.xyz/r/popover.json",
    "https://components.exe.xyz/r/utils.json"
  ],
  "files": [
    {
      "path": "src/components/ui/date-picker.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport {\n  CalendarDate,\n  endOfMonth,\n  endOfWeek,\n  endOfYear,\n  getLocalTimeZone,\n  getWeeksInMonth,\n  isSameDay,\n  isSameMonth,\n  startOfMonth,\n  startOfWeek,\n  startOfYear,\n  today,\n} from \"@internationalized/date\"\nimport { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/components/ui/button\"\nimport { IconButton } from \"@/components/ui/icon-button\"\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n} from \"@/components/ui/popover\"\n\nconst LOCALE = \"en-GB\"\nconst tz = () => getLocalTimeZone()\n\nfunction toJs(date: CalendarDate) {\n  return date.toDate(tz())\n}\n\nfunction formatLong(date: CalendarDate) {\n  return new Intl.DateTimeFormat(LOCALE, {\n    day: \"numeric\",\n    month: \"short\",\n    year: \"numeric\",\n  }).format(toJs(date))\n}\n\nfunction formatNumeric(date: CalendarDate) {\n  return new Intl.DateTimeFormat(LOCALE, {\n    day: \"2-digit\",\n    month: \"2-digit\",\n    year: \"numeric\",\n  }).format(toJs(date))\n}\n\nfunction formatMonth(date: CalendarDate) {\n  return new Intl.DateTimeFormat(LOCALE, {\n    month: \"long\",\n    year: \"numeric\",\n  }).format(toJs(date))\n}\n\nfunction validDate(year: number, month: number, day: number) {\n  if (month < 1 || month > 12 || day < 1 || day > 31) return null\n  const date = new CalendarDate(year, month, day)\n  if (date.year !== year || date.month !== month || date.day !== day)\n    return null\n  return date\n}\n\nfunction parseFlexible(text: string) {\n  const trimmed = text.trim()\n  const iso = trimmed.match(/^(\\d{4})-(\\d{2})-(\\d{2})$/)\n  if (iso) return validDate(Number(iso[1]), Number(iso[2]), Number(iso[3]))\n  const nums = trimmed.match(/^(\\d{1,2})[/.-](\\d{1,2})[/.-](\\d{4})$/)\n  if (!nums) return null\n  const a = Number(nums[1])\n  const b = Number(nums[2])\n  const year = Number(nums[3])\n  return validDate(year, b, a) ?? validDate(year, a, b)\n}\n\nfunction weekdayLabels() {\n  const start = startOfWeek(today(tz()), LOCALE)\n  return Array.from({ length: 7 }, (_, i) =>\n    new Intl.DateTimeFormat(LOCALE, { weekday: \"short\" }).format(\n      toJs(start.add({ days: i }))\n    )\n  )\n}\n\nfunction monthWeeks(month: CalendarDate) {\n  const start = startOfWeek(startOfMonth(month), LOCALE)\n  const count = getWeeksInMonth(month, LOCALE)\n  const weeks: CalendarDate[][] = []\n  let date = start\n  for (let w = 0; w < count; w++) {\n    const week: CalendarDate[] = []\n    for (let d = 0; d < 7; d++) {\n      week.push(date)\n      date = date.add({ days: 1 })\n    }\n    weeks.push(week)\n  }\n  return weeks\n}\n\nfunction inRange(date: CalendarDate, start: CalendarDate, end: CalendarDate) {\n  return date.compare(start) >= 0 && date.compare(end) <= 0\n}\n\ntype DateRangeValue = { start: CalendarDate; end: CalendarDate }\n\nfunction DateChip({\n  label,\n  value,\n  onCommit,\n}: {\n  label: string\n  value: CalendarDate | null\n  onCommit: (date: CalendarDate | null) => void\n}) {\n  const [draft, setDraft] = React.useState(value ? formatNumeric(value) : \"\")\n\n  React.useEffect(() => {\n    setDraft(value ? formatNumeric(value) : \"\")\n  }, [value])\n\n  const commit = () => {\n    if (!draft.trim()) {\n      onCommit(null)\n      return\n    }\n    const parsed = parseFlexible(draft)\n    if (parsed) onCommit(parsed)\n    else setDraft(value ? formatNumeric(value) : \"\")\n  }\n\n  return (\n    <input\n      aria-label={label}\n      value={draft}\n      placeholder=\"DD/MM/YYYY\"\n      onChange={(event) => setDraft(event.target.value)}\n      onBlur={commit}\n      onKeyDown={(event) => {\n        if (event.key === \"Enter\") {\n          event.preventDefault()\n          commit()\n        }\n      }}\n      className={cn(\n        \"h-7 w-[7.6rem] rounded-full border border-border bg-background px-2.5 font-mono text-[11px] outline-none\",\n        \"placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50\",\n        \"motion-safe:animate-in motion-safe:duration-150 motion-safe:fade-in-0\"\n      )}\n    />\n  )\n}\n\nfunction MonthGrid({\n  month,\n  focused,\n  onFocusedChange,\n  onDaySelect,\n  selected,\n  rangeStart,\n  rangeEnd,\n  showPrev,\n  showNext,\n  onPrev,\n  onNext,\n}: {\n  month: CalendarDate\n  focused: CalendarDate\n  onFocusedChange: (date: CalendarDate) => void\n  onDaySelect: (date: CalendarDate) => void\n  selected?: CalendarDate | null\n  rangeStart?: CalendarDate | null\n  rangeEnd?: CalendarDate | null\n  showPrev?: boolean\n  showNext?: boolean\n  onPrev?: () => void\n  onNext?: () => void\n}) {\n  const weeks = monthWeeks(month)\n  const labels = weekdayLabels()\n  const gridRef = React.useRef<HTMLDivElement>(null)\n  const start = rangeStart ?? selected ?? null\n  const end = rangeEnd ?? (rangeStart ? null : selected)\n\n  const moveFocus = (date: CalendarDate) => {\n    onFocusedChange(date)\n  }\n\n  return (\n    <div data-slot=\"calendar-month\" className=\"flex w-[16.5rem] flex-col gap-2\">\n      <div className=\"flex items-center justify-between px-1\">\n        {showPrev ? (\n          <IconButton\n            icon={ChevronLeftIcon}\n            size=\"small\"\n            variant=\"ghost\"\n            aria-label=\"Previous month\"\n            onClick={onPrev}\n          />\n        ) : (\n          <span className=\"size-8\" />\n        )}\n        <p className=\"text-sm font-medium\">{formatMonth(month)}</p>\n        {showNext ? (\n          <IconButton\n            icon={ChevronRightIcon}\n            size=\"small\"\n            variant=\"ghost\"\n            aria-label=\"Next month\"\n            onClick={onNext}\n          />\n        ) : (\n          <span className=\"size-8\" />\n        )}\n      </div>\n      <div\n        ref={gridRef}\n        role=\"grid\"\n        aria-label={formatMonth(month)}\n        tabIndex={0}\n        onKeyDown={(event) => {\n          const key = event.key\n          let next: CalendarDate | null = null\n          if (key === \"ArrowLeft\") next = focused.subtract({ days: 1 })\n          if (key === \"ArrowRight\") next = focused.add({ days: 1 })\n          if (key === \"ArrowUp\") next = focused.subtract({ days: 7 })\n          if (key === \"ArrowDown\") next = focused.add({ days: 7 })\n          if (key === \"Home\") next = startOfWeek(focused, LOCALE)\n          if (key === \"End\") next = endOfWeek(focused, LOCALE)\n          if (key === \"PageUp\") {\n            next = event.shiftKey\n              ? focused.subtract({ years: 1 })\n              : focused.subtract({ months: 1 })\n          }\n          if (key === \"PageDown\") {\n            next = event.shiftKey\n              ? focused.add({ years: 1 })\n              : focused.add({ months: 1 })\n          }\n          if (next) {\n            event.preventDefault()\n            moveFocus(next)\n            return\n          }\n          if (key === \"Enter\" || key === \" \") {\n            event.preventDefault()\n            onDaySelect(focused)\n          }\n        }}\n        className=\"outline-none\"\n      >\n        <div role=\"row\" className=\"grid grid-cols-7\">\n          {labels.map((label) => (\n            <div\n              key={label}\n              role=\"columnheader\"\n              className=\"flex size-8 items-center justify-center font-mono text-[10px] text-muted-foreground\"\n            >\n              {label.slice(0, 2)}\n            </div>\n          ))}\n        </div>\n        {weeks.map((week) => (\n          <div\n            key={week[0]?.toString()}\n            role=\"row\"\n            className=\"grid grid-cols-7\"\n          >\n            {week.map((date) => {\n              const isFocused = isSameDay(date, focused)\n              const inMonth = isSameMonth(date, month)\n              const isToday = isSameDay(date, today(tz()))\n              const isStart = start ? isSameDay(date, start) : false\n              const isEnd = end ? isSameDay(date, end) : false\n              const ranged =\n                start && end\n                  ? inRange(date, start, end) && !isStart && !isEnd\n                  : false\n              const isSelected =\n                isStart ||\n                isEnd ||\n                (selected ? isSameDay(date, selected) : false)\n\n              return (\n                <div\n                  key={date.toString()}\n                  role=\"gridcell\"\n                  aria-selected={isSelected || undefined}\n                  className=\"relative\"\n                >\n                  {start &&\n                  end &&\n                  inRange(date, start, end) &&\n                  start.compare(end) !== 0 ? (\n                    <span\n                      aria-hidden\n                      className={cn(\n                        \"absolute inset-y-1 bg-primary/10\",\n                        isStart && \"right-0 left-1/2\",\n                        isEnd && \"right-1/2 left-0\",\n                        !isStart && !isEnd && \"inset-x-0\"\n                      )}\n                    />\n                  ) : null}\n                  <button\n                    type=\"button\"\n                    tabIndex={-1}\n                    onClick={() => {\n                      onFocusedChange(date)\n                      onDaySelect(date)\n                    }}\n                    className={cn(\n                      \"relative z-10 flex size-8 items-center justify-center text-sm outline-none\",\n                      !inMonth && \"text-muted-foreground\",\n                      ranged && \"text-foreground\",\n                      isSelected &&\n                        \"rounded-full bg-primary text-primary-foreground\",\n                      isToday &&\n                        !isSelected &&\n                        \"rounded-full ring-1 ring-foreground\",\n                      isFocused && \"ring-3 ring-ring/50\"\n                    )}\n                  >\n                    {date.day}\n                  </button>\n                </div>\n              )\n            })}\n          </div>\n        ))}\n      </div>\n    </div>\n  )\n}\n\nfunction FooterActions({\n  onCancel,\n  onApply,\n  applyDisabled,\n}: {\n  onCancel: () => void\n  onApply: () => void\n  applyDisabled?: boolean\n}) {\n  return (\n    <div className=\"flex gap-1.5\">\n      <Button type=\"button\" size=\"sm\" variant=\"ghost\" onClick={onCancel}>\n        Cancel\n      </Button>\n      <Button\n        type=\"button\"\n        size=\"sm\"\n        onClick={onApply}\n        disabled={applyDisabled}\n      >\n        Apply\n      </Button>\n    </div>\n  )\n}\n\nfunction DatePicker({\n  value,\n  defaultValue = null,\n  onChange,\n  isDisabled = false,\n  \"aria-label\": ariaLabel = \"Date\",\n}: {\n  value?: CalendarDate | null\n  defaultValue?: CalendarDate | null\n  onChange?: (value: CalendarDate | null) => void\n  isDisabled?: boolean\n  \"aria-label\"?: string\n}) {\n  const isControlled = value !== undefined\n  const [internal, setInternal] = React.useState<CalendarDate | null>(\n    defaultValue\n  )\n  const committed = isControlled ? value : internal\n  const [open, setOpen] = React.useState(false)\n  const [pending, setPending] = React.useState<CalendarDate | null>(committed)\n  const [focused, setFocused] = React.useState<CalendarDate>(\n    committed ?? today(tz())\n  )\n  const [visibleMonth, setVisibleMonth] = React.useState(\n    startOfMonth(committed ?? today(tz()))\n  )\n\n  React.useEffect(() => {\n    if (!isSameMonth(focused, visibleMonth)) {\n      setVisibleMonth(startOfMonth(focused))\n    }\n  }, [focused, visibleMonth])\n\n  const snapshot = (next: boolean) => {\n    if (isDisabled && next) return\n    if (next) {\n      const current = committed\n      setPending(current)\n      const seed = current ?? today(tz())\n      setFocused(seed)\n      setVisibleMonth(startOfMonth(seed))\n    }\n    setOpen(next)\n  }\n\n  const apply = () => {\n    if (!isControlled) setInternal(pending)\n    onChange?.(pending)\n    setOpen(false)\n  }\n\n  return (\n    <Popover open={open} onOpenChange={snapshot} modal={false}>\n      <PopoverTrigger asChild>\n        <Button\n          type=\"button\"\n          variant=\"outline\"\n          disabled={isDisabled}\n          aria-haspopup=\"dialog\"\n          aria-expanded={open}\n          aria-label={ariaLabel}\n          className=\"min-w-48 justify-start gap-2 rounded-[2px] font-normal\"\n        >\n          <CalendarIcon />\n          {committed ? formatLong(committed) : \"Select date\"}\n        </Button>\n      </PopoverTrigger>\n      <PopoverContent\n        align=\"start\"\n        className=\"w-auto min-w-0 rounded-[2px] p-3\"\n        onOpenAutoFocus={(event) => event.preventDefault()}\n      >\n        <MonthGrid\n          month={visibleMonth}\n          focused={focused}\n          onFocusedChange={setFocused}\n          onDaySelect={setPending}\n          selected={pending}\n          showPrev\n          showNext\n          onPrev={() => {\n            setVisibleMonth((month) =>\n              startOfMonth(month.subtract({ months: 1 }))\n            )\n            setFocused((date) => date.subtract({ months: 1 }))\n          }}\n          onNext={() => {\n            setVisibleMonth((month) => startOfMonth(month.add({ months: 1 })))\n            setFocused((date) => date.add({ months: 1 }))\n          }}\n        />\n        <div className=\"mt-3 flex items-center justify-between gap-3 border-t border-border pt-3\">\n          <DateChip label=\"Date\" value={pending} onCommit={setPending} />\n          <FooterActions onCancel={() => setOpen(false)} onApply={apply} />\n        </div>\n      </PopoverContent>\n    </Popover>\n  )\n}\n\nfunction rangePresets(): {\n  id: string\n  label: string\n  range: DateRangeValue\n}[] {\n  const now = today(tz())\n  const yesterday = now.subtract({ days: 1 })\n  const lastWeekAnchor = now.subtract({ weeks: 1 })\n  const lastMonth = now.subtract({ months: 1 })\n  const lastYear = now.subtract({ years: 1 })\n  return [\n    { id: \"today\", label: \"Today\", range: { start: now, end: now } },\n    {\n      id: \"yesterday\",\n      label: \"Yesterday\",\n      range: { start: yesterday, end: yesterday },\n    },\n    {\n      id: \"last-week\",\n      label: \"Last week\",\n      range: {\n        start: startOfWeek(lastWeekAnchor, LOCALE),\n        end: endOfWeek(lastWeekAnchor, LOCALE),\n      },\n    },\n    {\n      id: \"this-month\",\n      label: \"This month\",\n      range: { start: startOfMonth(now), end: now },\n    },\n    {\n      id: \"last-month\",\n      label: \"Last month\",\n      range: { start: startOfMonth(lastMonth), end: endOfMonth(lastMonth) },\n    },\n    {\n      id: \"this-year\",\n      label: \"This year\",\n      range: { start: startOfYear(now), end: now },\n    },\n    {\n      id: \"last-year\",\n      label: \"Last year\",\n      range: { start: startOfYear(lastYear), end: endOfYear(lastYear) },\n    },\n    {\n      id: \"all-time\",\n      label: \"All time\",\n      range: { start: new CalendarDate(1970, 1, 1), end: now },\n    },\n  ]\n}\n\nfunction DateRangePicker({\n  value,\n  defaultValue = null,\n  onChange,\n  isDisabled = false,\n  \"aria-label\": ariaLabel = \"Date range\",\n}: {\n  value?: DateRangeValue | null\n  defaultValue?: DateRangeValue | null\n  onChange?: (value: DateRangeValue | null) => void\n  isDisabled?: boolean\n  \"aria-label\"?: string\n}) {\n  const isControlled = value !== undefined\n  const [internal, setInternal] = React.useState<DateRangeValue | null>(\n    defaultValue\n  )\n  const committed = isControlled ? value : internal\n  const [open, setOpen] = React.useState(false)\n  const [pendingStart, setPendingStart] = React.useState<CalendarDate | null>(\n    committed?.start ?? null\n  )\n  const [pendingEnd, setPendingEnd] = React.useState<CalendarDate | null>(\n    committed?.end ?? null\n  )\n  const [focused, setFocused] = React.useState<CalendarDate>(\n    committed?.start ?? today(tz())\n  )\n  const [visibleMonth, setVisibleMonth] = React.useState(\n    startOfMonth(committed?.start ?? today(tz()))\n  )\n  const [presets, setPresets] = React.useState(rangePresets)\n\n  const pendingRange: DateRangeValue | null =\n    pendingStart && pendingEnd ? { start: pendingStart, end: pendingEnd } : null\n\n  const snapshot = (next: boolean) => {\n    if (isDisabled && next) return\n    if (next) {\n      setPendingStart(committed?.start ?? null)\n      setPendingEnd(committed?.end ?? null)\n      const seed = committed?.start ?? today(tz())\n      setFocused(seed)\n      setVisibleMonth(startOfMonth(seed))\n      setPresets(rangePresets())\n    }\n    setOpen(next)\n  }\n\n  const selectDay = (date: CalendarDate) => {\n    if (!pendingStart || pendingEnd) {\n      setPendingStart(date)\n      setPendingEnd(null)\n      return\n    }\n    if (date.compare(pendingStart) < 0) {\n      setPendingEnd(pendingStart)\n      setPendingStart(date)\n      return\n    }\n    setPendingEnd(date)\n  }\n\n  const apply = () => {\n    if (!pendingRange) return\n    if (!isControlled) setInternal(pendingRange)\n    onChange?.(pendingRange)\n    setOpen(false)\n  }\n\n  const shiftMonth = (delta: number) => {\n    setVisibleMonth((month) => startOfMonth(month.add({ months: delta })))\n    setFocused((date) => date.add({ months: delta }))\n  }\n\n  React.useEffect(() => {\n    const right = visibleMonth.add({ months: 1 })\n    if (isSameMonth(focused, visibleMonth) || isSameMonth(focused, right))\n      return\n    setVisibleMonth(startOfMonth(focused))\n  }, [focused, visibleMonth])\n\n  const daysSelected =\n    pendingStart && pendingEnd ? pendingEnd.compare(pendingStart) + 1 : 0\n\n  const triggerLabel = committed\n    ? `${formatLong(committed.start)} – ${formatLong(committed.end)}`\n    : \"Select date range\"\n\n  return (\n    <Popover open={open} onOpenChange={snapshot} modal={false}>\n      <PopoverTrigger asChild>\n        <Button\n          type=\"button\"\n          variant=\"outline\"\n          disabled={isDisabled}\n          aria-haspopup=\"dialog\"\n          aria-expanded={open}\n          aria-label={ariaLabel}\n          className=\"min-w-64 justify-start gap-2 rounded-[2px] font-normal\"\n        >\n          <CalendarIcon />\n          {triggerLabel}\n        </Button>\n      </PopoverTrigger>\n      <PopoverContent\n        align=\"start\"\n        className=\"w-auto min-w-0 rounded-[2px] p-3\"\n        onOpenAutoFocus={(event) => event.preventDefault()}\n      >\n        <div className=\"flex gap-3\">\n          <div className=\"flex w-36 shrink-0 flex-col gap-0.5 border-r border-border pr-3\">\n            {presets.map((preset) => {\n              const pressed = Boolean(\n                pendingStart &&\n                pendingEnd &&\n                isSameDay(pendingStart, preset.range.start) &&\n                isSameDay(pendingEnd, preset.range.end)\n              )\n              return (\n                <button\n                  key={preset.id}\n                  type=\"button\"\n                  aria-pressed={pressed}\n                  onClick={() => {\n                    setPendingStart(preset.range.start)\n                    setPendingEnd(preset.range.end)\n                    setFocused(preset.range.end)\n                    setVisibleMonth(\n                      startOfMonth(preset.range.end.subtract({ months: 1 }))\n                    )\n                  }}\n                  className={cn(\n                    \"rounded-[2px] px-2 py-1 text-left text-sm outline-none hover:bg-muted focus-visible:ring-3 focus-visible:ring-ring/50\",\n                    pressed && \"bg-muted font-medium\"\n                  )}\n                >\n                  {preset.label}\n                </button>\n              )\n            })}\n          </div>\n          <div className=\"flex gap-3\">\n            <MonthGrid\n              month={visibleMonth}\n              focused={focused}\n              onFocusedChange={setFocused}\n              onDaySelect={selectDay}\n              rangeStart={pendingStart}\n              rangeEnd={pendingEnd}\n              showPrev\n              onPrev={() => shiftMonth(-1)}\n            />\n            <MonthGrid\n              month={visibleMonth.add({ months: 1 })}\n              focused={focused}\n              onFocusedChange={setFocused}\n              onDaySelect={selectDay}\n              rangeStart={pendingStart}\n              rangeEnd={pendingEnd}\n              showNext\n              onNext={() => shiftMonth(1)}\n            />\n          </div>\n        </div>\n        <div className=\"mt-3 flex flex-wrap items-center justify-between gap-3 border-t border-border pt-3\">\n          <div className=\"flex flex-wrap items-center gap-2\">\n            <DateChip\n              label=\"Start date\"\n              value={pendingStart}\n              onCommit={(date) => {\n                setPendingStart(date)\n                if (date && pendingEnd && date.compare(pendingEnd) > 0) {\n                  setPendingEnd(null)\n                }\n              }}\n            />\n            <span className=\"text-muted-foreground\">–</span>\n            <DateChip\n              label=\"End date\"\n              value={pendingEnd}\n              onCommit={(date) => {\n                setPendingEnd(date)\n                if (date && pendingStart && date.compare(pendingStart) < 0) {\n                  setPendingStart(date)\n                  setPendingEnd(pendingStart)\n                }\n              }}\n            />\n            <span className=\"rounded-full bg-muted px-2 py-0.5 font-mono text-[11px] text-muted-foreground\">\n              {daysSelected} days selected\n            </span>\n          </div>\n          <FooterActions\n            onCancel={() => setOpen(false)}\n            onApply={apply}\n            applyDisabled={!pendingRange}\n          />\n        </div>\n      </PopoverContent>\n    </Popover>\n  )\n}\n\nexport { DatePicker, DateRangePicker, CalendarDate }\nexport type { DateRangeValue }\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}