import type { DateInput } from '../utils/date-value.js'; import type { PrimitivePropsWithRef } from '../utils/primitive.js'; /** One cell in a {@link Calendar} grid week, as produced by `getVisibleWeeks`. */ export interface CalendarDay { date: Date; /** Whether this date falls outside the grid's displayed month (leading/trailing filler day). */ outsideMonth: boolean; } /** Selection mode determining the shape of `value`: `'single'` (`Date | null`), `'multiple'` (`Date[]`), or `'range'` (`{ start: Date; end: Date | null } | null`). */ export type CalendarSelectionMode = 'single' | 'multiple' | 'range'; /** A selected date range; `end` is `null` while the range is anchored, awaiting a closing date. */ export interface DateRange { /** First date of the range (always the earlier of the two once `end` is set). */ start: Date; /** Last date of the range, or `null` while only the anchor has been picked. */ end: Date | null; } /** Calendar's selection shape following {@link CalendarSelectionMode}. */ export type CalendarValue = Date | Date[] | DateRange | null; /** Lenient input form of {@link CalendarValue} that also accepts ISO date strings and wire strings (`'2026-08-10'` single, `'2026-08-10,2026-08-14'` set, `'2026-08-10/2026-08-14'` range) as local-time calendar dates. */ export type CalendarValueInput = DateInput | ReadonlyArray | { start: DateInput; end?: DateInput | null | undefined; } | null; /** Where a date sits inside a selected range, for the `data-range-*` styling hooks. */ export type RangePosition = 'start' | 'middle' | 'end'; /** * Navigation button step size: 'visible' pages by the whole displayed span so * pages never overlap; 'single' always moves one month. */ export type CalendarPageBehavior = 'visible' | 'single'; /** Which grid a {@link Calendar} is showing, ordered coarsest-last so `CalendarHeading` steps up this list and activation steps down it. */ export type CalendarView = 'day' | 'month' | 'year'; /** @internal Shared state distributed from {@link Calendar} to its parts via context. */ export interface CalendarContextValue { value: CalendarValue; selectionMode: CalendarSelectionMode; month: Date; /** Every displayed month, `numberOfMonths` long, starting at `month`. */ months: Date[]; focusedDate: Date; /** The date the calendar treats as today, pinned once per mount. */ today: Date; weekStartsOn: number; /** Whether week rows show their ISO-8601 week number. */ weekNumbers: boolean; locale?: string | undefined; /** Weekday names for `locale`, rotated to `weekStartsOn`, as `[short, long]` pairs. */ weekDayNames: Array<{ short: string; long: string; }>; minDate?: Date | undefined; maxDate?: Date | undefined; readOnly: boolean; disabled: boolean; /** Whether a day from an adjacent month is pickable rather than natively disabled. */ outsideDaySelectable: boolean; /** * Predicate marking arbitrary dates as disabled, in addition to `minDate`/`maxDate`; a disabled cell is neither focusable nor clickable. * @default undefined — no additional dates disabled. */ isDateDisabled?: ((date: Date) => boolean) | undefined; /** * Predicate marking dates as *unavailable* — a booked night, a closed day — staying keyboard-reachable and announced via `aria-disabled`, unlike `isDateDisabled`. * @default undefined — every date is available. */ isDateUnavailable?: ((date: Date) => boolean) | undefined; onValueChange: (date: Date) => void; onMonthChange: (month: Date) => void; /** Moves one page back (`-1`) or forward (`1`) and announces it. */ pageMonths: (direction: -1 | 1) => void; setFocusedDate: (date: Date) => void; view: CalendarView; minView: CalendarView; maxView: CalendarView; /** Sets the view, clamped into `minView`/`maxView`. */ setView: (view: CalendarView) => void; /** Moves the cursor onto `date`, pages to it, and steps the view down one level. */ navigateToUnit: (date: Date) => void; } /** @internal The month one {@link CalendarGrid} renders, so a multi-month calendar's cells know which month they belong to. */ export interface CalendarGridContextValue { gridMonth: Date; } /** Props for the {@link Calendar} root. */ export interface CalendarProps extends Omit, 'defaultValue' | 'onChange'> { /** * How many dates can be selected at once, determining the shape of `value` per {@link CalendarSelectionMode}. * @default 'single' */ selectionMode?: CalendarSelectionMode | undefined; /** * Current selection for controlled usage. Pair with `onValueChange`. * @default undefined — uncontrolled, tracks its own selection internally. */ value?: CalendarValueInput | undefined; /** * Initial selection for uncontrolled usage, follows `selectionMode`. * @default null for `single`/`range`, `[]` for `multiple`. */ defaultValue?: CalendarValueInput | undefined; /** * Fires with the whole new selection whenever a `CalendarCell` is chosen. * @default undefined — no-op if omitted. */ onValueChange?: ((value: CalendarValue) => void) | undefined; /** * Displayed month, for controlled usage. Pair with `onMonthChange`. * @default undefined — uncontrolled, tracks its own displayed month internally. */ month?: DateInput | undefined; /** * Initially displayed month, for uncontrolled usage. * @default undefined — defaults to the selection's month, or the current month. */ defaultMonth?: DateInput | undefined; /** * Fires with the newly displayed month whenever navigation changes it. * @default undefined — no-op if omitted. */ onMonthChange?: ((month: Date) => void) | undefined; /** * The date holding the grid's roving tab stop, for controlled usage (pair with `onFocusChange`); distinct from `value` since focus moves on every arrow key and selects nothing. * @default undefined — uncontrolled, tracks its own roving focus internally. */ focusedDate?: DateInput | undefined; /** * Initially focused date, for uncontrolled usage. * @default undefined — the selection's anchor date, else today when it is displayed, else the month. */ defaultFocusedDate?: DateInput | undefined; /** * Fires with the newly focused date whenever the grid's roving tab stop moves. * @default undefined — no-op if omitted. */ onFocusChange?: ((date: Date) => void) | undefined; /** * Which grid is showing, for controlled usage (pair with `onViewChange`); a non-day grid navigates only, since activating a cell steps the view down without touching `value`. * @default undefined — uncontrolled, tracks its own view internally. */ view?: CalendarView | undefined; /** * Which grid shows first, for uncontrolled usage. * @default 'day' */ defaultView?: CalendarView | undefined; /** * The most granular view the calendar steps down to; raising it makes the * calendar navigation-only. * @default 'day' */ minView?: CalendarView | undefined; /** * The least granular view the calendar steps up to, and how far * `CalendarHeading` goes. * @default 'year' */ maxView?: CalendarView | undefined; /** * Fires with the view now showing whenever it steps up or down. * @default undefined — no-op if omitted. */ onViewChange?: ((view: CalendarView) => void) | undefined; /** * How many consecutive months to display, starting at `month`. * @default 1 */ numberOfMonths?: number | undefined; /** * BCP 47 tag to derive month and weekday names via Intl, and default `weekStartsOn`. * @default undefined — the runtime's own locale. */ locale?: string | undefined; /** * Earliest selectable date; earlier dates render disabled. * @default undefined — no minimum. */ minDate?: DateInput | undefined; /** * Latest selectable date; later dates render disabled. * @default undefined — no maximum. */ maxDate?: DateInput | undefined; /** * Day of week the grid's first column represents (`0`=Sunday…`6`=Saturday), derived from `locale` when omitted. * @default undefined — derived from `locale`, falling back to `0` (Sunday). */ weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined; /** * How far the previous/next buttons move — `'visible'` pages by the whole displayed span so consecutive pages never overlap, `'single'` always moves one month. * @default 'visible' */ pageBehavior?: CalendarPageBehavior | undefined; /** * Labels each week with its ISO-8601 number, which runs from Monday independently of `weekStartsOn`. * @default false */ weekNumbers?: boolean | undefined; /** * Date treated as today, carrying `data-today` and `aria-current="date"`; pass explicitly in SSR to prevent a hydration mismatch across time zones. * @default undefined — `new Date()`, read once when the calendar mounts. */ today?: DateInput | undefined; /** * Non-editable: cells stay focusable, `onValueChange` never fires, and the grid carries `aria-readonly="true"`, distinct from `disabled`. * @default false */ readOnly?: boolean | undefined; /** * Disables the whole calendar: cells carry the native `disabled` attribute, and the grid leaves the tab order and carries `aria-disabled="true"`. * @default false */ disabled?: boolean | undefined; /** * Allow selecting adjacent-month days. Enabled cells stay focusable and page * the calendar on selection. * @default false */ outsideDaySelectable?: boolean | undefined; /** * Predicate marking arbitrary dates as disabled, in addition to `minDate`/`maxDate`; a disabled cell is not focusable or clickable. * @default undefined — no additional dates disabled. */ isDateDisabled?: ((date: Date) => boolean) | undefined; /** * Predicate marking dates as *unavailable* — a booked night, a closed day — staying keyboard-reachable and announced via `aria-disabled`, unlike `isDateDisabled`. * @default undefined — every date is available. */ isDateUnavailable?: ((date: Date) => boolean) | undefined; /** * Words the polite live-region message announced after a selection change in `multiple`/`range` mode; return `''` to announce nothing. * @default undefined — the built-in English wording ("Range selected, 1 May to 8 May."). */ getSelectionAnnouncement?: ((value: CalendarValue, locale: string | undefined) => string) | undefined; /** * Words the polite live-region message announced when paging the grid; return `''` for nothing. * @default undefined — `formatMonthLabel(month, locale)`, e.g. "février 2026". */ getMonthAnnouncement?: ((month: Date, locale: string | undefined) => string) | undefined; } /** Props for {@link CalendarHeader}, the purely presentational wrapper around the month navigation row. */ export interface CalendarHeaderProps extends PrimitivePropsWithRef<'div'> { } /** Props for {@link CalendarHeading}, the label for the displayed month. */ export interface CalendarHeadingProps extends PrimitivePropsWithRef<'div'> { /** * Which of the displayed months to label, `0` being the first. * @default 0 */ monthOffset?: number | undefined; } /** Props for {@link CalendarPreviousButton}, which navigates to the previous month. */ export interface CalendarPreviousButtonProps extends PrimitivePropsWithRef<'button'> { } /** Props for {@link CalendarNextButton}, which navigates to the next month. */ export interface CalendarNextButtonProps extends PrimitivePropsWithRef<'button'> { } /** Props for {@link CalendarGrid}, the `grid`-role container that owns arrow-key date navigation. */ export interface CalendarGridProps extends PrimitivePropsWithRef<'div'> { /** * Which of the displayed months this grid renders, `0` being the first — * only meaningful when the `Calendar` sets `numberOfMonths` above 1. * @default 0 */ monthOffset?: number | undefined; } /** Props for {@link CalendarWeekNumber}, a `rowheader` cell carrying one week's ISO-8601 number. */ export interface CalendarWeekNumberProps extends PrimitivePropsWithRef<'div'> { /** Any day of the week to number — `CalendarGridBody` passes the row's first cell. */ date: DateInput; } /** Props for {@link CalendarWeekNumberHeader}, the `columnheader` above the week-number column. */ export interface CalendarWeekNumberHeaderProps extends PrimitivePropsWithRef<'div'> { } /** Props for {@link CalendarMonthGrid}, the `grid`-role twelve-month view. */ export interface CalendarMonthGridProps extends PrimitivePropsWithRef<'div'> { } /** Props for {@link CalendarYearGrid}, the `grid`-role twenty-year view. */ export interface CalendarYearGridProps extends PrimitivePropsWithRef<'div'> { } /** Props for {@link CalendarGridHead}, the `rowgroup`-role wrapper for the weekday header. */ export interface CalendarGridHeadProps extends PrimitivePropsWithRef<'div'> { } /** Props for {@link CalendarWeekDay}, a single `columnheader` cell labeling a weekday. */ export interface CalendarWeekDayProps extends PrimitivePropsWithRef<'div'> { } /** Props for {@link CalendarGridBody}, the `rowgroup`-role wrapper around the week rows. */ export interface CalendarGridBodyProps extends PrimitivePropsWithRef<'div'> { } /** Props for {@link CalendarRow}, a `row`-role wrapper for one week of `CalendarCell`s. */ export interface CalendarRowProps extends PrimitivePropsWithRef<'div'> { } /** Props for {@link CalendarCell}, a single selectable `gridcell` date button. */ export interface CalendarCellProps extends PrimitivePropsWithRef<'button'> { /** The date value this calendar grid cell represents and selects when activated. */ date: DateInput; }