calendar-tsx

Day grid view

Day ranges, calendar periods and resource groups with spanning event bars.

Days as columns with spanning event bars, for a day, an N-day range, whole weeks, or calendar months, quarters and years.

Loading the preview…

Live, with sample events. Click an event to see what it reports.
export const dayGridsFeatures = calendarFeatures({});
export const dayGrids = calendarViews({
  features: dayGridsFeatures,
  views: {
    day: dayGridView({ days: 1, maxEventsPerDay: 3 }),
    threeDays: dayGridView({ days: 3, maxEventsPerDay: 3 }),
    workweek: dayGridView({ weeks: 1, hiddenDays: [6, 7], maxEventsPerDay: 3 }),
    year: dayGridView({ period: 'year', maxEventsPerDay: 3 }),
    months: dayGridView({ period: 'year', groupBy: 'month', fixedWeekCount: true, maxEventsPerDay: 3 }),
    rooms: dayGridView({ days: 1, groupBy: 'resource', maxEventsPerDay: 3 }),
    monthsThenRooms: dayGridView({ period: 'quarter', groupBy: ['month', 'resource'], maxEventsPerDay: 3 }),
    roomsThenMonths: dayGridView({ months: 3, groupBy: ['resource', 'month'], maxEventsPerDay: 3 }),
  },
});

Duration

Every view factory takes exactly one of days, weeks, months or period. Counts are positive integers.

DurationRangenext() / prev()
days: NN dates from date; 7 keeps week alignment.N days.
weeks: NN whole weeks from firstDayOfWeek.N weeks, keeping the weekday.
months: NN calendar months from the first of the anchor month.N months, landing on the first day.
period'month', 'quarter' or 'year', aligned to its calendar boundary.One period, landing on its first day.

Day ranges use one row, week ranges one row per week. Month-based ranges are padded to whole weeks, and padding days have isOutside: true. A continuous year is one section of weeks.

Grouping

groupBy accepts 'month', 'resource', or an ordered tuple of those distinct keys. Month grouping requires a month-based duration.

groupByResult
omittedOne continuous grid.
'month' or ['month']One section per month.
'resource' or ['resource']Resource columns within the grid.
['month', 'resource']Month sections, each containing resource columns.
['resource', 'month']Each resource's month sections, in resource input order and then month order.

groupOrder: 'resource-first' keeps each resource's dates together in a row so bars can span them. 'date-first' groups columns by date and splits bars at resource boundaries. Neither changes the section order groupBy sets.

An event appears under every resource it names; unassigned events appear only in ungrouped grids. A background event without resource IDs shades all resources. A cell is isUnavailable when its resource has no availability that day.

Overflow

SettingDescription
maxEventsPerDayVisible bar rows per grid row. Further rows are hidden and counted; Infinity removes the cap.
dayOverflow(date, resourceId?)Marks a date/resource cell as having more events, such as events not loaded yet.
fixedWeekCountSix rows per month. Allowed with period: 'month' or month grouping. Defaults to false.

See overflow.

Hidden weekdays

hiddenDays omits ISO weekdays. Use the exported DayOfWeek constants (DayOfWeek.Monday is 1, DayOfWeek.Sunday is 7) or the numbers; the same values work for firstDayOfWeek and weekly background daysOfWeek.

import { DayOfWeek, timeGridView } from '@calendar-tsx/core';

timeGridView({ weeks: 1, hiddenDays: [DayOfWeek.Saturday, DayOfWeek.Sunday] }); // a workweek

Hidden weekdays change neither the nominal period, the inclusive fetch range nor the navigation step. Hiding all seven weekdays throws. When every date in the range is hidden, such as a one-day view on a hidden Saturday, the range shows its dates anyway.

Bars project onto the remaining columns, and events entirely on hidden dates disappear. Month rows keep their calendar-week boundaries.

Example

day-grid-view.tsx
export const DayGrid = ({ calendar, onOverflow }: { calendar: DayCalendar; onOverflow: (date: string) => void }) => {
  const layout = useCalendarSelector((state: State) => state.layout);

  if (layout.kind !== 'dayGrid') return null;

  return (
    <DayGridView model={layout}>
      {layout.sections.map(section => (
        <DayGridViewSection key={section.key} section={section}>
          {section.month || section.resource ? (
            <DayGridViewSectionHeader>
              {section.resource?.title} {section.month?.toLocaleString('en-US', { month: 'long', year: 'numeric' })}
            </DayGridViewSectionHeader>
          ) : null}

          <DayGridViewHeader>
            {section.columns.map(column => (
              <DayGridViewHeaderCell key={column.key} column={column}>
                {column.resource?.title} {column.date.toLocaleString('en-US', { weekday: 'short' })}
              </DayGridViewHeaderCell>
            ))}
          </DayGridViewHeader>
          {section.rows.map(week => (
            <DayGridViewRow key={week.key} row={week}>
              {week.days.map(day => (
                <DayGridViewDay key={day.key} day={day}>
                  <DayGridViewDayTrigger calendar={calendar} day={day} view='day'>
                    {day.date.day}
                  </DayGridViewDayTrigger>
                  <DayGridViewOverflow day={day} onClick={() => onOverflow(day.date.toString())}>
                    {day.hiddenCount} more
                  </DayGridViewOverflow>
                </DayGridViewDay>
              ))}
              {week.backgroundEvents.map(segment => (
                <DayGridViewRowBackgroundEvent key={segment.key} segment={segment} />
              ))}
              {week.segments.map(segment => (
                <DayGridViewRowEvent key={segment.key} segment={segment}>
                  {segment.event.data.title}
                </DayGridViewRowEvent>
              ))}
            </DayGridViewRow>
          ))}
        </DayGridViewSection>
      ))}
    </DayGridView>
  );
};

Model

layout is { kind: 'dayGrid', sections }. The view context also has kind: 'dayGrid'.

Each section has key, period, range, optional month and resource, header columns, columnCount and rows. A section's month is its first date. isOutside is relative to the section's period, so a date can be outside in one month and inside the next.

Each row has key, days, bar segments, backgroundEvents, columnCount and stackCount (visible bar rows). Columns hold key, date and optional resource. Day cells add:

Day fieldDescription
isOutsideThe date pads the section and lies outside its period.
isTodayThe date is today in timeZone.
isUnavailableThe resource has no availability overlapping this date.
hiddenCountHidden bar segments crossing this cell.
hasMorehiddenCount > 0, or dayOverflow returned true.
eventsEvents on this date/resource, including hidden events, in order.
Bar segment fieldDescription
eventThe event, in the calendar's time zone.
colStart, colSpanFirst visible column, zero-based, and the number of visible columns covered.
rowStack row, zero-based.
isAllDayThe event is all-day.
isMultiDayThe event covers more than one date, before clipping. A timed event ending at midnight covers only its start date.
continuesBefore, continuesAfterThe event extends beyond the visible dates at that bar's edges.

Keys are stable within their parent. Render background events before bars. Background layers decorate each cell and respect its resource scope.

Parts

PartModel propElementNotes
DayGridViewmodeldivRegion labelled "Calendar". Contains sections.
DayGridViewSectionsectiondivrole='grid'; emits --cal-columns, data-month, and data-resource-id.
DayGridViewSectionHeadernonedivConsumer-supplied month or resource label.
DayGridViewHeadernonedivrole='row'.
DayGridViewHeaderCellcolumndivrole='columnheader', data-weekday, data-resource-id.
DayGridViewRowrowdivrole='row'; emits column and stack counts.
DayGridViewDaydaydivrole='gridcell', data-today, data-outside, data-weekday, data-unavailable, data-resource-id.
DayGridViewDayTriggercalendar, day, viewbuttonNavigates through calendar to view on this date.
DayGridViewRowEventsegmentbuttonA bar across the row's visible columns; data-all-day, data-multi-day.
DayGridViewRowBackgroundEventsegmentdivAn all-day background span; aria-hidden.
DayGridViewDayBackgroundbackgrounddivA background layer's shading; aria-hidden.
DayGridViewOverflowdaybuttonRenders only when day.hasMore.

Render each section's header and rows inside DayGridViewSection, and bars inside their row beside its day cells.

The default theme lays sections out in a responsive grid: --calendar-section-min-width (default 20rem) sets the minimum section width and --calendar-section-gap (default 1rem) the gap. Override them, or style the root's grid, for another arrangement.

For a year overview, use groupBy: 'month' with fixedWeekCount: true. Calendar-only multi-month grids emit data-multi-month, and the supplied themes give them compact rows, centered headings and padded month panels. --calendar-section-gap also sets their spacing (defaults 2rem between columns, 1.5rem between rows). With section and weekday headers present, CSS subgrid aligns neighboring months' header and week tracks. The root's --cal-month-rows is the largest section row count. Resource grids and single months keep their usual sizing.