Skip to content

Row selection

View as Markdown

Row selection identifies rows for operations such as delete, export, bulk edit, or a pinned side panel. It is separate from active-row context and rectangular cell selection.

type SelectedRowsConfig =
| { readonly kind: "none" }
| {
readonly kind: "enabled";
readonly mode: "single" | "range" | "multi";
readonly sync:
| { readonly kind: "follows-active-row" }
| { readonly kind: "independent" };
readonly keyboard: {
readonly space: "toggle-active-row" | "ignore";
};
};

follows-active-row derives the effective selection from the active row. It is a read-only projection. independent stores a path-local row selection that can differ from the active row.

type RowSelection =
| { readonly kind: "single"; readonly rowId: RowId }
| {
readonly kind: "range";
readonly anchor: RowId;
readonly head: RowId;
}
| { readonly kind: "set"; readonly rowIds: ReadonlySet<RowId> }
| null;

Read and change row state through a GridLevelRuntime:

const level = runtime.root;
const rowId = makeRowId(level.path, "task-1");
level.activeRow();
level.selectedRows();
level.selectedRowIds();
level.rowInteractionSnapshot();
level.selectRow(rowId);
level.toggleRowSelection(rowId);
level.extendRowSelectionTo(rowId);
level.clearRowSelection();

Selection commands normalize row ids against the current displayed rows and the configured selection mode. Non-selectable and stale rows are removed.

Level subscriptions correspond to distinct read models:

level.subscribeActiveRow(() => {
renderInspector(level.activeRow());
});
level.subscribeSelectedRows(() => {
persistSelection(level.selectedRows());
});
level.subscribeSelectedRowIds(() => {
updateCount(level.selectedRowIds().length);
});
level.subscribeRowInteractionSnapshot(() => {
updateRowChrome(level.rowInteractionSnapshot());
});

subscribeSelectedRows() observes the configured selection value. subscribeSelectedRowIds() also observes displayed-order changes that alter the projected ids. subscribeRowInteractionSnapshot() is designed for row decoration.

React components use the corresponding hooks:

useActiveRow(path);
useSelectedRows(path);
useSelectedRowIds(path);
useRowInteractionSnapshot(path);

rowSelectionColumn() creates a normal ColumnSchema that reads path-local row status and invokes the level selection commands.

import { rowSelectionColumn, text } from "@sapporta/grid/column-preset";
const columns = [
rowSelectionColumn(),
text({ id: "title", name: "Title", edit: "default" }),
];

Set the level’s rowHeaderColumn to { column: selectorColumnId } when row-header behavior should be attached to that column. Use "empty-selectable-cell" for a separate row header or "none" when the level has no row header.

Stored selection remains path-local. Use runtime.rowOperations when one command intentionally spans the expanded hierarchy.