Editing policies
Configure conversion, resource assignment, constraints, touch and edge scrolling.
Register only the policies and drivers you need. Factory configuration supplies defaults, and per-view features
entries override them by registry alias.
const businessHours: Calendar_WeeklyRule[] = [
{
daysOfWeek: [1, 2, 3, 4, 5],
startTime: Temporal.PlainTime.from('09:00'),
endTime: Temporal.PlainTime.from('17:00'),
},
];
export const features = calendarFeatures({
interactions: interactionFeature(),
pointer: pointerFeature({ distance: 4 }),
touch: touchFeature({ delay: 500, tolerance: 8 }),
scroll: autoScrollFeature({ edge: 40, speed: 600 }),
movement: eventMoveFeature({
relocationPolicy: {
allowConversion: true,
conversionDuration: 'reset',
defaultTimedMinutes: 60,
resourceMode: 'replace-all',
},
}),
resizing: eventResizeFeature({ fromStart: true }),
constraints: constraintsFeature({
preventOverlap: true,
businessHours,
resourceAvailability: true,
}),
});
export const views = calendarViews({
features,
views: {
editable: timeGridView({ days: 1 }),
readOnly: timeGridView({
days: 1,
features: {
movement: { enabled: false },
resizing: { enabled: false },
},
}),
},
});The read-only view disables its movement and resizing operations; their APIs stay installed. Settings can also arrive
as live calendar options such as movement, resizing and constraints. They resolve in this order: factory
defaults, live options, per-view features[alias], then direct per-view settings.
Interaction dependencies compare by reference, so keep rule arrays and predicates stable until their contents change. In React, declare them outside the component or memoize them; a business-hours array recreated on every render revalidates the draft each time.
Movement and resources
createEventRelocationPolicy types shared conversion and resource settings with inferred literals and returns the
object unchanged; it installs no operation. Pass it once as the calendar's eventRelocationPolicy, and movement,
external creation and transfer receiving inherit it. Each of those features accepts a partial relocationPolicy
override.
const eventRelocationPolicy = createEventRelocationPolicy({
allowConversion: true,
conversionDuration: 'reset',
defaultTimedMinutes: 60,
allowResourceChange: true,
resourceMode: 'replace-all',
});
export const features = calendarFeatures({
interactions: interactionFeature(),
movement: eventMoveFeature(),
creation: externalDropFeature({ relocationPolicy: { allowConversion: false } }),
transfers: eventTransferFeature({ relocationPolicy: { resourceMode: 'replace-source' } }),
});
export const calendar = createCalendar({
features,
views: calendarViews({ features, views: { day: timeGridView({ days: 1 }) } }),
view: 'day',
date: initialDate,
timeZone: 'UTC',
events: initialEvents,
eventRelocationPolicy,
id: 'bookings',
onNavigate: (next): void => calendar['~update']({ ...calendar.options, view: next.view, date: next.date }),
});Fields resolve from library defaults, then calendar policy, factory configuration, live feature settings, per-view
features[alias] and direct view settings, with later defined values winning. Omitted and undefined fields
inherit, and false overrides true. Policies merge by field, so overriding resource behavior keeps the inherited
conversion settings.
Here, movement inherits the calendar policy, external creation rejects kind conversion, and transfer receiving replaces only the dragged resource. A transfer uses the receiving calendar's policy.
Changing the calendar policy or a feature override revalidates the active draft and its preview. If the proposal becomes unavailable, the preview clears and commit is refused; restoring permission revalidates the same session. A recreated policy with equal values doesn't revalidate.
enabled and canMove are movement settings. The other fields belong to eventRelocationPolicy or a feature's
relocationPolicy.
| Setting | Default | Effect |
|---|---|---|
enabled | true | Allows movement while the feature is registered. |
allowConversion | false | Allows timed and all-day kinds to change. |
conversionDuration | 'reset' | A conversion becomes one all-day date or one timed hour. |
defaultTimedMinutes | 60 | Timed duration when conversion resets. |
allowResourceChange | true | Allows a destination resource to replace assignments. |
resourceMode | 'replace-all' | Replaces the whole resource list with the destination. |
canMove(event) | Unrestricted | Rejects events for which the predicate returns false. |
Moves within a kind keep the elapsed timed duration or the inclusive all-day length. Moving a timed event in a day
grid keeps its local start time. conversionDuration: 'preserve' keeps elapsed duration; timed-to-all-day conversion
rounds down to whole dates, at least one.
resourceMode: 'replace-source' replaces only the dragged assignment. For multi-resource events, put its resourceId
in the source descriptor or pass it to move.begin/move.evaluate. With allowResourceChange: false, a move to an
unassigned resource is rejected and assignments stay as they were.
Resizing
Both edges resize by default; eventResizeFeature({ fromStart: false }) allows only the end. resizing.enabled
disables the operation, and canResize(event, edge) rejects individual edits. The opposite edge stays fixed and the
event kind never changes. A timed end must be after its start. All-day ends are inclusive, so equal start and end is
one date.
Constraints
constraintsFeature checks selection, movement, resizing, creation and transfers, on every preview and again at
commit.
| Setting | Behavior |
|---|---|
preventOverlap | true once registered. Set false to keep other rules and allow overlap. |
allowedRanges | Background ranges, or a function of the proposed dates and time zone, containing the proposal. |
businessHours | Weekly rules containing the proposal, in the calendar time zone. |
resourceIds | Allowlist for assigned resource IDs. |
resourceAvailability | Each assigned resource must exist and its availability must contain the proposal. |
allow(proposal, input) | Synchronous predicate returning true, false or a rejection reason string. |
Rules apply to the whole proposed interval, and adjacent allowed windows join. Overlap checks cover timed, all-day and background events. Events with disjoint nonempty resource lists don't conflict, and an edited event doesn't conflict with its own original.
Constraints check the concrete events you supply. They don't expand recurring definitions, and these operations can't
edit generated occurrences; apply recurrence edits in your application. Shading a background doesn't enforce it:
pass the same rules to constraintsFeature.
Touch and scrolling
pointerFeature activates after four pixels (distance). touchFeature adds touch input with a 500 ms hold (delay) and an
eight-pixel tolerance; movement before activation scrolls the page as usual. autoScrollFeature scrolls nested
containers and the window near their edges, with a 40 px edge zone and 600 px/s maximum speed.
Each driver reads per-view settings and accepts enabled: false. None of them mounts listeners alone (use the
interaction bindings) or installs an operation: touch still needs selection, movement, resizing or a custom
operation.
Time zones and DST
Timed hits snap to the view's slot interval on its fixed wall-clock axis. A nonexistent local time is unavailable.
A repeated time keeps the source's offset when it matches, and otherwise takes the earlier occurrence. Hits keep the
local clock and candidate instants for custom feedback.
Hidden dates, visible-hour windows and resource grouping shape the surfaces. Register only the date/time track so labels and gutters don't skew hit testing.
Back to selection and event editing for bindings and ownership.