{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "calendar",
  "title": "calendar",
  "dependencies": [
    "class-variance-authority@^0.7",
    "react-day-picker@^10.0.2"
  ],
  "registryDependencies": [
    "@dwellir/dwellir-theme",
    "@dwellir/utils",
    "@dwellir/button",
    "@dwellir/rune-icons"
  ],
  "files": [
    {
      "path": "components/ui/calendar.tsx",
      "content": "'use client';\n\nimport type { VariantProps } from 'class-variance-authority';\nimport { type ComponentProps, type ReactNode, useEffect, useRef } from 'react';\nimport { type ChevronProps, type DayButton, DayPicker, type RootProps } from 'react-day-picker';\nimport {\n  RuneChevronDown,\n  RuneChevronLeft,\n  RuneChevronRight,\n  RuneChevronUp,\n} from '@/components/brand/rune-icons';\nimport { buttonVariants } from '@/components/ui/button';\nimport { cn } from '@/lib/utils';\n\ntype CalendarProps = ComponentProps<typeof DayPicker> & {\n  /* The look of the month buttons: ghost by default, outline or secondary on a busy ground. */\n  buttonVariant?: VariantProps<typeof buttonVariants>['variant'];\n};\n\n/*\n * react-day-picker 10 with the theme's classes; it loads no stylesheet of its own.\n * The calendar draws no surface: put it in a popover (see DatePicker) or a plain card.\n * Days are round, like every site control. A range is an accent band between two\n * primary ends. The days touch, so under a coarse pointer each column asks for\n * 44px but gives way to 36px, its size under a mouse: a calendar never grows\n * past a container that holds it at 36px.\n */\nfunction Calendar({\n  className,\n  classNames,\n  showOutsideDays = true,\n  captionLayout = 'label',\n  buttonVariant = 'ghost',\n  formatters,\n  components,\n  ...props\n}: CalendarProps) {\n  const nav = cn(\n    buttonVariants({ variant: buttonVariant, size: 'icon-sm' }),\n    'aria-disabled:pointer-events-none aria-disabled:opacity-50',\n  );\n  return (\n    <DayPicker\n      showOutsideDays={showOutsideDays}\n      captionLayout={captionLayout}\n      className={cn('w-fit p-3 pointer-coarse:max-w-full', className)}\n      formatters={{\n        formatMonthDropdown: (date) =>\n          date.toLocaleString(props.locale?.code ?? 'en-US', { month: 'short' }),\n        ...formatters,\n      }}\n      classNames={{\n        months: 'relative flex flex-col gap-4 sm:flex-row sm:gap-6',\n        month: 'flex w-full flex-col gap-3',\n        nav: 'absolute inset-x-0 top-0 flex w-full items-center justify-between pointer-coarse:h-11',\n        button_previous: nav,\n        button_next: nav,\n        month_caption: 'flex h-8 pointer-coarse:h-11 w-full items-center justify-center px-8',\n        caption_label: cn(\n          'font-ui text-sm font-bold select-none',\n          captionLayout !== 'label' &&\n            'flex h-8 pointer-coarse:h-11 items-center gap-1 rounded-full pr-2 pl-3 [&>svg]:size-3.5 [&>svg]:text-muted-foreground',\n        ),\n        dropdowns: 'flex h-8 pointer-coarse:h-11 w-full items-center justify-center gap-1.5',\n        /* A native select under its label: the label shows, the select takes the click. */\n        dropdown_root:\n          'relative rounded-full border border-input bg-field transition-[border-color] duration-200 hover:border-input-hover has-focus-visible:border-ring has-focus-visible:outline-solid has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-ring',\n        /* Unseen, but iOS zooms into a select under 16px: the Input's text sizes. */\n        dropdown:\n          'absolute inset-0 cursor-pointer bg-popover text-lg opacity-0 md:pointer-fine:text-sm',\n        month_grid: 'border-collapse pointer-coarse:w-full pointer-coarse:table-fixed',\n        weekdays: 'flex',\n        weekday:\n          'w-9 pointer-coarse:w-11 pointer-coarse:min-w-9 text-xs font-normal text-muted-foreground select-none',\n        week: 'mt-1 flex w-full',\n        week_number_header: 'w-9 pointer-coarse:w-11 pointer-coarse:min-w-9 select-none',\n        week_number:\n          'flex size-9 pointer-coarse:h-11 pointer-coarse:w-11 pointer-coarse:min-w-9 items-center justify-center text-xs text-muted-foreground select-none',\n        day: cn(\n          'relative size-9 pointer-coarse:h-11 pointer-coarse:w-11 pointer-coarse:min-w-9 p-0 text-center select-none',\n          /* The range band rounds off at the ends of each week. */\n          props.showWeekNumber\n            ? 'data-[selected=true]:nth-2:rounded-l-full'\n            : 'data-[selected=true]:first:rounded-l-full',\n          'data-[selected=true]:last:rounded-r-full',\n        ),\n        range_start: 'rounded-l-full bg-accent',\n        range_middle: 'bg-accent text-accent-foreground',\n        range_end: 'rounded-r-full bg-accent',\n        outside: 'text-muted-foreground',\n        disabled: 'opacity-50',\n        hidden: 'invisible',\n        ...classNames,\n      }}\n      components={{\n        Root: CalendarRoot,\n        Chevron: CalendarChevron,\n        DayButton: CalendarDayButton,\n        ...components,\n      }}\n      {...props}\n    />\n  );\n}\n\n/* Module level: a component made in render would remount the grid on every render. */\nfunction CalendarRoot({ className, rootRef, ...props }: RootProps) {\n  return <div data-slot=\"calendar\" ref={rootRef} className={className} {...props} />;\n}\n\nconst CHEVRONS = {\n  left: RuneChevronLeft,\n  right: RuneChevronRight,\n  up: RuneChevronUp,\n  down: RuneChevronDown,\n};\n\nfunction CalendarChevron({ className, orientation = 'left' }: ChevronProps) {\n  const Icon = CHEVRONS[orientation];\n  const sideways = orientation === 'left' || orientation === 'right';\n  return (\n    <Icon aria-hidden=\"true\" className={cn('size-4', sideways && 'rtl:rotate-180', className)} />\n  );\n}\n\n/* The state of a day for its button: an end of the selection, or inside a range. */\nfunction selection(modifiers: ComponentProps<typeof DayButton>['modifiers']) {\n  if (modifiers.range_middle) return 'range';\n  if (modifiers.selected) return 'selected';\n  return undefined;\n}\n\n/*\n * A voice-control user says the number on the button, so its name must contain it as a\n * word (WCAG 2.5.3). react-day-picker's label says \"31st\", so the number goes first:\n * \"31, Today, Saturday, October 31st, 2026, selected\".\n */\nfunction dayName(text: ReactNode, label: string | undefined) {\n  if (!label || (typeof text !== 'string' && typeof text !== 'number')) return label;\n  return label.split(/[^\\p{L}\\p{N}]+/u).includes(String(text)) ? label : `${text}, ${label}`;\n}\n\nfunction CalendarDayButton({\n  className,\n  day,\n  modifiers,\n  children,\n  'aria-label': label,\n  ...props\n}: ComponentProps<typeof DayButton>) {\n  const ref = useRef<HTMLButtonElement>(null);\n  /* react-day-picker moves focus by marking a day focused: the button takes it. */\n  useEffect(() => {\n    if (modifiers.focused) ref.current?.focus();\n  }, [modifiers.focused]);\n  const state = selection(modifiers);\n  return (\n    <button\n      ref={ref}\n      data-slot=\"calendar-day\"\n      data-day={day.isoDate}\n      data-state={state}\n      data-today={(modifiers.today && !state) || undefined}\n      className={cn(\n        'inline-flex size-9 pointer-coarse:h-11 pointer-coarse:w-full cursor-pointer items-center justify-center rounded-full text-sm tabular-nums transition-colors duration-200 outline-none hover:bg-muted focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring disabled:pointer-events-none',\n        'data-[state=selected]:bg-primary data-[state=selected]:font-bold data-[state=selected]:text-primary-foreground data-[state=selected]:hover:bg-primary-hover',\n        'data-[today=true]:font-bold data-[today=true]:text-accent-ink',\n        className,\n      )}\n      aria-label={dayName(children, label)}\n      {...props}\n    >\n      {children}\n    </button>\n  );\n}\n\nexport { Calendar, CalendarDayButton };\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}