{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "calendar",
  "type": "registry:ui",
  "dependencies": [
    "date-fns",
    "lucide-react",
    "react-day-picker"
  ],
  "registryDependencies": [
    "https://ui.flagon.io/r/button.json",
    "https://ui.flagon.io/r/select.json"
  ],
  "files": [
    {
      "path": "ui/calendar.tsx",
      "content": "\"use client\";\n\nimport type { ChangeEvent, ComponentProps } from \"react\";\nimport { ChevronLeft, ChevronRight, ChevronUp, ChevronDown } from \"lucide-react\";\nimport { format as formatDate } from \"date-fns\";\nimport { DayPicker, type DropdownProps } from \"react-day-picker\";\nimport { cn } from \"@/lib/utils\";\nimport { buttonClasses } from \"./button\";\nimport { SelectField } from \"./select\";\n\nexport type CalendarProps = ComponentProps<typeof DayPicker>;\n\n/**\n * Month/year navigation control. Uses the design system's adaptive SelectField,\n * so the picker is our themed Radix menu on desktop (no jarring OS-native list\n * in dark mode) and the native OS picker on mobile. Bridges the Radix string\n * value back to react-day-picker's native-select onChange contract.\n */\nfunction CaptionDropdown({ options, value, onChange, disabled, \"aria-label\": ariaLabel }: DropdownProps) {\n  const opts = (options ?? []).map((o) => ({\n    value: String(o.value),\n    label: o.label,\n    disabled: o.disabled,\n  }));\n  return (\n    <SelectField\n      options={opts}\n      value={value != null ? String(value) : undefined}\n      disabled={disabled}\n      aria-label={ariaLabel}\n      triggerClassName=\"h-8 w-auto gap-1 px-2.5 font-medium hover:bg-panel\"\n      contentClassName=\"max-h-64\"\n      onValueChange={(v) =>\n        onChange?.({ target: { value: v }, currentTarget: { value: v } } as unknown as ChangeEvent<HTMLSelectElement>)\n      }\n    />\n  );\n}\n\n/**\n * Month-grid date picker built on react-day-picker, themed with Flagon tokens.\n * Supports single date, multiple, and range selection (via `mode`), month/year\n * dropdowns (`captionLayout=\"dropdown\"`, bounded by `startMonth`/`endMonth`), and\n * any disabled-date matcher (`disabled`).\n */\nexport function Calendar({ className, classNames, showOutsideDays = true, ...props }: CalendarProps) {\n  const navButton = cn(\n    buttonClasses({ variant: \"outline\", size: \"icon\" }),\n    // pointer-events-auto: the nav bar spans the full width over the caption, so\n    // it is pointer-events-none (to let clicks reach the centered dropdowns\n    // beneath it); the arrows themselves must opt back in.\n    \"pointer-events-auto size-7 bg-transparent p-0 opacity-80 hover:opacity-100\",\n  );\n  return (\n    <DayPicker\n      showOutsideDays={showOutsideDays}\n      // `relative` makes the root the positioning context for the nav arrows.\n      className={cn(\"relative p-3\", className)}\n      classNames={{\n        months: \"flex flex-col gap-4 sm:flex-row\",\n        month: \"flex flex-col gap-4\",\n        // Classic layout: month/year dropdowns centered, a prev/next arrow at\n        // each end of the caption row (absolute, so they never reflow it). The\n        // px keeps the centered dropdowns clear of the corner arrows.\n        month_caption: \"flex h-8 items-center justify-center gap-1.5 px-8\",\n        caption_label: \"inline-flex items-center gap-1 text-sm font-medium text-foreground\",\n        // The month/year controls are our adaptive SelectField (see\n        // CaptionDropdown); this just lays the two out in a row.\n        dropdowns: \"flex items-center gap-1.5\",\n        nav: \"pointer-events-none absolute inset-x-3 top-3 flex h-8 items-center justify-between\",\n        button_previous: navButton,\n        button_next: navButton,\n        month_grid: \"w-full border-collapse\",\n        weekdays: \"flex\",\n        weekday: \"w-9 text-[0.72rem] font-normal text-muted-foreground\",\n        week: \"mt-1.5 flex w-full\",\n        day: \"relative size-9 p-0 text-center text-sm focus-within:z-20\",\n        day_button: cn(\n          buttonClasses({ variant: \"ghost\", size: \"icon\" }),\n          \"size-9 rounded-md p-0 font-normal aria-selected:opacity-100\",\n        ),\n        // rdp v9 applies these to the day <td>; target the inner button.\n        selected:\n          \"[&>button]:bg-primary [&>button]:font-medium [&>button]:text-primary-foreground [&>button]:hover:bg-primary [&>button]:hover:text-primary-foreground\",\n        today: \"[&>button:not([aria-selected='true'])]:bg-accent [&>button]:font-semibold\",\n        outside: \"text-muted-foreground/40\",\n        disabled: \"text-muted-foreground/40 opacity-50\",\n        range_middle:\n          \"[&>button]:rounded-none [&>button]:bg-accent [&>button]:text-accent-foreground [&>button]:hover:bg-accent\",\n        hidden: \"invisible\",\n        ...classNames,\n      }}\n      // Short month labels (\"Aug\") keep the dropdowns compact.\n      formatters={{\n        formatMonthDropdown: (month) => formatDate(month, \"LLL\"),\n        ...props.formatters,\n      }}\n      components={{\n        Dropdown: CaptionDropdown,\n        Chevron: ({ orientation, className: cls }) => {\n          const Icon =\n            orientation === \"left\"\n              ? ChevronLeft\n              : orientation === \"right\"\n                ? ChevronRight\n                : orientation === \"up\"\n                  ? ChevronUp\n                  : ChevronDown;\n          return <Icon className={cn(\"size-4\", cls)} />;\n        },\n      }}\n      {...props}\n    />\n  );\n}\n",
      "type": "registry:ui",
      "target": "components/ui/calendar.tsx"
    }
  ]
}
