A column written once and used in many tables: as a preset spread into a column, or as a component that brings its own presentation.
import { column } from "@umriss-ui/table";
column<P>() makes a preset bound to a property, not to a row kind; spread it into any table whose rows have that field.
import { Stack } from "@umriss-ui/core";
import { column, useTable } from "@umriss-ui/table";
/* The compiler checks the spread: a row without a numeric `amount` is
refused. With more than one field, name the field too -
`column<{ amount: number; id: string }, "amount">(…)`. */
const amount = column<{ amount: number }>({ value: "amount", label: "Amount (€)", format: { decimals: 2 }, aggregate: "sum" });
interface Invoice {
id: string;
amount: number;
}
interface Budget {
costCentre: string;
amount: number;
}
const INVOICES: Invoice[] = [
{ id: "INV-26-0318", amount: 1951.24 },
{ id: "INV-26-0309", amount: 7288 },
];
const BUDGETS: Budget[] = [
{ costCentre: "Marketing", amount: 68_000 },
{ costCentre: "IT", amount: 83_000 },
];
function Invoices() {
const { Table, Column } = useTable(INVOICES, { rowKey: (i) => i.id });
return (
<Table ariaLabel="Invoices">
<Column value="id" label="Invoice" rowHeader />
<Column {...amount} />
</Table>
);
}
function Budgets() {
const { Table, Column } = useTable(BUDGETS, { rowKey: (b) => b.costCentre });
return (
<Table ariaLabel="Monthly budgets">
<Column value="costCentre" label="Cost centre" rowHeader />
<Column {...amount} label="Monthly budget (€)" />
</Table>
);
}
export default function Presets() {
return (
<Stack gap={4}>
<Invoices />
<Budgets />
</Stack>
);
}
A column that brings its presentation becomes a component; it takes the table as of, typed with a row kind, and uses its Column.
import { Badge } from "@umriss-ui/core";
import { useTable } from "@umriss-ui/table";
import type { Table } from "@umriss-ui/table";
/* A wrapper generic over its rows does not compile - TypeScript does not
resolve the overloads against an open row type - and one without `of`
compiles but is unchecked. */
interface Incident {
id: string;
severity: "SEV1" | "SEV2" | "SEV3";
}
const TONE = { SEV1: "danger", SEV2: "warning", SEV3: "neutral" } as const;
const INCIDENTS: Incident[] = [
{ id: "INC-1048", severity: "SEV1" },
{ id: "INC-1047", severity: "SEV3" },
{ id: "INC-1046", severity: "SEV2" },
];
function SeverityColumn({ of }: { of: Table<Incident> }) {
return (
<of.Column value="severity" label="Severity">
{(severity) => <Badge tone={TONE[severity]}>{severity}</Badge>}
</of.Column>
);
}
export default function Wrapper() {
const t = useTable(INCIDENTS, { rowKey: (i) => i.id });
const { Table, Column } = t;
return (
<Table ariaLabel="Incidents">
<Column value="id" label="Incident" rowHeader />
<SeverityColumn of={t} />
</Table>
);
}
The keys of First table apply here.
From TableProps — the full table stands on First table.
| Name | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | Columns, RowDetail, RowActions and the unbound parts. |
| Name | Type | Default | Description |
|---|---|---|---|
ariaLabel | string | — | Accessible name of the table (not of the outer frame). |
From TableOptions — the full table stands on First table.
| Name | Type | Default | Description |
|---|---|---|---|
rowKeyrequired | (row: Z) => string | — | A stable key per row – the basis of selection and expansion. |
From FieldColumn — the full table stands on Column.
| Name | Type | Default | Description |
|---|---|---|---|
valuerequired | K | — | A field of the row. Supplies the column's id at the same time. |
id | string | — | An id of one's own instead of the field name – for two columns over the same field. |
format | FormatFor<Z[K]> | — | A standard presentation by name: "percent", "count" or
{ decimals } for numbers, "date", "time" or "dateTime" for
points in time. Alignment, sorting, export and footer stay with the value. |
filter | FilterFor<Z[K]> | — | A filter in the column header: "list" offers the values that occur,
"range" two bounds (numbers or points in time), a filter from
columnFilter asks whatever it asks itself. Its condition stands in the
table toolbar. |
group | GroupFor<Z[K]> | — | Groups points in time by "day", "week", "month" or "year". |
edit | EditFor<Z[K], Z> | — | Edits the column's cells in place in grid mode: Enter, F2 or typing
starts it, Enter or Tab commits, Escape cancels. The table applies
nothing - the edit is reported through the table's onCellEdit. from EditOptions |
editOptions | readonly Present<Z[K]>[] | the values that occur | What edit="select" offers. from EditOptions |
validate | (value: Z[K] | null, row: Z) => string | null | undefined | — | Checks a draft before it is reported: a message keeps the editor open
and stands beneath the field, as in a FormField; nothing lets it
through. from EditOptions |
children | Presentation<Z[K], Z> | — | How the value appears: (value, row). Never called for an absent
value. Required where the value has no text form. |
From ColumnBase — the full table stands on Column.
| Name | Type | Default | Description |
|---|---|---|---|
labelrequired | string | — | Names the column in its header, in the column menu and in the header row of the export. |
rowHeader | boolean | — | This column names the row: row header cells, never hideable, the column
stickyRowHeader pins, and the name by which the row's selection, expander and
actions are read out. At most one per table. |
numeric | boolean | decided by the first value present | Right-aligned with tabular figures. |
width | number | the browser's | Initial width in pixels. |
resizable | boolean | — | Shows the drag grip on the header cell. |
pin | Pin"start" | "end" | — | Keeps the column in view while the table scrolls sideways: in a block before every other column, or after them. The user can change it in the column menu; the view carries the change. Pinned blocks take at most half of the width the table shows: on a narrower one the end block, then the start block scrolls with the rest, and sticks again when there is room. |
sortable | boolean | true when the value is text, a number, a point in time or a boolean | Whether the column can be sorted by. |
searchable | boolean | true for text, false otherwise | Takes part in the search. |
groupable | boolean | true when its value is text, a number, a point in time or a boolean, or it has a groupValue | Can the table be grouped by this column? |
From ValuePaths — the full table stands on Column.
| Name | Type | Default | Description |
|---|---|---|---|
sortValue | (value: Present<W>) => string | number | Absent | — | What is sorted by, when not by the value itself. |
exportValue | (value: Present<W>) => string | number | Absent | — | What stands in the export, when not the value itself. |
groupValue | (value: Present<W>) => string | number | boolean | Date | Absent | — | What is grouped by, when not the value itself - a group header of a number, say. Rows whose group value is absent form a group of their own. |
From AggregateOptions — the full table stands on Aggregate.
| Name | Type | Default | Description |
|---|---|---|---|
aggregate | AggregateFor<W, Z> | — | What the column's values come to: in the footer over the filtered set,
in a group's header over its rows. "sum", "avg" for numbers; "min",
"max" for numbers and points in time; "range" for points in time;
"count", "distinct" for every value with a text form - or a function
of one's own. Always from the values, never from other aggregates; absent
values count towards nothing. |
share | boolean | — | The share bar in a group's header – the group's share of the filtered
set's sum. It stands under the first column with a sum, one per header;
false passes it on to the next. |