UNPKG

@typescript-package/history

Version:

A TypeScript package for tracking the history of values.

727 lines (715 loc) 18.2 kB
/** * @description * @export * @class RedoHistoryPeek * @template Type */ class RedoHistoryPeek { history; /** * Creates an instance of `RedoHistoryPeek` child class. * @constructor * @param {History<Type>} history */ constructor(history) { this.history = history; } /** * @description * @public * @param {number} [index=0] * @returns {(Type | undefined)} */ index(index = 0) { return this.history.redoHistory.peek(index); } /** * @description * @public * @returns {(Type | undefined)} */ last() { return this.history.redoHistory.peekLast(); } /** * @description * @public * @returns {(Type | undefined)} */ next() { return this.history.redoHistory.peekNext(); } } /** * @description The class * @export * @class UndoHistoryPeek * @template Type */ class UndoHistoryPeek { history; /** * Creates an instance of `UndoHistoryPeek` child class. * @constructor * @param {History<Type>} history */ constructor(history) { this.history = history; } /** * @description * @public * @param {number} [index=this.history.undoHistory.length - 1] * @returns {(Type | undefined)} */ index(index = this.history.undoHistory.length - 1) { return this.history.undoHistory.peek(index); } /** * @description * @public * @returns {(Type | undefined)} */ last() { return this.history.undoHistory.peekLast(); } /** * @description * @public * @returns {(Type | undefined)} */ next() { return this.history.undoHistory.peekNext(); } } /** * @description * @export * @class HistoryPeek * @template Type */ class HistoryPeek { /** * @description * @public * @readonly * @type {*} */ get redo() { return this.#redo; } /** * @description * @public * @readonly * @type {*} */ get undo() { return this.#undo; } /** * @description * @readonly * @type {*} */ #undo; /** * @description * @readonly * @type {*} */ #redo; /** * Creates an instance of `HistoryPeek`. * @constructor * @param {History<Type>} history */ constructor(history) { this.#redo = new RedoHistoryPeek(history); this.#undo = new UndoHistoryPeek(history); } } var _a; /** * @description The core class for history append and prepend. * @export * @abstract * @class HistoryCore * @template Type * @template {number} [Size=number] */ class HistoryCore { /** * @description A private property for history of `WeakMap` type. * @static * @readonly * @type {WeakMap} */ static #history = new WeakMap(); /** * @description Returns the `string` tag representation of the `HistoryCore` class when used in `Object.prototype.toString.call(instance)`. * @public * @readonly * @type {string} */ get [Symbol.toStringTag]() { return _a.name; } /** * @description Gets the history. * @protected * @readonly * @type {Type[]} */ get history() { return _a.#history.get(this); } /** * @description The length of the history. * @public * @readonly * @type {number} */ get length() { return this.history.length; } /** * @description The maximum size of the history. * @public * @readonly * @type {Size} */ get size() { return this.#size; } /** * @description The maximum size of the history. * @type {number} */ #size; /** * Creates an instance of `HistoryCore` child class. * @constructor * @param {Size} size */ constructor(size) { this.#initialize(); this.#size = size; } /** * @description Clears the history. * @public * @returns {this} The current instance. */ clear() { const history = this.history; history.length = 0; return this; } /** * @description Destroys the history of this instance. * @public * @returns {this} The current instance. */ destroy() { _a.#history.delete(this); return this; } /** * @description Gets the history. * @public * @returns {Readonly<Type[]>} */ get() { return this.history; } /** * @description Returns the value at the specified index in the history. * @public * @param {number} index The position in the history (0-based index). * @returns {Type | undefined} The value at the specified position. */ peek(index) { return this.history[index]; } /** * @description Initializes the history by setting an empty `Array`. * @private * @returns {this} The current instance. */ #initialize() { _a.#history.set(this, []); return this; } } _a = HistoryCore; // Abstract. /** * @description Class extends the `HistoryCore` class to maintain a history of values in a prepend manner. * This means that new entries are added to the beginning of the history, and older entries are shifted out as the history size exceeds its limit. * @export * @abstract * @class HistoryPrepend * @template Type * @template {number} [Size=number] */ class HistoryPrepend extends HistoryCore { /** * @description The default value of maximum history size. * @public * @static * @type {number} */ static size = 10; /** * @description Returns the `string` tag representation of the `HistoryPrepend` class when used in `Object.prototype.toString.call(instance)`. * @public * @readonly * @type {string} */ get [Symbol.toStringTag]() { return HistoryPrepend.name; } /** * Creates an instance of `HistoryPrepend` child class. * @constructor * @param {Size} [size=HistoryPrepend.size as Size] */ constructor(size = HistoryPrepend.size) { super(size); } /** * @description Adds the value to the history in a backward manner. * @public * @param {Type} value The value to store. * @returns {this} The current instance. */ add(value) { const history = super.history; history.length >= super.size && history.pop(); history.unshift(value); return this; } /** * @description Returns the last value that would be redone without modifying history. * @public * @returns {Type | undefined} The next redo value. */ peekLast() { return super.history[super.history.length - 1]; } /** * @description Returns the next value that would be redone without modifying history. * @public * @returns {Type | undefined} The next redo value. */ peekNext() { return super.history[0]; } /** * @description Takes the first value. * @public * @returns {(Type | undefined)} */ take() { return super.history.shift(); } } // Abstract. /** * @description Manages the redo history with prepend mechanism. * @export * @class RedoHistory * @template [Type=any] The type of elements stored in the history * @template {number} [Size=number] The maximum size of the history. * @extends {HistoryPrepend<Type, Size>} */ class RedoHistory extends HistoryPrepend { } ; // Abstract. /** * @description Class extends the `HistoryCore` class to maintain a history of values in a append manner. * This means that new entries are added to the end of the history, and as the history exceeds its size limit, entries from the beginning are removed. * - LIFO(Last in, First out). * @export * @abstract * @class HistoryAppend * @template Type * @template {number} [Size=number] */ class HistoryAppend extends HistoryCore { /** * @description The default value of maximum history size. * @public * @static * @type {number} */ static size = 10; /** * @description Returns the `string` tag representation of the `HistoryAppend` class when used in `Object.prototype.toString.call(instance)`. * @public * @readonly * @type {string} */ get [Symbol.toStringTag]() { return HistoryAppend.name; } /** * Creates an instance of `HistoryAppend` child class. * @constructor * @param {Size} [size=HistoryAppend.size as Size] */ constructor(size = HistoryAppend.size) { super(size); } /** * @description Adds the value to the history. * @public * @param {Type} value The value to store. * @returns {this} The current instance. */ add(value) { const history = super.history; history.length >= super.size && history.shift(); history.push(value); return this; } /** * @description Returns the last value that would be use to redo or undo without modifying history. * @public * @returns {Type | undefined} The next redo value. */ peekLast() { return super.history[0]; } /** * @description Returns the next value that would be use to redo or undo without modifying history. * @public * @returns {Type | undefined} The next redo value. */ peekNext() { return super.history[super.history.length - 1]; } /** * @description Takes the last value. * @public * @returns {(Type | undefined)} */ take() { return super.history.pop(); } } // Abstract. /** * @description Manages the undo history with append mechanism. * @export * @class UndoHistory * @template [Type=any] The type of elements stored in the history * @template {number} [Size=number] The maximum size of the history. * @extends {HistoryAppend<Type, Size>} */ class UndoHistory extends HistoryAppend { } ; // Abstract. /** * @description The class to manage the value changes. * @export * @abstract * @class History * @template Type * @template {number} [Size=number] */ class History { /** * @description The max size for undo history. * @public * @static * @type {number} */ static size = Infinity; /** * @description Returns the `string` tag representation of the `History` class when used in `Object.prototype.toString.call(instance)`. * @public * @readonly * @type {string} */ get [Symbol.toStringTag]() { return History.name; } /** * @description Gets the current value stored in history. * @public * @readonly * @type {Type} */ get current() { return this.#current.value; } /** * @description * @public * @readonly * @type {HistoryPeek<Type>} */ get peek() { return this.#peek; } /** * @description Returns the redo history. * @public * @readonly * @type {RedoHistory<any, number>} */ get redoHistory() { return this.#redo; } /** * @description Returns the undo history. * @public * @readonly * @type {UndoHistory<any, number>} */ get undoHistory() { return this.#undo; } /** * @description A private field to store the current value. * @type {{ value?: Type }} */ #current = {}; /** * @description * @type {boolean} */ #hasSetBeenCalled = false; /** * @description Privately stored callback function invoked on redo. * @type {(value: Type) => void} */ #onRedoCallback; /** * @description Privately stored callback function invoked on undo. * @type {(value: Type) => void} */ #onUndoCallback; /** * @description * @type {HistoryPeek} */ #peek; /** * @description The class to manage the redo history. * @type {RedoHistory} */ #redo; /** * @description The class to manage the undo history. * @type {UndoHistory} */ #undo; /** * Creates an instance of `History` child class. * @constructor * @param {{value?: Type, size?: Size}} param0 * @param {Type} param0.value * @param {Size} param0.size */ constructor({ value, size } = {}) { this.#peek = new HistoryPeek(this); this.#redo = new RedoHistory(Infinity); this.#undo = new UndoHistory(size || History.size); Object.hasOwn(arguments[0] || {}, 'value') && ((this.#hasSetBeenCalled = true), (this.#current = { value })); } /** * @description Clears the `undo` and `redo` history, removes the current value, and resets the `hasSetBeenCalled`. * @public * @returns {this} The current instance. */ clear() { delete this.#current.value; this.#hasSetBeenCalled = false; this.#redo.clear(); this.#undo.clear(); return this; } /** * @description Destroys the history of this instance. * @public * @returns {this} The current instance. */ destroy() { this.clear(); this.#undo.destroy(); this.#redo.destroy(); return this; } /** * @description Gets the undo and redo history. * @public * @returns {{ undo: Readonly<Type[]>; redo: Readonly<Type[]> }} */ get() { return { undo: this.#undo.get(), redo: this.#redo.get(), }; } /** * @description The instance method returns read-only redo history. * @public * @template Type * @returns {(Readonly<Type[]> | undefined)} */ getRedo() { return this.#redo.get(); } /** * @description The instance method returns read-only undo history. * @public * @template Type * @returns {(Readonly<Type[]> | undefined)} */ getUndo() { return this.#undo.get(); } /** * @description Checks whether the current value is set. * @public * @returns {boolean} */ hasCurrent() { return Object.hasOwn(this.#current, 'value'); } /** * @description Sets the callback function invoked on redo. * @public * @param {(value: Type) => void} callbackFn The callback function to invoke. * @returns {this} */ onRedo(callbackFn) { this.#onRedoCallback = callbackFn; return this; } /** * @description Sets the callback function invoked on undo. * @public * @param {(value: Type) => void} callbackFn The callback function to invoke. * @returns {this} */ onUndo(callbackFn) { this.#onUndoCallback = callbackFn; return this; } /** * @description Returns the specified by index value from redo history. * @public * @param {number} [index=0] * @returns {(Type | undefined)} */ peekRedo(index = 0) { return this.#redo.peek(index); } /** * @description Returns the specified by index value from undo history. * @public * @param {number} [index=this.#undo.length - 1] * @returns {(Type | undefined)} */ peekUndo(index = this.#undo.length - 1) { return this.#undo.peek(index); } /** * @description Returns the last value that would be redone without modifying history. * @public * @returns {Type | undefined} The last redo value. */ peekLastRedo() { return this.#redo.peekLast(); } /** * @description Returns the last value that would be undone without modifying history. * @public * @returns {Type | undefined} The last undo value. */ peekLastUndo() { return this.#undo.peekLast(); } /** * @description Returns the next value that would be redone without modifying history. * @public * @returns {Type | undefined} The next redo value. */ peekNextRedo() { return this.#redo.peekNext(); } /** * @description Returns the next value that would be undone without modifying history. * @public * @returns {Type | undefined} The next undo value. */ peekNextUndo() { return this.#undo.peekNext(); } /** * @description Pick the undo or redo history. * @public * @param {('undo' | 'redo')} type * @returns {Readonly<Type[]>} */ pick(type) { return (type === 'undo' ? this.#undo : this.#redo).get(); } /** * @description Redoes the last undone action. * @public * @returns {this} The current instance. */ redo() { const redo = this.#redo; if (redo.get()?.length) { const value = redo.take(); this.#undo.add(this.#current.value); this.#current = { value }; this.#onRedoCallback?.(value); } return this; } /** * @description Sets a new value and updates the undo history. * @public * @param {Type} value * @returns {this} The current instance. */ set(value) { this.#hasSetBeenCalled === true && this.#undo.add(this.#current.value); this.#current = { value }; this.#redo.clear(); this.#hasSetBeenCalled === false && (this.#hasSetBeenCalled = true); return this; } /** * @description Undoes the last action and moves it to redo history. * @public * @returns {this} The current instance. */ undo() { const undo = this.#undo; if (undo.get()?.length) { const value = undo.take(); this.#redo.add(this.#current.value); this.#current = { value }; this.#onUndoCallback?.(value); } return this; } } /* * Public API Surface of history */ /** * Generated bundle index. Do not edit. */ export { History, HistoryAppend, HistoryCore, HistoryPrepend }; //# sourceMappingURL=typescript-package-history.mjs.map