api reference
Editing, selection and history
grid.edit and validation, grid.selection and ranges, grid.history, column permissions, and your own menu items.
API reference › Editing, selection and history
grid.edit
| Method | Returns | Description |
|---|---|---|
| start(key, colId) | void | Open an edit session. The row must be rendered. |
| stop(cancel?, opts?) | object | Commit or discard. Pass { value, key, colId } to write a value. |
| undo() / redo() | void | Depth from edit.undoDepth. |
| setCells(writes, type?) | number | Write many cells as one undoable step. Returns how many landed. |
| pasteInto(anchor, text, extent?) | number | Paste tab-separated text, using Excel's tiling rules. |
| settle(id, ok, reason?) | boolean | Report the outcome of an optimistic write. Only needed with edit.confirm: 'manual'; the id arrives on cell:pending. |
| pending() | OpenWrite[] | Writes still awaiting an outcome. Empty unless edit.commit is set. |
| status(key, colId) | 'pending' | null | Whether a cell has a write in flight. |
grid.selection
| Method | Returns | Description |
|---|---|---|
| keys() | string[] | Selected row keys. |
| rows() | Row[] | |
| all() | Row[] | Including rows selected but currently filtered out. |
| set(keys) | void | Replace the selection. |
| clear() | void | |
| ranges() | Range[] | Cell ranges, for spreadsheet-style selection. |
| setRange(range) | void | Replace every range with one. |
| addRange(range) | void | Add a range without discarding the others, the API form of ctrl-click. Becomes the anchor extendRange grows. |
| startRange(rowIndex, colId, opts) | void | Begin a range at a cell. opts.additive keeps the existing ranges. |
| extendRange(rowIndex, colId) | void | Extend the newest range, keeping its anchor. |
| corner() | { row, colId } | null | Bottom-right cell of the newest range, where the fill handle sits. |
| inRange(rowIndex, colId) | boolean | Is a cell inside any selected range? |
| cells() | { key, colId }[] | Every cell in the selected ranges. |
| statistics() | object | null | Everything summary() reports plus median, quartiles, deviation, distinct and outliers: over the selected cells, so a rectangle spanning three columns is one set of numbers. Null with nothing selected. |
| summary() | object | count, sum, min, max, avg over the range. |
grid.history
Undo across the whole grid, not only edits. Sorts, filters, column moves, grouping, an applied view and a restore all record an entry, and each carries a label written for a button: "sort by Region", not "sort".
| Method | Returns | Description |
|---|---|---|
| undo() | object | null | The entry that was undone. |
| redo() | object | null | |
| canUndo() / canRedo() | boolean | |
| peek(direction?) | object | null | What the next undo or redo would do, so a control can name it before it is pressed. |
| list() | object[] | The timeline, newest first. |
| transaction(label, fn) | object | null | Group several changes into one entry. Nested transactions join the outer one. |
| clear() | void |
A multi-cell paste is one entry, not one per cell. An AI plan is one entry however many actions it contains, labelled with what it did.
grid.permissions
Four levels per column, resolved from configuration or a callback. They are the four corners of read × write rather than a ladder:
| Level | Visible | Readable | Editable | For |
|---|---|---|---|---|
| hidden | , | , | , | Absent from the grid, the tool panel, exports, the clipboard, saved state, the filter model and formula references. |
| read | yes | yes | , | No editor opens; paste, fill and range-clear skip it. |
| writeOnly | yes | , | yes | A secret: an API key a user may rotate but never read. The cell shows a mask and the editor opens empty, and a formula in another cell cannot reference it. |
| write | yes | yes | yes | The default, so the feature is opt-in. |
permissions: 'read' // blanket
permissions: { salary: 'read', ssn: 'hidden' } // map; '*' sets the default
permissions: (column, ctx) => ctx.context.role === 'admin' ? 'write' : 'read'
permissions: { default: 'read', columns: { name: 'write' }, resolve }
grid.permissions.setContext({ role: 'clerk' }); // re-resolves everything
For three of the four this is a usability control, not a security boundary. Anything the grid can render it has already loaded, and devtools reaches it. writeOnly is the exception, and the reason it is worth having: nothing in the grid needs the value, so your server can send null for that field and the column still works: at which point the secret is genuinely not on the page. Enforce everything else on the server; permittedColumns and permittedExport are pure and dependency-free so the same policy can run there.
Your own menu items and buttons
The cell menu's function form is handed the cell that was clicked and the built-in items, so adding one entry does not mean reproducing the other thirteen.
createGrid(el, {
contextMenu: (params, defaults) => [
...defaults,
{ separator: true },
{
name: `Open ${params.value} in CRM`,
action: (ctx) => open(`/crm/${ctx.data.accountId}`),
},
],
});
params and the action's argument carry the same cell:
{ key, colId, value, row, data, column, index, grid }, where data is
your original row object. Return the array you want shown: add, remove, reorder or replace.
Returning an empty array suppresses the menu; returning nothing at all leaves the defaults
alone, so a missing return cannot silently delete the menu.
columnMenu takes the same form for the header's menu: both the 3-dot button
and a right-click on a heading. Its params is
{ colId, column, grid }. Anything of your own that you put on a column definition
is on column.def, so an item can appear on some columns and not others.
createGrid(el, {
columns: [{ field: 'jan', title: 'Jan', context: { month: 1 } }],
columnMenu: (params, defaults) => {
// Your own keys live on the definition you wrote.
const month = params.column.def.context?.month;
if (!month) return defaults;
return [...defaults, { separator: true },
{ name: 'Select quarter', action: () => selectQuarter(month) }];
},
});
The rail takes host buttons the same way. A string names a built-in and an object is yours, placed where it appears in the list rather than appended after the built-ins.
createGrid(el, {
toolPanel: {
side: 'left',
actions: ['undo', 'redo', {
name: 'sync',
title: 'Sync to the server', // or a function, re-read on every repaint
icon: 'restore',
run: ({ grid, keys, cells }) => api.sync(keys),
enabled: () => grid.history.canUndo(),
}],
},
});
grid.form
The row edit form, a drawer or dialog holding one control per field. Present whether or not
rowForm is configured; without it every method declines rather than throwing, so
a caller need not guard. See
Editing a row on a form.
| Method | Returns | Description |
|---|---|---|
| open(key) | boolean | Open a row by key. False if there is no such row, or no form is configured. |
| close() | void | Close without saving. Focus returns to where it was. |
| save() | boolean | Commit the fields and close. False if a validator refused, or there is nothing to save. |
| isOpen() | boolean | Whether the panel is showing. |