Multi-view calendar (year / month / week / day / timeline) with drag-to-create, drag-to-move, drag-to-resize, and full keyboard navigation. Events and resources are inputs; the component emits semantic (eventCreate) / (eventUpdate) / (eventDelete) requests that the consumer translates into its own data mutations.
event-create for that whole day; Space opens the day popover (its event list, plus New event and Show day).event-createrequest carrying the range. The scheduler doesn't create or store the event itself — this demo's handler does that with its own defaults, then calls clearSelection(). Esc clears the selection without emitting a request.event-selected.event-delete; the date popover and the event editor carry delete buttons for pointer users.event-update, Delete emits event-delete, Esc cancels. Fields follow the permission table; disable the whole editor with [eventEditor]="false". Colour is two-state: Inherit from resource (checked whenever the event carries no color) keeps it tracking its resource — including after a move to another resource — while unchecking pins the swatch onto the event.+N more link opens it, and so does a plain day-cell click by default (dayClickAction: 'popover' — date-click still emits first; set 'none' to opt out). Clicking the day number in month view always drills into the day view instead. When resources exist, the New event action carries the picked resource. Esc closes it.resource-update), Esc cancels.No events yet. Try clicking on the scheduler or loading sample data.
[]
<bs-scheduler
[view]="view()"
[date]="date()"
[events]="events()"
[options]="options()"
(eventCreate)="onEventCreate($event)"
(eventUpdate)="onEventUpdate($event)"
(eventDelete)="onEventDelete($event)">
</bs-scheduler>
import { Component, computed, signal } from '@angular/core';
import {
BsSchedulerComponent,
SchedulerEventCreateDetail,
SchedulerEventUpdateDetail,
SchedulerEventDeleteDetail,
} from '@mintplayer/ng-bootstrap/scheduler';
import {
ViewType,
SchedulerEvent,
SchedulerOptions,
generateEventId,
} from '@mintplayer/ui-core/scheduler-core';
@Component({
selector: 'my-calendar',
templateUrl: './my-calendar.component.html',
imports: [BsSchedulerComponent],
})
export class MyCalendarComponent {
view = signal<ViewType>('week');
date = signal<Date>(new Date());
events = signal<SchedulerEvent[]>([]);
options = computed<Partial<SchedulerOptions>>(() => ({
slotDuration: 1800, // 30 minutes
timeFormat: '24h',
firstDayOfWeek: 1, // Monday
nowIndicator: true,
}));
// The WC emits a *request* with the selected range. The consumer
// decides whether to materialise an event (or open a dialog first).
onEventCreate(e: SchedulerEventCreateDetail): void {
this.events.update((events) => [...events, {
id: generateEventId(),
title: 'New Event',
start: e.range.start,
end: e.range.end,
color: '#3788d8',
}]);
}
onEventUpdate(e: SchedulerEventUpdateDetail): void {
this.events.update((events) =>
events.map((ev) => (ev.id === e.event.id ? e.event : ev)));
}
onEventDelete(e: SchedulerEventDeleteDetail): void {
this.events.update((events) => events.filter((ev) => ev.id !== e.event.id));
}
}
// Per PRD scheduler-controlled-selection the WC no longer auto-clears
// its selection after (eventCreate). The consumer commits, then calls
// clearSelection() so subsequent gestures don't re-emit the same range.
private scheduler = viewChild<BsSchedulerComponent>(BsSchedulerComponent);
onEventCreate(e: SchedulerEventCreateDetail): void {
this.api.createEvent(e.range).then((created) => {
this.events.update((events) => [...events, created]);
this.scheduler()?.clearSelection();
});
}