Skip to main content

CommandManager

The plugin allows you to undo and redo changes made to the graph. It also provides a transaction mechanism to group multiple changes into a single command.

To learn more about the plugin, check out the Undo & Redo section.

constructor

constructor(options: CommandManager.Options);

The dia.CommandManager constructor accepts several parameters:

graph

Is the graph the CommandManager listens to.

cmdBeforeAdd

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 dia.CommandManager({
graph,
cmdBeforeAdd: (cmdName, cell, collection, 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 });

cmdNameRegex

A regular expression specifying what cell's attributes the CommandManager listens to.

The default regex is /^(?:add|remove|change:\w+)$/.

revertOptionsList

An array of options property names that are meant to be passed in undo actions. It defaults to ['propertyPath'].

const commandManager = new dia.CommandManager({
/* ... */
revertOptionsList: ['myOptionAttribute1']
});

element.set('attribute', 'value', { myOptionAttribute1: 5, myOptionAttribute2: 6 });

/* `undo()` calls element.set('attribute', 'prevValue', { myOptionAttribute1: 5 }); */
commandManager.undo();

Alternatively the revertOptionList can be defined as a function.

/* pass over everything except `doNotPassMe` attribute */
revertOptionsList: (attrValue, attrName) => attrName !== 'doNotPassMe';

applyOptionsList

An array of options property names that are meant to be passed in redo actions. It defaults to ['propertyPath'].

const commandManager = new dia.CommandManager({
/* ... */
applyOptionsList: ['myOptionAttribute1']
});

element.set('attribute', 'value', { myOptionAttribute1: 5, myOptionAttribute2: 6 });
commandManager.undo();

/* `redo()` calls element.set('attribute', 'value', { myOptionAttribute1: 5 }); */
commandManager.redo();

Alternatively the applyOptionList can be defined as a function.

/* pass over everything except `doNotPassMe` attribute */
applyOptionsList: (attrValue, attrName) => attrName !== 'doNotPassMe';

stackLimit

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.

Methods

undo()

commandManager.undo(opt?: CommandManager.EventOptions): void;

The undo() method 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' });

redo()

commandManager.redo(opt?: CommandManager.EventOptions): void;

The redo() method travels the history one command forward (to what was previously undo()ed). It accepts an option parameter (opt) that is applied when function makes changes to the graph. e.g.

commandManager.redo({ redoneBy: 'user123' });

cancel()

commandManager.cancel(opt?: CommandManager.EventOptions): void;

Function is the same as undo() but does not store the undo-ed command to the redoStack. Canceled command therefore cannot be redo-ed. It accepts an option parameter (opt) that is applied when function makes changes to the graph. e.g.

commandManager.cancel({ canceledBy: 'user123' });

hasUndo()

commandManager.hasUndo(): boolean;

Function returns true if there is something in the undoStack.

hasRedo()

commandManager.hasRedo(): boolean;

Function returns true if there is something in the redoStack.

squashUndo()

commandManager.squashUndo(n?: number): void;

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.

squashRedo()

commandManager.squashRedo(n?: number): void;

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.

reset()

commandManager.reset(opt?: CommandManager.EventOptions): void;

Function reset() clears both undoStack and redoStack.

toJSON()

commandManager.toJSON(): CommandManager.JSON;

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 });

fromJSON()

commandManager.fromJSON(
json: CommandManager.JSON,
opt?: CommandManager.EventOptions
): void;

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);

initBatchCommand()

commandManager.initBatchCommand(): void;

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.

storeBatchCommand()

commandManager.storeBatchCommand(opt?: CommandManager.EventOptions): void;

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.

Properties

There are several properties that can be accessed:

undoStack

commandManager.undoStack: CommandManager.Commands;

The undoStack is an array of commands that can be reverted. It is automatically populated when the graph is modified.

redoStack

commandManager.redoStack: CommandManager.Commands;

The redoStack is an array of commands that has been reverted and can be re-applied.

info

The redoStack is reset every time the graph is modified other than by redo() or undo().

Events

The plugin fires the following events:

stack:undo

Triggered when a command was undone.

commandManager.on('stack:undo', (
commandUndone: CommandManager.BatchCommand,
opt: CommandManager.EventOptions
) => {
// the `commandUndone` has been reverted
});

stack:redo

Triggered when a command were redone.

commandManager.on('stack:redo', (
commandRedone: CommandManager.BatchCommand,
opt: CommandManager.EventOptions
) => {
// the `commandRedone` has been re-applied
});

stack:push

Triggered when a command were added to the undoStack.

commandManager.on('stack:push', (
commandPushedToUndoStack: CommandManager.BatchCommand,
opt: CommandManager.EventOptions
) => {
// the `commandPushedToUndoStack` has been added to the undo stack
});

stack:cancel

Triggered when a command was canceled.

commandManager.on('stack:cancel', (
commandCanceled: CommandManager.BatchCommand,
opt: CommandManager.EventOptions
) => {
// the `commandCanceled` has been canceled (undone and removed from history)
});

stack:reset

Triggered when all commands were reset or loaded from JSON.

commandManager.on('stack:reset', (opt: CommandManager.EventOptions) => {
// the stack has been reset
});

stack

Triggered when any change was made to stacks.

commandManager.on('stack', (opt: CommandManager.EventOptions) => {
if (commandManager.hasUndo()) { /* do something */ }
if (commandManager.hasRedo()) { /* do something else */ }
});

Types

The plugin uses the following types:

BatchCommand

An array of commands.

type BatchCommand = Command[];

Command

A structure representing a single command.

interface Command {
// is part of a batch command?
batch: boolean;
// the type of graph change
// (e.g. 'add', 'remove', 'change:attrs', ...)
action: string;
// options when the graph change set(), prop(), attr() methods.
options: any;
// is graph model or a cell changed
graphChange: boolean;
// see `CommandData` interface
data: CommandData;
}

CommandData

The delta of the cell/graph attributes.

interface CommandData {
// the cell id
id: string | number;
// the cell type
type: string;
// the cell previous state if action is `change`
previous?: Partial<Cell.Attributes>;
// the cell next state if action is `change`
next?: Partial<Cell.Attributes>;
// the cell attributes if action is `add` or `remove`
attributes?: Cell.Attributes;
}

Commands

An array of commands or batch commands.

type Commands = Array<Command | BatchCommand>;

EventOptions

interface EventOptions {
[key: string]: any;
}

JSON

interface JSON {
undoStack: BatchCommand[];
redoStack: BatchCommand[];
[key: string]: any;
}

Options

interface Options {
graph: dia.Graph;
cmdBeforeAdd?: (eventName: string, ...eventArgs: any[]) => boolean;
cmdNameRegex?: RegExp;
applyOptionsList?: string[] | ReduceOptionsCallback;
revertOptionsList?: string[] | ReduceOptionsCallback;
stackLimit?: number;
}

ReduceOptionsCallback

type ReduceOptionsCallback = (attrValue: any, attrName: string) => boolean;