Skip to content

Undo and redo

The history is off by default. With history on, every action is an entry: a drag, an updateTask, a link that was drawn, a link that was removed. undo() applies the inverse of the last entry and redo() puts it back; canUndo and canRedo say whether there is anything to do, and onHistoryChange tells you when either flips, which is what the buttons follow.

The keys are off by default too. keyboard.undo makes Ctrl or Cmd + Z undo and Ctrl or Cmd + Shift + Z (or Ctrl + Y) redo, while the focus is on a bar or on the chart; the chart calls the same two methods as the buttons.

Terminal window
pnpm add @ganttlane/dom # vanilla
pnpm add @ganttlane/react react react-dom # React
View on GitHub
data.ts
import type { Link, Task } from '@ganttlane/core';
// 2026-03-02 is a Monday. Drag a few bars, draw a link, then undo them one by one.
export const tasks: Task[] = [
{ id: 'design', name: 'Design', start: '2026-03-02', end: '2026-03-06', progress: 100 },
{ id: 'api', name: 'API', start: '2026-03-09', end: '2026-03-13', progress: 50 },
{ id: 'ui', name: 'UI', start: '2026-03-09', end: '2026-03-17', progress: 30 },
{ id: 'docs', name: 'Docs', start: '2026-03-16', end: '2026-03-19' },
{ id: 'qa', name: 'QA', start: '2026-03-18', end: '2026-03-20' },
{ id: 'review', name: 'Review', start: '2026-03-23', end: '2026-03-24' },
{ id: 'launch', name: 'Launch', type: 'milestone', start: '2026-03-25' },
];
export const links: Link[] = [
{ source: 'design', target: 'api' },
{ source: 'design', target: 'ui' },
{ source: 'api', target: 'qa' },
{ source: 'ui', target: 'qa' },
{ source: 'qa', target: 'review' },
{ source: 'review', target: 'launch' },
];

Files

  • data.ts: seven tasks and six links.
  • options.ts: history with its limit, keyboard (bars take the focus, the undo keys), the view mode, the first scroll and the layout.
  • helpers.ts: undoAll, and the lines of the status.
  • vanilla.ts and react.tsx: the buttons, the select for the limit and the events.

Options

Option Value What it does
history { limit: 5 } Undo and redo on, and the last five actions kept; true is on with the default 100
keyboard { barFocus, undo: true } Bars take the focus; Ctrl or Cmd + Z undoes, Ctrl or Cmd + Shift + Z and Ctrl + Y redo
onHistoryChange a function (in the example) ({ canUndo, canRedo }), only when one of them changes: the buttons follow it
onTasksChange a function (in the example) A change set, also for an undo or a redo (reason: 'undo' or 'redo')
onLinkCreate, onLinkDelete functions (in the example) A link drawn or removed, also by an undo or a redo
viewMode 'week' The scale of the timeline (React: defaultViewMode)
scroll.initial 'first-task' Opens on the plan
layout.rowHeight, .barHeight 36, 24 Smaller rows, so that every task is in view

Methods

Method What it does
undo() and redo() Undo the last entry, redo the last undone one; they return { status } at once
canUndo and canRedo Whether there is an entry to undo or to redo
setOptions({ history }) Changes the limit (or turns the history off) without recreating the chart

What to try

  • Drag API and UI, change a progress with the little handle, draw a link from Docs to Review. Each is an entry, and the buttons turn on as soon as there is something to undo.
  • Press Undo: the bar goes back, onTasksChange reports it with reason: 'undo', and the call returns undone. Redo puts it back. Make a new change after an undo: canRedo goes false, because the redo entries no longer apply.
  • Click a bar, or Tab to one, then press Ctrl+Z (Cmd+Z on a Mac), and Shift+Ctrl+Z to redo. The keys work while the focus is on a bar or on the chart; in the select they are the browser’s.
  • Undo the link you drew: it is removed, and onLinkDelete reports it.
  • Choose Keep the last 3 actions, make five changes and press Undo all: it undoes three. Lowering the limit drops the oldest entries at once.

Notes

  • What is an entry. Committed changes (drags and updateTask, with everything they moved), link creations and link removals. Selection, scrolling and the view mode are not.
  • No hooks, but a check. An undo or a redo does not ask onBeforeTaskChange, so a hook cannot stand in the way of putting things back (Before-hooks). It is checked against the data all the same: with controlled data, an entry that no longer fits the data gives stale and clears the history. With the chart’s own data, replacing the tasks with setTasks clears the history at once (canUndo goes false).
  • The results. undone, redone, empty (nothing to do), busy (a hook is waiting, or controlled data holds a change its owner has not applied) and stale.
  • The limit. history: true keeps the last 100 actions, { limit: n } the last n (undo and redo entries together, the oldest dropped first), { enabled: false } or leaving it out turns the history off.
  • Edit sessions. A session that is rolled back (edit.rollback()) puts the history back as it was when the session began.
  • React. history is an ordinary prop. The buttons follow onHistoryChange through state; useGanttState could read the flags from the snapshot instead.