Skip to content

Scheduler

Advanced EnterpriseSince 0.6.0
import { Scheduler } from '@absoluteui/components/scheduler';
import '@absoluteui/components/scheduler/style.css';

Using Angular? See Scheduler for Angular.

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.

  • 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.
  • 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.
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;
});
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 });
});
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);
});
}
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;
});
}

See section 33 and metadata/scheduler/examples.json: team week, editing with save and rollback, rooms timeline from a data source, global configuration.

<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.

Scheduler.create(host: HTMLElement, options: SchedulerOptions): Scheduler

Global configuration key: scheduler (see Configuration).

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.
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).
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).
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.
Area Description
renderEvent Custom, non-interactive event content.

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.
Slot Default icon
previous chevron-left
next chevron-right

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

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 }

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).

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)
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.

Criterion Level Note
1.3.1 Info and Relationships A
1.4.1 Use of Color A
1.4.3 Contrast (Minimum) AA
1.4.10 Reflow AA
1.4.11 Non-text Contrast AA
2.1.1 Keyboard A
2.4.3 Focus Order A
2.4.7 Focus Visible AA
2.5.7 Dragging Movements AA
2.5.8 Target Size (Minimum) AA
3.2.2 On Input A
4.1.2 Name, Role, Value A
4.1.3 Status Messages AA

Automated test results: Accessibility and compliance.

  • 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.

Views; loading (aria-busy, status); load error (status with Retry); edit mode; cut (move pending); dragging; resizing; selection; recurring-scope dialog.

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
  • 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.

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.

direction: 'rtl' mirrors column order, timeline flow, chevrons, Left/Right keys, and Shift+Left/Right resizing.

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.

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.

Instance options override global and theme defaults; all are dynamic through update() (dayEndHour must stay after dayStartHour).

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).

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.

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.

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.

Tested with 1,000 occurrences per visible range and 500 timeline resources; the undo history keeps 50 snapshots by default.

  • 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..dayEndHour are 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 monthEventLimit rows; hidden events are counted in “+N more” and in the cell name.
  • A drop that changes nothing emits no event.
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.

  • 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.
  • HTML in titles; drag and drop from outside; printing layouts.