{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "calendar", "title": "Calendar", "description": "Interactive date calendar for browsing and selecting dates by month.", "dependencies": [ "react-day-picker", "lucide-react" ], "registryDependencies": [ "button", "native-select" ], "files": [ { "path": "packages/ui/src/calendar.tsx", "content": "\"use client\";\n\nimport {\n ChevronDown as ChevronDownIcon,\n ChevronLeft as ChevronLeftIcon,\n ChevronRight as ChevronRightIcon,\n} from \"lucide-react\";\nimport * as React from \"react\";\nimport {\n type DayButton,\n DayPicker,\n getDefaultClassNames,\n} from \"react-day-picker\";\nimport { Button, buttonVariants } from \"./button\";\nimport { NativeSelect } from \"./native-select\";\nimport { cn } from \"./utils\";\n\n// Hoisted to module scope — pure function with no parameters, avoids repeated\n// allocations on every render (CalendarDayButton alone renders ~42 instances).\nconst defaultClassNames = getDefaultClassNames();\n\n// =============================================================================\n// Calendar\n// =============================================================================\n\nfunction Calendar({\n className,\n classNames,\n showOutsideDays = true,\n captionLayout = \"label\",\n buttonVariant = \"ghost\",\n formatters,\n components,\n ...props\n}: React.ComponentProps & {\n /**\n * The variant to use for the navigation buttons.\n * @default \"ghost\"\n */\n buttonVariant?: React.ComponentProps[\"variant\"];\n}) {\n return (\n svg]:rotate-180`,\n String.raw`rtl:**:[.rdp-button\\_previous>svg]:rotate-180`,\n className\n )}\n classNames={{\n root: cn(\"w-fit\", defaultClassNames.root),\n months: cn(\n \"relative flex flex-col gap-4 md:flex-row\",\n defaultClassNames.months\n ),\n month: cn(\"flex w-full flex-col gap-4\", defaultClassNames.month),\n nav: cn(\n \"absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1\",\n defaultClassNames.nav\n ),\n button_previous: cn(\n buttonVariants({ variant: buttonVariant }),\n \"size-(--cell-size) select-none p-0 aria-disabled:opacity-50\",\n defaultClassNames.button_previous\n ),\n button_next: cn(\n buttonVariants({ variant: buttonVariant }),\n \"size-(--cell-size) select-none p-0 aria-disabled:opacity-50\",\n defaultClassNames.button_next\n ),\n month_caption: cn(\n \"flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)\",\n defaultClassNames.month_caption\n ),\n dropdowns: cn(\n \"flex h-(--cell-size) w-full items-center justify-center gap-1.5 font-medium text-sm\",\n defaultClassNames.dropdowns\n ),\n dropdown_root: cn(\"relative\", defaultClassNames.dropdown_root),\n dropdown: cn(defaultClassNames.dropdown),\n caption_label: cn(\n \"select-none font-medium text-sm\",\n captionLayout !== \"label\" && \"hidden\",\n defaultClassNames.caption_label\n ),\n table: \"w-full border-collapse\",\n weekdays: cn(\"flex\", defaultClassNames.weekdays),\n weekday: cn(\n \"flex-1 select-none rounded-(--cell-radius) font-normal text-[0.8rem] text-muted-foreground\",\n defaultClassNames.weekday\n ),\n week: cn(\"mt-2 flex w-full\", defaultClassNames.week),\n week_number_header: cn(\n \"w-(--cell-size) select-none\",\n defaultClassNames.week_number_header\n ),\n week_number: cn(\n \"select-none text-[0.8rem] text-muted-foreground\",\n defaultClassNames.week_number\n ),\n day: cn(\n \"group/day relative aspect-square h-full w-full select-none rounded-(--cell-radius) p-0 text-center has-[:focus-visible]:z-20 [&:last-child[data-selected=true]_button]:rounded-r-(--cell-radius)\",\n props.showWeekNumber\n ? \"[&:nth-child(2)[data-selected=true]_button]:rounded-l-(--cell-radius)\"\n : \"[&:first-child[data-selected=true]_button]:rounded-l-(--cell-radius)\",\n defaultClassNames.day\n ),\n range_start: cn(\n \"relative isolate -z-0 rounded-l-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:right-0 after:w-4 after:bg-muted\",\n defaultClassNames.range_start\n ),\n range_middle: cn(\"rounded-none\", defaultClassNames.range_middle),\n range_end: cn(\n \"relative isolate -z-0 rounded-r-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:left-0 after:w-4 after:bg-muted\",\n defaultClassNames.range_end\n ),\n today: cn(\n \"rounded-(--cell-radius) bg-muted text-foreground data-[selected=true]:rounded-none\",\n defaultClassNames.today\n ),\n outside: cn(\n \"text-muted-foreground aria-selected:text-muted-foreground\",\n defaultClassNames.outside\n ),\n disabled: cn(\n \"text-muted-foreground opacity-50\",\n defaultClassNames.disabled\n ),\n hidden: cn(\"invisible\", defaultClassNames.hidden),\n ...classNames,\n }}\n components={{\n Root: ({ className, rootRef, ...props }) => (\n \n ),\n Chevron: ({ className, orientation, ...props }) => {\n if (orientation === \"left\") {\n return (\n \n );\n }\n\n if (orientation === \"right\") {\n return (\n \n );\n }\n\n return (\n \n );\n },\n Dropdown: ({\n options,\n components: _components,\n classNames: _classNames,\n className: _className,\n size: htmlSize,\n ...selectProps\n }) => (\n \n {options?.map(({ value, label, disabled }) => (\n \n ))}\n \n ),\n DayButton: CalendarDayButton,\n WeekNumber: ({ children, ...props }) => (\n \n
\n {children}\n
\n \n ),\n ...components,\n }}\n formatters={{\n formatMonthDropdown: (date) =>\n date.toLocaleString(\"default\", { month: \"short\" }),\n ...formatters,\n }}\n showOutsideDays={showOutsideDays}\n {...props}\n />\n );\n}\n\n// =============================================================================\n// CalendarDayButton\n// =============================================================================\n\nfunction CalendarDayButton({\n className,\n day,\n modifiers,\n ...props\n}: React.ComponentProps) {\n const ref = React.useRef(null);\n React.useEffect(() => {\n if (modifiers.focused) {\n ref.current?.focus();\n }\n }, [modifiers.focused]);\n\n return (\n span]:flex-col [&>span]:gap-0.5\",\n \"data-[range-end=true]:rounded-(--cell-radius) data-[range-end=true]:rounded-r-(--cell-radius)\",\n \"data-[range-middle=true]:rounded-none\",\n \"data-[range-start=true]:rounded-(--cell-radius) data-[range-start=true]:rounded-l-(--cell-radius)\",\n defaultClassNames.day,\n className\n )}\n data-day={day.date.toLocaleDateString()}\n data-range-end={modifiers.range_end}\n data-range-middle={modifiers.range_middle}\n data-range-start={modifiers.range_start}\n data-selected-single={\n modifiers.selected &&\n !modifiers.range_start &&\n !modifiers.range_end &&\n !modifiers.range_middle\n }\n data-slot=\"calendar-day-button\"\n ref={ref}\n size=\"icon\"\n variant=\"ghost\"\n {...props}\n />\n );\n}\n\n// =============================================================================\n// Exports\n// =============================================================================\n\nexport { Calendar, CalendarDayButton };\n", "type": "registry:ui" } ], "type": "registry:ui" }