Scheduler
Headless, SSR-deterministic day/week/month scheduler with a pure event layout engine.
|
|
| Package |
@vizejs/ui/scheduler |
| Maturity |
stable |
| Own the source |
vize lib pull scheduler |
| Requires |
calendar, context, controllable-state, datetime-field, drag-and-drop, id, locale |
| Aliases |
calendar view, event calendar, agenda, week planner, big calendar |
| Covers |
FullCalendar views, React Big Calendar, WAI-ARIA grid, Google Calendar layout |
Usage
import { Scheduler, SchedulerRoot, SchedulerDayColumn, SchedulerEvent, SchedulerHeading, SchedulerMonthDay, SchedulerMonthGrid, SchedulerNav, SchedulerTimeGrid } from "@vizejs/ui/scheduler";
Or copy the source into your project with vize lib pull scheduler (see Source Distribution).
API
Scheduler<Data>
Source: src/families/date-time/scheduler/scheduler-root.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null | undefined |
undefined |
Consumer-owned base id; nullish values use a deterministic fallback. |
events |
readonly SchedulerEvent<Data>[] | undefined |
[] |
Events to lay out; data keeps the consumer payload type. |
view |
SchedulerView | undefined |
undefined |
Controlled view; undefined selects uncontrolled mode. |
defaultView |
SchedulerView | undefined |
"week" |
Initial uncontrolled view. |
date |
PlainDate | null | undefined |
undefined |
Controlled anchor date deciding the visible period. |
defaultDate |
PlainDate | null | undefined |
undefined |
Initial uncontrolled anchor date. |
today |
PlainDate | null | undefined |
undefined |
Explicit current date; the SSR-safe way to anchor and 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. |
locale |
string | undefined |
undefined |
BCP 47 locale; defaults to the nearest LocaleProvider. |
dir |
"ltr" | "rtl" | undefined |
undefined |
Text direction; defaults to the nearest LocaleProvider. |
weekStartsOn |
Weekday | undefined |
undefined |
First day of week; defaults to the locale preference. |
dayStartHour |
number | undefined |
0 |
First visible hour of the time grid. |
dayEndHour |
number | undefined |
24 |
Hour where the time grid ends, exclusive. |
slotMinutes |
number | undefined |
30 |
Minutes per time-grid slot row. |
snapMinutes |
number | undefined |
slotMinutes |
Minutes that drag and keyboard moves snap to. |
minimumEventMinutes |
number | undefined |
15 |
Minimum rendered event length in minutes. |
maxLanes |
number | undefined |
3 |
Month-view lanes rendered per week before counting overflow. |
disabled |
boolean | undefined |
false |
Disable navigation, activation, and editing. |
readOnly |
boolean | undefined |
false |
Keep navigation and activation but block move and resize requests. |
ariaLabel |
string | undefined |
undefined |
Accessible name for the scheduler region. |
Events
| Event |
Payload |
Description |
update:view |
[view: SchedulerView] |
Fired when the scheduler requests a new controlled view. |
update:date |
[date: PlainDate] |
Fired when navigation requests a new anchor date. |
event-activate |
[event: SchedulerEvent<Data>, nativeEvent: Event] |
Fired when an event is clicked or activated with Enter/Space. |
slot-activate |
[range: SchedulerSlotRange, nativeEvent: Event] |
Fired when a time-grid slot is clicked or activated. |
day-activate |
[date: PlainDate, nativeEvent: Event] |
Fired when a month-view day is clicked or activated. |
event-move |
[change: SchedulerEventChange<Data>, nativeEvent: Event | null] |
Fired when a drag or Alt+Arrow requests a new start (duration preserved). |
event-resize |
[change: SchedulerEventChange<Data>, nativeEvent: Event | null] |
Fired when a resize drag or Alt+Shift+Arrow requests a new end. |
Slots
| Slot |
Slot props |
Description |
default |
SchedulerSlotState<Data> |
Scheduler composition. Receives typed layout state; defaults to a header plus the view's grid. |
SchedulerRoot<Data>
Source: src/families/date-time/scheduler/scheduler-root.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null | undefined |
undefined |
Consumer-owned base id; nullish values use a deterministic fallback. |
events |
readonly SchedulerEvent<Data>[] | undefined |
[] |
Events to lay out; data keeps the consumer payload type. |
view |
SchedulerView | undefined |
undefined |
Controlled view; undefined selects uncontrolled mode. |
defaultView |
SchedulerView | undefined |
"week" |
Initial uncontrolled view. |
date |
PlainDate | null | undefined |
undefined |
Controlled anchor date deciding the visible period. |
defaultDate |
PlainDate | null | undefined |
undefined |
Initial uncontrolled anchor date. |
today |
PlainDate | null | undefined |
undefined |
Explicit current date; the SSR-safe way to anchor and 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. |
locale |
string | undefined |
undefined |
BCP 47 locale; defaults to the nearest LocaleProvider. |
dir |
"ltr" | "rtl" | undefined |
undefined |
Text direction; defaults to the nearest LocaleProvider. |
weekStartsOn |
Weekday | undefined |
undefined |
First day of week; defaults to the locale preference. |
dayStartHour |
number | undefined |
0 |
First visible hour of the time grid. |
dayEndHour |
number | undefined |
24 |
Hour where the time grid ends, exclusive. |
slotMinutes |
number | undefined |
30 |
Minutes per time-grid slot row. |
snapMinutes |
number | undefined |
slotMinutes |
Minutes that drag and keyboard moves snap to. |
minimumEventMinutes |
number | undefined |
15 |
Minimum rendered event length in minutes. |
maxLanes |
number | undefined |
3 |
Month-view lanes rendered per week before counting overflow. |
disabled |
boolean | undefined |
false |
Disable navigation, activation, and editing. |
readOnly |
boolean | undefined |
false |
Keep navigation and activation but block move and resize requests. |
ariaLabel |
string | undefined |
undefined |
Accessible name for the scheduler region. |
Events
| Event |
Payload |
Description |
update:view |
[view: SchedulerView] |
Fired when the scheduler requests a new controlled view. |
update:date |
[date: PlainDate] |
Fired when navigation requests a new anchor date. |
event-activate |
[event: SchedulerEvent<Data>, nativeEvent: Event] |
Fired when an event is clicked or activated with Enter/Space. |
slot-activate |
[range: SchedulerSlotRange, nativeEvent: Event] |
Fired when a time-grid slot is clicked or activated. |
day-activate |
[date: PlainDate, nativeEvent: Event] |
Fired when a month-view day is clicked or activated. |
event-move |
[change: SchedulerEventChange<Data>, nativeEvent: Event | null] |
Fired when a drag or Alt+Arrow requests a new start (duration preserved). |
event-resize |
[change: SchedulerEventChange<Data>, nativeEvent: Event | null] |
Fired when a resize drag or Alt+Shift+Arrow requests a new end. |
Slots
| Slot |
Slot props |
Description |
default |
SchedulerSlotState<Data> |
Scheduler composition. Receives typed layout state; defaults to a header plus the view's grid. |
SchedulerDayColumn
Source: src/families/date-time/scheduler/scheduler-day-column.vue
Props
| Prop |
Type |
Default |
Description |
column (required) |
SchedulerDayColumnState<unknown> |
undefined (required) |
Day and timed placements rendered by this column. |
Slots
| Slot |
Slot props |
Description |
event |
TimeGridPlacement<unknown> |
Event content for each placement; defaults to the event title. |
SchedulerEvent
Source: src/families/date-time/scheduler/scheduler-event.vue
Props
| Prop |
Type |
Default |
Description |
event (required) |
SchedulerEvent<unknown> |
undefined (required) |
Event rendered by this item. |
placement |
TimeGridPlacement<unknown> | undefined |
undefined |
Time-grid placement; drives --vize-scheduler-* position variables. |
row |
RowPlacement<unknown> | undefined |
undefined |
Row placement for all-day lanes and month weeks. |
resizable |
boolean |
true |
Render a resize handle at the end of time-grid events. |
Slots
| Slot |
Slot props |
Description |
default |
{ readonly event: SchedulerEvent<unknown>; readonly placement: TimeGridPlacement<unknown> | undefined; readonly row: RowPlacement<unknown> | undefined; readonly label: string; } |
Event content. Receives the event and its placement; defaults to the title. |
SchedulerHeading
Source: src/families/date-time/scheduler/scheduler-heading.vue
Slots
| Slot |
Slot props |
Description |
default |
{ readonly heading: string } |
Heading content. Receives the localized period label. |
SchedulerMonthDay
Source: src/families/date-time/scheduler/scheduler-month-day.vue
Props
| Prop |
Type |
Default |
Description |
week (required) |
SchedulerWeekState<unknown> |
undefined (required) |
Week row that owns this day. |
index (required) |
number |
undefined (required) |
Day index inside the week. |
Slots
| Slot |
Slot props |
Description |
day |
SchedulerDay |
Day number content. |
event |
RowPlacement<unknown> |
Event bar content for events starting in this cell. |
more |
{ readonly count: number; readonly day: SchedulerDay } |
Overflow indicator content when more events exist than maxLanes. |
SchedulerMonthGrid
Source: src/families/date-time/scheduler/scheduler-month-grid.vue
Slots
| Slot |
Slot props |
Description |
weekday |
SchedulerDay |
Weekday column header content. |
day |
SchedulerDay |
Day number content. |
event |
RowPlacement<unknown> |
Event bar content. |
more |
{ readonly count: number; readonly day: SchedulerDay } |
Overflow indicator content. |
SchedulerNav
Source: src/families/date-time/scheduler/scheduler-nav.vue
Props
| Prop |
Type |
Default |
Description |
action (required) |
"previous" | "next" | "today" |
undefined (required) |
Navigation performed by the button. |
ariaLabel |
string |
undefined |
Accessible name; defaults to "Previous period", "Next period", or "Today". |
Slots
| Slot |
Slot props |
Description |
default |
{ readonly action: "previous" | "next" | "today" } |
Button content. Receives the action. |
SchedulerTimeGrid
Source: src/families/date-time/scheduler/scheduler-time-grid.vue
Props
| Prop |
Type |
Default |
Description |
timeHeader |
string |
"Time" |
Corner column header announced for the time labels. |
allDayLabel |
string |
"All day" |
Accessible name of the all-day lane group. |
Slots
| Slot |
Slot props |
Description |
dayHeader |
SchedulerDay |
Day column header content. |
time |
SchedulerSlotRow |
Time label content for each slot row. |
event |
TimeGridPlacement<unknown> |
Timed event content. |
allDayEvent |
RowPlacement<unknown> |
All-day event content. |
Behavior
Normative state x input -> outcome table for the headless Scheduler compound
(@vizejs/ui/scheduler): scheduler-root.vue (generic over the event payload
Data), scheduler-heading.vue, scheduler-nav.vue,
scheduler-time-grid.vue, scheduler-day-column.vue,
scheduler-month-grid.vue, scheduler-month-day.vue, and
scheduler-event.vue, plus the pure layout engine scheduler-layout.ts.
Every row is proven by the named test.
| # |
State |
Input |
Outcome |
Proven by |
| S1 |
layout |
overlapping timed events |
events sort by start (longer first), group into overlap clusters, take the first free column, and widen across free columns; top/height are fractions of the visible window |
overlapping events split into columns and widen into free space, three-way overlaps widen short events into free columns |
| S2 |
layout |
window / short / overnight / all-day |
placements clip to dayStartHour–dayEndHour with continuation flags, get a minimum rendered length, and skip all-day events |
the day window clips events, enforces a minimum length, and skips all-day events |
| S3 |
layout |
multi-day and all-day events |
row layout stacks events into lanes (longer first) with continuation flags across the row |
row layout stacks multi-day events into lanes with continuation flags |
| S4 |
layout |
views / anchors / pointer |
visibleDays, monthWeeks, shiftAnchor (clamping month days), snapMinute, and minuteAtPoint are pure and deterministic |
views, month rows, anchors, snapping, and pointer mapping are deterministic |
| S5 |
week view |
render |
a role="region" holds a <table role="grid"> labelled by the live heading: day column headers (aria-current="date" on today), time row headers, and one roving tabindex=0 slot button; events render in day columns with --vize-scheduler-top/height/column/columns/span, and all-day events in a labelled lane group with --vize-scheduler-start/end/lane |
week view renders an APG slot grid, day columns, laid-out events, and all-day lanes |
| S6 |
time grid |
Arrow / Home / End / PageUp / PageDown / Enter |
±slot, ±day (RTL flips), first/last slot, ±period; leaving the visible days moves the anchor (update:date); Enter/Space/click emits slot-activate with a { start, end } range |
slot keyboard navigation moves by slot, day, edges, and period, and activation emits a range |
| S7 |
events |
click / Enter / Alt+Arrow / Alt+Shift+Arrow |
activation emits the typed event; Alt+Up/Down moves by snapMinutes, Alt+Left/Right by a day, Alt+Shift+Up/Down resizes the end (never below start + snapMinutes); read-only blocks requests and hides resize handles |
events activate and Alt+Arrow keys request typed moves and resizes |
| S8 |
events |
pointer drag (drag-and-drop) |
dragging an event to a day column emits event-move snapped to the pointer (grab offset kept, duration preserved); dragging the resize handle emits event-resize ending at the pointer's slot |
pointer drags move and resize events through drop targets |
| S9 |
month view |
render / keyboard / Alt+Arrow |
week rows with weekday headers and adjacent-month flags; events beyond maxLanes count into +N; arrows/Home/End/PageUp/PageDown move day focus and cross months; Enter emits day-activate; Alt+Down moves an event a week |
month view lays out week rows, overflow, keyboard day navigation, and day drops |
| S10 |
navigation / API |
nav buttons / controlled view / expose |
previous, next, and today buttons page the anchor; controlled view emits update:view only; navigate, goToToday, setDate, setView, and focus are exposed; disabled schedulers disable slots |
navigation controls, controlled view, pending without a clock, and the exposed API |
| S11 |
composition |
default and part slots |
the root slot receives SchedulerSlotState<Data> with typed payloads; grid parts accept slot content |
custom composition exposes typed slot state and individual parts |
| S12 |
SSR |
isolated requests / hydration |
week, day, and month render byte-identically and hydrate silently; without today/now the server renders a pending shell |
renders byte-identical week, day, and month markup with deterministic layout, hydrates every view without mismatches, without today or now the server renders a pending shell and hydrates silently |
SSR determinism
Layout is a pure function of the events, the anchor date, and props.
The anchor comes from date, then defaultDate, then the calendar "today" rules
(today, then an injected now + timeZone, then the client clock after
mount). Drag-and-drop listeners attach only on the client.
Headless positioning
Consumers position events with CSS custom properties. Time-grid events expose
--vize-scheduler-top and --vize-scheduler-height (percent of the day
column), plus --vize-scheduler-column, --vize-scheduler-columns, and
--vize-scheduler-span. Row events expose --vize-scheduler-start and
--vize-scheduler-end (CSS grid lines) and --vize-scheduler-lane. The time
grid root also exposes --vize-scheduler-days and --vize-scheduler-slots.