Hapio Calendar

Views

Choose and configure the views with defineViews, from N-day time grids to agenda lists, and format their dates.

A view is a name and a definition: one of three kinds with its options. The calendar shows the views of views in its switcher, in order, and opens on defaultView (or the first one). Without views it has defaultViews:

NameDefinitionShows
weektimeGrid({ days: 7 })seven day columns over a time axis
daytimeGrid({ days: 1 })one day
monthdayGrid({ unit: 'month' })the month in week rows of day cells
listlist({ unit: 'week' })the week's events, grouped by day

Your own views

defineViews keeps the names you give, so every option that takes a view name is checked against them:

components/team-calendar.tsx
'use client'

import {
  dayGrid,
  defineViews,
  list,
  timeGrid,
  type CalendarEventInput,
  type ViewName,
} from '@hapio/calendar-react'

import { HapioCalendar } from '@/components/hapio-calendar/hapio-calendar'

// The switcher shows them in this order; the first is the default view.
const views = defineViews({
  workWeek: timeGrid({ days: 5, align: 'week', slotMin: '07:00', slotMax: '19:00' }),
  threeDays: timeGrid({ days: 3, step: 1 }), // "3 days", moving a day at a time
  month: dayGrid({ unit: 'month', weekNumbers: true, maxEvents: 3 }),
  agenda: list({ unit: 'month', label: 'Agenda' }),
})

// 'workWeek' | 'threeDays' | 'month' | 'agenda'
type View = ViewName<typeof views>

export function TeamCalendar({
  events,
  initialView,
}: {
  events: readonly CalendarEventInput[]
  initialView: View
}) {
  return (
    <HapioCalendar
      views={views}
      defaultView={initialView} // a name `views` lacks is a type error
      hiddenDays={[7]} // no Sundays, in every view
      events={events}
      timeZone="Europe/Stockholm"
      navLinks={{ day: 'threeDays', week: 'workWeek' }} // where day and week numbers link
      className="h-[36rem]"
    />
  )
}

The switcher labels its views by unit ("Week", "Day", "Month", "List", "3 days") unless a view has a label, and screen readers hear each radio as "Week view", "Agenda view" and so on. View names are yours and appear in callbacks (onViewChange, RangeChange.view) and in the root's data-view.

Define views in a client module (or at module scope of a page): a view map whose formats are functions cannot cross from a Server Component to a client component. Only plain data and view names can.

Time grids: timeGrid()

Day columns over a time axis, with an all-day row on top.

  • days (1 to 31, default 7) and align: 'week' starts on weekStartsOn (the default for 7 days), 'day' starts on the date (the default otherwise). step is how many days Previous and Next move (default days; whole weeks for a week-aligned grid).
  • slotDuration ('PT30M'), snapDuration (drags and resizes snap to it; default the slot), slotLabelInterval (default an hour), slotMin and slotMax ('00:00' to '24:00'; times past midnight are not supported in 1.0), scrollTime (where the grid starts scrolled, default '06:00').
  • allDaySlot: false leaves out the all-day row.
  • overlap: 'stack' (default) lets later events overlap earlier ones; 'columns' gives overlapping events side by side columns. maxStack caps the columns and adds a "+N" that opens the hidden events.
  • The lanes are a wall-clock axis: on the days daylight saving time changes, the hour that does not exist and the hour that happens twice are marked bands. See Time zones.

Day grids: dayGrid()

Week rows of day cells: a month (unit: 'month', the default), a week or a day.

  • maxEvents: how many rows of events a cell shows before "+N more" opens the rest in a popover. 'auto' (the month's default) fits as many as the cell's height allows; a number fixes it, the "+N more" row counting as one; false shows every event and lets the rows grow.
  • fixedWeekCount (default true) always shows six weeks; showOtherMonthDays: false leaves the adjacent months' days empty.
  • weekNumbers adds ISO week numbers; with navLinks they link to a week view.
  • A timed event of one day shows as a dot with its start time; all-day and multi-day events as bars across the cells.

Lists: list()

Events grouped by day, as a list of links and buttons: unit 'day', 'week' (default) or 'month', and showEmptyDays to list days without events too. An event that spans days shows on each day ("From 22:00", "All day", "Until 11:00"). With no events the list shows strings.noEvents ("No events to display"), or what renderEmpty renders.

Options for every view

Most time-grid options can also be given to the calendar, for every time grid at once (slotDuration, snapDuration, slotMin, slotMax, scrollTime, maxStack); a view's own option wins. The same holds for hiddenDays (ISO weekdays, Monday is 1) and formats. maxEvents set on the calendar applies to the day grids and caps the time grids' all-day rows too.

navLinks turns day headers (and week numbers) into links to a day or week view: true picks the first one-day and seven-day time grids of views, { day, week } names them.

Formats

Every date the calendar shows comes from a format, Intl.DateTimeFormat options in the calendar's locale and timeZone:

lib/calendar-formats.ts
'use client'

import type { CalendarFormats } from '@hapio/calendar-react'

const H24 = { hour: '2-digit', minute: '2-digit', hour12: false } as const

export const formats: Partial<CalendarFormats> = {
  // `Intl.DateTimeFormat` options, plus Hapio's flags.
  dayHeader: { weekday: 'short', day: 'numeric', omitCommas: true }, // "Thu 22"
  eventTime: H24, // "09:00 – 10:30"
  slotLabel: H24,
  moreTitle: { weekday: 'long', day: 'numeric', month: 'long' },
  // A function receives a Temporal value (it needs the global Temporal) and the context.
  listDay: (value, { locale }) => value.toLocaleString(locale, { weekday: 'long' }),
}
FormatWhere
titlethe toolbar title (formatted as a range)
dayHeadercolumn headers
slotLabelthe time axis
eventTimeevent times
listDaya list day's heading
listDayAlta list day's second heading
moreTitlethe "+N more" popover's heading
weekNumberweek numbers

Four flags post-process Intl's output: omitCommas ("Thu, 29 Oct" → "Thu 29 Oct"), forceCommas, omitZeroMinute ("9:00 AM" → "9 AM") and meridiem ('short', 'narrow' or false). A function format receives a Temporal.ZonedDateTime (timed) or Temporal.PlainDate (all-day) and { locale, timeZone }.

Kinds from later versions

renderers maps view kinds to the components that render them. The block renders the three built-in kinds; a kind your copied files have no renderer for (a view kind added in a later version, before you update the files) shows UnsupportedView, a short notice (strings.unsupportedView(label): "The “Timeline” view can’t be shown here."), instead of breaking. See Upgrading.