React grid framework
Sapporta Grid
Editable, hierarchical data is rendered in React by Sapporta Grid. Levels, columns, editing rules, and interactions are defined in a schema. Grid operations are handled beneath the rendered surface by a JavaScript runtime.
Schema and data-source state are rendered directly by the default components. Individual renderers and editors can be replaced by the application. The same grid operations are available to tests, scripts, and other view layers through the runtime, without rendering the React components.
ProjectGrid.tsx const schema = gridSchema({
rootLevel: "projects",
levels: {
projects: level({
columns: [text("project"), select("status"), date("due")],
children: { tasks: ({ row }) => taskSource(row.id) },
}),
tasks: level({
columns: [text("task", { edit: true }), select("state"), text("assignee")],
}),
},
});
const runtime = createGridRuntime({
schema,
dataSource: projectSource,
});
<GridRuntimeProvider runtime={runtime}>
<GridLevel path={runtime.root.path} />
</GridRuntimeProvider> Project expansion, task editing, sorting, and draft creation are available in the example below. Each operation is connected to the controls.
Levels and data
Hierarchical grids and level data sources
Each kind of grid is described by a level. Project columns and a project endpoint belong to the project level. A task level with different columns, editing rules, and an independent endpoint can be loaded by expanding a project. Focus, selection, expansion, and editing are maintained across the visible hierarchy.
Local memory or an existing remote API can supply data to each level through its own data source. Loading, ready, refreshing, and failure states are published inside the requesting level. The project grid therefore remains visible when a task request fails.
const projectSource = remoteGridDataSource({
async load({ sort, filters }) {
const response = await api.projects.list({ sort, filters });
return response.items.map(toGridRow);
},
async write({ rowId, columnId, value }) {
return api.projects.update(rowId, { [columnId]: value });
},
}); Editing and persistence
Cell editing and persistence
Editable cells open in place. A value can be applied directly to an object by a local source. The same commit can be translated into the application’s update call by a remote source. Confirmed or rejected values are returned by that call. Saving and failure feedback appears in the row while the update is in progress.
New records begin as draft rows. The nonblank condition, validation, and commit timing are controlled by the application. Entered values remain in the row for correction and retry if creation fails. This editable state is created locally by the Add a project control in the example.
level.drafts.add("new-task", { title: "" });
level.drafts.setCell("new-task", "title", "Review import");
await level.drafts.commit("new-task"); Query state
Local and remote query state
Sortable columns support ascending and descending order. Filters, sorting, refetching, and pagination can be run in memory or passed through a remote adapter. The visible grid becomes a linkable application view when its query state is connected to the browser URL. The example URL changes as the filter above is edited.
Keyboard operation
Keyboard interaction presets
Cell movement and range extension are assigned by a spreadsheet-style preset. Record movement and Space key selection are assigned by row-oriented presets. One coherent keyboard model is selected for the application, without page-local key handlers.
| Key | Visible result in cell-grid mode |
|---|---|
| Arrow keys | Move the active cell. |
| Shift + Arrow | Extend the selected cell range. |
| Enter or F2 | Start editing an editable cell. |
| Tab / Shift + Tab | Move to the next or previous grid target. |
| Ctrl/Cmd + Home | Move to the first grid target. |
| Page Up / Page Down | Move ten rows up or down. |
Runtime and rendering
The JavaScript runtime and React renderer
Row loading, level expansion, sorting, filtering, editing, creation, selection, and row operations are exposed by a core JavaScript object. Its state is rendered by the supplied React primitives. The same public API can be operated directly by an integration test or a script.
Column presets and a default stylesheet are included with Sapporta Grid. The public DOM and state attributes can be styled with application CSS. Custom cells and editors can also be supplied while focus, selection, copy, and editing behavior remain under runtime control.
const runtime = createGridRuntime({ schema, dataSource });
const orderId = makeRowId(runtime.root.path, "O1");
await runtime.root.data.query?.sort?.set([
{ colId: "amount", direction: "asc" },
]);
runtime.root.toggleExpand(orderId);
runtime.root.writeCell({ rowId: orderId, colId: "amount" }, 125);