Data table
<cae-data-table> shows rows of data in a real <table>: a header cell per
column, and the first rowHeader column as the header of its row, so a screen
reader names each cell by its column and its row. Register it with
@contraagent/elements/data-table/define; the types and sortRows are in
@contraagent/elements/data-table.
Reference: <cae-data-table>
// A data table: hand-written rows, one sortable column, a row's action. The// host holds the rows and the order, and answers the events.import '@contraagent/elements/data-table/define';import { sortRows, type CaeDataTable, type DataColumn } from '@contraagent/elements/data-table';import { pill } from '@contraagent/elements';
interface Job { id: string; name: string; minutes: number; state: 'done' | 'failed' | 'running';}
const jobs: Job[] = [ { id: 'j1', name: 'Build', minutes: 4, state: 'done' }, { id: 'j2', name: 'Unit tests', minutes: 11, state: 'failed' }, { id: 'j3', name: 'Deploy preview', minutes: 2, state: 'running' },];
const columns: DataColumn<Job>[] = [ { id: 'name', header: 'Job', rowHeader: true, cell: (j) => j.name }, { id: 'minutes', header: 'Minutes', align: 'end', width: '96px', cell: (j) => j.minutes, compare: (a, b) => a.minutes - b.minutes }, { id: 'state', header: 'State', width: '120px', cell: (j) => pill({ text: j.state, tone: j.state }) },];
export function mount(host: HTMLElement): void { const table = document.createElement('cae-data-table') as CaeDataTable<Job>; table.caption = 'Jobs'; table.columns = columns; table.rowKey = (j) => j.id; table.rows = jobs; table.addEventListener('cae-sort', (e) => { table.sort = e.detail; table.rows = sortRows(jobs, columns, e.detail); }); const shown = document.createElement('p'); table.addEventListener('cae-activate', (e) => (shown.textContent = `Opened: ${e.detail.id}`)); host.replaceChildren(table, shown);}Columns
Section titled “Columns”A column is a DataColumn: its header, a CSS grid track as its width, and a
function that draws its cell for a row (a template, a string or a number).
Every row is a grid of the columns’ tracks, so the rows line up with the header.
/** A column: its header, its width, and what a cell shows for a row. */export interface DataColumn<R> { /** Unique among the columns: `data-col` on its cells, the `id` of `cae-sort`. */ id: string; header: string; /** The header's tooltip. */ tip?: string; /** `end` for numbers: right-aligned, tabular figures. */ align?: 'start' | 'end'; /** A CSS grid track: '72px', 'minmax(0, 1fr)' (the default). */ width?: string; /** The cell: a template, a string or a number. */ cell: (row: R, index: number) => unknown; /** The cell as plain text: what `sortRows` orders by when the column has no `compare`. */ text?: (row: R) => string; /** The order of two rows by this column, ascending. */ compare?: (a: R, b: R) => number; /** * The header is a button that sends `cae-sort` (true when the column has `compare`). * Without `compare` and `text`, `sortRows` orders by the cell when it is a string or a number. */ sortable?: boolean; /** The cell names its row (`<th scope="row">`): the first such column does. */ rowHeader?: boolean;}Keyboard and events
Section titled “Keyboard and events”The rows are one tab stop. The arrow keys move between rows, and Home and End
move to the first and the last row. Enter or a click on a row sends
cae-activate with the row’s id (rowKey). With activatable off, they choose
the row as the selection and send cae-select; the selected row has the part
row-selected. A click on a button or a link inside a cell is that control’s.
A column with compare (or sortable) has a header button that sends
cae-sort with the column and the direction. The table does not reorder the
rows: the host sets sort and the rows, ordered by sortRows or its own way.
sortRows orders by the column’s compare, else its text, else its cell when
the cell is a string or a number: a column of plain values needs only sortable.
Long lists
Section titled “Long lists”Above windowAt rows (200), the table draws only the rows on screen and a
spacer for the rows above and below them, so the scroll height stays that of
the whole list. The rows’ height is measured; rowHeight is the first
estimate. The table finds the element that scrolls it, across shadow roots, or
the window.
--cae-data-table-gap sets the space between the columns and
--cae-data-table-row-padding a row’s padding. The parts are table,
caption, head, header-cell, row, row-selected, cell and empty;
rowTone gives a row a tone (data-tone) and rowAttrs more attributes. What
the cells draw lives in the table’s shadow root: cellStyles adds styles there,
after the table’s own (the rows are tr, the cells th and td, each with
data-col set to its column’s id).
The words are strings (Strings): empty with no rows (or the
empty slot), and the sort buttons’ tooltips.