Scheduler
import { Scheduler } from '@absoluteui/components/scheduler';import '@absoluteui/components/scheduler/style.css';Using Angular? See Scheduler for Angular.
What it does
Section titled “What it does”Shows and edits events over time. One component covers the AbsoluteUI specification Scheduler, Calendar Scheduler (day, week, work week, month, agenda), and Timeline Scheduler (resource rows over the slots of a day, or the days of a week or month).
Events are timed (Instant start and exclusive end) or all-day (PlainDate start and inclusive last day), laid out in the scheduler timezone by their wall-clock times. Recurring series use RecurrenceRule (RRULE subset from @absoluteui/core) with EXDATE cancellations and exception events (seriesId + originalStart). A Business Calendar definition supplies the work week, business-hours shading, and holidays.
In edit mode events move and resize by pointer drag, by Ctrl/Cmd+X then Ctrl/Cmd+V, by Shift+Arrow keys, and by toolbar buttons (single pointer). Changes to a recurring occurrence apply to that occurrence, this and following, or the whole series (asked in a dialog, or configured). Every change is dispatched first as the cancelable aui-event-change with the changes and every event, and is undoable. Events come from events or from loadEvents per visible range (cancellable, timed out, validated, cached). Enter on an empty cell, selections, pointer ranges, and double-clicks emit aui-slot-activate so the application can create events.
When to use
Section titled “When to use”- Team calendars, room or equipment booking, shift or appointment planning.
- Resource planning where rows are people, rooms, or machines (timeline view).
- Any calendar where events repeat and timezones and DST must be exact.
When not to use
Section titled “When not to use”- Project plans with dependencies: use Gantt.
- Picking a date: use Date Picker; picking a time range: Schedule Picker.
- Tens of thousands of events in one visible range: aggregate on the server first.
Examples
Section titled “Examples”A team week with a daily standup
Section titled “A team week with a daily standup”import { parseInstant, parseRecurrenceRule, plainDate } from '@absoluteui/core';import { Scheduler, type SchedulerEvent, type SchedulerEventActivateDetail } from '@absoluteui/components/scheduler';
const at = (text: string) => parseInstant(text) ?? parseInstant('1970-01-01T00:00:00Z')!;const events: SchedulerEvent[] = [ { id: 'standup', title: 'Standup', start: at('2026-09-28T08:00:00Z'), end: at('2026-09-28T08:15:00Z'), timeZone: 'Europe/London', recurrence: parseRecurrenceRule('FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR') }, { id: 'review', title: 'Design review', start: at('2026-09-29T13:00:00Z'), end: at('2026-09-29T14:30:00Z'), location: 'Room 4', color: 3 }, { id: 'offsite', title: 'Offsite', start: plainDate(2026, 10, 1), end: plainDate(2026, 10, 2) },];const host = document.createElement('div');document.body.append(host);Scheduler.create(host, { label: 'Team calendar', events, timezone: 'Europe/London', date: plainDate(2026, 9, 29) });host.addEventListener('aui-event-activate', (event) => { host.dataset.open = (event as CustomEvent<SchedulerEventActivateDetail>).detail.event.id;});Editing with save and rollback
Section titled “Editing with save and rollback”import { Scheduler, type SchedulerEvent, type SchedulerEventChangeDetail, type SchedulerSlotActivateDetail } from '@absoluteui/components/scheduler';
const host = document.createElement('div');document.body.append(host);const scheduler = Scheduler.create(host, { label: 'Bookings', events: [] as SchedulerEvent[], editable: true, recurringEditScope: 'ask', timezone: 'America/New_York' });host.addEventListener('aui-event-change', (event) => { const { changes } = (event as CustomEvent<SchedulerEventChangeDetail>).detail; void fetch('/api/events', { method: 'PATCH', body: JSON.stringify(changes) }).then((r) => { if (!r.ok) scheduler.undo(); }, () => scheduler.undo());});host.addEventListener('aui-slot-activate', (event) => { const { start, end, resourceId } = (event as CustomEvent<SchedulerSlotActivateDetail>).detail; host.dataset.create = JSON.stringify({ start, end, resourceId });});Rooms timeline from a range data source
Section titled “Rooms timeline from a range data source”import { Scheduler, type SchedulerEvent, type SchedulerLoadErrorDetail } from '@absoluteui/components/scheduler';
const host = document.querySelector<HTMLElement>('#rooms');if (host) { Scheduler.create(host, { label: 'Rooms', view: 'timeline', resources: [{ id: 'r1', label: 'Room 1' }, { id: 'r2', label: 'Room 2', color: 2 }], businessCalendar: { workingDays: [1, 2, 3, 4, 5] }, dayStartHour: 7, dayEndHour: 20, loadEvents: async (range, signal) => { const url = `/api/bookings?from=${encodeURIComponent(new Date(range.start.epochMilliseconds).toISOString())}&to=${encodeURIComponent(new Date(range.end.epochMilliseconds).toISOString())}`; const response = await fetch(url, { signal }); if (!response.ok) throw new Error(`Bookings request failed: ${String(response.status)}`); return (await response.json()) as SchedulerEvent[]; }, }); host.addEventListener('aui-load-error', (event) => { host.dataset.failed = String((event as CustomEvent<SchedulerLoadErrorDetail>).detail.error); });}Global slot size and working hours
Section titled “Global slot size and working hours”import { configureAbsoluteUI } from '@absoluteui/core';
configureAbsoluteUI({ components: { scheduler: { slotDuration: 15, dayStartHour: 7, dayEndHour: 21, scrollToHour: 9 } } });import { parseInstant, parseRecurrenceRule, plainDate } from '@absoluteui/core';import { Scheduler, type SchedulerEvent, type SchedulerEventChangeDetail } from '@absoluteui/components/scheduler';import '@absoluteui/components/button/style.css';import '@absoluteui/components/dialog/style.css';import '@absoluteui/components/scheduler/style.css';
const at = (text: string) => parseInstant(text) ?? parseInstant('1970-01-01T00:00:00Z')!;let saved: SchedulerEvent[] = [ { id: 'standup', title: 'Standup', start: at('2026-09-28T08:00:00Z'), end: at('2026-09-28T08:15:00Z'), timeZone: 'Europe/London', recurrence: parseRecurrenceRule('FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR') }, { id: 'offsite', title: 'Offsite', start: plainDate(2026, 10, 1), end: plainDate(2026, 10, 2) },];const host = document.querySelector<HTMLElement>('#calendar');if (host) { Scheduler.create(host, { label: 'Team calendar', events: saved, editable: true, timezone: 'Europe/London', businessCalendar: { workingDays: [1, 2, 3, 4, 5] } }); host.addEventListener('aui-event-change', (event) => { saved = (event as CustomEvent<SchedulerEventChangeDetail>).detail.events; });}More usage
Section titled “More usage”See section 33 and metadata/scheduler/examples.json: team week, editing with save and rollback, rooms timeline from a data source, global configuration.
Anatomy
Section titled “Anatomy”<div class="aui-scheduler aui-scheduler--week [--editable]" dir="ltr"> (consumer host) <div class="aui-scheduler__toolbar" role="toolbar" aria-label="{label} controls"> <div class="aui-scheduler__nav"> Today | Previous week | Next week <h2 class="aui-scheduler__title" aria-live="polite">28 Sept - 4 Oct 2026</h2> <div class="aui-scheduler__views" role="group" aria-label="View"> Day | Week (aria-pressed) | Work week | Month | Agenda | Timeline <div class="aui-scheduler__edit" role="group" aria-label="Editing"> Move | Move here | Shorten | Lengthen | Undo | Redo <div class="aui-scheduler__status" role="status" hidden> (loading, error + Retry) <div class="aui-scheduler__body"> (scrolls both ways) <table class="aui-scheduler__grid aui-scheduler__grid--time" role="grid" aria-label="{label}, {period}"> <thead><tr><th class="__corner">Time</th><th class="__day-header" abbr="Tuesday, 29 September 2026">Tue 29 [holiday]</th>... <tbody> <tr class="__all-day-row"><th>All day</th><td class="__cell --all-day" aria-label="..."> <button class="aui-scheduler__event --c1 --all-day" data-aui-key="offsite" aria-label="Offsite, 30 Sept 2026 to 1 Oct 2026">... <tr class="__slot-row --hour"><th class="__time">09:00</th> <td class="aui-scheduler__cell [--non-working] [--selected]" aria-label="Tuesday 29 September, 09:00 to 09:30, 1 event" tabindex="0|-1"> <button class="aui-scheduler__event --c3" data-aui-key="review" aria-label="Design review, 10:00 to 11:30, …, Room 4" style="--aui-top:0; --aui-span:3; --aui-column:0; --aui-columns:2; --aui-extent:1"> <span class="__event-content"><span class="__event-time">10:00</span> <span class="__event-title">Design review</span> ...</span> <span class="aui-scheduler__resize" aria-hidden="true"> (edit mode) <span class="aui-scheduler__now" aria-hidden="true"> (current time)Month: one row per week; day cells hold the date, bars (__event--bar, spanning --aui-span cells in lanes --aui-lane), chips (__event--chip), and “+N more” (__more). Timeline: row headers are resources (plus “Unassigned”), bars are placed by --aui-offset/--aui-span in column units. Agenda: section.__agenda-day > h3 + ul.__agenda-list > li > time + event button.
API reference
Section titled “API reference”Factory
Section titled “Factory”Scheduler.create(host: HTMLElement, options: SchedulerOptions): SchedulerGlobal configuration key: scheduler (see Configuration).
Options
Section titled “Options”| Option | Type | Default | Description |
|---|---|---|---|
label |
string |
Accessible name; grids are named “{label}, {period}”. Required. | |
events |
readonly SchedulerEvent[] |
[] |
Events: { id, title, start: Instant | PlainDate, end (exclusive instant or inclusive last day), timeZone?, recurrence?, seriesId?, originalStart?, resourceId?, color?, location?, description?, readOnly? }. |
loadEvents |
(range: SchedulerRange, signal: AbortSignal) => Promise<readonly SchedulerEvent[]> |
Range data source instead of events (single events overlapping the range, series masters that may occur in it, and their exceptions). | |
resources |
readonly SchedulerResource[] |
Timeline rows: { id, label, description?, color? }. | |
view |
'day' | 'week' | 'workWeek' | 'month' | 'agenda' | 'timeline' |
"week" |
Current view. |
views |
readonly SchedulerView[] |
Views in the switcher. Default: day, week, work week, month, agenda (plus timeline with resources). | |
date |
PlainDate |
A day in the visible period. Default: today in the timezone. | |
editable |
boolean |
false |
Move and resize events (pointer, keyboard, toolbar). |
recurringEditScope |
'ask' | 'occurrence' | 'following' | 'series' |
"ask" |
How changes to recurring occurrences apply; ask shows a dialog. |
businessCalendar |
BusinessCalendarDefinition |
Working days (work week), working hours (shading), holidays (headers). | |
firstDayOfWeek |
IsoWeekday |
First day of weeks. Default: the locale’s first day of week. | |
createId |
(base: Readonly<SchedulerEvent>, hint: string) => string |
Id of events created from base (exceptions, split series). Default “{id}~{hint}”. | |
renderEvent |
(occurrence: SchedulerOccurrence, context: SchedulerRenderContext) => Node |
Event content (non-interactive). | |
now |
Instant |
Advanced: fixed current time (tests, demos); disables the per-minute refresh. | |
icons |
Partial<{ previous: IconRef; next: IconRef }> |
Icon slot overrides. | |
slotDuration |
15 | 30 | 60 |
30 |
Minutes per slot (time view rows, day timeline columns). Globally configurable. |
snapDuration |
5 | 10 | 15 | 30 | 60 |
15 |
Minutes moves and resizes snap to. Globally configurable. |
dayStartHour |
number |
0 |
First hour shown in time views (0-23). Globally configurable. |
dayEndHour |
number |
24 |
Last hour shown, exclusive (1-24, after dayStartHour). Globally configurable. |
scrollToHour |
number |
8 |
Hour scrolled into view (and focused) when a time view opens. Globally configurable. |
monthEventLimit |
number |
3 |
Rows of events per month cell before “+N more”. Globally configurable. |
agendaDays |
number |
7 |
Days listed by the agenda (1-62). Globally configurable. |
timelineScale |
'day' | 'week' | 'month' |
"day" |
Timeline columns: slots of a day, days of a week, or days of a month. Globally configurable. |
requestTimeoutMs |
number |
30000 |
loadEvents timeout. Globally configurable. |
historyLimit |
number |
50 |
Undo depth (0 disables undo). Globally configurable. |
locale |
string |
"en-US" |
Month and weekday names, dates, numbers, the first day of the week, and messages. Globally configurable. Inherited from common configuration. |
timezone |
string |
"UTC" |
Timezone of the layout, today, slots, and new times. Globally configurable. Inherited from common configuration. |
disambiguation |
'compatible' | 'earlier' | 'later' | 'reject' |
"compatible" |
DST policy for wall-clock times in a gap or overlap. Globally configurable. Inherited from common configuration. |
direction |
'ltr' | 'rtl' |
"ltr" |
Reading direction; Left/Right arrow keys and chevrons follow it. Globally configurable. Inherited from common configuration. |
Properties
Section titled “Properties”| Property | Type | Description |
|---|---|---|
host |
HTMLElement |
Consumer host. |
view |
SchedulerView |
Current view. |
date |
PlainDate |
Anchor date of the visible period. |
range |
SchedulerRange |
{ start, end (instants), startDate, endDate (inclusive) } of the visible days. |
events |
SchedulerEvent[] |
Every event (copies), for saving. |
occurrences |
readonly SchedulerOccurrence[] |
Occurrences in the visible range, sorted (key, event, start, end, allDay, recurring, seriesId, originalStart). |
editable |
boolean |
Edit mode. |
loading |
boolean |
A loadEvents request is pending. |
canUndo / canRedo |
boolean |
History state. |
businessCalendar |
BusinessCalendarEngine |
Engine of the business calendar definition. |
element |
HTMLElement | null |
The current grid (or the agenda region). |
Methods
Section titled “Methods”| Method | Description |
|---|---|
update(options: Partial<SchedulerOptions>): void |
Merge and apply options; events replace the list and clear history. |
setView(view: SchedulerView): void |
Change the view (no aui-navigate). |
goTo(date: PlainDate): void; next(): void; previous(): void; today(): void |
Change the period (next and previous emit aui-navigate). |
refresh(): void |
Discard cached ranges and reload the visible range. |
moveEvent(key: string, start: Instant | PlainDate, options?: { resourceId?: string; scope?: SchedulerEditScope }): boolean |
Programmatic move of a visible occurrence (emits the cancelable aui-event-change; false when refused). |
resizeEvent(key: string, end: Instant | PlainDate, options?: { scope?: SchedulerEditScope }): boolean |
Programmatic end change. |
undo(): boolean; redo(): boolean |
Step through edits (emit aui-event-change with reason undo/redo). |
focus(): void |
Focus the current cell or event. |
destroy(): void |
Abort requests and remove rendering, listeners, timers, and subscriptions. |
expandEvents(events, range): SchedulerOccurrence[]; applyEdit(events, request): { events; changes } | null; packColumns(spans); packLanes(spans) |
Module functions: the pure model and layout (server-side expansion, tests). |
Events
Section titled “Events”| Event | Detail | Bubbles | Cancelable | Description |
|---|---|---|---|---|
aui-navigate |
{ view: SchedulerView; date: PlainDate; range: SchedulerRange } |
Yes | No | The user changed the view or period. |
aui-slot-activate |
{ start: Instant | PlainDate; end: Instant | PlainDate; allDay: boolean; resourceId: string | null } |
Yes | No | The user asked to create an event (Enter on an empty cell, Shift+Enter, a selection, a pointer range, or a double-click). |
aui-event-activate |
{ occurrence: SchedulerOccurrence; event: SchedulerEvent } |
Yes | No | The user activated an event (click, Enter, Space). |
aui-event-change |
{ reason: "move" | "resize" | "undo" | "redo"; occurrence: SchedulerOccurrence | null; scope: SchedulerEditScope | null; changes: { added; updated; removed }; events: SchedulerEvent[] } |
Yes | Yes | Before a change is applied; preventDefault() refuses it. |
aui-event-delete |
{ occurrence: SchedulerOccurrence; event: SchedulerEvent } |
Yes | No | Delete or Backspace on an editable event (the application removes it). |
aui-load-error |
{ range: SchedulerRange; error: unknown } |
Yes | No | loadEvents failed, timed out, or returned invalid events. |
Content areas
Section titled “Content areas”| Area | Description |
|---|---|
renderEvent |
Custom, non-interactive event content. |
CSS classes
Section titled “CSS classes”All rules live in the aui.components cascade layer, so application styles override them without specificity battles.
| Class | Purpose |
|---|---|
aui-scheduler | --day | --week | --workWeek | --month | --agenda | --timeline | --editable |
Host and view modifiers; custom properties –aui-scheduler-slot, –aui-scheduler-lane, –aui-scheduler-date, –aui-scheduler-height size the grid. |
aui-scheduler__toolbar | __nav | __title | __views | __view | __edit | __button | __button--{action} |
Toolbar. |
aui-scheduler__status | __retry |
Loading and error status. |
aui-scheduler__body | __grid | __grid--time | --month | --timeline |
Scroll container and grids. |
aui-scheduler__corner | __day-header | __day-header--today | --non-working | __weekday | __day-number | __holiday | __row-header | __time | __time-header | __resource |
Headers. |
aui-scheduler__cell | --all-day | --day | --non-working | --selected | --today | --outside | __date | __chips | __more |
Cells. |
aui-scheduler__all-day-row | __slot-row | __slot-row--hour | __resource-row | __spacer |
Rows. |
aui-scheduler__event | --c1..--c8 | --all-day | --bar | --chip | --recurring | --read-only | --cut | --dragging | --short | __event-content | __event-time | __event-title | __event-location | __resize |
Events. |
aui-scheduler__now |
Current-time line (aria-hidden). |
aui-scheduler__agenda | __agenda-day | __agenda-date | __agenda-list | __agenda-time | __empty |
Agenda. |
Icon slots
Section titled “Icon slots”| Slot | Default icon |
|---|---|
previous |
chevron-left |
next |
chevron-right |
Design tokens
Section titled “Design tokens”Tokens this stylesheet reads (verified in CI). Override them in a theme or with the Theme Designer.
--aui-border-width | --aui-border-width-strong | --aui-color-border | --aui-color-border-strong | --aui-color-chart-1 | --aui-color-chart-2 | --aui-color-chart-3 | --aui-color-chart-4 | --aui-color-chart-5 | --aui-color-chart-6 | --aui-color-chart-7 | --aui-color-chart-8 | --aui-color-error-fg | --aui-color-focus-ring | --aui-color-hover | --aui-color-link | --aui-color-selected | --aui-color-selected-text | --aui-color-surface | --aui-color-surface-sunken | --aui-color-text | --aui-color-text-disabled | --aui-color-text-muted | --aui-color-warning-fg | --aui-duration-fast | --aui-easing-standard | --aui-focus-ring-offset | --aui-focus-ring-width | --aui-font-family-sans | --aui-font-size-lg | --aui-font-size-sm | --aui-font-size-xs | --aui-font-weight-bold | --aui-font-weight-medium | --aui-font-weight-regular | --aui-font-weight-semibold | --aui-icon-size-sm | --aui-line-height-tight | --aui-radius-md | --aui-radius-sm | --aui-shadow-md | --aui-space-1 | --aui-space-2 | --aui-space-3 | --aui-touch-target-min
TypeScript definitions
Section titled “TypeScript definitions”interface SchedulerEvent { id: string; title: string; start: Instant | PlainDate; end: Instant | PlainDate; // timed: exclusive end; all-day: inclusive last day timeZone?: string; recurrence?: RecurrenceRule | null; seriesId?: string; originalStart?: Instant | PlainDate; // exception of a series occurrence resourceId?: string; color?: number; location?: string; description?: string; readOnly?: boolean;}interface SchedulerResource { id: string; label: string; description?: string; color?: number }interface SchedulerOccurrence { key: string; event: SchedulerEvent; start: Instant | PlainDate; end: Instant | PlainDate; allDay: boolean; recurring: boolean; seriesId: string | null; originalStart: Instant | PlainDate | null }type SchedulerView = 'day' | 'week' | 'workWeek' | 'month' | 'agenda' | 'timeline';type SchedulerEditScope = 'occurrence' | 'following' | 'series';interface SchedulerRange { start: Instant; end: Instant; startDate: PlainDate; endDate: PlainDate }interface SchedulerEventChanges { added: SchedulerEvent[]; updated: SchedulerEvent[]; removed: string[] }interface SchedulerEventChangeDetail { reason: 'move' | 'resize' | 'undo' | 'redo'; occurrence: SchedulerOccurrence | null; scope: SchedulerEditScope | null; changes: SchedulerEventChanges; events: SchedulerEvent[] }interface SchedulerSlotActivateDetail { start: Instant | PlainDate; end: Instant | PlainDate; allDay: boolean; resourceId: string | null }Accessibility
Section titled “Accessibility”Pattern: WAI-ARIA APG grid for every view except the agenda: table role=grid with day or slot gridcells under one roving tab stop; events are buttons inside the cell where they start (APG “cells containing widgets”: Enter/F2 in, Escape/F2 out). The agenda is a region with day headings and lists under one roving tab stop. Moves have three equivalent paths (drag, Ctrl/Cmd+X then Ctrl/Cmd+V, toolbar Move / Move here).
Keyboard
Section titled “Keyboard”| Key | Context | Action |
|---|---|---|
| Arrow keys | Cell | Move between days or slots; past the edge of a day or week, the neighbouring period (month: the previous or next week row). |
| Home / End | Cell | First / last cell of the row; with Ctrl/Cmd, the first / last cell of the grid. |
| PageUp / PageDown | Cell or agenda | Previous / next period. |
| Shift+Arrow | Cell | Extend a selection (within one day in time views, one resource in the timeline). |
| Enter / F2 | Cell | Move to the first event of the cell (or “+N more”); on an empty cell or with a selection, emit aui-slot-activate. |
| Shift+Enter | Cell | Emit aui-slot-activate for the cell or selection. |
| ArrowUp / ArrowDown | Event | Previous / next event of the same cell. |
| Enter / Space | Event | Emit aui-event-activate. |
| Escape / F2 | Event | Back to the cell. |
| Ctrl/Cmd+X, then Ctrl/Cmd+V | Event, then a cell (edit mode) | Move the event to that cell (time kept on day cells); Escape on a cell cancels. |
| Shift+ArrowDown / Shift+ArrowUp | Timed event in day or week views (edit mode) | Lengthen / shorten by snapDuration. |
| Shift+ArrowRight / Shift+ArrowLeft | Bars in month, all-day row, timeline (edit mode) | Lengthen / shorten by a day or a snap (mirrored in RTL). |
| Delete / Backspace | Event (edit mode) | Emit aui-event-delete. |
| Ctrl/Cmd+Z, Ctrl/Cmd+Shift+Z or Ctrl/Cmd+Y | Grid (edit mode) | Undo / redo. |
| Tab | Scheduler | Toolbar buttons, then the grid (one tab stop). |
| Element | Role |
|---|---|
| toolbar | toolbar |
| view switcher, edit buttons | group of buttons (aria-pressed on views) |
| period title | heading (aria-live=polite) |
| status | status |
| grid | grid |
| day/time headers, row headers | columnheader / rowheader |
| cells | gridcell |
| events | button |
| continuation segments, current-time line | none (aria-hidden) |
ARIA attributes
Section titled “ARIA attributes”| Attribute | Element | Value |
|---|---|---|
aria-label |
grid | “{label}, {period}” |
aria-label |
cell | Date (and slot time, and resource), then “non-working time”, holiday, and the number of events starting there |
aria-label |
event | Title, time or dates, location, “repeats …” for series, “read-only” in edit mode |
aria-selected |
cell | “true” on cells of a keyboard or pointer selection |
aria-current |
today header / month cell | “date” |
aria-busy |
grid | “true” while loadEvents is pending |
aria-rowcount / aria-rowindex |
virtual timeline | Total rows and the index of each rendered row |
aria-disabled |
edit buttons | “true” when not applicable (buttons stay focusable) |
One roving tab stop per view; changing the period moves it to the anchor date (at scrollToHour in time views). After an edit, focus follows the event (its key changes when an exception is created); after toolbar edits focus stays on the toolbar. The recurring-scope dialog restores focus on close.
WCAG 2.2 success criteria addressed
Section titled “WCAG 2.2 success criteria addressed”Automated test results: Accessibility and compliance.
Notes and known caveats
Section titled “Notes and known caveats”- Event colors are never the only cue: titles, times, and names carry the meaning; non-working cells say so in their names.
- Moves and resizes are announced (“Design review moved to 30 Sept 2026, 11:00 to 12:30.”).
- Pointer dragging has single-pointer alternatives (toolbar Move / Move here / Shorten / Lengthen), WCAG 2.5.7.
- Screen-reader behavior has not yet been manually verified with NVDA, JAWS, and VoiceOver.
- Very long cell names in dense weeks are read in full; the count of events keeps them short.
Behavior
Section titled “Behavior”States
Section titled “States”Views; loading (aria-busy, status); load error (status with Retry); edit mode; cut (move pending); dragging; resizing; selection; recurring-scope dialog.
State transitions
Section titled “State transitions”| From | Trigger | To |
|---|---|---|
| cell | Enter / F2 | first event of the cell (or “+N more”) |
| empty cell or selection | Enter, Shift+Enter, pointer range, double-click | aui-slot-activate |
| event | Ctrl/Cmd+X or Move | cut (dashed); Escape on a cell cancels |
| cut | Ctrl/Cmd+V or Move here on a cell | moved (dialog first for recurring when ask) |
| event | drag / resize handle / Shift+Arrow / Shorten / Lengthen | moved or resized |
| change requested | preventDefault() on aui-event-change |
unchanged |
| changed | Ctrl/Cmd+Z / Undo | previous events (reason: 'undo') |
| range | navigation | loadEvents (older request aborted) -> loaded, or error with Retry |
Accessibility notes
Section titled “Accessibility notes”- Moving and resizing never require dragging (WCAG 2.5.7): cut and paste, Shift+Arrow keys, and toolbar Move / Move here / Shorten / Lengthen.
- Colors are never the only cue; non-working time is named and hatched in forced colors.
- Continuation segments of multi-day events are hidden from assistive technology; the first segment carries the event.
- Manual screen-reader verification (NVDA, JAWS, VoiceOver) is pending.
Responsive behavior
Section titled “Responsive behavior”Toolbar wraps. The body scrolls in two dimensions with sticky headers (WCAG 1.4.10 exempts data grids); minimum column widths: 6rem per day, 3rem per timeline column, 42rem for months. Use views (for example agenda and day) on phones.
Right-to-left
Section titled “Right-to-left”direction: 'rtl' mirrors column order, timeline flow, chevrons, Left/Right keys, and Shift+Left/Right resizing.
Theming
Section titled “Theming”Events: chart palette bar and 18 % tint over the surface, body text. Non-working time: sunken surface. Current time: error-colored line. Forced colors: CanvasText outlines, hatched non-working time, Highlight selection; reduced motion removes drag shadows.
Global configuration
Section titled “Global configuration”configureAbsoluteUI({ components: { scheduler: { slotDuration: 15, dayStartHour: 7, dayEndHour: 21, scrollToHour: 9, timelineScale: 'week' } } });Global options: slotDuration, snapDuration, dayStartHour, dayEndHour, scrollToHour, monthEventLimit, agendaDays, timelineScale, requestTimeoutMs, historyLimit, plus inherited locale, timezone, disambiguation, direction.
Component configuration
Section titled “Component configuration”Instance options override global and theme defaults; all are dynamic through update() (dayEndHour must stay after dayStartHour).
Timezones
Section titled “Timezones”The layout timezone is the configured timezone (UTC unless configured), never the host zone. Timed events are exact instants positioned by their wall-clock time in that zone; on DST days rows are wall-clock slots (a skipped hour still has its slot; creating there resolves with the disambiguation policy, compatible by default). Series expand in the event’s timeZone (default the scheduler zone): a 09:00 London standup stays at 09:00 London across both DST changes, and is shown at the matching wall-clock time in other scheduler zones. Moves keep the real duration and the wall-clock time on day cells; recurring series edits shift the rule by the wall-clock delta (weekly BYDAY and positive BYMONTHDAY follow day shifts; EXDATE/RDATE move too). loadEvents receives instants of local midnights.
previous (chevron-left), next (chevron-right), swapped in RTL. Toolbar: grip (Move), check (Move here), minus, plus, undo, redo, refresh (Retry).
Security
Section titled “Security”Titles, locations, and names render through textContent; renderEvent returns application DOM. Positions are CSS custom properties set through the CSSOM (CSP-safe). Events and data-source answers are validated before use (SCHEDULER_OPTIONS, SCHEDULER_DATA_SOURCE). loadEvents receives an AbortSignal and times out.
Data handling
Section titled “Data handling”No persistence or telemetry. Loaded ranges are cached in memory until an edit, refresh(), or update({ loadEvents }). Edits are reported through aui-event-change (changes and every event); save them and roll back with undo() or update({ events }) when the server refuses.
Performance
Section titled “Performance”Each render rebuilds the view (a week has 336 slot cells; timelines above 60 resources render only the rows in view). Series expand per visible range with fast-forward when there is no COUNT. Budgets (e2e): week view with 300 events create < 120 ms; next period in a month with 1,000 events (200 recurring) < 120 ms; timeline with 500 resources and 2,000 events create < 150 ms.
Supported data scale
Section titled “Supported data scale”Tested with 1,000 occurrences per visible range and 500 timeline resources; the undo history keeps 50 snapshots by default.
Edge cases
Section titled “Edge cases”- Overlapping timed events share the day width; an event extends into free columns to its right.
- Multi-day timed events show one segment per day in time views and a bar in month and timeline views.
- Events outside
dayStartHour..dayEndHourare clipped to the visible hours. - An exception event replaces its occurrence; EXDATE cancels one; moving an exception with “All events” moves the series.
- “This and following” on the first occurrence edits the whole series; COUNT is split between the two series.
- Month cells show
monthEventLimitrows; hidden events are counted in “+N more” and in the cell name. - A drop that changes nothing emits no event.
Errors
Section titled “Errors”| Code | Category | Cause |
|---|---|---|
SCHEDULER_OPTIONS |
configuration | Empty label; invalid events (ids, kinds, end before start, exceptions without originalStart, recurrence on an exception), resources, views, date, callbacks, business calendar, hours; moveEvent/resizeEvent with unknown keys or wrong kinds. |
SCHEDULER_DATA_SOURCE |
configuration | loadEvents answered invalid events (reported through aui-load-error). |
BUSINESS_CALENDAR_DEFINITION |
configuration | Invalid businessCalendar. |
A failed construction leaves the host untouched.
Known limitations
Section titled “Known limitations”- No conversion between all-day and timed by dragging; multi-day timed events are not shown in the all-day row.
- Fixed timeline scales (day slots, week days, month days).
- Deleting and creating are delegated to the application.
- One resource per event.
- Manual screen-reader verification is pending.
Unsupported scenarios
Section titled “Unsupported scenarios”- HTML in titles; drag and drop from outside; printing layouts.