0
# History and Undo/Redo
1
2
Comprehensive history tracking and undo/redo functionality for all table operations including cell edits, row additions, deletions, and movements.
3
4
## Capabilities
5
6
### History Management
7
8
Core functions for managing operation history and performing undo/redo operations.
9
10
```javascript { .api }
11
/**
12
* Undo the last operation
13
* @returns True if operation was undone, false if no operations to undo
14
*/
15
undo(): boolean;
16
17
/**
18
* Redo the last undone operation
19
* @returns True if operation was redone, false if no operations to redo
20
*/
21
redo(): boolean;
22
23
/**
24
* Get the number of operations that can be undone
25
* @returns Number of undo operations available
26
*/
27
getHistoryUndoSize(): number;
28
29
/**
30
* Get the number of operations that can be redone
31
* @returns Number of redo operations available
32
*/
33
getHistoryRedoSize(): number;
34
35
/**
36
* Clear all history
37
*/
38
clearHistory(): void;
39
```
40
41
**History Configuration:**
42
43
```javascript { .api }
44
interface HistoryOptions {
45
history?: boolean;
46
}
47
```
48
49
**Usage Examples:**
50
51
```javascript
52
import { Tabulator } from "tabulator-tables";
53
54
// Enable history tracking
55
const table = new Tabulator("#table", {
56
data: [
57
{ id: 1, name: "Alice", age: 25, department: "Engineering" },
58
{ id: 2, name: "Bob", age: 30, department: "Sales" },
59
{ id: 3, name: "Charlie", age: 28, department: "Marketing" }
60
],
61
columns: [
62
{ title: "ID", field: "id" },
63
{ title: "Name", field: "name", editor: "input" },
64
{ title: "Age", field: "age", editor: "number" },
65
{ title: "Department", field: "department", editor: "list",
66
editorParams: { values: ["Engineering", "Sales", "Marketing"] } }
67
],
68
history: true // Enable history tracking
69
});
70
71
// Undo/Redo operations
72
document.getElementById("undo-btn").addEventListener("click", () => {
73
const undone = table.undo();
74
if (undone) {
75
console.log("Operation undone");
76
} else {
77
console.log("Nothing to undo");
78
}
79
});
80
81
document.getElementById("redo-btn").addEventListener("click", () => {
82
const redone = table.redo();
83
if (redone) {
84
console.log("Operation redone");
85
} else {
86
console.log("Nothing to redo");
87
}
88
});
89
90
// Check history state
91
function updateHistoryUI() {
92
const undoCount = table.getHistoryUndoSize();
93
const redoCount = table.getHistoryRedoSize();
94
95
document.getElementById("undo-count").textContent = undoCount;
96
document.getElementById("redo-count").textContent = redoCount;
97
98
document.getElementById("undo-btn").disabled = undoCount === 0;
99
document.getElementById("redo-btn").disabled = redoCount === 0;
100
}
101
102
// Update UI after operations
103
table.on("historyUndo", updateHistoryUI);
104
table.on("historyRedo", updateHistoryUI);
105
table.on("dataChanged", updateHistoryUI);
106
107
// Clear history
108
document.getElementById("clear-history").addEventListener("click", () => {
109
table.clearHistory();
110
updateHistoryUI();
111
});
112
```
113
114
## Trackable Operations
115
116
History automatically tracks the following operations when enabled:
117
118
### Cell Editing
119
120
All cell value changes are tracked with old and new values.
121
122
```javascript
123
// Cell edits are automatically tracked
124
table.on("cellEdited", function(cell) {
125
console.log(`Cell ${cell.getField()} changed from ${cell.getOldValue()} to ${cell.getValue()}`);
126
// This change is now in the history stack
127
});
128
129
// Programmatic cell updates are also tracked
130
table.updateData([
131
{ id: 1, name: "Alice Updated" } // This update will be tracked
132
]);
133
```
134
135
### Row Operations
136
137
Row additions, deletions, and movements are tracked.
138
139
```javascript
140
// Add row - tracked in history
141
table.addRow({ id: 4, name: "Diana", age: 32, department: "HR" });
142
143
// Delete row - tracked in history
144
table.deleteRow(1);
145
146
// Move rows - tracked in history (if movableRows is enabled)
147
const row = table.getRow(2);
148
row.move(0); // Move to top
149
```
150
151
### Bulk Operations
152
153
Multiple operations can be undone as a single action when performed together.
154
155
```javascript
156
// Multiple edits in quick succession
157
table.updateData([
158
{ id: 1, name: "Alice Smith" },
159
{ id: 2, name: "Bob Johnson" },
160
{ id: 3, name: "Charlie Brown" }
161
]);
162
163
// Single undo will revert all changes
164
table.undo(); // Reverts all three name changes
165
```
166
167
## Keyboard Shortcuts
168
169
Standard keyboard shortcuts are automatically enabled when history is active:
170
171
- **Ctrl+Z / Cmd+Z**: Undo last operation
172
- **Ctrl+Y / Cmd+Y**: Redo last undone operation
173
- **Ctrl+Shift+Z / Cmd+Shift+Z**: Alternative redo shortcut
174
175
```javascript
176
// Keyboard shortcuts work automatically
177
const table = new Tabulator("#table", {
178
history: true // Shortcuts are enabled automatically
179
});
180
181
// Custom keyboard shortcut handling
182
document.addEventListener("keydown", function(e) {
183
if ((e.ctrlKey || e.metaKey) && e.key === "z" && !e.shiftKey) {
184
if (table.getHistoryUndoSize() > 0) {
185
e.preventDefault();
186
table.undo();
187
}
188
}
189
190
if ((e.ctrlKey || e.metaKey) && (e.key === "y" || (e.key === "z" && e.shiftKey))) {
191
if (table.getHistoryRedoSize() > 0) {
192
e.preventDefault();
193
table.redo();
194
}
195
}
196
});
197
```
198
199
## History Events
200
201
React to history operations with dedicated events.
202
203
```javascript
204
// History operation events
205
table.on("historyUndo", function(action, component, data) {
206
console.log("Undone:", action, data);
207
showNotification(`Undone: ${action}`);
208
});
209
210
table.on("historyRedo", function(action, component, data) {
211
console.log("Redone:", action, data);
212
showNotification(`Redone: ${action}`);
213
});
214
215
// Track specific operation types
216
table.on("historyUndo", function(action, component, data) {
217
switch(action) {
218
case "cellEdit":
219
console.log(`Undid cell edit: ${data.oldValue} -> ${data.newValue}`);
220
break;
221
case "rowAdd":
222
console.log("Undid row addition");
223
break;
224
case "rowDelete":
225
console.log("Undid row deletion");
226
break;
227
case "rowMove":
228
console.log(`Undid row move: position ${data.posFrom} -> ${data.posTo}`);
229
break;
230
}
231
});
232
```
233
234
## Advanced History Features
235
236
### History State Management
237
238
Track and display history state information.
239
240
```javascript
241
function getHistoryStatus() {
242
const undoSize = table.getHistoryUndoSize();
243
const redoSize = table.getHistoryRedoSize();
244
245
return {
246
canUndo: undoSize > 0,
247
canRedo: redoSize > 0,
248
undoCount: undoSize,
249
redoCount: redoSize,
250
totalOperations: undoSize + redoSize
251
};
252
}
253
254
// Update UI based on history state
255
function updateHistoryDisplay() {
256
const status = getHistoryStatus();
257
258
document.getElementById("history-status").innerHTML = `
259
<div>Operations: ${status.totalOperations}</div>
260
<div>Can undo: ${status.undoCount} operations</div>
261
<div>Can redo: ${status.redoCount} operations</div>
262
`;
263
}
264
265
// Monitor all data changes
266
table.on("dataChanged", updateHistoryDisplay);
267
table.on("historyUndo", updateHistoryDisplay);
268
table.on("historyRedo", updateHistoryDisplay);
269
```
270
271
### Conditional History Clearing
272
273
Clear history based on specific conditions or events.
274
275
```javascript
276
// Clear history on major operations
277
table.on("dataLoaded", function() {
278
table.clearHistory(); // Clear when new data is loaded
279
});
280
281
// Clear history periodically
282
setInterval(() => {
283
const undoSize = table.getHistoryUndoSize();
284
if (undoSize > 100) { // Limit history size
285
table.clearHistory();
286
console.log("History cleared - too many operations");
287
}
288
}, 60000); // Check every minute
289
290
// Clear history on specific user actions
291
document.getElementById("reset-data").addEventListener("click", () => {
292
table.clearData();
293
table.clearHistory(); // Clear history when resetting data
294
});
295
```
296
297
### History Integration with Other Features
298
299
History works seamlessly with other Tabulator features:
300
301
```javascript
302
const table = new Tabulator("#table", {
303
history: true,
304
clipboard: true, // Clipboard operations are tracked
305
movableRows: true, // Row movements are tracked
306
pagination: false, // Works better with history enabled
307
dataTree: true, // Tree operations are tracked
308
groupBy: "department" // Group changes are tracked
309
});
310
311
// Grouped data operations are tracked
312
table.setGroupBy("age"); // Tracked
313
table.setGroupBy(false); // Tracked
314
315
// Tree operations are tracked
316
const row = table.getRow(1);
317
row.addTreeChild({ name: "New Child" }); // Tracked
318
```
319
320
### Memory Management
321
322
For large datasets, consider history memory usage:
323
324
```javascript
325
// Monitor history memory usage
326
function getHistoryMemoryUsage() {
327
const undoSize = table.getHistoryUndoSize();
328
const redoSize = table.getHistoryRedoSize();
329
330
// Estimate memory usage (approximate)
331
const estimatedSize = (undoSize + redoSize) * 100; // bytes per operation
332
333
return {
334
operations: undoSize + redoSize,
335
estimatedBytes: estimatedSize
336
};
337
}
338
339
// Clear history when memory usage is high
340
table.on("dataChanged", function() {
341
const usage = getHistoryMemoryUsage();
342
343
if (usage.operations > 500) { // Threshold
344
console.warn("High history usage, consider clearing");
345
// Optionally auto-clear oldest operations
346
table.clearHistory();
347
}
348
});
349
```
350
351
## Custom History Actions
352
353
For advanced use cases, you can create custom undoable actions:
354
355
```javascript
356
// Note: This is internal API - use with caution
357
// Custom actions would need to be implemented through table extensions
358
359
// Example of tracking custom operations
360
let customHistory = [];
361
362
function trackCustomOperation(description, undoAction, redoAction) {
363
customHistory.push({
364
description,
365
undoAction,
366
redoAction,
367
timestamp: Date.now()
368
});
369
}
370
371
// Custom bulk operation with undo
372
function customBulkUpdate(updates) {
373
const originalData = updates.map(update => {
374
const row = table.getRow(update.id);
375
return { id: update.id, data: row.getData() };
376
});
377
378
// Perform updates
379
table.updateData(updates);
380
381
// Track for custom undo (this would need integration with Tabulator's history)
382
trackCustomOperation(
383
"Bulk update",
384
() => {
385
// Restore original data
386
originalData.forEach(original => {
387
table.updateRow(original.id, original.data);
388
});
389
},
390
() => {
391
// Reapply updates
392
table.updateData(updates);
393
}
394
);
395
}
396
```
397
398
## Types
399
400
```javascript { .api }
401
interface HistoryOptions {
402
history?: boolean;
403
}
404
405
interface HistoryAction {
406
type: "cellEdit" | "rowAdd" | "rowDelete" | "rowMove";
407
component: any;
408
data: any;
409
}
410
411
interface CellEditData {
412
oldValue: any;
413
newValue: any;
414
}
415
416
interface RowAddData {
417
data: any;
418
pos: boolean;
419
index: any;
420
}
421
422
interface RowDeleteData {
423
data: any;
424
pos: boolean;
425
index: any;
426
}
427
428
interface RowMoveData {
429
posFrom: number;
430
posTo: number;
431
to: any;
432
after: boolean;
433
}
434
```