first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,105 @@
# Calendar
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs --> - calendar.test-fixtures — Wave-3h local test fixture…
- [calendar.test-fixtures](./calendar.test-fixtures.md) — Wave-3h local test fixture that augments the chassis test i18n instance with the `react-ui.calendar.*` keyset. The chas…
- [calendar.variants](./calendar.variants.md) — `calendarVariants` is the cva config that powers the outer wrapper of `<Calendar>`. The recipe is intentionally minimal…
- calendar.test-fixtures — Wave-3h local test fixture…
- [calendar.test-fixtures](./calendar.test-fixtures.md) — Wave-3h local test fixture that augments the chassis test i18n instance with the `react-ui.calendar.*` keyset. The chas…
- [calendar.variants](./calendar.variants.md) — `calendarVariants` is the cva config that powers the outer wrapper of `<Calendar>`. The recipe is intentionally minimal…
- calendar.test-fixtures — Wave-3h local test fixture…
- [calendar.test-fixtures](./calendar.test-fixtures.md) — Wave-3h local test fixture that augments the chassis test i18n instance with the `react-ui.calendar.*` keyset. The chas…
- [calendar.variants](./calendar.variants.md) — `calendarVariants` is the cva config that powers the outer wrapper of `<Calendar>`. The recipe is intentionally minimal…
- calendar.test-fixtures — Wave-3h local test fixture…
- [calendar.test-fixtures](./calendar.test-fixtures.md) — Wave-3h local test fixture that augments the chassis test i18n instance with the `react-ui.calendar.*` keyset. The chas…
- [calendar.variants](./calendar.variants.md) — `calendarVariants` is the cva config that powers the outer wrapper of `<Calendar>`. The recipe is intentionally minimal…
- [calendar.test-fixtures](./calendar.test-fixtures.md) — Wave-3h local test fixture that augments the chassis test i18n instance with the `react-ui.calendar.*` keyset. The chas…
- [calendar.variants](./calendar.variants.md) — `calendarVariants` is the cva config that powers the outer wrapper of `<Calendar>`. The recipe is intentionally minimal…
## Purpose
`<Calendar>` is the chassis date-grid primitive — a thin wrapper over `react-day-picker` (v9) that adds the chassis cva recipe, theme-aware lucide chevrons for previous/next-month navigation, locale-driven `aria-label` overrides, and a labelled outer wrapper. The chassis derives the active locale from `useTranslation()` so date formatting and accessible labels stay in lock-step with the rest of the app surface.
The chassis exposes the upstream surface verbatim through `Omit<DayPickerProps, "className" | "locale">` — every supported `mode` (`single`, `multiple`, `range`), the `selected` / `onSelect` controlled API, `numberOfMonths`, `showOutsideDays`, `weekStartsOn`, `disabled`, `fromMonth`/`toMonth`, the `modifiers` system, and the rest of the upstream API documented at <https://daypicker.dev>. Two upstream props are intercepted: `className` (so it merges with the chassis recipe via `cn()`) and `locale` (so the chassis can drive it from `useI18n()` rather than the consumer wiring it manually). For the rare case where a single calendar must diverge from the active app locale, pass a `localeOverride` (a `Partial<DayPickerLocale>` per the upstream API).
## Features
- Wraps `react-day-picker` v9 with zero additional state — fully controlled or uncontrolled per the upstream API.
- Three modes — `single` / `multiple` / `range` — selected via the upstream `mode` prop.
- Locale-aware: the chassis reads the active locale from `useTranslation()` so the calendar stays in sync with the app surface.
- `aria-label` on the outer wrapper resolves through the active i18n via `labelKey` (defaults to `react-ui.calendar.label`).
- Previous / next month navigation buttons get translated `aria-label`s via `previousMonthKey` / `nextMonthKey`.
- Lucide-iconography for navigation chevrons (`ChevronLeftIcon` / `ChevronRightIcon`) wired into `react-day-picker`'s `components.Chevron` slot.
- Forwards refs to the outer wrapper `<div>`.
- All other upstream props pass through unchanged.
## Usage: Basic Example
```tsx
import { Calendar } from "@suaveplan/react-ui";
import { useState } from "react";
export function ScheduleCalendar() {
const [date, setDate] = useState<Date | undefined>(new Date());
return <Calendar mode="single" selected={date} onSelect={setDate} />;
}
```
## Usage: Advanced Example
```tsx
import { Calendar } from "@suaveplan/react-ui";
import type { DateRange } from "react-day-picker";
import { useState } from "react";
export function BookingCalendar() {
const [range, setRange] = useState<DateRange | undefined>();
const today = new Date();
return (
<Calendar
mode="range"
selected={range}
onSelect={setRange}
numberOfMonths={2}
showOutsideDays
disabled={{ before: today }}
weekStartsOn={1}
previousMonthKey="react-ui.calendar.previousMonth"
nextMonthKey="react-ui.calendar.nextMonth"
/>
);
}
```
## API Reference
### `<Calendar>`
`forwardRef<HTMLDivElement, CalendarProps>` — renders an outer wrapper `<div role="group" aria-label={…}>` containing the `<DayPicker>` instance.
#### `CalendarProps`
| Prop | Type | Default | Description |
|---|---|---|---|
| `mode` | `"single" \| "multiple" \| "range"` | `undefined` | Selection mode — drives the `selected` / `onSelect` shape per the upstream API. |
| `labelKey` | `TranslationKey` | `"react-ui.calendar.label"` | `aria-label` for the outer wrapper. |
| `previousMonthKey` | `TranslationKey` | `"react-ui.calendar.previousMonth"` | `aria-label` for the previous-month navigation button. |
| `nextMonthKey` | `TranslationKey` | `"react-ui.calendar.nextMonth"` | `aria-label` for the next-month navigation button. |
| `localeOverride` | `Partial<DayPickerLocale>` | `undefined` | Override the date-fns locale for this calendar instance. When unset, `react-day-picker` falls back to its bundled `enUS`. |
| _every other_ `DayPickerProps` field | _upstream_ | _upstream_ | Forwarded as-is. See <https://daypicker.dev>. |
## Implementation Notes
- The chassis intentionally does NOT expose the upstream `locale` prop directly — `localeOverride` is a renamed escape hatch that documents the chassis's intent to drive locale from `useTranslation()` by default.
- `react-day-picker` ships its own classnames for the grid, day buttons, and navigation chrome via its internal `classNames` map. The chassis only scopes the OUTER container — overriding the inner classnames belongs in a theme variant override.
- Keyboard navigation (arrow keys, Home/End, Enter/Space, PageUp/Down) is supplied entirely by `react-day-picker` — the chassis does not wrap or intercept it.
- The chevron icons are passed via `components.Chevron` rather than `classNames` so the chassis can swap them for theme-provided lucide / radix icons in a future Wave 5d theme-icon-pack pass.
- `react-day-picker` renders its day grid as either a `<table>` (default) or a `[role="grid"]` element depending on the upstream version — the chassis does not assume one or the other in tests, hitting both via the `grid` selector.
- When migrating from a previous `<Calendar>` consumer (e.g. an upstream shadcn block), the only required change is to drop the `locale` prop — the chassis derives it automatically.
- Per the brief reconciliation note: the chassis casts the unreachable `?? "default"` fallback for `(i18n as { readonly locale: string }).locale` is NOT needed here because the locale is read indirectly via `useTranslation()` rather than from `i18n.locale`.