Interaction
Drag & Drop
Entries can be moved and resized by dragging. Move an entry to a different time or day. Resize from the top or bottom edge in week/day views to change duration. In month view, drag entries between days.
// Intercept entry move (e.g. to validate or snap)
calendar.events.entryMoving$.subscribe(args => {
// args: { entry, newStartTime, newEndTime, cancel }
// Cancel the move
args.cancel = true;
// Or snap to 15-minute intervals
const start = new Date(args.newStartTime);
const minutes = Math.round(start.getMinutes() / 15) * 15;
start.setMinutes(minutes, 0, 0);
args.newStartTime = start;
});
// Intercept entry resize
calendar.events.entryResizing$.subscribe(args => {
// args: { entry, newStartTime, newEndTime, edge, cancel }
// Prevent resizing shorter than 15 minutes
const duration = args.newEndTime.getTime() - args.newStartTime.getTime();
if (duration < 15 * 60 * 1000) {
args.cancel = true;
}
});Double-click & "+N more"
Handle double-click to open an edit dialog. In month view, handle the "+N more" overflow click to show all events for that day.
// Open edit dialog on double-click
calendar.events.entryDblClick$.subscribe(({ entry }) => {
openEntryDialog(entry);
});
// Handle "+N more" click in month view (switch to day view)
calendar.events.moreClick$.subscribe(({ date, entries }) => {
calendar.goToDate(date);
calendar.viewMode = 'day';
});Selection
Click an entry to select it. Hold Ctrl/Cmd to multi-select.
// Get selected entries
const selected = calendar.selectedEntries;
const single = calendar.selectedEntry; // last selected or null
// Set selection programmatically
calendar.selectedEntry = entry;
calendar.selectedEntries = [entry1, entry2];
// Clear selection
calendar.selectedEntry = null;
// Listen for selection changes
calendar.events.selectedEntriesChanged$.subscribe(entries => {
console.log('Selected:', entries.length, 'entries');
});Creating Events
Drag on an empty area in week/day views to create a new entry. Click an empty date cell in month view. The new item is added to the default calendar.
// Intercept entry creation
calendar.events.entryCreating$.subscribe(args => {
// args: { startTime, endTime, isAllDay, cancel }
// Prevent creation on weekends
const day = args.startTime.getDay();
if (day === 0 || day === 6) {
args.cancel = true;
}
});
// Listen for date clicks (e.g. to open a creation dialog)
calendar.events.dateClick$.subscribe(args => {
// args: { date, isAllDay, domEvent }
console.log('Clicked on', args.date, args.isAllDay ? '(all-day)' : '');
});Editing Recurring Events
When a recurring entry is moved, resized, or deleted, the operation can apply to a single occurrence, this and all following occurrences, or the entire series.
Recurrence Scope
| this | Only this occurrence. Creates a RecurrenceException with overrides. |
| thisAndFollowing | This and all future occurrences. Splits the series at this point. |
| all | All occurrences. Modifies the master CalendarItem directly. |
// Intercept deletion of a recurring entry
calendar.events.entryDeleting$.subscribe(args => {
// args: { entry, isRecurring, scope, cancel }
// scope is 'this' | 'thisAndFollowing' | 'all' (for recurring items)
if (args.isRecurring && args.scope === 'all') {
const confirmed = window.confirm('Delete all occurrences?');
if (!confirmed) args.cancel = true;
}
});Keyboard Navigation
Built-in keyboard shortcuts for common actions:
| Arrow keys | Navigate between entries or dates. |
| Enter | Open / confirm the selected entry. |
| Escape | Cancel current operation or deselect. |
| Delete | Delete selected entries (triggers entryDeleting$). |
| Ctrl+Z | Undo |
| Ctrl+Y / Ctrl+Shift+Z | Redo |
| Ctrl+C | Copy selected entries |
| Ctrl+X | Cut selected entries |
| Ctrl+V | Paste entries |
Filtering
Filter visible entries by toggling calendar visibility in the sidebar or programmatically.
// Toggle calendar visibility (built-in via sidebar calendar list)
const workCal = calendar.data.getCalendarById('c1');
workCal.isVisible = false; // hides all items in this calendar
// Show only specific calendars
calendar.data.getCalendarById('c2').isVisible = true;Undo & Redo
Full undo/redo support for all data changes — entry moves, resizes, additions, removals, and property modifications.
calendar.undo();
calendar.redo();
// Observe availability (e.g. for toolbar button state)
calendar.canUndo$.subscribe(canUndo => {
undoButton.disabled = !canUndo;
});
calendar.canRedo$.subscribe(canRedo => {
redoButton.disabled = !canRedo;
});
// Listen for undo/redo
calendar.events.undone$.subscribe(() => console.log('Undone'));
calendar.events.redone$.subscribe(() => console.log('Redone'));Copy & Paste
Copy and paste entries using the clipboard.
// Copy selected entries
await calendar.copyEntries();
// Copy specific entries
await calendar.copyEntries([entry1, entry2]);
// Cut (copy + remove)
await calendar.cutEntries();
// Paste
await calendar.pasteEntries();Visible Range & Lazy Loading
Listen for changes to the visible date range to load data on demand. This fires when the user navigates or switches view mode.
// Load events on demand as the user navigates
calendar.events.viewDateRangeChanged$.subscribe(({ start, end }) => {
// Fetch events for the new visible range from your backend
fetchEvents(start, end).then(items => {
calendar.data.loadItems(items);
});
});Synchronizing Changes
Use change events to synchronize the calendar state with a backend API or database. Events fire for every user interaction — dragging, resizing, creating, deleting, and editing.
// Track item changes (moves, resizes, property edits)
calendar.events.itemChanged$.subscribe(({ item }) => {
api.updateItem(item.id, {
calendarId: item.calendarId,
title: item.title,
startTime: item.startTime,
endTime: item.endTime,
isAllDay: item.isAllDay,
recurrenceRule: item.recurrenceRule,
});
});
// Track new items
calendar.events.itemAdded$.subscribe(({ item }) => {
api.createItem({
id: item.id,
calendarId: item.calendarId,
title: item.title,
startTime: item.startTime,
endTime: item.endTime,
});
});
// Track removed items
calendar.events.itemRemoved$.subscribe(({ item }) => {
api.deleteItem(item.id);
});
// Track calendar changes (name, color, visibility)
calendar.events.calendarChanged$.subscribe(({ calendar: cal }) => {
api.updateCalendar(cal.id, { name: cal.name, color: cal.color });
});
calendar.events.calendarAdded$.subscribe(({ calendar: cal }) => {
api.createCalendar({ id: cal.id, name: cal.name, color: cal.color });
});
calendar.events.calendarRemoved$.subscribe(({ calendar: cal }) => {
api.deleteCalendar(cal.id);
});