/* Copyright 2026 Marimo. All rights reserved. */ import { getLocalTimeZone, today } from "@internationalized/date"; import { ChevronLeft, ChevronRight } from "lucide-react"; import * as React from "react"; import { Button as AriaButton, Calendar as AriaCalendar, CalendarCell as AriaCalendarCell, type CalendarCellProps as AriaCalendarCellProps, CalendarGrid as AriaCalendarGrid, CalendarGridBody as AriaCalendarGridBody, type CalendarGridBodyProps as AriaCalendarGridBodyProps, CalendarGridHeader as AriaCalendarGridHeader, type CalendarGridHeaderProps as AriaCalendarGridHeaderProps, type CalendarGridProps as AriaCalendarGridProps, CalendarHeaderCell as AriaCalendarHeaderCell, type CalendarHeaderCellProps as AriaCalendarHeaderCellProps, type CalendarProps as AriaCalendarProps, type DateValue as AriaDateValue, Heading as AriaHeading, RangeCalendar as AriaRangeCalendar, type RangeCalendarProps as AriaRangeCalendarProps, RangeCalendarStateContext as AriaRangeCalendarStateContext, composeRenderProps, Text, useLocale, } from "react-aria-components"; import { buttonVariants } from "@/components/ui/button"; import { cn } from "@/utils/cn"; const CalendarHeading = (props: React.HTMLAttributes) => { const { direction } = useLocale(); return (
{direction === "rtl" ? ( ) : ( )} {direction === "rtl" ? ( ) : ( )}
); }; const CalendarGrid = ({ className, ...props }: AriaCalendarGridProps) => ( ); const CalendarGridHeader = ({ ...props }: AriaCalendarGridHeaderProps) => ( ); const CalendarHeaderCell = ({ className, ...props }: AriaCalendarHeaderCellProps) => ( ); const CalendarGridBody = ({ className, ...props }: AriaCalendarGridBodyProps) => ( tr>td]:p-0", className)} {...props} /> ); const CalendarCell = ({ className, ...props }: AriaCalendarCellProps) => { const isRange = Boolean(React.use(AriaRangeCalendarStateContext)); return ( cn( buttonVariants({ variant: "ghost" }), "relative flex size-9 items-center justify-center p-0 text-sm font-normal", /* Disabled */ renderProps.isDisabled && "text-muted-foreground opacity-50", /* Selected */ renderProps.isSelected && "bg-primary text-primary-foreground data-focused:bg-primary data-focused:text-primary-foreground", /* Hover */ renderProps.isHovered && renderProps.isSelected && (renderProps.isSelectionStart || renderProps.isSelectionEnd || !isRange) && "data-hovered:bg-primary data-hovered:text-primary-foreground", /* Selection Start/End */ renderProps.isSelected && isRange && !renderProps.isSelectionStart && !renderProps.isSelectionEnd && "rounded-none bg-accent text-accent-foreground", /* Outside Month */ renderProps.isOutsideMonth && "text-muted-foreground opacity-50 data-selected:bg-accent/50 data-selected:text-muted-foreground data-selected:opacity-30", /* Current Date */ renderProps.date.compare(today(getLocalTimeZone())) === 0 && !renderProps.isSelected && "bg-accent text-accent-foreground", /* Unavailable Date */ renderProps.isUnavailable && "cursor-default text-destructive ", renderProps.isInvalid && "bg-destructive text-destructive-foreground data-focused:bg-destructive data-hovered:bg-destructive data-focused:text-destructive-foreground data-hovered:text-destructive-foreground", className, ), )} {...props} /> ); }; interface CalendarProps extends AriaCalendarProps { errorMessage?: string; } const Calendar = ({ errorMessage, className, ...props }: CalendarProps) => { return ( cn("w-fit", className), )} {...props} > {(day) => {day}} {(date) => } {errorMessage && ( {errorMessage} )} ); }; interface RangeCalendarProps< T extends AriaDateValue, > extends AriaRangeCalendarProps { errorMessage?: string; } const RangeCalendar = ({ errorMessage, className, ...props }: RangeCalendarProps) => { return ( cn("w-fit", className), )} {...props} > {(day) => {day}} {(date) => } {errorMessage && ( {errorMessage} )} ); }; export { Calendar, CalendarCell, CalendarGrid, CalendarGridBody, CalendarGridHeader, CalendarHeaderCell, CalendarHeading, RangeCalendar, }; export type { CalendarProps, RangeCalendarProps };