diff --git a/apps/web/app/shared/ui/calendar.tsx b/apps/web/app/shared/ui/calendar.tsx index a7b6f4a..0a19a55 100644 --- a/apps/web/app/shared/ui/calendar.tsx +++ b/apps/web/app/shared/ui/calendar.tsx @@ -2,6 +2,8 @@ import * as React from "react"; import { DayPicker, getDefaultClassNames, + useDayPicker, + type CustomComponents, type DayButton, type Locale, } from "react-day-picker"; @@ -14,6 +16,74 @@ import { ChevronDownIcon, } from "lucide-react"; +/** + * The `components` overrides live out here rather than inline in `Calendar`'s + * render, which is where shadcn's generator puts them. + * + * An arrow function written inside the render body is a new component type on + * every pass, and DayPicker keys its subtree on these identities: re-rendering + * the calendar unmounts and remounts the whole grid rather than updating it. + * That is not only wasted work — `CalendarDayButton` holds a ref and an effect + * that focuses the day matching `modifiers.focused`, and a remount drops the + * focus it had just moved. The range picker re-renders on every click, so this + * ran on the path a keyboard user is actually using. + */ +const CalendarRoot: CustomComponents["Root"] = ({ + className, + rootRef, + ...props +}) => { + return ( +
+ ); +}; + +const CalendarChevron: CustomComponents["Chevron"] = ({ + className, + orientation, + ...props +}) => { + if (orientation === "left") { + return ; + } + + if (orientation === "right") { + return ; + } + + return ; +}; + +/** + * Reads the locale back out of DayPicker's own context instead of closing over + * `Calendar`'s prop, which is what forced this one to be defined inline. It is + * the same value: `Calendar` hands `locale` to `DayPicker`, and this renders + * inside it. + */ +const CalendarDayButtonSlot: CustomComponents["DayButton"] = (props) => { + const { dayPickerProps } = useDayPicker(); + + return ; +}; + +const CalendarWeekNumber: CustomComponents["WeekNumber"] = ({ + children, + ...props +}) => { + return ( + +
+ {children} +
+ + ); +}; + function Calendar({ className, classNames, @@ -136,48 +206,10 @@ function Calendar({ ...classNames, }} components={{ - Root: ({ className, rootRef, ...props }) => { - return ( -
- ); - }, - Chevron: ({ className, orientation, ...props }) => { - if (orientation === "left") { - return ( - - ); - } - - if (orientation === "right") { - return ( - - ); - } - - return ( - - ); - }, - DayButton: ({ ...props }) => ( - - ), - WeekNumber: ({ children, ...props }) => { - return ( - -
- {children} -
- - ); - }, + Root: CalendarRoot, + Chevron: CalendarChevron, + DayButton: CalendarDayButtonSlot, + WeekNumber: CalendarWeekNumber, ...components, }} {...props}