🎉 JointJS has new documentation! 🥳
CommandManager keeps track of graph changes and allows you to travel the history of those changes back and forth. There is no limitation put into the number of levels one can undo/redo.
Include joint.dia.command.js
to your HTML:
<script src="joint.dia.command.js"></script>
const graph = new joint.dia.Graph({}, { cellNamespace: joint.shapes });
const paper = new joint.dia.Paper({
el: document.getElementById('paper'),
width: 500,
height: 500,
model: graph,
cellViewNamespace: joint.shapes
});
const commandManager = new joint.dia.CommandManager({ graph });
document.getElementById('undo-button').addEventListener(() => commandManager.undo());
document.getElementById('redo-button').addEventListener(() => commandManager.redo());
CommandManager listens to graph changes and when any cell's attribute is changed it stores the difference (command).
CommandManager stores every change made on graph's cells into undoStack
and every reverted change into redoStack
.
It allows you to revert the changes stored in those stacks.
The joint.dia.CommandManager
constructor takes up to three parameters.
Is the graph the CommandManager listens to.
A function evaluated before any command is added. If the function returns false
, the command does not get stored.
This way you can control which commands do not get registered for undo/redo.
const commandManager = new joint.dia.CommandManager({
graph,
cmdBeforeAdd: (cmdName, cell, graph, options) => {
options = options || {};
return !options.ignoreCommandManager;
}
});
// ...
// Note that the last argument to set() is an options object that gets passed to the cmdBeforeAdd() function.
element.set({ foo: 'bar' }, { ignoreCommandManager: true });
A regular expression specifying what cell's attributes the CommandManager listens to. Default regex is /^(?:add|remove|change:\w+)$/
.
An array of options property names that are meant to be passed in undo actions. It defaults to ['propertyPath']
.
commandManager = new joint.dia.CommandManager({
revertOptionsList: ['myOptionAttribute1']
})
element.set('attribute', 'value', { myOptionAttribute1: 5, myOptionAttribute2: 6 });
commandManager.undo(); // -> calls element.set('attribute', 'prevValue', { myOptionAttribute1: 5 });
Alternatively the revertOptionList
can be defined as a function.
revertOptionsList: function(attrValue, attrName) {
return attrName !== 'doNotPassMe'; /* pass over everything except `doNotPassMe` attribute */
}
An array of options property names that are meant to be passed in redo actions. It defaults to ['propertyPath']
.
commandManager = new joint.dia.CommandManager({
applyOptionsList: ['myOptionAttribute1']
})
element.set('attribute', 'value', { myOptionAttribute1: 5, myOptionAttribute2: 6 });
commandManager.undo();
commandManager.redo(); // -> calls element.set('attribute', 'value', { myOptionAttribute1: 5 });
Alternatively the applyOptionList
can be defined as a function.
applyOptionsList: function(attrValue, attrName) {
return attrName !== 'doNotPassMe'; /* pass over everything except `doNotPassMe` attribute */
}
The maximum number of commands in the stack. If the number of commands exceeds the stackLimit
, the oldest commands in the undoStack are forgotten. It defaults to Infinity.
Function undo(opt)
travels the history one command back. It accepts an option parameter (opt
) that is applied when function makes changes to the graph. e.g commandManager.undo({ undoneBy: 'user123' })
Function redo(opt)
go back to whatever was previously undo()
ed.
Function cancel(opt)
same as undo()
but does not store the undo-ed command to the redoStack. Canceled command therefore cannot be redo-ed.
Function hasUndo()
true
if there is something in the undoStack.
Function hasRedo()
true
if there is something in the redoStack
The method squashUndo([n])
merges n
number of commands in the undo stack into a single batch command. If no n
is provided all commands are merged.
The method squashRedo([n])
merges n
number of commands in the redo stack into a single batch command. If no n
is provided all commands are merged.
Function reset()
clears both undoStack and redoStack.
The method exports the current state of the undo and redo stack to JSON. The export depends on the current state of graph and should be always accompanied by the graph export.
const graphJSON = graph.toJSON();
const historyJSON = commandManager.toJSON();
// a user-defined method to store data
await saveToDB({ historyJSON, graphJSON });
The method fromJSON(json, [opt])
imports an existing undo and redo stack.
// a user-defined method to retrieve data
const { historyJSON, graphJSON } = await loadFromDB();
graph.fromJSON(graphJSON);
commandManager.fromJSON(historyJSON);
Function initBatchCommand()
gives you the ability to gather multiple changes into a single command.
These commands could be revert with single undo()
call.
From the moment the function is called every change made on graph is not stored into the undoStack. Changes are temporarily kept in the CommandManager until storeBatchCommand()
is called.
Calling function storeBatchCommand()
tells the CommandManager to store all changes temporarily kept in the undoStack. In order to store changes you have to call this function as many times as initBatchCommand()
had been called.
The plugin fires the following events.
Event Name | Handler Signature | Description |
---|---|---|
stack:undo | (commandUndone, opt) |
Triggered when a command was undone. |
stack:redo | (commandRedone, opt) |
Triggered when a command were redone. |
stack:push | (commandPushedToUndoStack, opt) |
Triggered when a command were added to the stack. |
stack:cancel | (commandCanceled, opt) |
Triggered when a command was canceled. |
stack:reset | (opt) |
Triggered when all commands were reset or loaded from JSON. |
stack | (opt) |
Triggered when any change was made to stacks. |
commandManager.on('stack', function(opt) {
if (this.hasUndo()) { /* do something */ }
if (this.hasRedo()) { /* do something else */ }
});