src/whiteboard/WhiteboardHistory.js
/**
* @typedef {Object} WhiteboardHistoryItem
* @property {string} id the identifier of the stored shape.
* @property {string} previous previous shape state in stringified format.
* @property {string} current new shape state in stringified format.
*/
const MAX_HISTORY_LENGTH = 20;
/**
* Class that stores the changes made in the whiteboard and allows you to get the
* shapes that undo the changes introduced in the whiteboard. As long as no new
* changes are introduced this class also allows to obtain the shapes that reverts
* the changes made (redo action).
*/
export class WhiteboardHistory {
constructor() {
/**
* History pointer.
* @private
* @type {number}
*/
this._index = 0;
/**
* Changes introduced into history.
* @private
* @type {Array<WhiteboardHistoryItem>}
*/
this._history = [];
}
/**
* Reset the history.
*/
clear() {
this._index = 0;
this._history = [];
}
/**
* Get the previous state of a given shape.
* @private
* @param {string} id shape identifier.
* @return {Object} previous state of shape .
*/
_getPreviousShape(id) {
const item = this._history.slice(0, this._index)
.filter(x => x.id === id)
.pop();
return item ? item.current : JSON.stringify({id: id});
}
/**
* Add a new shape state to the history.
* @param {Object} shape the new state of the shape.
*/
add(shape) {
const id = shape.id;
const previous = this._getPreviousShape(id);
shape = JSON.stringify(shape);
if (previous === shape) {
return;
}
this._history.length = this._index;
this._history.push({
id: id,
previous: previous,
current: shape,
});
if (this._index >= MAX_HISTORY_LENGTH) {
this._history.shift();
} else {
this._index++;
}
}
/**
* Check if there are items stored in the history can be used to undo an action.
* @return {boolean} true if there is at least one item stored in the history.
*/
canUndo() {
return this._index > 0;
}
/**
* Check if there are items stored in the history that can be used to redo an action.
* @return {boolean} true if there is at least one item stored in the history.
*/
canRedo() {
return this._index < this._history.length;
}
/**
* Get a shape that can be used to undo last added shape.
* @return {Object} shape that reverts last change.
*/
undo() {
if (!this.canUndo()) {
return null;
}
const shape = JSON.parse(this._history[this._index - 1].previous);
this._index--;
return shape;
}
/**
* Get a shape that can be used to revert last undo action.
* @return {Object} shape that reverts last undo action.
*/
redo() {
if (!this.canRedo()) {
return null;
}
const shape = JSON.parse(this._history[this._index].current);
this._index++;
return shape;
}
}