"use client"; /* eslint-disable complexity, react/no-unstable-nested-components, react-x/no-nested-component-definitions, react/prop-types */ import {ChevronDown, ChevronLeft, ChevronRight} from "lucide-react"; import * as React from "react"; import {DayButton, DayPicker, type DateRange, type DayPickerProps, type Matcher} from "react-day-picker"; import {Button} from "@/components/ui/button"; import {cn} from "@/lib/utilities"; import buttonStyles from "./button.module.css"; import styles from "./calendar.module.css"; type CalendarButtonVariant = NonNullable["variant"]>; type DayPickerComponents = NonNullable["components"]>; type CalendarWeekNumberProps = React.ComponentProps>; /** * Props for the shared calendar component. */ type CalendarProps = React.ComponentProps & { /** * Visual variant applied to the navigation buttons. * @default "ghost" */ buttonVariant?: React.ComponentProps["variant"]; }; const calendarButtonVariantStyles: Record = { default: buttonStyles.default, destructive: buttonStyles.destructive, ghost: buttonStyles.ghost, link: buttonStyles.link, outline: buttonStyles.outline, secondary: buttonStyles.secondary, }; /** * Renders a styled calendar built on top of `react-day-picker`. * * @remarks * - Renders the `DayPicker` calendar root * - Built on `react-day-picker` with shared button styling from the component library * - Preserves the V1 public API while aligning visuals with the current design system * - Overrides the default DayPicker `Root`, `Chevron`, `DayButton`, and `WeekNumber` * components while still allowing consumers to replace them through the `components` prop * - Override the default chevron icons with `components={{Chevron: YourChevronComponent}}` * * @example * ```tsx * console.log(date)} * /> * ``` * * @see {@link https://daypicker.dev | React Day Picker Docs} */ const Calendar = React.forwardRef( ( {className, classNames, showOutsideDays = true, captionLayout = "label", buttonVariant = "ghost", formatters, components, ...props}, forwardedRef, ) => { return ( { const {locale} = props; return date.toLocaleString(locale?.code ?? "default", {month: "short"}); }, ...formatters, }} classNames={{ ...classNames, root: cn(styles.root, classNames?.root), months: cn(styles.months, classNames?.months), month: cn(styles.month, classNames?.month), nav: cn(styles.nav, classNames?.nav), button_previous: cn( buttonStyles.button, buttonStyles.sizeIcon, calendarButtonVariantStyles[buttonVariant], styles.navButton, classNames?.button_previous, ), button_next: cn( buttonStyles.button, buttonStyles.sizeIcon, calendarButtonVariantStyles[buttonVariant], styles.navButton, classNames?.button_next, ), month_caption: cn(styles.monthCaption, classNames?.month_caption), dropdowns: cn(styles.dropdowns, classNames?.dropdowns), dropdown_root: cn(styles.dropdownRoot, classNames?.dropdown_root), dropdown: cn(styles.dropdown, classNames?.dropdown), caption_label: cn(styles.captionLabel, captionLayout !== "label" && styles.captionLabelDropdown, classNames?.caption_label), month_grid: cn(styles.monthGrid, classNames?.month_grid), weekdays: cn(styles.weekdays, classNames?.weekdays), weekday: cn(styles.weekday, classNames?.weekday), week: cn(styles.week, classNames?.week), week_number_header: cn(styles.weekNumberHeader, classNames?.week_number_header), week_number: cn(styles.weekNumber, classNames?.week_number), day: cn(styles.day, classNames?.day), range_start: cn(styles.rangeStart, classNames?.range_start), range_middle: cn(styles.rangeMiddle, classNames?.range_middle), range_end: cn(styles.rangeEnd, classNames?.range_end), today: cn(styles.today, classNames?.today), outside: cn(styles.outside, classNames?.outside), disabled: classNames?.disabled, hidden: cn(styles.hidden, classNames?.hidden), }} components={{ Root: ({className: rootClassName, rootRef, ...rootProps}) => (
), Chevron: ({className: chevronClassName, orientation, ...chevronProps}) => { if (orientation === "left") { return ( ); } if (orientation === "right") { return ( ); } return ( ); }, DayButton: CalendarDayButton, WeekNumber: CalendarWeekNumber, ...components, }} {...props} /> ); }, ); function CalendarWeekNumber({week, children, ...tdProps}: Readonly): React.JSX.Element { return (
{children}
); } function CalendarDayButton({className, day, modifiers, ...props}: Readonly>): React.JSX.Element { const ref = React.useRef(null); React.useEffect(() => { if (modifiers["focused"]) { ref.current?.focus(); } }, [modifiers]); return (