TGrid interactions, columns, and writes
View as MarkdownActive row
Section titled “Active row”TGrid projects the standalone GridActiveRow into the session’s RowsByLevel
mapping. Each projection includes the row’s identity and kind-specific
properties plus levelId, values, level, and runtime.
Data rows expose a complete typed values object for their level. Phantom,
rollup, opening, closing, subtotal, and footer rows expose partial values.
Consumers must narrow kind === "data" before treating values as a complete
persisted record. levelId narrows the applicable row type in a multi-level
definition.
const session = useTGridSession(definition);const activeRow = useTGridActiveRow(session);
const task = activeRow?.kind === "data" && activeRow.levelId === "tasks" ? activeRow.values : null;useTGridActiveRow(session) returns React state backed by the session
subscription. The value changes when the cursor moves, the active row
disappears, or its displayed values change. Render a detail region directly from
it.
Non-React owners use session.activeRow() and
session.subscribeActiveRow(listener). Snapshots preserve identity until active
identity or displayed values change.
TGridActiveRow<RowsByLevel> is the exported projection type.
Row activation
Section titled “Row activation”Active-row state describes the current row. Row activation reports each configured semantic command.
import { ROW_PRIMARY_MASTER_DETAIL_WITH_ACTIVATION } from "@sapporta/grid";
const definition = defineTGrid<RowsByLevel>({ rootLevel: "tasks", interaction: ROW_PRIMARY_MASTER_DETAIL_WITH_ACTIVATION, levels,});
<TGrid session={session} onRowActivate={({ activeRow, trigger }) => { if (activeRow.kind === "data" && activeRow.levelId === "tasks") { navigate(`/tasks/${activeRow.values.id}/edit`); } }}/>;TGrid accepts onRowActivate. Non-React or shared session owners use
session.onRowActivate(handler). Each event contains the typed activeRow and
the normalized keyboard or pointer trigger. Repeated activation of the same
row produces repeated events.
TGridRowActivatedEvent<RowsByLevel> is the exported event type.
The TGrid definition owns the interaction configuration. The callback does not
enable gestures by itself. ROW_PRIMARY_MASTER_DETAIL_WITH_ACTIVATION enables
Enter and double-click, reserves Enter for activation, and retains left/right
hierarchy expansion. Custom configurations use activeRow.activation.startsOn.
SchemaTableGridView accepts an interaction configuration but does not expose
active-row state or activation callbacks as props. Use useSchemaTableGrid(),
then render TGrid with the returned session inside the application layout.
Column and write behavior
Section titled “Column and write behavior”- The table adapter composes
ColumnSchema.kindwith ColumnPreset draft parsers at cell commit. - Numeric drafts become finite numbers. Clearing a non-text cell becomes an
explicit
null. An untouched field remains absent from the patch. Empty text remains"". - A plain Delete or Backspace writes
nullinto the focused editable cell and the other selected cells of its column. The write takes the edit path, so it reaches the same save and validation boundary as a typed edit. TGrid setsdisableBackspaceCellClearonnotNullcolumns, so a required cell cannot be emptied with a keystroke; a column may set it directly to refuse the clear. - A column whose meta is
gridEditable: falseorapiWritable: falserenders read-only in a grid cell and in a record’s detail fields. UnlikeapiWritable: false,gridEditable: falsestill leaves the column in the table API and a create form. - Invalid editor text remains available to the editor and reaches the authoritative server validation boundary.
- Select-backed columns preserve exact option identity, and render their value as plain text.
dateandtimestampare separate presets, chosen from the column’s declared kind. A date cell renders2026-08-23; a timestamp cell renders2026-08-23 16:38in the active workspace’s time zone and describes the full moment on hover.- The timestamp preset offers no date-picker editor. An
<input type="date">has nowhere to put the time component and would drop it on commit. - Cell renderers, activations, editors, copy handlers, and write handlers
receive a path-bound
GridLevelRuntimeascontext.level. context.runtimecontains grid-wide schema, events, registered levels, active-row state, and cross-path row operations.
Tree rows
Section titled “Tree rows”On a
tree level,
TGrid wraps the tree column with withTreeColumn. The column indents each row
by its depth and shows a chevron on rows with children. Space expands or
collapses the row, and Enter does the same on a cell that cannot be edited. The
Grid level declares parentKeyValue from the table’s primary key, so a child
draft stores its parent key with the column’s type, such as a number for an
integer id.
On a writable tree level, the row context menu offers Add child row. It adds a draft under the row with the parent column filled in and, in a cell grid, opens the tree column’s editor in the draft. Leaving the draft saves it like any new row; a draft left untouched is removed.
Rows present only because a descendant matched the filter or search carry
data-tree-context="true", and the Sapporta preset renders them in the muted
foreground color.
Column widths
Section titled “Column widths”TGrid accepts columnSizing, every ColumnPreset column-sizing option except
storageKey. TGrid stores dragged widths under
sapporta:grid-columns:<root table>:<level>. minWidths raises the floors of
the named widths, such as { numeric: 128 } for currency in a larger face:
const columnSizing = { minWidths: { numeric: 128, timestamp: 176 } };
<TGrid session={session} presentation="tabular" columnSizing={columnSizing} />;Give the object a stable identity, at module level or in useMemo; the grid
chrome is rebuilt whenever it changes. See
Column sizing.
Direct GridCore and ColumnPreset contracts live in the standalone Grid Reference.