calendar
Accessible, internationalised calendar and date-range calendar.
A headless month-grid calendar following the WAI-ARIA grid pattern. Locale drives
the month/weekday names, the calendar system (Gregorian, Buddhist, Hebrew,
Islamic…) and the first day of the week. Full keyboard navigation, min/max
bounds, per-date availability, and a two-click range mode. Date math is powered
by @fluixi/date-core (built on @internationalized/date), so
values are immutable and time-zone safe.
Installation
pnpm add @fluixi/calendar
Usage
import {
Calendar,
CalendarHeader,
CalendarHeading,
CalendarPrevButton,
CalendarNextButton,
CalendarGrid,
} from '@fluixi/calendar';
import { today, getLocalTimeZone } from '@fluixi/date-core';
<Calendar locale="en-US" defaultValue={today(getLocalTimeZone())} onChange={console.log}>
<CalendarHeader>
<CalendarPrevButton>‹</CalendarPrevButton>
<CalendarHeading />
<CalendarNextButton>›</CalendarNextButton>
</CalendarHeader>
<CalendarGrid />
</Calendar>
Range calendar
RangeCalendar swaps single-date selection for a two-click range. The first
click sets an anchor; the grid previews anchor → cursor as the pointer or
keyboard sweeps, and the next click commits an ordered { start, end }.
import { RangeCalendar, CalendarGrid } from '@fluixi/calendar';
<RangeCalendar locale="fr-FR" onChange={(range) => console.log(range?.start, range?.end)}>
<CalendarGrid />
</RangeCalendar>
Props (Calendar / RangeCalendar)
| Prop | Type | Default | Description |
|---|---|---|---|
locale |
string |
— | BCP-47 locale; drives names, week start and calendar system. |
value |
DateValue | DateRange | null |
— | Controlled selection (a date, or { start, end } for ranges). |
defaultValue |
DateValue | DateRange | null |
— | Initial uncontrolled selection. |
onChange |
(value) => void |
— | Fired when the selection changes. |
minValue |
DateValue |
— | Earliest selectable date; disables earlier cells and paging. |
maxValue |
DateValue |
— | Latest selectable date. |
isDateUnavailable |
(date) => boolean |
— | Marks individual dates unavailable (e.g. booked days). |
isDisabled |
boolean |
false |
Disables the whole calendar. |
isReadOnly |
boolean |
false |
Selection is fixed but still focusable. |
defaultFocusedValue |
DateValue |
today | Month/day the grid opens on when nothing is selected. |
firstDayOfWeek |
'sun' … 'sat' |
locale | Overrides the locale's natural week start. |
visibleDuration |
DateDuration |
{ months: 1 } |
How much is visible at once. |
timeZone |
string |
local | IANA zone used for today / conversions. |
createCalendar |
(id) => Calendar |
Gregorian | Inject a non-Gregorian calendar system. |
Parts
| Component | Element | Description |
|---|---|---|
Calendar / RangeCalendar |
div[role=group] |
Root; owns state and provides context. |
CalendarHeader |
div |
Layout row for the nav controls and heading. |
CalendarHeading |
h2 |
Live, locale-formatted month + year. |
CalendarPrevButton / CalendarNextButton |
button |
Page by the visible duration; auto-disable at bounds. |
CalendarGrid |
table[role=grid] |
Weekday header + week rows; owns keyboard navigation. |
Day cells expose data-* hooks for styling: data-selected, data-today,
data-disabled, data-unavailable, data-outside-month, and, for ranges,
data-selection-start / data-selection-end.
Keyboard
| Key | Action |
|---|---|
← / → |
Previous / next day |
↑ / ↓ |
Previous / next week |
Home / End |
First / last day of the week |
Page Up / Page Down |
Previous / next month |
Shift + Page Up/Down |
Previous / next year |
Enter / Space |
Select the focused day |