Moltenship Time
API reference

React components and hooks

createCalendar, Root and its props, the components, hooks, meta, and diagnostics.

Everything on this page is exported from @moltenship/calendar.

createCalendar

const Calendar = createCalendar<TEvent, TResource, TSelection, TFocus>()(options);

Creates a calendar: a set of components and hooks typed to your records. Call it once at module scope. It's called twice: the first call takes your types, and the second infers the view names from views and the ID types from the adapter.

  • TEvent: your event record. Required.
  • TResource: your resource record. Defaults to unknown; use never if you don't have resources.
  • TSelection, TFocus: the types of the selection and focus state. Default to CalendarSelection and CalendarFocus, the days, slots, and events the built-in views read and write. A calendar with only custom views can use its own types.

Prop

Type

The returned object holds the components and hooks below, plus views, the list you passed in. Use it to derive the view-name type: (typeof Calendar.views)[number]["name"].

defineCalendarView

const view = defineCalendarView({ name, defaultOptions, strategy, Renderer });

Defines a view. defaultOptions is required only when the strategy's options can't be undefined. Custom views covers the strategy, the projection input, and the Renderer props. The related types are CalendarViewStrategy, CalendarViewProjectionInput, and CalendarViewRendererProps.

resolveViewInstantRange(range, timeZone) turns visible dates into the instant range that Root passes to project as instantRange, and resolveViewLoadRange(strategy, anchor, environment, options, range) gives the loadRange it passes. Use them to run a strategy outside React, for example in a test.

Root

<Calendar.Root> holds the state and provides it to everything inside. It renders no element of its own.

Prop

Type

You can write most props inline. locale, weekRules, now, and controlled values are compared by value; events and resources are compared item by item; viewOptions, onDiagnosticsChange, and the change handlers are read from the latest render. Keep a clock object stable, for example at module scope.

Root throws on its first render if it has no date (calendar-root-requires-anchor-date), no time zone (calendar-root-requires-time-zone), or no view (calendar-root-requires-view).

Components

Use these inside Root. Each renders one plain element with a data-calendar-part attribute and accepts that element's usual props, including className, style, ref, and event handlers.

ComponentRendersNotes
Frame<section>Outer wrapper. Gets the {id}-frame ID and is labelled by the title.
Toolbar<header>Wrapper for controls.
Title<span>The active view's title, or its children. Gets the {id}-title ID.
PreviousTrigger<button>Moves back one step of the active view. Disabled when no view is active.
NextTrigger<button>Moves forward one step of the active view.
TodayTrigger<button>Moves to today in the calendar's time zone, from now, clock, or the system clock.
ViewSwitcher<select>One option per view. Requires getViewLabel(name).
Viewport<div>Wrapper for the view. Gets the {id}-viewport ID.
ActiveViewview outputRenders the active view, or fallback if it isn't registered.
Eventyour outputLooks up one event by eventId and calls children with it.
Announcer<div>A hidden live region that speaks moves, resizes, and creates. Requires messages.
  • The buttons default to type="button" and set aria-controls to the viewport. Give each one text or an aria-label.
  • Your onClick or onChange runs first. Call event.preventDefault() in it to skip the built-in action.
  • Frame, Title, and Viewport don't accept id. Set id on Root instead, so the IDs and aria-controls always match.

Event passes your record and its normalized form:

<Calendar.Event eventId="standup" fallback={<p>Not found</p>}>
  {({ event, normalized }) => (
    <p>
      {event.title} starts at {normalized.start.toString()}
    </p>
  )}
</Calendar.Event>

Hooks

The hooks on your calendar work anywhere inside its Root. Using them, or any component above, outside Root throws calendar-hook-outside-root.

HookReturns
useCalendar(){ state, actions, meta }
useCalendarState()The six state values
useCalendarActions()The actions below. The object never changes.
useCalendarMeta()The meta object
useCalendarSelector(selector, opts?)selector(state). Re-renders only when it changes.
useVisibleRange()The active view's visible dates, or null
useViewProjection()The active view and its projection, or null

useCalendarSelector compares results with Object.is. Pass { isEqual } to compare another way. The selector can be an inline function.

Announcer's messages, and the keyboard hooks for your own views (useCalendarKeyboard, useRovingFocus, useCalendarInteraction, useCalendarAnnouncer), are covered in Accessibility.

useNow({ intervalMs }) is exported on its own, not from your calendar. It returns the current instant from the nearest Root's now or clock, or the system clock, and samples it every intervalMs (one minute by default). It returns null during server rendering and hydration unless Root has now.

useAnchorDateRequests(listener) calls listener(date, range) with every date the calendar is asked to show: from Previous, Next, Today, keys paging past the view, or setAnchorDate. That includes a date that is already the anchor. range holds the dates the active view shows for date, so a week view can tell Today inside its week from a date that moves it. A view whose scroll position can run ahead of the anchor uses it to show its range again when Today asks for it. The scrolling views do this.

Actions

ActionEffect
setView(name)Changes the view
setAnchorDate(date)Changes the date
setTimeZone(zone)Changes the time zone
setSelection(value)Changes the selection (null clears it)
setFocus(value)Changes the focus (null clears it)
setExpandedResourceIds(ids)Changes the expanded resources
navigatePrevious(), navigateNext()Moves by one step of the active view
navigateToday()Moves to today in the calendar's time zone
proposeEventChange(proposal)Proposes a change to an event and resolves with the outcome. See Interactions.

Setting a value equal to the current one does nothing. Calling two actions in a row builds on the first, so navigateNext(); navigateNext() moves two steps, even for controlled values.

Meta

Prop

Type

Diagnostics

Problems that don't stop rendering are collected in meta.diagnostics and passed to onDiagnosticsChange. Time doesn't log them.

codeseverityCause
unregistered-active-viewerrorThe view state names a view that isn't registered.
invalid-eventerrorA record was skipped. issue has the details (issue codes).
Edit on GitHub

Last updated on

On this page