Integration & API
The whole surface in one place: the inputs and outputs every view shares, the CalendarEvent model you feed in, the pure headless engine behind the components, the timezone-correct date adapter, and the accessibility and security guarantees. All views are standalone, zoneless, OnPush and SSR-safe, and speak the same contract — the host owns the data, the library renders it.
Inputs
These inputs are shared across the views. [events] and [viewDate] are required everywhere; the rest are optional and fall back to sensible defaults (or the app-wide values you set via withDefaults()). The colour inputs mirror the theming contract, so a view drops onto any colour scheme without touching its CSS.
| Input | Type | Default | Notes |
|---|---|---|---|
events | CalendarEvent[] | — | Required. The immutable event array to render. Replace it (don't mutate) to update. |
viewDate | ZonedDateTime | — | Required. The instant the view is anchored around (which month / week / day). |
today | ZonedDateTime | host now | Which day is marked "today". Pass it explicitly for deterministic SSR / tests. |
now | ZonedDateTime | host now | Drives the "now" line on time-grid / timeline. |
themeMode | 'light' | 'dark' | 'light' | Selects the light or dark token set; contrast is guaranteed in both. |
baseColor | string (hex) | neutral | Neutral anchor colour; the whole neutral ramp derives from it. |
accentColor | string (hex) | — | Interactive / selection colour. |
accentInk | string (hex) | auto | Explicit ink colour on accent surfaces when you need to override the derived one. |
statusColors | Record<string, string> | {} | Maps an event status key to a colour; each emits a --cal-event-<key> triplet. |
locale | string | 'en-US' | BCP-47 locale for date / number formatting. |
timezone | string (IANA) | event zone | Wall-clock zone the view renders in; DST-correct. |
weekStartsOn | 0–6 | 0 | First day of the week (0 = Sunday). |
calendarSystem | CalendarSystem | 'iso8601' | Calendar system for date math (pluggable via the adapter). |
Time-grid & timeline additions
The time-based views (cal-time-grid, cal-timeline-view) add inputs for the visible window, snapping and interactive editing.
| Input | Type | Default | Notes |
|---|---|---|---|
days | number | 7 | How many day columns to show (1 = single day, 7 = week). |
dayStartMinutes / dayEndMinutes | number | 0 / 1440 | Visible time window, in minutes from midnight (e.g. 360 = 6am). |
slotMinutes | number | 60 | Gridline density; labels stay hourly. |
snapMinutes | number | 15 | Drag / resize snap granularity. |
orientation | 'vertical' | 'horizontal' | 'vertical' | Horizontal transposes time left→right, days as rows. |
editable | boolean | false | Enables drag / resize / create interactions. |
inlineEdit | boolean | false | Enables in-place title editing on an event. |
validateChange | (change: EventChange) => boolean | — | Veto a pending move / resize before it commits (e.g. keep within working hours). |
Timeline & month additions
| Input | Type | Default | Notes |
|---|---|---|---|
resources | CalendarResource[] | — | Timeline only, required. Flat list or tree (parentId + expanded) of lanes. |
headerGroupings | TimeHeaderUnit[] | ['day', 'hour'] | Timeline. Stacked header rows across the top. |
hourWidth | number | auto | Timeline. Pixels per hour along the axis. |
laneHeight | number | auto | Timeline. Pixel height of each resource lane. |
maxLanes | number | — | Month. Caps visible event rows per day; the rest collapse into "+N more". |
Outputs
Views are strictly one-way: they never mutate [events]. Every interaction surfaces as an output carrying a plain payload; you apply it to your own store and feed a new immutable array back in.
| Output | Payload | When |
|---|---|---|
eventClicked | { event } | An event chip / block is activated (click or keyboard). |
eventChanged | EventChange | A drag, resize, create or inline-edit commits. |
daySelected | { date } | A day cell is selected (month / year / agenda). |
monthSelected | { date } | A month is selected in the year view. |
slotSelected | { date, … } | An empty time slot is clicked / dragged (time-grid / timeline). |
viewPeriodChanged | { start, end, zone } | The visible period changes — hook to fetch that range's events. |
resourceToggled | { resource, … } | A timeline resource group is expanded / collapsed. |
externalDrop | { date, resourceId, … } | An item from outside the calendar is dropped onto a lane / slot. |
Event model
CalendarEvent is the single shape you feed to every view. Only id and start are required; the optional flags gate per-event interactivity and styling.
import { CalendarEvent, ZonedDateTime } from '@ascentsparksoftware/angular-calendar';
const zone = 'America/New_York';
const z = (iso: string): ZonedDateTime => ({ epochMs: Date.parse(iso), zone });
// The only required fields are id and start. Everything else is optional.
const job: CalendarEvent = {
id: 'job-1842',
title: 'AC install — 14 Elm St',
start: z('2026-06-15T13:00:00Z'),
end: z('2026-06-15T15:30:00Z'),
status: 'scheduled', // -> keyed into statusColors / --cal-event-<key>
resourceIds: ['alice'], // which timeline lane(s) it sits on
resizable: { beforeStart: true, afterEnd: true },
draggable: true,
meta: { workOrderId: 1842 }, // opaque passenger data, echoed back to you
};Headless engine
The components are a thin shell over pure, DOM-free functions: view-model builders, overlap layout, recurrence expansion and event queries. Everything the views use is exported, so you can drive your own surfaces or unit-test the math in isolation.
import {
buildMonthView,
packColumns,
expandRecurringEvents,
filterByStatus,
DateFnsDateAdapter,
} from '@ascentsparksoftware/angular-calendar';
import { provideDateFnsAdapter } from '@ascentsparksoftware/angular-calendar/date-fns';
// The same pure functions the components call. No DOM, no Angular — testable in isolation.
const adapter = new DateFnsDateAdapter();
// buildMonthView returns a plain view-model (weeks, days, lane-packed chips):
const vm = buildMonthView(adapter, {
viewDate: { epochMs: Date.parse('2026-06-15T12:00:00Z'), zone: 'America/New_York' },
events: filterByStatus(myEvents, ['scheduled', 'en-route']),
weekStartsOn: 1,
maxLanes: 3,
});
vm.weeks.forEach((w) => w.days.forEach((d) => console.log(d.date, d.chips.length)));
// Layout + recurrence helpers are exported too, for building your own surfaces:
const columns = packColumns(intervals); // overlap -> side-by-side columns
const occurrences = expandRecurringEvents(seriesEvents, ctx); // RRULE -> instancesDate adapter
Timezone-correct from day one. Every instant is an explicit IANA zone, never a host-local Date, so wall-clock positioning stays right across DST. Wire the adapter once at the app root.
import { ApplicationConfig } from '@angular/core';
import { provideCalendar, withDateAdapter, withDefaults } from '@ascentsparksoftware/angular-calendar';
import { provideDateFnsAdapter } from '@ascentsparksoftware/angular-calendar/date-fns';
export const appConfig: ApplicationConfig = {
providers: [
provideCalendar(
// Default adapter: date-fns + date-fns-tz. A Temporal adapter can drop in later.
withDateAdapter(provideDateFnsAdapter()),
// Optional app-wide defaults so every view inherits them.
withDefaults({ timezone: 'America/New_York', weekStartsOn: 1, locale: 'en-US' }),
),
],
};Accessibility
Accessibility is a hard requirement, not an afterthought — the library targets WCAG 2.2 AA and passes axe with zero violations.
- Correct ARIA
grid/listbox/applicationpatterns per view. - Every interaction is keyboard-operable — navigation, selection, drag, resize and create.
CalFocusTrapguards the event dialog: Tab / Shift+Tab wrap within it and focus is restored on close.- Colour tokens run through
ensureContrastAA, so status and accent surfaces stay legible in light and dark. prefers-reduced-motionis honoured — transitions and animations back off.
Security
Caller content is treated as untrusted throughout. The rendering path is Trusted-Types / strict-CSP clean, and npm audit stays clean because runtime dependencies ship as peers.
- Caller event content is never set via
innerHTML; rendering is sanitized. - Trusted-Types and strict-CSP compatible; no inline-script or unsafe-eval requirements.
- No
eval/Functionanywhere in the codebase. - Runtime dependencies (date-fns, date-fns-tz, rrule) are declared as peers so you control and audit their versions.
FAQ
Developer questions about the headless engine, the state contract, theming, security and SSR.
Is the layout engine usable without the components?
Yes. All date math, recurrence expansion and overlap layout are pure exported functions (buildMonthView, packColumns, expandRecurringEvents, …) with no DOM — use them headless.
How do I persist a drag or resize?
Handle (eventChanged): it emits an EventChange; update your own event array and feed the new immutable array back into [events]. The library never mutates your data.
Can I fully restyle it?
Yes. Every colour/size/radius is a scoped --cal-* CSS custom property on the host; override any of them, or drive the two-colour deriveTheme pipeline.
Is it safe against untrusted event content?
Yes. Caller content is never set via innerHTML; rendering is sanitized and Trusted-Types / strict-CSP clean, with no eval.
Does it work with server-side rendering?
Yes. Standalone, zoneless, OnPush and SSR-safe; this very docs site is statically prerendered.