A sortable, filterable table: per-column funnel filters (text, set, number, date), snippet cell renderers, sticky header, pagination, responsive card mode, and keyboard-activatable rows.
SortableFilteredPaginatedCards on phonesEmpty
| Requests | 428.6 |
| Response time | 82.1 |
const filters = createColumnFilters([ { key: 'kind', type: 'set' }, { key: 'value', type: 'number' }]);<DataTable {columns} {rows} {filters} defaultSort="value" onRowClick={open} />Installation
pnpm add @chanrg/4uiImport the stylesheet once, at the root of your app:
import '@chanrg/4ui/styles.css';Usage
import { DataTable } from '@chanrg/4ui';const filters = createColumnFilters([ { key: 'kind', type: 'set' }, { key: 'value', type: 'number' }]);<DataTable {columns} {rows} {filters} defaultSort="value" onRowClick={open} />Composition
Use the following composition to build a data table:
DataTable ├── columns[].cell (snippet) ├── ColumnFilter (via filters) └── Pager (via pageSize)
Paginated
Set pageSize to page the sorted rows; page is bindable and clamps when a filter shrinks the set.
| Unit | ||
|---|---|---|
| Cache hit rate | — | % |
| Requests | 428.6 | /s |
Page 1 of 3
<DataTable {columns} {rows} pageSize={2} bind:page />Compact density
Tightens cell padding for dense management screens.
| Unit | ||
|---|---|---|
| Requests | 428.6 | /s |
| Response time | 82.1 | ms |
| CPU load | 78.4 | % |
| Queue depth | 3.1 | k |
<DataTable {columns} {rows} density="compact" />Cards on phones
With mobileCards, each row becomes a labelled card below 480px; columns place themselves via mobileRole.
| Unit | ||
|---|---|---|
| Requests | 428.6 | /s |
| Response time | 82.1 | ms |
| CPU load | 78.4 | % |
| Queue depth | 3.1 | k |
Below 480px each row becomes a labelled card.
const columns = [ { key: 'name', label: 'Service', mobileRole: 'primary' }, { key: 'value', label: 'Value', numeric: true }, { key: 'unit', label: 'Unit', hideBelow: 550 }];<DataTable {columns} {rows} mobileCards />API reference
Props
| Prop | Type | Default | Description |
|---|---|---|---|
columns | DataTableColumn[] | [] | key, label, sortable, numeric, width, value(), format(), or a cell snippet. |
rows | Row[] | [] | Caller-owned data. |
filters | ColumnFilters | — | Controller from createColumnFilters(); adds funnel popovers to columns. |
defaultSort / defaultDir | string / asc | desc | — / desc | Initial sort. |
onRowClick | (row) => void | — | Makes rows interactive (click + Enter/Space). |
maxHeight | number | 280 | Scroll height in px; 0 grows freely. |
locale | string | runtime | Collation locale for text sorting. |
caption | string | — | Screen-reader description. |
pageSize / page | number | null / number | null / 1 | Paginates the sorted rows; `page` is bindable and clamps when filters shrink the set. |
density | regular | compact | regular | Tightens cell padding for dense management screens. |
mobileCards | boolean | false | Below 480px each row becomes a card; columns place themselves via column.mobileRole. |
column.mobileRole | primary | control | actions | full | hidden | — | Card placement. Unset renders a labelled meta cell. |
column.hideBelow | 550 | 700 | — | Drops the column under that viewport width. |
CSS variables
| Variable | Description |
|---|---|
--4ui-surface | Primary component surface. |
--4ui-text | Primary foreground color. |
--4ui-border | Default control and panel border. |
Accessibility
- Sort headers are real buttons and the active column exposes aria-sort.
- Filter panels are dialogs that close on Escape and outside pointer.
- Custom cells use snippets — no HTML string injection.
- Card mode labels every value with its column name via a data-label pseudo-element.