demo D92
Undo and redo
A history stack over edits, fills and pastes
grid.history
The configuration
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.27.0/lattice-grid.min.css">
<div id="grid"></div>
<script type="module">
import React from 'https://esm.sh/react@18';
import { createRoot } from 'https://esm.sh/react-dom@18/client';
import { createGrid } from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.27.0/lattice-grid.esm.min.js';
import createLatticeGrid from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.27.0/modules/react.esm.min.js';
const LatticeGrid = createLatticeGrid({ React, createGrid });
// One history stack over every kind of write: a typed cell, a fill, a paste
// and a range clear each land as one entry, so undo reverses the gesture
// rather than the cells it happened to touch. undoDepth sets how far back it
// reaches. The undo and redo buttons on the rail, and ctrl-z and ctrl-shift-z,
// both walk the same stack.
const columns = [
{ field: 'id', title: 'Order', layout: { width: 120, pin: 'start' } },
{ field: 'reference', title: 'Reference', layout: { width: 130 } },
{ field: 'team', title: 'Team', filter: { type: 'set' }, edit: true },
// A real dropdown: the stage is picked from a fixed list rather than typed,
// so the value is always one the grid knows about.
{ field: 'stage', title: 'Stage', filter: { type: 'set' },
lookup: { options: [
{ id: 'planned', label: 'Planned' },
{ id: 'approved', label: 'Approved' },
{ id: 'in-flight', label: 'In-flight' },
{ id: 'blocked', label: 'Blocked' },
{ id: 'done', label: 'Done' },
] },
edit: { enabled: true, editor: 'select', props: { allowClear: true } } },
{ field: 'due', title: 'Due', type: 'date', layout: { width: 140 }, edit: true },
{ field: 'circuits', title: 'Circuits', type: 'number', layout: { width: 110 }, edit: true },
{ field: 'impact', title: 'Impact', type: 'number', layout: { width: 100 }, edit: true },
{ field: 'effort', title: 'Effort', type: 'number', layout: { width: 100 }, edit: true },
{ field: 'budget', title: 'Budget', type: 'number', layout: { width: 140 },
format: { style: 'currency', currency: 'GBP' }, total: 'sum', edit: true },
{ field: 'notes', title: 'Notes', layout: { flex: 1, min: 220 }, edit: { enabled: true, editor: 'textarea' } },
];
const rows = [/* work orders */];
function App() {
return (
<LatticeGrid
rowKey="id"
selection={{ mode: 'multiple', ranges: true }}
edit={{ enabled: true, undoDepth: 100 }}
toolPanel={{ side: 'left', panels: ['columns'], actions: ['undo', 'redo', 'restore'], exportName: 'work-orders' }}
columns={columns}
rows={rows}
style={{ height: '540px' }}
/>
);
}
createRoot(document.getElementById('grid')).render(<App />);
</script>
Reversing edits with a grid-level history stack
Undo and redo give a user a way back out of a mistaken edit, fill, or paste without reloading data or hand-correcting cells. A developer reaches for this wherever the grid accepts direct manipulation rather than a form: inline editing, drag-fill, range paste, or a bulk edit. Lattice Grid tracks these mutations through grid.history, which records each committed change as a single entry regardless of how many cells it touched, so a paste covering four hundred cells undoes in one step rather than four hundred. The stack groups by transaction rather than by keystroke, which matters for a JavaScript data grid used for data entry: a user editing one cell across several keypresses gets one undo step for the commit, not one per character. Entries store the affected range and prior values rather than a full grid snapshot, so undoing a change on a hundred-thousand-row dataset costs the same as undoing one on a hundred-row dataset, and the operation replays through the same validation and change-detection path as a live edit, so a reverted cell re-triggers any dependent formula recalculation or row-level validation exactly as the original edit did. Keyboard handling follows platform convention, with Ctrl+Z and Ctrl+Shift+Z (or Cmd on macOS) wired to the stack by default and available as direct calls for a custom toolbar.
How do I add undo and redo to a JavaScript data grid?
Use grid.history, which maintains a stack of committed edits, fills, and pastes and exposes undo() and redo() alongside the default Ctrl+Z / Ctrl+Shift+Z bindings. Each stack entry represents one transaction, so a multi-cell paste or bulk edit reverses in a single step rather than one call per affected cell.