API Reference
Everything the package exports, on one page. The other pages explain how the pieces behave; this one says what they are called and what they take.
Entry Points
| Entry | What it holds |
|---|---|
@sv5ui/scheduler | Scheduler, the five views, the sidebar parts, EventChip, dragSource, config and every type |
@sv5ui/scheduler/locales | Thirteen label packs, one export each, so a bundle carries only the ones it imports |
@sv5ui/scheduler/theme.css | One @source line that lets Tailwind see the classes inside the package |
// Components
import {
Scheduler,
MonthView, WeekView, DayView, YearView, AgendaView,
EventChip, DateNavigator, CalendarList, SearchBox, DragSourceList
} from '@sv5ui/scheduler';
// Functions and attachments
import { dragSource, defineSchedulerConfig, createTimeScale, mergeLabels } from '@sv5ui/scheduler';
// Types, a selection
import type {
EventInput, SchedulerEvent, Mutation, EventSourceFn,
ViewDefinition, LayoutStrategy, InteractionPlugin, StoreMiddleware,
SchedulerLabels, SchedulerProps
} from '@sv5ui/scheduler';
// Label packs
import { en, vi, ja } from '@sv5ui/scheduler/locales';Components
| Component | Description |
|---|---|
Scheduler | The whole calendar. Props below |
MonthView, WeekView, DayView, YearView, AgendaView | The five built-in view components, exported to be reused as the component of a ViewDefinition. Each takes ViewProps |
EventChip | The default rendering of one event, for use inside an event snippet or on its own |
DateNavigator | A month calendar with a dot under every day that has an event; picks a date |
CalendarList | A checklist that shows and hides calendars; binds hiddenCalendars |
SearchBox | A text field for search with a keyboard shortcut |
DragSourceList | A list of items that create an event where they are dropped |
Functions and Attachments
| Export | Description |
|---|---|
dragSource(data) | Attach to any element so it can be dropped on a scheduler grid |
defineSchedulerConfig(config) | Slot classes and variants every instance starts from. Replaces the previous config |
createTimeScale(options) | Wall clock to pixels and back, for a layout or view of your own |
mergeLabels(overrides) | A complete label set with English filling the gaps |
Scheduler Props
SchedulerProps<T>, where T is the shape of event.data and is inferred from the array you bind. Any other attribute goes to the root div.
Grouped the way the pages are.
Data and saving
| Prop | Type | Default |
|---|---|---|
events | EventInput<T>[] | - |
source | EventSourceFn<T> | - |
onLoadError | (error: unknown) => void | - |
onMutate | (m: Mutation<T>) => MutationResult<T> | Promise<...> | - |
onConflict | (m: Mutation<T>, server: SchedulerEvent<T>) => ConflictResolution | 'keep-server' |
onError | (m: Mutation<T>, error: unknown) => void | - |
middleware | StoreMiddleware<T>[] | [] |
Views and navigation
| Prop | Type | Default |
|---|---|---|
view | string | 'week' |
date | ZonedDateTime | today |
days | number | - |
weekStartsOn | WeekDay | 1 |
hiddenDays | WeekDay[] | [] |
dayStartHour | number | 0 |
dayEndHour | number | 24 |
slotMinutes | number | 30 |
slotHeight | number | 24 |
weekNumbers | boolean | false |
toolbar | boolean | Snippet<[ToolbarSnippetProps]> | true |
toolbarActions | Snippet | - |
onMenu | () => void | - |
views | ViewDefinition<T>[] | [] |
layouts | LayoutStrategy[] | [] |
interactions | InteractionPlugin<T>[] | [] |
Time and language
| Prop | Type | Default |
|---|---|---|
timeZone | TimeZoneId | local zone |
locale | string | 'en-US' |
hour12 | boolean | from locale |
labels | PartialLabels | English |
dir | 'ltr' | 'rtl' | inherited |
businessHours | BusinessHours | - |
holidays | Holiday[] | [] |
Calendars and filtering
| Prop | Type | Default |
|---|---|---|
calendars | SchedulerCalendar[] | [] |
hiddenCalendars | string[] | [] |
search | string | '' |
filter | (event: SchedulerEvent<T>) => boolean | - |
Interaction and details
| Prop | Type | Default |
|---|---|---|
creatable | boolean | true |
editable | boolean | true |
onEventClick | (event: SchedulerEvent<T>) => void | - |
onSelectSlot | (selection: SlotSelection) => void | - |
detail | 'popover' | 'slideover' | false | 'popover' |
eventDetail | Snippet<[EventDetailSnippetProps<T>]> | - |
eventPanel | Snippet<[EventPanelSnippetProps<T>]> | - |
createPanel | Snippet<[CreatePanelSnippetProps<T>]> | - |
draft | SlotSelection | null | null |
dragSources | DragSourceData<T>[] | [] |
Sidebar
| Prop | Type | Default |
|---|---|---|
sidebar | boolean | Snippet<[SidebarSnippetProps<T>]> | false |
sidebarHeader | Snippet<[SidebarSnippetProps<T>]> | - |
sidebarFooter | Snippet<[SidebarSnippetProps<T>]> | - |
sidebarOpen | boolean | true |
sidebarSide | 'start' | 'end' | 'start' |
sidebarBreakpoint | number | 1024 |
Size and appearance
| Prop | Type | Default |
|---|---|---|
height | number | string | - |
compactBreakpoint | number | 640 |
compactDays | number | null | 3 |
ui | Partial<Record<SchedulerSlots, ClassNameValue>> | - |
class | ClassNameValue | - |
ref | HTMLElement | null | null |
Rendering snippets
| Prop | Type | Default |
|---|---|---|
event | Snippet<[EventSnippetProps<T>]> | - |
cell | Snippet<[CellSnippetProps]> | - |
header | Snippet<[HeaderSnippetProps]> | - |
empty | Snippet<[EmptySnippetProps]> | - |
Snippet Arguments
What each snippet receives. Every argument type is exported so a snippet declared outside the component can be typed.
| Type | Fields |
|---|---|
EventSnippetProps | event, position, view, isDragging, isResizing, isSelected |
CellSnippetProps | date, view, isToday, isAnchor, isWeekend, isHoliday, isBusinessHours, isOutside |
HeaderSnippetProps | date, view, label, isToday, isAnchor, holiday |
EmptySnippetProps | view, range |
EventDetailSnippetProps | event, close |
EventPanelSnippetProps | event, close, remove, update, deletable |
CreatePanelSnippetProps | start, end, allDay, close, create |
ToolbarSnippetProps | title, date, range, view, views, scheduler, step, today, navigate, setView, toggleSidebar, sidebarOpen, compact |
SidebarSnippetProps | date, view, range, events, scheduler, navigate, close, docked |
Building Block Props
The props of the five standalone components, in short. The Building Blocks page has the full tables.
| Type | Props |
|---|---|
EventChipProps | event, position, locale, hour12, color, size, variant, showTime, selected, dragging, ui, class, children, ref |
DateNavigatorProps | date, onSelect, events, timeZone, locale, weekStartsOn, labels, ui, class, ref |
CalendarListProps | calendars, hiddenCalendars, title, labels, item, ui, class, ref |
CalendarListItemProps | calendar, visible, toggle |
SearchBoxProps | value, shortcut, labels, ui, class, ref, plus HTML input attributes |
DragSourceListProps | items, calendars, title, labels, item, ui, class, ref |
DragSourceListItemProps | item, color |
Types
Sixty five types, grouped by what they describe. The listings below are trimmed to their fields; JSDoc on every field is in the package.
| Group | Types |
|---|---|
| Events | SchedulerEvent, EventInput, NewEventInput, EventChanges, EventColor, SchedulerCalendar, SchedulerResource |
| Ranges | DateInput, DateRange, TimeZoneId, WeekDay, BusinessHours, Holiday |
| Recurrence | RecurrenceRule, RecurrenceRuleInput, RecurrenceFrequency, OrdinalWeekDay |
| Saving | Mutation, MutationKind, MutationResult, MutationHandlers, ConflictResolution, EventPatch, StoreMiddleware |
| Loading | EventSourceFn, LoadContext |
| Views | ViewDefinition, ViewProps, ViewSnippets, ViewPreview, SchedulerContext |
| Layout | LayoutStrategy, LayoutContext, PositionedEvent, TimePosition, SpanPosition, TimeScale, TimeScaleOptions |
| Interaction | InteractionPlugin, InteractionContext, InteractionPreview, HitTarget, GridFocus, SlotSelection, DragSourceData |
| Labels and config | SchedulerLabels, PartialLabels, SchedulerConfig |
| Component props | SchedulerProps, EventChipProps, DateNavigatorProps, CalendarListProps, CalendarListItemProps, SearchBoxProps, DragSourceListProps, DragSourceListItemProps |
Events
// What the scheduler holds. Times are always ZonedDateTime; end is exclusive.
interface SchedulerEvent<T = unknown> {
id: string; // stable; mutations are keyed by it
title: string;
start: ZonedDateTime;
end: ZonedDateTime;
allDay?: boolean; // all-day row and month cells rather than the time grid
calendarId?: string; // joins one of `calendars`; supplies the colour
color?: EventColor; // overrides the calendar colour
background?: boolean; // drawn behind other events, never editable
editable?: boolean; // false locks this event
recurrence?: RecurrenceRule; // makes the event a series
seriesId?: string; // set on an occurrence: the id of its series
resourceId?: string; // stored and returned; no built-in view reads it
data?: T; // your payload, never read by the scheduler
}
// What you pass in: the same, with dates as strings or ZonedDateTime
type EventInput<T> = Omit<SchedulerEvent<T>, 'start' | 'end' | 'recurrence'> & {
start: DateInput; end: DateInput; recurrence?: RecurrenceRuleInput;
};
type DateInput = string | ZonedDateTime;
type NewEventInput<T> = Omit<EventInput<T>, 'id'> & { id?: string }; // createPanel.create
type EventChanges<T> = Partial<Omit<EventInput<T>, 'id'>>; // eventPanel.update
type EventColor = 'primary' | 'secondary' | 'tertiary' | 'success'
| 'warning' | 'error' | 'info' | 'surface';
interface SchedulerCalendar { id: string; title: string; color?: EventColor }
interface SchedulerResource { id: string; title: string; parentId?: string; capacity?: number; order?: number }Ranges, Hours and Holidays
type TimeZoneId = string; // IANA, e.g. 'Asia/Ho_Chi_Minh'
type WeekDay = 0 | 1 | 2 | 3 | 4 | 5 | 6; // 0 is Sunday, as Date.getDay()
interface DateRange { start: ZonedDateTime; end: ZonedDateTime } // [start, end)
interface BusinessHours {
start: string; // 'HH:mm'
end: string; // 'HH:mm', exclusive
days: WeekDay[];
}
interface Holiday {
date: string; // 'YYYY-MM-DD', read in the scheduler's timeZone
title?: string;
}Recurrence
type RecurrenceFrequency = 'daily' | 'weekly' | 'monthly' | 'yearly';
interface RecurrenceRule {
freq: RecurrenceFrequency;
interval?: number; // default 1
byDay?: Array<WeekDay | OrdinalWeekDay>; // plain weekdays are evaluated; ordinals are not
count?: number; // wins over until when both are set
until?: ZonedDateTime; // latest start, inclusive
exDates?: ZonedDateTime[]; // occurrences to drop, matched by instant
weekStart?: WeekDay; // for weekly rules with interval > 1
// Accepted so stored data keeps its shape, not evaluated, one dev warning per series:
rDates?: ZonedDateTime[]; bySetPos?: number[]; byMonthDay?: number[];
byMonth?: number[]; byWeekNo?: number[]; byYearDay?: number[];
}
interface OrdinalWeekDay { day: WeekDay; ordinal: number } // -1 is the last
// The input form takes DateInput for until, exDates and rDates
type RecurrenceRuleInput = Omit<RecurrenceRule, 'until' | 'exDates' | 'rDates'> & {
until?: DateInput; exDates?: DateInput[]; rDates?: DateInput[];
};Saving
type MutationKind = 'create' | 'update' | 'delete' | 'move' | 'resize';
interface Mutation<T = unknown> {
id: string; // unique per mutation, assigned by the scheduler
kind: MutationKind;
eventId: string;
before: SchedulerEvent<T> | null; // null on create
after: SchedulerEvent<T> | null; // null on delete
}
type MutationResult<T> = void | EventInput<T>; // return the server's version to compare
type ConflictResolution = 'keep-server' | 'keep-local';
interface MutationHandlers<T> {
onMutate?: (mutation: Mutation<T>) => MutationResult<T> | Promise<MutationResult<T>>;
onConflict?: (mutation: Mutation<T>, server: SchedulerEvent<T>) => ConflictResolution;
onError?: (mutation: Mutation<T>, error: unknown) => void;
}
// What reaches the store, and what middleware wraps
type EventPatch<T> =
| { type: 'upsert'; event: SchedulerEvent<T> }
| { type: 'remove'; eventId: string }
| { type: 'reset'; events: SchedulerEvent<T>[] };
type StoreMiddleware<T> = (next: (patch: EventPatch<T>) => void) => (patch: EventPatch<T>) => void;Loading
interface LoadContext {
range: DateRange; // return every event that overlaps it
timeZone: TimeZoneId;
signal: AbortSignal; // aborted when the range changes before this load settles
}
type EventSourceFn<T = unknown> = (context: LoadContext) => EventInput<T>[] | Promise<EventInput<T>[]>;Views
interface ViewDefinition<T = unknown> {
name: string; // the value of `view` that activates it
label?: string; // switcher text; built-in views take theirs from labels
layout: string; // 'time-grid' | 'month-grid' | 'list' | a registered LayoutStrategy
columnsPerRow?: number; // the month view uses 7
range: (anchor: ZonedDateTime, context: SchedulerContext) => DateRange;
step: (anchor: ZonedDateTime, direction: 1 | -1, context: SchedulerContext) => ZonedDateTime;
title?: (anchor: ZonedDateTime, range: DateRange, context: SchedulerContext) => string;
shortTitle?: (anchor: ZonedDateTime, range: DateRange, context: SchedulerContext) => string;
component: Component<ViewProps<T>>;
}
// Everything a view component receives. It reads no context of its own.
interface ViewProps<T = unknown> {
view: string; anchor: ZonedDateTime; range: DateRange;
days: ZonedDateTime[]; // one per column, hiddenDays already removed
columnsPerRow: number;
events: SchedulerEvent<T>[]; // filtered and expanded for the range
scheduler: SchedulerContext;
scale: TimeScale;
positioned: PositionedEvent<T>[]; // what the layout produced
snippets: ViewSnippets<T>; // { event?, cell?, header?, empty?, detail? }
focus: GridFocus | null;
interactions: { grid: Attachment<HTMLElement>; event: (position: PositionedEvent<T>) => Attachment<HTMLElement> };
preview: ViewPreview<T> | null; // InteractionPreview plus its positioned placements
selectedEventId: string | null;
loading: boolean; // true while source fetches the range
detailPopover: boolean;
onSelectEvent: (eventId: string | null) => void;
onDeleteEvent: (eventId: string) => void;
onOpenEvent?: (eventId: string) => void;
navigate: (date: ZonedDateTime, view?: string) => void;
}
// Read-only settings shared by views, layouts and interactions
interface SchedulerContext {
timeZone: TimeZoneId; locale: string; weekStartsOn: WeekDay; hour12?: boolean;
businessHours?: BusinessHours; holidays: Holiday[]; calendars: SchedulerCalendar[];
dayCount?: number; weekNumbers: boolean; hiddenDays: WeekDay[];
editable: boolean; direction: 'ltr' | 'rtl'; compact: boolean;
labels: SchedulerLabels;
now: ZonedDateTime; // refreshed once a minute
}Layout
interface LayoutStrategy {
name: string;
layout<T>(events: SchedulerEvent<T>[], range: DateRange, context: LayoutContext): PositionedEvent<T>[];
}
interface LayoutContext { scale: TimeScale; days: ZonedDateTime[]; columnsPerRow: number; scheduler: SchedulerContext }
type PositionedEvent<T> = TimePosition<T> | SpanPosition<T>;
interface TimePosition<T> { // one segment in one day column of the time grid
kind: 'time'; event: SchedulerEvent<T>; dayIndex: number;
segmentStart: ZonedDateTime; segmentEnd: ZonedDateTime;
top: number; height: number; // pixels, from TimeScale
left: number; width: number; // fractions of the column width
column: number; columns: number; // place inside the overlap cluster
}
interface SpanPosition<T> { // one segment across whole-day cells
kind: 'span'; event: SchedulerEvent<T>;
row: number; lane: number; startColumn: number; endColumn: number; // endColumn exclusive
continuesBefore: boolean; continuesAfter: boolean;
}
// Wall clock to pixels and back; the same instance is shared by the view, its layout and the gestures
interface TimeScale {
readonly slotMinutes: number; readonly slotHeight: number;
readonly startHour: number; readonly endHour: number; readonly dayHeight: number;
toPixel(date: ZonedDateTime): number;
toDate(pixel: number, dayStart: ZonedDateTime): ZonedDateTime;
}
interface TimeScaleOptions { slotMinutes?: number; slotHeight?: number; startHour?: number; endHour?: number }
function createTimeScale(options?: TimeScaleOptions): TimeScale;Interaction
interface InteractionPlugin<T = unknown> {
name: string; // a built-in name (create, move, resize, keyboard, external) replaces that plugin
attach: (context: InteractionContext<T>) => Attachment<HTMLElement>;
attachEvent?: (context: InteractionContext<T>, position: PositionedEvent<T>) => Attachment<HTMLElement>;
}
interface InteractionContext<T = unknown> {
// read the grid
view: string; viewLabel: string; range: DateRange; days: ZonedDateTime[]; columnsPerRow: number;
scale: TimeScale; scheduler: SchedulerContext;
events: readonly SchedulerEvent<T>[]; // filtered and expanded for the range
getEvent: (eventId: string) => SchedulerEvent<T> | undefined;
hitTest: (clientX: number, clientY: number) => HitTarget | null;
snap: (date: ZonedDateTime) => ZonedDateTime;
// state
selectedEventId: string | null; select: (eventId: string | null) => void;
focus: GridFocus | null; setFocus: (focus: GridFocus | null) => void;
setPreview: (preview: InteractionPreview<T> | null) => void;
// act
commit: (mutation: Omit<Mutation<T>, 'id'>) => void; // through the save pipeline
selectSlot: (point: Pick<HitTarget, 'date' | 'allDay'>) => void; // reports one slot
selectRange: (selection: SlotSelection) => void; // reports and opens createPanel
newEventId: () => string;
step: (direction: 1 | -1) => void;
navigate: (date: ZonedDateTime, view?: string) => void;
announce: (message: string) => void;
}
interface HitTarget { date: ZonedDateTime; dayIndex: number; allDay: boolean; keepsTime: boolean; eventId: string | null }
interface GridFocus { dayIndex: number; minutes: number | null } // null minutes is a whole-day cell
interface SlotSelection { start: ZonedDateTime; end: ZonedDateTime; allDay: boolean }
interface InteractionPreview<T> { kind: 'create' | 'move' | 'resize'; event: SchedulerEvent<T> }
// What a dragSource element carries; the drop fills in start, end and a generated id
type DragSourceData<T> = Omit<EventInput<T>, 'id' | 'start' | 'end' | 'recurrence'>
& { id?: string; durationMinutes?: number };
function dragSource<T>(data: () => DragSourceData<T>): Attachment<HTMLElement>;Labels and Config
// Every string the scheduler shows or announces. The full list is on the Localization page.
interface SchedulerLabels { today: string; /* ... */ more: (count: number) => string; announce: { /* ... */ } }
// Any subset; missing keys fall back to English
type PartialLabels = Partial<Omit<SchedulerLabels, 'announce'>> & { announce?: Partial<SchedulerLabels['announce']> };
function mergeLabels(overrides?: PartialLabels): SchedulerLabels;
// App-wide defaults, keyed by component name
type SchedulerConfig = Record<string, { defaultVariants?: Record<string, unknown>; slots?: Record<string, string> } | undefined>;
function defineSchedulerConfig(config: SchedulerConfig): void;Locales
Each pack is a complete SchedulerLabels, exported from @sv5ui/scheduler/locales.
| Export | Language |
|---|---|
ar | Arabic |
de | German |
en | English (the default) |
es | Spanish |
fr | French |
it | Italian |
ja | Japanese |
ko | Korean |
nl | Dutch |
pt | Portuguese |
ru | Russian |
vi | Vietnamese |
zh | Chinese |
Stability
Everything on this page is public. The package is in its 0.x line, where a minor release may
change an export; the changelog names each such change. Internals stay unexported: the
layout maths, the event store, the mutation pipeline and the gesture engine change freely.
The data-sch-* attributes listed on the Theming page are public too.