Drag, Drop & Selection
Two flags unlock the interactive behaviors: editable lets users move and resize events, selectable lets them drag across empty space to pick a range. Sensible popover UIs are built in — callbacks let you replace them.
Built-in popovers (zero config)
With no callbacks configured, the calendar behaves like Google Calendar out of the box: clicking an event opens a details popover (time, calendar, location, description, delete); clicking or drag-selecting empty space opens a quick-create popover with a title input and calendar picker.
The quick-create popover lets you fine-tune the picked range before saving: start and end are editable date/time inputs (free down to the minute — no snapping to slotDuration), with an “all day” toggle and multi-day ranges supported.
Custom handling via callbacks
What users can do
- Drag an event block to another time or day (week/day views).
- Drag an event pill to another day (month view), or an all-day bar along the all-day lane.
- Drag the bottom edge of a block to resize it.
- Drag a recurring instance — a confirm popover offers “this event” or “this and following”.
- Drag across empty slots to select a time range (fires onSelect).
- Drag across month cells to select a day range.
- Click an empty slot or day (fires onDateClick).
Callbacks
| Callback | Fires when | Payload |
|---|---|---|
onEventClick | an event is clicked or activated by keyboard | (instance, mouseOrKeyboardEvent) |
onDateClick | an empty slot / day cell is clicked | (date, allDay) |
onSelect | a drag-selection finishes | { start, end, allDay } |
onEventChange | a drag or resize lands | { event, oldStart, oldEnd, start, end, revert } |
onEventCreate | the quick-create popover adds an event | (event) |
onEventDelete | an event (or one occurrence of a series) is deleted | (event, occurrence?) |
onSeriesDetach | a dragged/resized occurrence is split out of its series | { series, detached, occurrence } |
onRangeChange | the visible date range changes — fetch events here | (start, end) |
onViewChange / onDateChange | the view / focused date changes | (view) / (date) |
Persisting to a server, with rollback
The calendar applies the change optimistically, then calls onEventChange. If your API call fails, call revert() to snap the event back:
async function onEventChange({ event, start, end, revert }) {
try {
await api.updateEvent(event.id, { start, end });
} catch {
revert(); // restore the previous times
}
}Permission rules
- editable on the calendar is the master switch.
- source.editable = false locks all events of that calendar.
- event.editable overrides both, per event.
- Instances of recurring events are never draggable — a recurring series has no single “time” to move. Change the series’ start/recurrence in your data instead.