Frozen columns
A pinned column beside a grid that pans
height 200 · chosen r1
Usage
import { FrozenTable, TableCell, TableHeaderCell, TableRow } from "@gpuixui/data";
<FrozenTable
height={320}
frozenWidth={180}
contentWidth={420}
rows={table.rows}
rowKey={(row) => row.id}
aria-label="Files"
frozenHeader={
<TableRow>
<TableHeaderCell width={180}>Name</TableHeaderCell>
</TableRow>
}
frozenRow={(row) => (
<TableRow selected={table.isSelected(row.id)} onClick={() => table.pressRow(row)}>
<TableCell width={180}>{row.name}</TableCell>
</TableRow>
)}
header={
<TableRow>
<TableHeaderCell width={140}>Status</TableHeaderCell>
</TableRow>
}
row={(row) => (
<TableRow selected={table.isSelected(row.id)} onClick={() => table.pressRow(row)}>
<TableCell width={140}>{row.status}</TableCell>
</TableRow>
)}
/>;API
FrozenTable
| Prop | Type | Default | Consequence |
|---|---|---|---|
height | number | — | the grid's definite height; one the layout cannot paint reads as no height (see below) |
frozenWidth | number | — | the frozen pane's width; one the layout cannot paint reads as no width (see below) |
contentWidth | number | — | the scrolling columns' total width; one the layout cannot paint reads as no width |
rows | ReadonlyArray<Row> | — | the records, in paint order; a value that is not a list paints the empty message (see below) |
rowKey | (row: Row) => string | — | the stable React key of a record |
frozenHeader | ReactNode | — | the frozen column's header cells |
frozenRow | (row: Row) => ReactNode | — | the frozen column's row |
header | ReactNode | — | the scrolling columns' header cells |
row | (row: Row) => ReactNode | — | the scrolling columns' row |
empty | ReactNode | No rows | the data pane's empty message, wrapped in a TableEmpty row |
frozenTestId / bodyTestId / viewportTestId | string | — | locators for a probe |
style / testId / ref / aria-label | host props | — | on the grid |
The grid's box
height bounds both row scrollers: the frozen pane's and the data pane's each
get the caller's height less the taller of the two measured headers, so a record
keeps one y across the seam.
A height is often computed — a share of a window, a pane's measured box, a value
read from storage before the first layout — and one the layout cannot paint
(NaN, an infinity, a negative) reads as no height: the composition takes
the share of the pane it sits in and bounds both scrollers from the height that
box painted. A height that does paint is the caller's, exactly.
Measured in the renderer on 0.11.0: in a 320-point pane, height={NaN} painted
both row scrollers at 0 points against the 276 the same grid paints with
a readable height, so the grid showed no row at all and neither pane could
scroll. src/behaviour/paintable-height.test.tsx holds the case beside the four
other bounded surfaces, and the rule itself lives in internal/size.ts.
The two widths are sizes too, and a caller computes them the same way — a share
of the pane, the sum of the columns a release ships, a saved layout read before
the first measurement. A width the layout cannot paint (NaN, an infinity, a
negative) reads as no width: the frozen pane takes the track its own cells
declare, and the data pane's column takes the pane it sits in. A width that does
paint is the caller's, exactly.
Measured in the renderer on 0.11.0, in a 1280-point frame with a 120-point frozen
cell and a readable frozenWidth={180}: frozenWidth={-180} painted no frozen
pane at all (0 points) and handed the data pane the whole frame (1280), while
frozenWidth={NaN} painted the pane at its own content (120) against the 180 the
readable width gives it; contentWidth={-420} painted the data pane's column at
0 points, so the scrolling header and rows had no box at all.
src/behaviour/paintable-width.test.tsx holds the three beside two readable
widths as its control.
The rows a store kept
rows is stored data as often as a literal — a saved workspace restores a
panel's rows, and snapshot?.rows is undefined while the snapshot is still
absent. It is read through listOf, so a value that is not a list fills no pane
and the data pane paints its empty message (a TableEmpty row, No rows by
default): an empty grid, not a crashed one.
Measured before the read: rows={null} threw
Cannot read properties of null (reading 'map') out of the first render, the
frozen pane, the data pane and the empty message all failed to paint, and
rows={undefined} threw the same way. src/behaviour/stored-rows.test.tsx
mounts both absences and reads the message out of the renderer, with a readable
list as its own control.
Edit this page on GitHubpackages/data/docs/frozen-columns.mdx