Hapio Calendar

Compat

@hapio/calendar-compat: the migration component's options, callback payloads and controller.

@hapio/calendar-compat maps the options of the calendar you migrate from onto HapioCalendar's props, so the event-calendar item's EventCalendar takes them unchanged. The migration guide explains it option by option; this page lists the types.

The component's props

The props of the compat EventCalendar: the supported options of the source library's current API with their names, plus availableViews (the switcher's views in order; the first is the default, initialView aside), controller and className. Compat stays uncontrolled like the source library: initialView/initialDate seed the calendar once, so an app that re-creates it with a React key keeps working. Options outside the mapping table are reported in unsupported (with a development warning); resource options name release 1.3.

Prop

Type

Options of one view type, unit or name in views (the cascade is type, then unit, then name). type plus duration/dayCount defines a custom view. The resource and timeline keys are accepted and ignored while their views are not in availableViews.

Prop

Type

Hooks and the mapping

function useCompatProps<P = Dictionary>(props: EventCalendarProps<P>, ui?: CompatUi): HapioCalendarProps<P, undefined, ViewMap>;

Compat props → HapioCalendar props, for the event-calendar registry item: <HapioCalendar {...useCompatProps(props, { timeClass: EVENT_TIME_CLASS, titleClass: EVENT_TITLE_CLASS })} />. It runs the pure mapping (fromCompatOptions) with a session kept across renders (the last reported range gives eventClick and the class callbacks their view; a new range makes the class callbacks that read it run again), memoized on the props' values (handlers such as datesSet and eventClick may be new functions on every render: the latest committed one is called), keeps the first initialView/initialDate (the calendar is uncontrolled: a key remount starts over), keeps a move or resize the callbacks accept where it was made until the app's input for that event changes (as the source library's own store; EventChangeInfo has the rules), and in development reports each unsupported option once: resource options and views as a console.error naming release 1.3 (M11), schedulerLicenseKey as one notice that everything is free, the rest as warnings. With props.controller it renders no toolbar of its own: toolbar becomes a bridge element (rendered inside the calendar's provider) that attaches the controller to the calendar's commands and toolbar model.

function useCalendarController(): CalendarController;

A controller for a toolbar outside the calendar: pass it as controller to the compat EventCalendar (useCompatProps attaches it). The calling component re-renders when the attached calendar's view, title, date or button states change (a useSyncExternalStore subscription); each change gives a new controller object, while its methods keep their identity, so memoized components (React Compiler) see the change and onClick={controller.next} stays stable. today, prev, next, gotoDate and changeView run the calendar's commands; getButtonState() gives the toolbar model's texts ("Today", hint "This week"; "Previous Week"/"Next Week"; per view "Week" with hint "Week view") and isDisabled: prev and next at the validRange edges, Today while the period shows today or when today is outside validRange (as in the source library). Before a calendar is attached (and after it unmounts), view is undefined, getDate() is an invalid Date, the buttons are disabled and the methods do nothing.

A controller for a toolbar outside the calendar (useCalendarController() from @hapio/calendar-compat/react), passed to the calendar as controller. The component that called useCalendarController re-renders when the view, the date or the button states change. Methods are bound function properties (onClick={controller.next} is safe). Before a calendar is attached, view is undefined, getDate() is an invalid Date and the methods do nothing.

Prop

Type

function fromCompatOptions<P = Dictionary>(opts: EventCalendarProps<P>, ui?: CompatUi): CompatResult<P>;

Maps compat options onto HapioCalendar props, purely: no warnings, no state, the same output for the same input (callbacks are wrapped, not called). The mapping:

  • views: availableViews + views (compatViews; a used view's options that do not map are reported as views.<key>.<option>; a generic view's length from views or the top-level duration/dayCount); initialView → defaultView (without it, the first available view, or the month when availableViews is not given either), initialDate → defaultDate (compat is uncontrolled: an app re-creates the calendar with a key to reset it);
  • zone and locale: timeZone, locale (a code; another value, such as a locale object, is reported), direction → dir, firstDay (0 → 7) → weekStartsOn, weekends: false/ hiddenDays → ISO hiddenDays (weekdays as numbers or their strings; others reported);
  • data: events (array: splitEventInputs; function: one event source), eventSources;
  • callbacks: datesSet ← onRangeChange (toDatesSetInfo), eventClick ← onEventClick (a jsEvent.preventDefault() cancels the link navigation; a middle click does not call it), loading ← onLoadingChange;
  • editing (M5; like selection it needs an engine, which the registry's <CalendarEditing> gives the compat component; compat never imports it): eventDrop or eventResize, then eventChange ← onEventChange, with event (after), oldEvent, relatedEvents (the group at its new times, from applyRelated()), view, jsEvent (the change's nativeEvent) and revert(): called before the handlers return, or before a promise one of them returns settles, it makes the result false (the calendar reverts the change and announces it); a throw or a rejection reverts too. A move or resize that is kept stays shown until the app's events echo it (settleTimeoutMs); with a session that re-maps (useCompatProps) it is held over the app's input instead (see EventChangeInfo). delta is elapsed milliseconds lane to lane in a time grid, else days (plus the time of day an all-day event takes on a lane); startDelta/endDelta are each edge's elapsed milliseconds, or days for all-day events. A move between the all-day and timed sections, or of an event given without an end, leaves event.end null;
  • selection (M4; time grids and selectable need the selection engine, which the registry's <CalendarSelection> gives the compat component, as the source library's interaction plugin did; compat itself never imports it): dateClick ← onDateClick (date/dateStr the slot's start or the day, dayEl the lane or day cell, jsEvent the click, or the Enter/Space keydown, view); select ← onSelect (jsEvent the click, null for a keyboard selection); unselect ← onSelectionChange(null) (Escape, a press outside, a navigation away from it; jsEvent null) and, in the source library's order, before the select of a selection that replaces one (jsEvent that selection's click), which needs the session useCompatProps keeps (a mapping made on every render starts with none); selectable, selectMirror → selectionPreview: 'event', unselectAuto → clearSelectionOnOutsidePointer, selectConstraint/selectOverlap/selectAllow → rules.select (a selectOverlap function gets each overlapped event's EventApi and null; background events never block a selection in Hapio, so it is not called for them);
  • rendering: eventContent → renderEvent (true or nothing renders Default), backgroundEventContent → renderBackgroundEvent (the wrapper then has no role, so the content's role="img" names it), the class options → classNames, Hapio's strings as given, with noEventsText → strings.noEvents and allDayText → strings.allDay on top;
  • display: nowIndicator, navLinks + navLinkDayClick/navLinkWeekClick (default timeGridDay/timeGridWeek when available) → navLinks: { day, week }, dayMaxEvents/ dayMaxEventRows → each day grid's maxEvents (true on either → 'auto'; a numeric dayMaxEvents N → N + 1, since Hapio's limit counts the "+N more" row; dayMaxEventRows as is) and the calendar's maxEvents, which caps a time grid's all-day row (true → 5 rows, the source library's all-day limit), weekNumberCalculation: 'ISO' (Hapio's only week numbers), eventMaxStack N → maxStack N + 1 (the "+N" column counts too), eventOrder (string keys parsed into a comparator) and eventOrderStrict, slotEventOverlap: false → overlap: 'columns', scrollTime, scrollTimeReset;
  • editing options: editable, eventStartEditable (moves), eventDurationEditable (resizes), eventResizableFromStart → editable flags, with each event's and source's editable, startEditable, durationEditable and resourceEditable over them field by field (a start resize needs the event's duration editable); eventConstraint/eventOverlap (its function gets each overlapped event and the moving event before the change)/eventAllow → rules.event, each event's or source's constraint, overlap and allow overriding them (eventOverlap: false is also each event's own overlap unless it or its source sets one, so an event that allows overlap is still not dropped onto one that does not); snapDuration; long-press (eventLongPressDelay ?? selectLongPressDelay ?? longPressDelay, one delay in Hapio); dragScroll: true and allDayMaintainDuration: false are Hapio's behaviour;
  • external drops (M6; they need the external-drop engine, which the registry's <CalendarExternalDrops> inside <CalendarEditing> or <CalendarSelection> gives the compat component; compat never imports it): droppable; dropAccept → acceptExternal, whose selector form matches the draggable element (else the drop's data when it is an element; anything else is accepted) and whose function form gets the element (else the data); droppable without dropAccept accepts every item, as the source library does (a Hapio calendar without acceptExternal accepts none); drop, then eventReceive ← onExternalDrop: date/dateStr/allDay from the drop's span, draggedEl the draggable element (else the drop's data), jsEvent the drop's nativeEvent, view; eventReceive (only for a drop whose data is an object: the source library calls it only for a drop with event data) gets as event the drop's data read as an EventInput (the source library reads its Draggable's eventData) at the drop's span, with relatedEvents [] and revert(), which rejects the drop when called before the handlers return or before a promise one of them returns settles (a throw or a rejection rejects it too). A kept drop shows a placeholder until the app adds the event to events (or settleTimeoutMs passes);
  • recurrence (M7; with recurrence, an engine such as rrule() from @hapio/calendar-recurrence, passed through: compat never imports it): the events of events and eventSources arrays with simple recurrence (daysOfWeek, startTime, endTime, startRecur, endRecur: FREQ=WEEKLY;BYDAY=… or FREQ=DAILY of wall times in the calendar zone from startRecur's day, 1 January 1970 without it, UNTIL the last day before endRecur, the times as DTSTART and the length) or the rrule plugin's fields (rrule as a string with DTSTART/RRULE/EXDATE/RDATE lines or as its object form, exdate, duration) → series, with the event's other fields (groupId kept: Hapio ignores it for occurrences) and extendedProps → data; recurrenceScope → recurrenceScope, 'all' by default (the source library moves every instance). A move or resize of an occurrence calls eventDrop/eventResize/eventChange with the occurrence after (event) and before (oldEvent), revert() reverting the whole change, relatedEvents [] and Hapio's recurrence (scope and plan). Without an engine the fields are reported in unsupported; recurring background events and recurring events from an events function stay unsupported;
  • formats: compatFormats (with the "+N more" heading default DEFAULT_POPOVER_FORMAT; meridiem: 'lowercase' → 'short');
  • EventApi: an event given without an end has end: null and endStr: '';
  • shell: height/contentHeight → style.height ('auto' → none; numbers in px), className, borderless* (with ui.borderlessClasses), headingLevel; legacyEventVars: LEGACY_EVENT_VARS always;
  • ui.timeClass/ui.titleClass → EventDisplayInfo.timeClass/titleClass.

Unsupported (reported): every option outside the mapping, such as mutation and imperative API, *DidMount/*WillUnmount, { html }/{ domNodes } content, other class hooks, the toolbar DSL (headerToolbar, footerToolbar, buttons), feed sources, slotMaxTime over 24:00, format strings, omitTrailing/weekdayJustify, multiMonth*, dayGridYear, listYear, the recurrence fields without a recurrence engine (and exrule with one), v6 aliases, unselectCancel (an outside press always clears; unselectAuto: false keeps selections), selectMinDistance and eventDragMinDistance (drags start past 5 px), eventDragStart/ eventDragStop/eventResizeStart/eventResizeStop (no such callbacks), dragScroll: false (Hapio always scrolls), dragRevertDuration (200 ms), allDayMaintainDuration: true, fixedMirrorParent, eventLeave (no dragging out of a calendar in v1); the timeline (1.2), resource (1.3) and resource time-grid (1.4) views and options. The source library's Draggable and ThirdPartyDraggable classes are not provided: make an element draggable with useExternalDraggable (@hapio/calendar-react/interaction) or the registry's CalendarDraggable, whose data is what eventReceive reads as the event (another drag-and-drop library reaches a HapioCalendar through its store's hitTest). schedulerLicenseKey and plugins are ignored without a report entry, and controller is attached by useCompatProps.

What fromCompatOptions returns: the HapioCalendar props, and the names of the given options it could not map ('eventDidMount', 'headerToolbar', 'resources', 'views.resourceTimelineDay', 'eventTimeFormat.omitTrailing', …), in option order.

Prop

Type

Classes the event-calendar registry item passes in (EVENT_TIME_CLASS, EVENT_TITLE_CLASS from calendar-event.tsx), so eventContent can style its parts like the default content.

Prop

Type

const LEGACY_EVENT_VARS: {
  readonly color: "--fc-event-color";
  readonly contrast: "--fc-event-contrast-color";
};

The extra names of the event color variables that compat asks the calendar to set (legacyEventVars), so classes written against the source library's variables, such as The reference app's open-time tint, keep working.

Events

One event. extendedProps becomes data, url becomes href (a real <a href>), display: 'background' makes it a background event, className lands on the event's root element (the anchor when url is set), color/contrastColor are any CSS color expressions passed through as --event-color/--event-contrast (and the legacy aliases). An event without id gets one derived from its position and start (a development warning asks for ids, which keep focus and pending state across updates).

Prop

Type

An entry of eventSources. JSON-feed, Google Calendar and iCalendar sources are not supported. The editing fields and rules (M5) apply to each of its events, under the event's own: field by field for the editable flags (startEditable and the others fall back to editable first), whole for constraint, overlap and allow.

Prop

Type

A read-only event facade with Date start and end. Timed events: true instants. All-day events: the instants of their days' starts in the calendar zone; allDay tells them apart. An event given without an end has end: null and endStr: '' (the calendar still draws it with the default duration). Mutation methods (setProp, remove, …) are not supported.

Prop

Type

Callback payloads

datesSet: the rendered range as true instants (start/end; Stockholm's DST week is 2026-10-19T00:00:00+02:00 → 2026-10-26T00:00:00+01:00), ISO strings with offsets, the zone and the view.

Prop

Type

The view in callbacks. currentStart/currentEnd are the instants of the navigation period's first and end day starts in the calendar zone (zone.startOfDay); activeStart/activeEnd those of the rendered range.

Prop

Type

eventClick. jsEvent is the element's native click (Enter on a link fires one too); calling jsEvent.preventDefault() cancels the link navigation. Modified clicks call eventClick too, but the browser opens the link whatever the callback does; a middle click (auxclick) does not call it, as in the source library.

Prop

Type

eventContent, backgroundEventContent and the event class callbacks. timeClass and titleClass are the registry's classes (EVENT_TIME_CLASS, EVENT_TITLE_CLASS), passed in by the event-calendar item. isNarrow is always false (no container queries); isFuture is !isPast && !isToday; color/contrastColor are the event's ('' without).

Prop

Type

dateClick (M4): a click or tap on a day cell, a time-grid lane (the slot under the pointer) or an all-day cell, or Enter (and Space where it does not start a selection) on a day cell or the slot cursor. dayEl is the day cell or lane; jsEvent is the click, or the keydown of a keyboard activation (the source library types it as a mouse event; a keydown has no pointer coordinates). Time grids need the selection engine.

Prop

Type

select (M4). jsEvent is the click or tap that made the selection; null for a keyboard selection.

Prop

Type

eventDrop (M5). delta is how far the start moved: elapsed milliseconds for a timed event moved within a time grid's lanes, else calendar days (plus the new time of day for an all-day event dropped on a lane).

Prop

Type

eventResize (M5). startDelta/endDelta are how far each edge moved (elapsed milliseconds for timed events, calendar days for all-day ones); the edge that stayed has a zero duration.

Prop

Type

What eventDrop, eventResize and eventChange share (M5; editing needs the editing engine, which the registry's <CalendarEditing> gives the compat component): the event after (event) and before (oldEvent) the change, the events of its group (groupId) at their new times (relatedEvents), and revert(), which cancels the change when called before the callback returns, or before the promise it returns settles (a rejection cancels it too). The calendar shows the change until the app's events echo it: update events (or refetch) when the change is saved, as with any Hapio calendar.

For an instance of a recurring event (M7, with a recurrence engine), event is that instance after the change and oldEvent before it (both with the recurring event's id), revert() reverts the whole change, and recurrence says what changed. Unlike the source library, relatedEvents is empty (it lists the other instances it moved): the scope says which occurrences move, and the plan has the series as they are after the change.

With useCompatProps (the registry's event-calendar), a change the callbacks keep stays where it was made, as the source library keeps it in its own store: compat holds it over the app's events (and eventSources arrays) until the app's input for that event changes its start, end or allDay (an echo of the change, or any other value: the app's data wins) or leaves the input. A reverted change is not held (the event returns to where it was before it). Changes to an instance of a recurring event, or to an event an events function returned, are not held: save them (the plan, or a refetch), or they return at the settle timeout.

There is no el: the change may come from the keyboard or a menu, not from an element drag.

Prop

Type

drop (M6): an external item was dropped on a droppable calendar, by a pointer or touch drag, or picked up and placed with the keyboard, a click or a tap (external drops need the external-drop engine: the registry's <CalendarExternalDrops>). Called before eventReceive (which is called only for a drop whose data is an object). date, dateStr and allDay are the slot or day it landed on.

Prop

Type

eventReceive (M6), after drop: the event an external drop proposes, the drop's data read as an EventInput (id, title, extendedProps, colors, classes; the source library reads its Draggable's eventData) at the slot or day it landed on. Called only for a drop whose data is an object (event data, as the source library calls it only for a draggable with event data); a drop of other data calls drop alone. The calendar does not add it: add it to events (a placeholder shows there until then, or until settleTimeoutMs). revert() rejects the drop when called before the callback returns, or before the promise it returns settles (a throw or a rejection rejects it too); relatedEvents is empty.

Prop

Type

Day cells, day headers and day lanes in class callbacks (dayCellClass, dayHeaderClass, dayLaneClass). dow is 0 (Sunday) – 6.

Prop

Type