or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cell-editing.mdclipboard.mdcolumn-management.mddata-management.mddata-sorting.mddata-trees.mdevent-system.mdfiltering-search.mdhistory-undo.mdimport-export.mdindex.mdpagination.mdrange-selection.mdrow-grouping.mdtable-construction.mdvalidation.md

history-undo.mddocs/

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

```