Views

Time Grid

The week and day views draw the same grid: an hour gutter, one column per day and a scale that maps the clock to pixels. Nine props shape it.

Configure the Grid

Change a control and the grid follows. The props below are the ones the grid reads; everything else about the week view comes from the events.

Oct 19 – 25, 2026

GMT+7
Mon 19
Tue 20
Wed 21
Thu 22
Fri 23
Sat 24
Sun 25
All day
Lunch break
<Scheduler
  bind:events
  view="week"
  slotMinutes={30}        <!-- minutes per grid line: the snap, the key step, the shortest event -->
  slotHeight={24}         <!-- pixels per slot; 24 x 48 slots is a 1152px day -->
  dayStartHour={0}        <!-- first hour drawn -->
  dayEndHour={24}         <!-- hour the grid ends at -->
  weekStartsOn={1}        <!-- 0 is Sunday -->
  hiddenDays={[]}         <!-- [0, 6] for a working week -->
  weekNumbers={false}     <!-- ISO 8601 week numbers -->
/>

Slots

slotMinutes is the unit of the grid, and it reaches further than the lines. A drag snaps to it, the up and down keys move by it, an event created or resized by hand is never shorter than it, and a drop that has no length of its own takes two of them. slotHeight is pixels per slot. Fifteen minute slots at the default height make a day twice as tall as thirty minute ones; shrink the height to keep the day the same size.

// slotMinutes is the unit of the grid. It sets:
//   the lines drawn between hours              (60 / slotMinutes per hour)
//   where a drag snaps                         (nearest slot boundary)
//   the step of the up and down keys           (one slot)
//   the shortest event a drag or resize makes  (one slot)
//   the length of a drop from a dragSource with no durationMinutes,
//     and of an all-day event dropped onto the grid       (two slots)

// slotHeight is pixels per slot. Together they fix the scale of the day:
//   dayHeight = (dayEndHour - dayStartHour) * 60 / slotMinutes * slotHeight

Visible Hours

dayStartHour and dayEndHour trim the day to the hours that matter. An event crossing an edge is cut there; an event that lies entirely outside is not drawn at all, though it stays in the array and still counts elsewhere. Both must be whole hours inside one day with start before end, or the grid warns and shows all twenty four.

<!-- Draw 7:00 to 20:00. Events that start earlier are cut at the top; events
     entirely outside stay in the array and are simply not drawn. -->
<Scheduler bind:events dayStartHour={7} dayEndHour={20} />

<!-- Whole hours inside one day, start before end. Anything else prints a warning
     and falls back to 0 and 24:
     [@sv5ui/scheduler] Visible hours 8 to 8 are not a range inside one day; showing the whole day. -->

Hidden Days

hiddenDays drops week days from the week and month views, typically the weekend. The day view, the year and the agenda keep every day, and the toolbar title names the days actually shown. An event on a hidden day is not lost: it is still in the array and comes back in the other views.

<!-- Leave the weekend out of the week and month views. The day view, the year and
     the agenda keep every day. The toolbar title follows what is shown: Sep 14 – 18. -->
<Scheduler bind:events hiddenDays={[0, 6]} />

<!-- Hiding all seven days shows all of them again, with a warning -->

Overlapping Events

Events that overlap within a day share its width. A cluster of overlapping events is split into as many columns as it needs, and each event takes one: the earlier start goes left, and of two starting together the longer one goes left. Two identical events become two halves. A background event is never part of a cluster; it is drawn full width behind the others.

October 20, 2026

Tuesday

October 20, 2026

Today GMT+7
Focus block
// Events that overlap in one day share the column. A cluster of overlapping
// events is split into as many columns as it needs; each event takes one.
// Earlier start goes left; on the same start, the longer event goes left.
const events = [
  { id: 'a', title: 'Design review',  start: '2026-09-15T13:00', end: '2026-09-15T14:30' },
  { id: 'b', title: 'Pairing',        start: '2026-09-15T14:00', end: '2026-09-15T15:30' }, // overlaps a
  { id: 'c', title: 'Vendor call',    start: '2026-09-15T14:00', end: '2026-09-15T14:30' }, // overlaps a and b
  { id: 'd', title: 'Standup',        start: '2026-09-15T09:00', end: '2026-09-15T09:15' }, // alone: full width
  { id: 'e', title: 'Retro',          start: '2026-09-15T16:00', end: '2026-09-15T17:00' },
  { id: 'f', title: 'Retro (dup)',    start: '2026-09-15T16:00', end: '2026-09-15T17:00' }  // identical: two halves
];

// Background events never take a column: they are drawn full width behind the rest.

The All-Day Row

Under the headers, a row for events that own a whole day: those marked allDay and any timed event of twenty four hours or more. The row appears only while the visible range has at least one of them. That has one consequence for drag and drop: with no all-day event in the week there is no row to drop a timed event onto, so it cannot be turned all-day there. The month view always can.

With an all-day event in the week

Oct 19 – 25, 2026

GMT+7
Mon 19
Tue 20
Wed 21
Thu 22
Fri 23
Sat 24
Sun 25
All day
Lunch break

Without one: no row

Oct 19 – 25, 2026

GMT+7
Mon 19
Tue 20
Wed 21
Thu 22
Fri 23
Sat 24
Sun 25
Lunch break
// Goes to the all-day row: an allDay event, or any event that lasts 24 hours or more
{ id: 'offsite', title: 'Team offsite', start: '2026-09-17', end: '2026-09-19', allDay: true }
{ id: 'oncall',  title: 'On call', start: '2026-09-14T09:00', end: '2026-09-15T09:00' }

// The row exists only while the range holds at least one such event. With none,
// there is nowhere to drop a timed event to make it all-day; use the month view.

Current Time

A red line with a dot marks the current time in today's column, with the time itself in the gutter beside it. It moves once a minute and disappears outside the visible hours. Today's column is tinted too, in every view.

Where the Grid Scrolls To

Each time the range changes, the grid scrolls to what is most likely wanted, leaving half an hour of room above it. The day view aims at the first event of the day, the week view at the current time; both fall back to the other, then to the opening hour of businessHours, then to 7:00.

// Where the grid scrolls to each time the range changes, half an hour above the target:
//   day view:   the first event that starts today, else the current time, else the opening hour
//   week view:  the current time (when today is in the range), else the first event, else the opening hour
// The opening hour is businessHours.start, or 7:00 without business hours.

Week Numbers and the Zone Label

weekNumbers adds ISO 8601 week numbers: in the corner above the gutter here, beside the day title in the day view, at the start of each month row and down the side of the year grid. The same corner always names the time zone the grid is drawn in, so a reader in another zone knows what 9:00 means. See Time Zones.

Oct 19 – 25, 2026

Week 43 GMT+1
Mon 19
Tue 20
Wed 21
Thu 22
Fri 23
Sat 24
Sun 25
All day
Lunch break
<!-- ISO 8601: week 1 holds the year's first Thursday. Shown beside the header in
     the week and day views, at the start of each month row, and down the year grid. -->
<Scheduler bind:events weekNumbers />

<!-- The corner above the hour gutter names the zone the way Intl does for the
     locale: GMT+7 for Ho Chi Minh City, GMT+1 for London in summer, EDT for New York -->
<Scheduler bind:events timeZone="Europe/London" />

Short Events

A chip is never shorter than half a slot, and it drops detail as it gets shorter so the title stays readable. A fifteen minute standup at the default scale is a twelve pixel chip with its title in small type.

October 21, 2026

Wednesday

October 21, 2026

GMT+7
// slotMinutes is the unit of the grid. It sets:
//   the lines drawn between hours              (60 / slotMinutes per hour)
//   where a drag snaps                         (nearest slot boundary)
//   the step of the up and down keys           (one slot)
//   the shortest event a drag or resize makes  (one slot)
//   the length of a drop from a dragSource with no durationMinutes,
//     and of an all-day event dropped onto the grid       (two slots)

// slotHeight is pixels per slot. Together they fix the scale of the day:
//   dayHeight = (dayEndHour - dayStartHour) * 60 / slotMinutes * slotHeight

What a chip shows by height

Chip heightShows
Under 20pxOne line of small text, centred; the title only
20px to 38pxThe title in small type, without the time
38px and upThe title and the time range

Props

The props the time grid reads.

PropTypeDefault
dayStartHournumber0
dayEndHournumber24
slotMinutesnumber30
slotHeightnumber24
hiddenDaysWeekDay[][]
weekNumbersbooleanfalse
daysnumber-
timeZoneTimeZoneIdlocal zone