schedgrid/time
timeAxis maps a time range onto fractional indices, plus ticks.
import { timeAxis } from 'schedgrid/time';The grid never sees a Date. timeAxis maps a continuous time range onto
axis indices so that index i covers [start + i·slot, start + (i+1)·slot).
Everything is plain millisecond arithmetic, so a day that contains a DST
change simply has fewer or more slots than 24 hours would.
timeAxis(options)
Prop
Type
const day = timeAxis({
start: new Date(2026, 8, 7),
end: new Date(2026, 8, 8),
slot: 15 * 60_000,
size: 24,
});
day.count; // 96
day.axis; // { count: 96, size: 24 } — pass to createGrid or setColumns
day.toIndex(new Date(2026, 8, 7, 9, 37)); // 38.47
day.toRange(a, b); // [toIndex(a), toIndex(b)], ready for an Item
day.fromIndex(42); // Date at 10:30The axis
Prop
Type
toIndex is not clamped: a moment before start maps to a negative index.
That is deliberate, so an item that starts before the visible range still
lands in the right place once you offset it.
timeAxis throws a RangeError if slot is not greater than zero — a
zero-length slot has no meaningful index — and ticks throws the same for
every. Nothing else in the library throws.
Ticks
ticks(every, { align }) returns the moments every every milliseconds,
counted from align (default start), that fall inside the range. Each
tick carries its fractional index, so drawing a gridline is
columns.offsetAt(tick.index).
Prop
Type
Steps are fixed lengths, so use ticks for hours and minutes. Calendar
steps (days across DST, months) are not fixed lengths. Compute those
Dates yourself and map them with toIndex.
Offsetting for a label column
The grid's column 0 is often a pinned label column, which means time slot
i lives in grid column i + 1. Keep that offset in one place:
const columns = {
count: day.count + 1,
size: (i: number) => (i === 0 ? 160 : 24),
pinned: 1,
};
const [c0, c1] = day.toRange(start, end);
const item = { id, rows: [row, row + 1], columns: [c0 + 1, c1 + 1] };