Mark matrix
<cae-mark-matrix> shows marks in a table: a row per series (its label, then a
value), a column per item, the columns under nested groups. Register it with
@contraagent/elements/matrix/define; the types are in
@contraagent/elements/matrix.
Reference: <cae-mark-matrix>
// A mark matrix: a hand-written week of nightly checks, a row per service, a column// per check under a group per night. The host sorts the rows and keeps the selection.import '@contraagent/elements/matrix/define';import type { MarkMatrixInput, MatrixRowInput } from '@contraagent/elements/matrix';import type { StateStyles } from '@contraagent/elements/primitives';
const states: StateStyles = { pass: { label: 'pass', tone: 'done', shape: 'round', rank: 0 }, fail: { label: 'fail', tone: 'failed', shape: 'square', rank: 2 }, skip: { label: 'skip', tone: 'muted', shape: 'round', rank: 1 },};
const nights = ['Mon', 'Tue'];const checks = ['lint', 'unit'];const results: Record<string, string[]> = { api: ['pass', 'pass', 'pass', 'fail'], web: ['pass', 'skip', 'pass', 'pass'], worker: ['fail', 'fail', 'pass', 'pass'] };
const rows: MatrixRowInput[] = Object.entries(results).map(([service, rs]) => ({ id: service, label: service, value: rs.filter((r) => r === 'fail').length, strong: rs.includes('fail'), cells: rs.map((state, i) => { const at = `${nights[Math.floor(i / 2)]} ${checks[i % 2]}`; return { id: `${service}|${i}`, state, label: `${service} | ${at} | ${state}`, tip: `${service}, ${at}` }; }),}));
const byFailures = (rs: readonly MatrixRowInput[]) => [...rs].sort((a, b) => Number(b.value) - Number(a.value));
const matrix = (sort: string): MarkMatrixInput => ({ caption: 'Nightly checks', label: 'Nightly checks by service', facts: ['2 nights', '3 services'], head: [ nights.map((n) => ({ id: n, label: n, span: 2, rows: 1, type: 'group' as const })), nights.flatMap((n) => checks.map((c) => ({ id: `${n}|${c}`, label: c, span: 1, rows: 1, type: 'column' as const }))), ], columns: nights.flatMap((n) => checks.map((c) => ({ id: `${n}|${c}`, groups: [n] }))), rows: sort === 'failures' ? byFailures(rows) : rows, value: { label: 'Failures' }, sorts: [ { id: 'name', label: 'Name' }, { id: 'failures', label: 'Failures' }, ], sort,});
export function mount(host: HTMLElement): void { const el = document.createElement('cae-mark-matrix'); el.matrix = matrix('name'); el.states = states; el.strings = { rowsHeader: 'Service' }; el.addEventListener('cae-sort', (e) => (el.matrix = matrix(e.detail.id))); el.addEventListener('cae-select', (e) => (el.selected = e.detail.id)); host.replaceChildren(el);}The model
Section titled “The model”A MarkMatrixInput holds the header rows (outermost first), the columns, the rows
and the line above the table. A column names the groups it is under: a border marks
where a group starts. A mark is drawn in its state’s look (states, as in the
layered graph).
export interface MatrixRowInput { /** Its id: what the matrix highlights (the series of the selected or hovered mark). */ id: string; label: string; tip?: string; /** Its label in the plain, muted face (a row that stands for no item of its own). */ muted?: boolean; /** The value column's cell, and whether it stands out. */ value?: string | number; strong?: boolean; /** One per column: its mark, or null. */ cells: readonly (MatrixMark | null)[];}
export interface MarkMatrixInput { /** The line above the table, and its tooltip. */ caption: string; captionTip?: string; /** The table's accessible name. */ label: string; /** Facts beside the caption. */ facts?: readonly (string | Fact)[]; /** The header rows, outermost first. */ head: readonly (readonly MatrixHeadCell[])[]; columns: readonly MatrixColumnInput[]; /** Already in the order chosen (`sort`): the host sorts them. */ rows: readonly MatrixRowInput[]; /** A column of one value per row, after the rows' labels, and its header. */ value?: { label: string; tip?: string }; /** The orders the reader may choose, and the one chosen. */ sorts?: readonly { id: string; label: string }[]; sort?: string;}Keyboard and events
Section titled “Keyboard and events”The marks are one tab stop. The arrow keys, Home, End, Page Up and Page Down move
between them (Ctrl with Home and End: the first and the last row). A click or the
focus sends cae-select; Enter or a double click sends cae-activate, or
cae-drill for a mark with drill. The sort control sends cae-sort: the host
sorts the rows and sets sort. Above windowAt rows (150), only the rows in view
are drawn.
The words are strings (Strings): the sort control’s label and the
header of the rows’ labels.