Week and day view
A time grid with an all-day row and overlap lanes.
Days as columns on a time axis, with timed events placed by time and overlapping events side by side or cascaded. All-day events sit in a row above.
Loading the preview…
import { timeGridView } from '@calendar-tsx/core';
timeGridView({ days: 7 }); // a week
timeGridView({ days: 1 }); // a day
timeGridView({ weeks: 1, hiddenDays: [6, 7] }); // a workweekSettings
Every view factory takes exactly one of days, weeks, months or period. Counts are positive integers.
| Duration | Range | next() / prev() |
|---|---|---|
days: N | N dates from date; 7 keeps week alignment. | N days. |
weeks: N | N whole weeks from firstDayOfWeek. | N weeks, keeping the weekday. |
months: N | N 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. |
| Setting | Type | Default | Description |
|---|---|---|---|
allDay | boolean | true | false drops the all-day row. |
slotMinutes | number | 30 | Positive integer slot length; the final slot may be shorter. |
minColumnWidth | string | none | Passed through for horizontal scrolling, for example '8rem'. |
groupBy | 'resource' or ['resource'] | none | One column per resource and day. See resource view. |
groupOrder | 'resource-first' | 'date-first' | 'resource-first' | Column order when grouped. |
overlap | 'side-by-side' | 'cascade' | 'side-by-side' | How overlapping events share a column. See overlap. |
minEventMinutes | number | half of slotMinutes | Shorter events are laid out this long. 0 turns it off. |
shortEventMinutes | number | 30 | Events laid out shorter than this are flagged isShort. |
hiddenDays | ISO weekdays | none | See hidden weekdays. |
timeWindow | { startMinutes, endMinutes } | [0, 1440) | See visible hours. |
Overlap
Each overlapping event takes the lowest lane free at its start, then widens to the right until the next lane holding
an event it overlaps, or to the column's edge. 'side-by-side' keeps events beside each other. 'cascade' doubles
each event's width, up to the column's edge, so a later lane covers at most half of it; later lanes paint on top.
Brief events
The core lays out an event shorter than minEventMinutes (half a slot by default) as that long, and gives it its own
lane if it would then cover the next event. The event keeps its real end, so label it from segment.event. Both
settings are minutes, measured on the displayed clock: half a slot is 24px with the default 3rem slots and 16px with
compact.css, before the gap between events.
An event laid out shorter than shortEventMinutes gets isShort and data-short. The default theme puts its content
on one line, and hides the content of an event too short for one line.
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 workweekHidden 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.
Visible hours
timeWindow limits the clock hours shown, in integer clock minutes: 0 <= startMinutes < 1440 and
startMinutes < endMinutes <= startMinutes + 1440. The default is [0, 1440).
timeGridView({ days: 7, timeWindow: { startMinutes: 480, endMinutes: 1080 } }); // 08:00 to 18:00
timelineView({ days: 1, timeWindow: { startMinutes: 1320, endMinutes: 1800 } }); // 22:00 to 06:00An overnight window belongs to its starting date, so hiding Tuesday still shows Monday's Tuesday-morning portion.
All-day items keep their calendar dates. slotMinutes need not divide the window; the final slot keeps its actual
size, and each slot's dayOffset labels after-midnight times. Fetch events for navigation's inputRange, which adds
the following date when an overnight window needs it.
Example
export const Week = ({ calendar }: { calendar: DayCalendar }) => {
const grid = useCalendarSelector((state: State) => state.layout);
if (grid.kind !== 'timeGrid') return null;
return (
<TimeGridView model={grid}>
<TimeGridViewHeader>
{grid.columns.map(column => (
<TimeGridViewColumnHeader key={column.key} column={column}>
<TimeGridViewColumnTrigger calendar={calendar} column={column} view='day'>
{column.date.toLocaleString('en-US', { weekday: 'short', day: 'numeric' })}
</TimeGridViewColumnTrigger>
</TimeGridViewColumnHeader>
))}
</TimeGridViewHeader>
{grid.allDay && (
<TimeGridViewAllDay>
{/* before the bars: grid items paint in DOM order */}
{grid.allDay.backgroundEvents.map(segment => (
<TimeGridViewAllDayBackgroundEvent key={segment.key} segment={segment} />
))}
{grid.allDay.segments.map(segment => (
<TimeGridViewAllDayEvent key={segment.key} segment={segment}>
{segment.event.data.title}
</TimeGridViewAllDayEvent>
))}
</TimeGridViewAllDay>
)}
<TimeGridViewBody model={grid}>
<TimeGridViewAxis>
{grid.slots.map(slot => (
<TimeGridViewAxisLabel key={slot.key} slot={slot}>
{slot.time.minute === 0 ? slot.time.toLocaleString('en-US', { hour: 'numeric' }) : null}
</TimeGridViewAxisLabel>
))}
</TimeGridViewAxis>
{grid.columns.map(column => (
<TimeGridViewColumn key={column.key} column={column}>
{column.backgroundEvents.map(segment => (
<TimeGridViewBackgroundEvent key={segment.key} segment={segment} />
))}
{column.segments.map(segment => (
<TimeGridViewEvent key={segment.key} segment={segment}>
{segment.event.data.title}
</TimeGridViewEvent>
))}
<TimeGridViewNowIndicator column={column} />
</TimeGridViewColumn>
))}
</TimeGridViewBody>
</TimeGridView>
);
};Model
layout is { kind: 'timeGrid', coordinates, slotUnits, slotMinutes, hourOffset, slots, columns, allDay, minColumnWidth }.
slotsholds{ key, time, dayOffset, start, span }for axis labels, as fractions of the visible window.slotMinutesis each slot's length, andhourOffsetthe slot units from the window's start to its first hour (0on the hour). Themes draw hour lines from them.columnsholds one column per day.allDayholds barsegments,backgroundEventsandrows, with columns indexed likecolumns. Its segments are the day grid's bar segments. It is absent when the view setsallDay: false.
Positions inside a column are fractions of its visible clock window.
Time grids and timelines use a fixed clock axis in timeZone: 09:00 sits at the 09:00 position on daylight-saving
days too, and missing hours stay on the axis. The two occurrences of a repeated hour share clock positions;
overlapping events then take separate lanes or stack rows. A bar that crosses a transition encloses every clock
position its real interval visits, and segment.event keeps the real timestamps and offsets. Each timed segment's
fragments keeps its exact coverage (offsets, occurrences and the reason each fragment stops) apart from that
enclosing rectangle.
The core builds one coordinate plan (layout.coordinates) per stable view definition and reuses it across event,
resource and clock updates. Navigation, timeZone, firstDayOfWeek, hiddenDays or timeWindow changes rebuild it.
| Column field | Description |
|---|---|
date | The day. |
isToday | The day is today in timeZone. |
segments | Timed events in this column. |
lanes | The most lanes any overlap cluster in the column uses. |
backgroundEvents | See background events. |
now | Fraction of the window containing now, including overnight columns. |
resource, isUnavailable, unavailable | See resource view. |
| Timed segment field | Description |
|---|---|
event | The event, in the calendar's time zone. |
start, span | Fractions of the visible window. |
fragments | Exact coverage before clock enclosure and minimum sizing. |
lane, lanes | Its lane, and the lane count of its overlap cluster. |
inlineStart, inlineSize | Fractions of the column's width, after widening and any cascade. |
isShort | Laid out shorter than shortEventMinutes. |
continuesBefore, continuesAfter | The event is clipped at a window boundary. |
Events that overlap, directly or through others, form a cluster that shares one lane count. Events that only touch do
not overlap. Place events with inlineStart and inlineSize; lane and lanes remain for CSS written against them.
Parts
| Part | Model prop | Element | Notes |
|---|---|---|---|
TimeGridView | model | div | role='region', labelled "Time grid". Scrolls. |
TimeGridViewHeader | none | div | Sticky column headers. |
TimeGridViewColumnHeader | column | div | data-today, data-weekday, data-unavailable, data-resource-id. |
TimeGridViewColumnTrigger | calendar, column, view | button | Navigates through calendar to view on the column's date. |
TimeGridViewAllDay | none | div | Render it only when allDay is set. |
TimeGridViewAllDayEvent | segment | button | A bar segment; data-all-day, data-multi-day. |
TimeGridViewAllDayBackgroundEvent | segment | div | aria-hidden. Render it before the bars. |
TimeGridViewBody | model | div | Holds the axis and the columns. Emits --cal-slot-units for the grid height. |
TimeGridViewAxis | none | div | Optional. |
TimeGridViewAxisLabel | slot | div | slot is the time slot, not the HTML attribute. Emits --cal-start, --cal-span. |
TimeGridViewColumn | column | div | data-today, data-weekday, data-unavailable. |
TimeGridViewBackgroundEvent | segment | div | aria-hidden. Render it before the events. |
TimeGridViewBackground | background | div | aria-hidden. A background layer's shading. |
TimeGridViewEvent | segment | button | Placed by time and lane; data-overlap when it shares lanes, data-short when isShort. |
TimeGridViewNowIndicator | column | div | aria-hidden. Renders only in today's column. |
Give the root a height, such as max-block-size: 40rem, to scroll inside the page instead of showing all 24 hours.