Presets

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";

Examples

Share a column between tables

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>
  );
}

Wrap a column in a component

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>
  );
}

Keyboard

The keys of First table apply here.

Props on this page

From TableProps — the full table stands on First table.

NameTypeDefaultDescription
childrenReactNode—Columns, RowDetail, RowActions and the unbound parts.

Accessibility

NameTypeDefaultDescription
ariaLabelstring—Accessible name of the table (not of the outer frame).

From TableOptions — the full table stands on First table.

NameTypeDefaultDescription
rowKeyrequired(row: Z) => string—A stable key per row – the basis of selection and expansion.

From FieldColumn — the full table stands on Column.

NameTypeDefaultDescription
valuerequiredK—A field of the row. Supplies the column's id at the same time.
idstring—An id of one's own instead of the field name – for two columns over the same field.
formatFormatFor<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.
filterFilterFor<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.
groupGroupFor<Z[K]>—Groups points in time by "day", "week", "month" or "year".
editEditFor<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
editOptionsreadonly Present<Z[K]>[]the values that occurWhat 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
childrenPresentation<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.

NameTypeDefaultDescription
labelrequiredstring—Names the column in its header, in the column menu and in the header row of the export.
rowHeaderboolean—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.
numericbooleandecided by the first value presentRight-aligned with tabular figures.
widthnumberthe browser'sInitial width in pixels.
resizableboolean—Shows the drag grip on the header cell.
pinPin
"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.
sortablebooleantrue when the value is text, a number, a point in time or a booleanWhether the column can be sorted by.
searchablebooleantrue for text, false otherwiseTakes part in the search.
groupablebooleantrue when its value is text, a number, a point in time or a boolean, or it has a groupValueCan the table be grouped by this column?

From ValuePaths — the full table stands on Column.

NameTypeDefaultDescription
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.

NameTypeDefaultDescription
aggregateAggregateFor<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.
shareboolean—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.

Every page of @umriss-ui/table

Getting started

Columns

Finding rows

Grouping

Rows

Around the table

Many rows

Editing

Limits and alarms

API index