Range Calendar
Accessible, unstyled two-click date range calendar that reuses the Calendar grid parts.
|
|
| Package |
@vizejs/ui/range-calendar |
| Maturity |
stable |
| Own the source |
vize lib pull range-calendar |
| Requires |
calendar, context, controllable-state, id, locale |
| Aliases |
date range calendar, booking calendar, two-click range |
| Covers |
React Aria RangeCalendar, Reka UI RangeCalendar, WAI-ARIA APG grid |
Usage
import { RangeCalendar, RangeCalendarRoot, RangeCalendarGrid, RangeCalendarHeading, RangeCalendarMonthSelect, RangeCalendarNext, RangeCalendarPrev, RangeCalendarYearSelect } from "@vizejs/ui/range-calendar";
Or copy the source into your project with vize lib pull range-calendar (see Source Distribution).
API
RangeCalendar
Source: src/families/date-time/range-calendar/range-calendar-root.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null | undefined |
undefined |
Consumer-owned base id; nullish values use a deterministic fallback. |
modelValue |
DateRange | null | undefined |
undefined |
Controlled range; undefined selects uncontrolled mode and null clears. |
defaultValue |
DateRange | null | undefined |
null |
Initial uncontrolled range. |
allowNonContiguousRanges |
boolean | undefined |
false |
Allow committed ranges to span unavailable dates. |
focusedDate |
PlainDate | null | undefined |
undefined |
Controlled keyboard focus date; also decides which months are visible. |
min |
PlainDate | null | undefined |
undefined |
Earliest selectable date, inclusive. |
max |
PlainDate | null | undefined |
undefined |
Latest selectable date, inclusive. |
isDateUnavailable |
DateMatcher | undefined |
undefined |
Predicate for dates that stay focusable but cannot be selected. |
locale |
string | undefined |
undefined |
BCP 47 locale; defaults to the nearest LocaleProvider. |
calendar |
string | undefined |
undefined |
Intl calendar used for display labels only, for example japanese. |
numberingSystem |
string | undefined |
undefined |
Intl numbering system for labels. |
dir |
CalendarDirection | undefined |
undefined |
Text direction; defaults to the nearest LocaleProvider. |
weekStartsOn |
Weekday | undefined |
undefined |
First day of week (0 = Sunday); defaults to the locale preference. |
weekdayFormat |
CalendarWeekdayFormat | undefined |
"short" |
Weekday column label width. |
numberOfMonths |
number | undefined |
1 |
Number of consecutive months rendered. |
pagedNavigation |
boolean | undefined |
false |
Move month controls by numberOfMonths instead of one month. |
fixedWeeks |
boolean | undefined |
false |
Always render six week rows per month. |
today |
PlainDate | null | undefined |
undefined |
Explicit current date; the SSR-safe way to mark today. |
now |
DateTimeNow | undefined |
undefined |
Injectable clock evaluated during setup on server and client. |
timeZone |
string | undefined |
undefined |
IANA time zone used with now and the post-mount host clock. |
disabled |
boolean | undefined |
false |
Disable navigation, focus, and selection. |
readOnly |
boolean | undefined |
false |
Allow navigation while blocking selection. |
startName |
string | undefined |
undefined |
Hidden input name that submits the ISO start date with forms. |
endName |
string | undefined |
undefined |
Hidden input name that submits the ISO end date with forms. |
ariaLabel |
string | undefined |
undefined |
Accessible name for the calendar group. |
ariaLabelledby |
string | undefined |
undefined |
Ids that label the calendar group; defaults to the heading. |
ariaDescribedby |
string | undefined |
undefined |
Ids that describe the calendar group. |
Events
| Event |
Payload |
Description |
update:modelValue |
[value: DateRange | null] |
Fired when the calendar requests a new controlled range. |
change |
[value: DateRange | null, previous: DateRange | null, nativeEvent: Event | null] |
Fired after a distinct committed range with the previous range and triggering event. |
select |
[value: DateRange, nativeEvent: Event] |
Fired for every completed range selection, even when the range is unchanged. |
anchor-change |
[anchor: PlainDate | null] |
Fired when the first endpoint of a new range is picked, or null when cancelled or completed. |
update:focusedDate |
[date: PlainDate] |
Fired when keyboard or navigation moves the focus date. |
Slots
| Slot |
Slot props |
Description |
default |
CalendarSlotState |
Range calendar composition. Receives months, weekdays, heading, and state; defaults to a header plus one grid per month. |
Exposed
| Member |
Type |
Description |
value |
— |
|
anchor |
— |
|
setValue |
— |
|
cancel |
— |
|
RangeCalendarRoot
Source: src/families/date-time/range-calendar/range-calendar-root.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null | undefined |
undefined |
Consumer-owned base id; nullish values use a deterministic fallback. |
modelValue |
DateRange | null | undefined |
undefined |
Controlled range; undefined selects uncontrolled mode and null clears. |
defaultValue |
DateRange | null | undefined |
null |
Initial uncontrolled range. |
allowNonContiguousRanges |
boolean | undefined |
false |
Allow committed ranges to span unavailable dates. |
focusedDate |
PlainDate | null | undefined |
undefined |
Controlled keyboard focus date; also decides which months are visible. |
min |
PlainDate | null | undefined |
undefined |
Earliest selectable date, inclusive. |
max |
PlainDate | null | undefined |
undefined |
Latest selectable date, inclusive. |
isDateUnavailable |
DateMatcher | undefined |
undefined |
Predicate for dates that stay focusable but cannot be selected. |
locale |
string | undefined |
undefined |
BCP 47 locale; defaults to the nearest LocaleProvider. |
calendar |
string | undefined |
undefined |
Intl calendar used for display labels only, for example japanese. |
numberingSystem |
string | undefined |
undefined |
Intl numbering system for labels. |
dir |
CalendarDirection | undefined |
undefined |
Text direction; defaults to the nearest LocaleProvider. |
weekStartsOn |
Weekday | undefined |
undefined |
First day of week (0 = Sunday); defaults to the locale preference. |
weekdayFormat |
CalendarWeekdayFormat | undefined |
"short" |
Weekday column label width. |
numberOfMonths |
number | undefined |
1 |
Number of consecutive months rendered. |
pagedNavigation |
boolean | undefined |
false |
Move month controls by numberOfMonths instead of one month. |
fixedWeeks |
boolean | undefined |
false |
Always render six week rows per month. |
today |
PlainDate | null | undefined |
undefined |
Explicit current date; the SSR-safe way to mark today. |
now |
DateTimeNow | undefined |
undefined |
Injectable clock evaluated during setup on server and client. |
timeZone |
string | undefined |
undefined |
IANA time zone used with now and the post-mount host clock. |
disabled |
boolean | undefined |
false |
Disable navigation, focus, and selection. |
readOnly |
boolean | undefined |
false |
Allow navigation while blocking selection. |
startName |
string | undefined |
undefined |
Hidden input name that submits the ISO start date with forms. |
endName |
string | undefined |
undefined |
Hidden input name that submits the ISO end date with forms. |
ariaLabel |
string | undefined |
undefined |
Accessible name for the calendar group. |
ariaLabelledby |
string | undefined |
undefined |
Ids that label the calendar group; defaults to the heading. |
ariaDescribedby |
string | undefined |
undefined |
Ids that describe the calendar group. |
Events
| Event |
Payload |
Description |
update:modelValue |
[value: DateRange | null] |
Fired when the calendar requests a new controlled range. |
change |
[value: DateRange | null, previous: DateRange | null, nativeEvent: Event | null] |
Fired after a distinct committed range with the previous range and triggering event. |
select |
[value: DateRange, nativeEvent: Event] |
Fired for every completed range selection, even when the range is unchanged. |
anchor-change |
[anchor: PlainDate | null] |
Fired when the first endpoint of a new range is picked, or null when cancelled or completed. |
update:focusedDate |
[date: PlainDate] |
Fired when keyboard or navigation moves the focus date. |
Slots
| Slot |
Slot props |
Description |
default |
CalendarSlotState |
Range calendar composition. Receives months, weekdays, heading, and state; defaults to a header plus one grid per month. |
Exposed
| Member |
Type |
Description |
mode |
CalendarSelectionMode |
Selection model of the root. |
months |
readonly CalendarMonthState[] |
Visible month grids; empty while pending. |
weekdays |
readonly CalendarWeekdayLabel[] |
Localized weekday labels in column order. |
heading |
string |
Localized heading for the visible months. |
focusedDate |
PlainDate | null |
Keyboard focus target, or null while pending. |
today |
PlainDate | null |
Resolved current date, or null before a clock is available. |
locale |
string |
Resolved BCP 47 locale. |
direction |
CalendarDirection |
Resolved text direction. |
weekStartsOn |
Weekday |
Resolved first day of week. |
disabled |
boolean |
Whether every control is disabled. |
readOnly |
boolean |
Whether selection is blocked while navigation works. |
pending |
boolean |
Whether no date is known yet: no value, focused date, today, or now was supplied, so the grid waits for the client clock after mount. |
state |
CalendarState |
Stable root state token. |
root |
HTMLDivElement | null |
Rendered root element. |
focus |
(options?: FocusOptions) => boolean |
Focus the roving day button; returns whether focus moved. |
setFocusedDate |
(date: PlainDate) => void |
Move the keyboard focus target (and visible months) to a date, clamped to min/max. |
setVisibleMonth |
(month: PlainYearMonth) => void |
Show a month as the first visible month. |
navigate |
(unit: CalendarNavigationUnit, direction: -1 | 1) => boolean |
Navigate by one page of months or years; returns whether the view moved. |
value |
DateRange | null |
Current committed range. |
anchor |
PlainDate | null |
First endpoint of a range that is still being picked, or null. |
setValue |
(value: DateRange | null) => boolean |
Request a new range; returns whether it differs. |
cancel |
() => boolean |
Cancel a pending anchor; returns whether one was pending. |
RangeCalendarGrid
Source: src/families/date-time/calendar/calendar-grid.vue
Props
| Prop |
Type |
Default |
Description |
monthIndex |
number |
0 |
Zero-based index of the visible month this grid renders. |
showWeekNumbers |
boolean |
false |
Render a leading row header with the ISO 8601 week number of each row. |
weekNumberLabel |
string |
"Wk" |
Visible week-number column header. |
weekNumberHeader |
string |
"Week" |
Full week-number column header used as abbr. |
Slots
| Slot |
Slot props |
Description |
weekday |
CalendarWeekdaySlotState |
Weekday column header content. Receives the weekday, its labels, and column index. |
weekNumber |
CalendarWeekNumberSlotState |
Week-number row header content. Receives the ISO week and the row's days. |
day |
CalendarDayState |
Day button content. Receives the complete day state; defaults to the localized day number. |
RangeCalendarHeading
Source: src/families/date-time/calendar/calendar-heading.vue
Slots
| Slot |
Slot props |
Description |
default |
CalendarHeadingSlotState |
Heading content. Receives the localized heading and visible months. |
RangeCalendarMonthSelect
Source: src/families/date-time/calendar/calendar-month-select.vue
Props
| Prop |
Type |
Default |
Description |
ariaLabel |
string |
"Month" |
Accessible name of the native select. |
Events
| Event |
Payload |
Description |
change |
[month: number, nativeEvent: Event] |
Fired after the user picks a month, with the ISO month number. |
RangeCalendarNext
Source: src/families/date-time/calendar/calendar-next.vue
Props
| Prop |
Type |
Default |
Description |
unit |
CalendarNavigationUnit |
"month" |
Whether the control pages by month (or numberOfMonths when paged) or by year. |
ariaLabel |
string |
undefined |
Accessible name; defaults to "Next month" or "Next year". |
Events
| Event |
Payload |
Description |
click |
[nativeEvent: MouseEvent] |
Fired before navigation. Call preventDefault() to keep the view unchanged. |
Slots
| Slot |
Slot props |
Description |
default |
CalendarNavigationSlotState |
Control content. Receives the unit and whether navigation is possible. |
RangeCalendarPrev
Source: src/families/date-time/calendar/calendar-prev.vue
Props
| Prop |
Type |
Default |
Description |
unit |
CalendarNavigationUnit |
"month" |
Whether the control pages by month (or numberOfMonths when paged) or by year. |
ariaLabel |
string |
undefined |
Accessible name; defaults to "Previous month" or "Previous year". |
Events
| Event |
Payload |
Description |
click |
[nativeEvent: MouseEvent] |
Fired before navigation. Call preventDefault() to keep the view unchanged. |
Slots
| Slot |
Slot props |
Description |
default |
CalendarNavigationSlotState |
Control content. Receives the unit and whether navigation is possible. |
RangeCalendarYearSelect
Source: src/families/date-time/calendar/calendar-year-select.vue
Props
| Prop |
Type |
Default |
Description |
from |
number |
undefined |
First listed year; defaults to the min year or ten years before the view. |
to |
number |
undefined |
Last listed year; defaults to the max year or ten years after the view. |
ariaLabel |
string |
"Year" |
Accessible name of the native select. |
Events
| Event |
Payload |
Description |
change |
[year: number, nativeEvent: Event] |
Fired after the user picks a year. |
Behavior
Normative state x input -> outcome table for range-calendar-root.vue
(@vizejs/ui/range-calendar). The root publishes the same context as
CalendarRoot, so calendar-grid.vue, calendar-heading.vue,
calendar-prev.vue, calendar-next.vue, and the month/year selects are reused
as RangeCalendar* parts; keyboard, locale, bounds, and SSR rules from
calendar.behavior.md apply unchanged. Every row is proven by the named test.
| # |
State |
Input |
Outcome |
Proven by |
| R1 |
empty |
first activation |
the date becomes the anchor (data-anchor, anchor-change); grids are aria-multiselectable |
two activations anchor, preview, and commit an ordered range |
| R2 |
anchored |
pointer enter |
the preview range is marked with data-in-range, data-range-start, data-range-end, and data-preview; previewed middles are aria-selected="false" |
two activations anchor, preview, and commit an ordered range |
| R3 |
anchored |
second activation |
an ordered range commits: anchor-change(null), update:modelValue, change; middles become range-middle and aria-selected="true"; startName/endName inputs submit ISO |
two activations anchor, preview, and commit an ordered range |
| R4 |
anchored |
keyboard focus / Escape |
focus movement previews the range; Escape cancels the anchor and is consumed only when an anchor exists |
keyboard focus previews the range and Escape cancels the anchor |
| R5 |
unavailable dates |
completing activation |
a range spanning an unavailable date restarts the anchor unless allowNonContiguousRanges |
unavailable dates break ranges unless non-contiguous ranges are allowed |
| R6 |
controlled / read-only |
render / expose / activation |
controlled ranges render across months (outside days included); setValue, cancel, anchor are exposed; read-only blocks anchoring |
controlled ranges, read-only mode, and the exposed API |
| R7 |
SSR/hydration |
isolated requests |
byte-identical markup, no anchor or preview state, silent hydration |
renders byte-identical range markup across isolated SSR requests, hydrates a range calendar without mismatches |
Props and emits beyond CalendarRoot
| Surface |
Contract |
modelValue / defaultValue |
DateRange | null; ranges are normalized so start <= end. |
allowNonContiguousRanges |
Permit committed ranges that include unavailable dates. Default false. |
startName / endName |
Hidden inputs submitting the ISO endpoints. |
anchor-change(anchor) |
Fires when the first endpoint is picked, cancelled, or completed. |
select(range, event) |
Fires for every completed selection, even when unchanged. |
| root data |
data-mode="range", data-start, data-end, data-anchor. |