calendar-tsx

Migrating from FullCalendar

Map FullCalendar views, options, events and callbacks to calendar-tsx.

FullCalendar is a configured widget: you pass options and it renders. calendar-tsx splits that into a calendar you create from options, which computes the layout, and parts you render yourself. Most FullCalendar options map onto view factories, calendar options or features; its render hooks become the children of parts.

This guide maps FullCalendar v7 names. Read concepts first: controlled navigation and events-as-input change how the pieces fit together.

Steps

  1. Install the packages, plus @calendar-tsx/themes if you want a look out of the box. See installation.
  2. Import @calendar-tsx/themes/fullcalendar.css to keep FullCalendar's classic look while you migrate. See themes.
  3. Rewrite the view list as view factories, and keep view and date in your own state.
  4. Convert events to Temporal values, shifting all-day end dates.
  5. Render the parts for each layout, moving eventContent and friends into their children.
  6. Register features for editing, backgrounds and recurrence as you need them.

Views

FullCalendar picks views by name. Here each view is a key of your own bound to a factory:

FullCalendarcalendar-tsx
dayGridMonthdayGridView({ period: 'month', maxEventsPerDay: 4 })
dayGridWeekdayGridView({ weeks: 1, maxEventsPerDay: 4 })
timeGridWeektimeGridView({ weeks: 1 })
timeGridDaytimeGridView({ days: 1 })
listWeeklistView({ weeks: 1 })
multiMonthYeardayGridView({ period: 'year', groupBy: 'month', maxEventsPerDay: 2 })
resourceTimeGridDaytimeGridView({ days: 1, groupBy: 'resource' })
resourceTimelineWeektimelineView({ weeks: 1, groupBy: 'resource' })
duration: { days: 4 }days: 4 on any factory

See the day grid, week and day, list, timeline and resource pages.

Options

FullCalendarcalendar-tsx
initialView, initialDateview and date, which you own and update from onNavigate
datesSetonNavigate, which receives range, period and inputRange
timeZonetimeZone, an IANA name; there is no 'local'
firstDay (0 is Sunday)firstDayOfWeek, ISO (7 is Sunday); use DayOfWeek
hiddenDays (0 is Sunday)hiddenDays on the view factory, ISO weekdays
slotMinTime, slotMaxTimetimeWindow: { startMinutes, endMinutes } on time grids and timelines
slotDurationslotMinutes
dayMaxEventsmaxEventsPerDay, required on day grids
fixedWeekCountfixedWeekCount on day grids; off by default here
slotEventOverlapoverlap: 'cascade'; the default here is 'side-by-side'
eventMinHeight, eventShortHeightminEventMinutes, shortEventMinutes, in minutes instead of pixels
eventOrdereventOrder, a comparator
nowIndicatorRender TimeGridViewNowIndicator or TimelineViewNowIndicator; see now and today
localelocale for accessible labels; you format all visible text
headerToolbarYour own buttons calling api.prev(), api.next(), api.today(); see navigation
height, contentHeightYour CSS

Events

FullCalendar events take Dates or strings, a boolean allDay and an exclusive end. Here kind picks the shape, timed events take Temporal.ZonedDateTime and all-day events take Temporal.PlainDate with an inclusive end. Extra fields go in data.

import { createEvent } from '@calendar-tsx/core';
import { Temporal } from 'temporal-polyfill';

// FullCalendar input with ISO strings; timed strings carry an offset
type FullCalendarEvent = { id: string; title: string; start: string; end?: string; allDay?: boolean };

const fromFullCalendar = (event: FullCalendarEvent, timeZone: string) => {
  const data = { title: event.title };

  if (event.allDay) {
    const start = Temporal.PlainDate.from(event.start);
    // FullCalendar's all-day end is exclusive; ours is inclusive
    const end = event.end ? Temporal.PlainDate.from(event.end).subtract({ days: 1 }) : start;

    return createEvent({ id: event.id, kind: 'allDay', start, end, data });
  }

  const start = Temporal.Instant.from(event.start).toZonedDateTimeISO(timeZone);
  const end = event.end ? Temporal.Instant.from(event.end).toZonedDateTimeISO(timeZone) : start;

  return createEvent({ id: event.id, kind: 'timed', start, end, data });
};
  • resourceId and resourceIds become resourceIds.
  • display: 'background' keeps its meaning. 'inverse-background' has no equivalent; use an inverse background layer.
  • Event sources, JSON feeds and refetchEvents have no equivalent. Fetch for onNavigate's inputRange and pass the array; pass a new array to change it. See events.
  • rrule, exdate and exrule from @fullcalendar/rrule move to @calendar-tsx/rrule, with Temporal seeds. See recurrence.

Rendering and callbacks

Render hooks become part children: write the event's markup inside the event part, and the day number inside the day part. There is no built-in toolbar or more-link popover (FullCalendar's event popover): the overflow part tells you when a day has hidden events, and you open your own popover. See overflow.

Parts are native elements, so eventClick becomes an onClick on the event part, and eventDidMount becomes a ref.

Editing

editable, selectable and droppable become features you register. They report proposals; your callback applies them to your events.

FullCalendarcalendar-tsx
editable, eventDropeventMoveFeature() and onEventMove
eventResizeeventResizeFeature() and onEventResize
selectable, selectdateSelectionFeature() and onSelectionChange
Draggable, droppable, eventReceivecreateExternalDraggable, externalDropFeature() and onExternalDrop
eventLeave, eventReceive between calendarseventTransferFeature(), a calendar id and onEventTransfer
longPressDelay, eventLongPressDelay, selectLongPressDelaytouchFeature() with delay
eventOverlap, selectOverlapconstraintsFeature(), which prevents overlap unless preventOverlap: false; FullCalendar allows overlap by default
eventConstraint, selectConstraintallowedRanges and businessHours on constraintsFeature()
eventAllow, selectAllowallow on constraintsFeature()
eventStartEditable, eventDurationEditablecanMove and canResize

All interactions also need interactionFeature() and, for pointer input, pointerFeature(). See selection and event editing, editing policies and external drops and transfers.

Business hours

FullCalendar's businessHours shades those hours, and eventConstraint: 'businessHours' limits edits to them. Here: shade with an inverse backgroundFeature layer built from createWeeklyRanges, and constrain edits with businessHours on constraintsFeature(). Per-resource hours become layers scoped to resources, or each resource's availability. See background layers.