Skip to content

Lanes

<cae-lanes> shows bars on a time axis, a lane per group: each bar as long as the thing it stands for took. Register it with @contraagent/elements/lanes/define; the types and the placement functions are in @contraagent/elements/lanes.

docs/examples/lanes.ts
// Lanes: hand-written jobs on a time axis, a lane per stage, one job running.
// The host names the looks of the bars; their labels are the legend.
import '@contraagent/elements/lanes/define';
import type { Lane, LaneStyle } from '@contraagent/elements/lanes';
const barStyles: Record<string, LaneStyle> = {
passed: { label: 'passed', color: 'var(--ca-status-done)' },
failed: { label: 'failed', color: 'var(--ca-status-failed)', pattern: 'hatched' },
running: { label: 'running', color: 'var(--ca-status-running)', pattern: 'striped' },
};
export function mount(host: HTMLElement): void {
const now = Date.now();
const min = 60e3;
const lanes: Lane[] = [
{
id: 'build',
label: 'Build',
bars: [
{ id: 'b1', start: now - 9 * min, end: now - 6 * min, style: 'failed', label: 'Build #1\nfailed' },
{ id: 'b2', start: now - 6 * min, end: now - 3 * min, style: 'passed', label: 'Build #2\npassed' },
],
},
{
id: 'test',
label: 'Test',
bars: [
{ id: 't1', start: now - 3 * min, end: null, style: 'running', label: 'Unit tests\nrunning' },
{ id: 't2', start: now - 2.5 * min, end: now - 1 * min, style: 'passed', label: 'Lint\npassed' },
],
},
];
const el = document.createElement('cae-lanes');
el.lanes = lanes;
el.barStyles = barStyles;
el.live = true;
el.omitted = 3;
el.strings = { omitted: (n) => `${n} older jobs not shown` };
host.replaceChildren(el);
}

A lane is a Lane, a bar a LaneBar: a start and an end in epoch milliseconds, the key of its look and its tooltip. A bar with no end is running: it grows until now, and a line marks now. Set live while a bar runs, so the element draws again every second.

src/lanes/types.ts
/** Something that took time: from `start` to `end` (epoch ms); `end` null while it runs. */
export interface LaneBar {
id: string;
start: number;
/** Null: running, the bar grows to now. */
end: number | null;
/** The key of its look in the element's `barStyles`. */
style: string;
/** The bar's tooltip: one item per line. */
label: string;
}

The overlapping bars of a lane are stacked, each on the first row whose last bar has ended (stack), so no bar hides another. The bars grow in when they are first drawn.

barStyles maps each key to a LaneStyle: a label, a colour, and a pattern (solid, hatched, striped, which moves on a bar, or outline). Give a status a pattern besides its colour, so it does not depend on colour alone. The labels are the legend, a row per group, in key order; omitted adds a note of the bars the host left out (strings.omitted).

By default the axis fits the bars (fitRange): from the first start to the last end; with a running bar, to now plus 20 seconds, rounded up to the minute. Set range to fix it. The axis shows five times, with seconds when it spans less than 10 minutes, in locale (by default the page’s lang).

--cae-lanes-label-width sets the width of the lanes’ labels (112px, 78px when the element is narrower than 760 px). The parts are axis, lane, lane-label, track, bar, now, legend and empty; with no bars the element shows strings.empty, or the empty slot.