Interactive table generation JavaScript library with sorting, filtering, editing, formatting, and extensive customization capabilities
—
Comprehensive history tracking and undo/redo functionality for all table operations including cell edits, row additions, deletions, and movements.
Core functions for managing operation history and performing undo/redo operations.
/**
* Undo the last operation
* @returns True if operation was undone, false if no operations to undo
*/
undo(): boolean;
/**
* Redo the last undone operation
* @returns True if operation was redone, false if no operations to redo
*/
redo(): boolean;
/**
* Get the number of operations that can be undone
* @returns Number of undo operations available
*/
getHistoryUndoSize(): number;
/**
* Get the number of operations that can be redone
* @returns Number of redo operations available
*/
getHistoryRedoSize(): number;
/**
* Clear all history
*/
clearHistory(): void;History Configuration:
interface HistoryOptions {
history?: boolean;
}Usage Examples:
import { Tabulator } from "tabulator-tables";
// Enable history tracking
const table = new Tabulator("#table", {
data: [
{ id: 1, name: "Alice", age: 25, department: "Engineering" },
{ id: 2, name: "Bob", age: 30, department: "Sales" },
{ id: 3, name: "Charlie", age: 28, department: "Marketing" }
],
columns: [
{ title: "ID", field: "id" },
{ title: "Name", field: "name", editor: "input" },
{ title: "Age", field: "age", editor: "number" },
{ title: "Department", field: "department", editor: "list",
editorParams: { values: ["Engineering", "Sales", "Marketing"] } }
],
history: true // Enable history tracking
});
// Undo/Redo operations
document.getElementById("undo-btn").addEventListener("click", () => {
const undone = table.undo();
if (undone) {
console.log("Operation undone");
} else {
console.log("Nothing to undo");
}
});
document.getElementById("redo-btn").addEventListener("click", () => {
const redone = table.redo();
if (redone) {
console.log("Operation redone");
} else {
console.log("Nothing to redo");
}
});
// Check history state
function updateHistoryUI() {
const undoCount = table.getHistoryUndoSize();
const redoCount = table.getHistoryRedoSize();
document.getElementById("undo-count").textContent = undoCount;
document.getElementById("redo-count").textContent = redoCount;
document.getElementById("undo-btn").disabled = undoCount === 0;
document.getElementById("redo-btn").disabled = redoCount === 0;
}
// Update UI after operations
table.on("historyUndo", updateHistoryUI);
table.on("historyRedo", updateHistoryUI);
table.on("dataChanged", updateHistoryUI);
// Clear history
document.getElementById("clear-history").addEventListener("click", () => {
table.clearHistory();
updateHistoryUI();
});History automatically tracks the following operations when enabled:
All cell value changes are tracked with old and new values.
// Cell edits are automatically tracked
table.on("cellEdited", function(cell) {
console.log(`Cell ${cell.getField()} changed from ${cell.getOldValue()} to ${cell.getValue()}`);
// This change is now in the history stack
});
// Programmatic cell updates are also tracked
table.updateData([
{ id: 1, name: "Alice Updated" } // This update will be tracked
]);Row additions, deletions, and movements are tracked.
// Add row - tracked in history
table.addRow({ id: 4, name: "Diana", age: 32, department: "HR" });
// Delete row - tracked in history
table.deleteRow(1);
// Move rows - tracked in history (if movableRows is enabled)
const row = table.getRow(2);
row.move(0); // Move to topMultiple operations can be undone as a single action when performed together.
// Multiple edits in quick succession
table.updateData([
{ id: 1, name: "Alice Smith" },
{ id: 2, name: "Bob Johnson" },
{ id: 3, name: "Charlie Brown" }
]);
// Single undo will revert all changes
table.undo(); // Reverts all three name changesStandard keyboard shortcuts are automatically enabled when history is active:
// Keyboard shortcuts work automatically
const table = new Tabulator("#table", {
history: true // Shortcuts are enabled automatically
});
// Custom keyboard shortcut handling
document.addEventListener("keydown", function(e) {
if ((e.ctrlKey || e.metaKey) && e.key === "z" && !e.shiftKey) {
if (table.getHistoryUndoSize() > 0) {
e.preventDefault();
table.undo();
}
}
if ((e.ctrlKey || e.metaKey) && (e.key === "y" || (e.key === "z" && e.shiftKey))) {
if (table.getHistoryRedoSize() > 0) {
e.preventDefault();
table.redo();
}
}
});React to history operations with dedicated events.
// History operation events
table.on("historyUndo", function(action, component, data) {
console.log("Undone:", action, data);
showNotification(`Undone: ${action}`);
});
table.on("historyRedo", function(action, component, data) {
console.log("Redone:", action, data);
showNotification(`Redone: ${action}`);
});
// Track specific operation types
table.on("historyUndo", function(action, component, data) {
switch(action) {
case "cellEdit":
console.log(`Undid cell edit: ${data.oldValue} -> ${data.newValue}`);
break;
case "rowAdd":
console.log("Undid row addition");
break;
case "rowDelete":
console.log("Undid row deletion");
break;
case "rowMove":
console.log(`Undid row move: position ${data.posFrom} -> ${data.posTo}`);
break;
}
});Track and display history state information.
function getHistoryStatus() {
const undoSize = table.getHistoryUndoSize();
const redoSize = table.getHistoryRedoSize();
return {
canUndo: undoSize > 0,
canRedo: redoSize > 0,
undoCount: undoSize,
redoCount: redoSize,
totalOperations: undoSize + redoSize
};
}
// Update UI based on history state
function updateHistoryDisplay() {
const status = getHistoryStatus();
document.getElementById("history-status").innerHTML = `
<div>Operations: ${status.totalOperations}</div>
<div>Can undo: ${status.undoCount} operations</div>
<div>Can redo: ${status.redoCount} operations</div>
`;
}
// Monitor all data changes
table.on("dataChanged", updateHistoryDisplay);
table.on("historyUndo", updateHistoryDisplay);
table.on("historyRedo", updateHistoryDisplay);Clear history based on specific conditions or events.
// Clear history on major operations
table.on("dataLoaded", function() {
table.clearHistory(); // Clear when new data is loaded
});
// Clear history periodically
setInterval(() => {
const undoSize = table.getHistoryUndoSize();
if (undoSize > 100) { // Limit history size
table.clearHistory();
console.log("History cleared - too many operations");
}
}, 60000); // Check every minute
// Clear history on specific user actions
document.getElementById("reset-data").addEventListener("click", () => {
table.clearData();
table.clearHistory(); // Clear history when resetting data
});History works seamlessly with other Tabulator features:
const table = new Tabulator("#table", {
history: true,
clipboard: true, // Clipboard operations are tracked
movableRows: true, // Row movements are tracked
pagination: false, // Works better with history enabled
dataTree: true, // Tree operations are tracked
groupBy: "department" // Group changes are tracked
});
// Grouped data operations are tracked
table.setGroupBy("age"); // Tracked
table.setGroupBy(false); // Tracked
// Tree operations are tracked
const row = table.getRow(1);
row.addTreeChild({ name: "New Child" }); // TrackedFor large datasets, consider history memory usage:
// Monitor history memory usage
function getHistoryMemoryUsage() {
const undoSize = table.getHistoryUndoSize();
const redoSize = table.getHistoryRedoSize();
// Estimate memory usage (approximate)
const estimatedSize = (undoSize + redoSize) * 100; // bytes per operation
return {
operations: undoSize + redoSize,
estimatedBytes: estimatedSize
};
}
// Clear history when memory usage is high
table.on("dataChanged", function() {
const usage = getHistoryMemoryUsage();
if (usage.operations > 500) { // Threshold
console.warn("High history usage, consider clearing");
// Optionally auto-clear oldest operations
table.clearHistory();
}
});For advanced use cases, you can create custom undoable actions:
// Note: This is internal API - use with caution
// Custom actions would need to be implemented through table extensions
// Example of tracking custom operations
let customHistory = [];
function trackCustomOperation(description, undoAction, redoAction) {
customHistory.push({
description,
undoAction,
redoAction,
timestamp: Date.now()
});
}
// Custom bulk operation with undo
function customBulkUpdate(updates) {
const originalData = updates.map(update => {
const row = table.getRow(update.id);
return { id: update.id, data: row.getData() };
});
// Perform updates
table.updateData(updates);
// Track for custom undo (this would need integration with Tabulator's history)
trackCustomOperation(
"Bulk update",
() => {
// Restore original data
originalData.forEach(original => {
table.updateRow(original.id, original.data);
});
},
() => {
// Reapply updates
table.updateData(updates);
}
);
}interface HistoryOptions {
history?: boolean;
}
interface HistoryAction {
type: "cellEdit" | "rowAdd" | "rowDelete" | "rowMove";
component: any;
data: any;
}
interface CellEditData {
oldValue: any;
newValue: any;
}
interface RowAddData {
data: any;
pos: boolean;
index: any;
}
interface RowDeleteData {
data: any;
pos: boolean;
index: any;
}
interface RowMoveData {
posFrom: number;
posTo: number;
to: any;
after: boolean;
}Install with Tessl CLI
npx tessl i tessl/npm-tabulator-tables