UNPKG

@macfja/svelte-undoable

Version:
236 lines (229 loc) 8.54 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Undoable = {})); }(this, (function (exports) { 'use strict'; function noop() { } function safe_not_equal(a, b) { return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function'); } const subscriber_queue = []; /** * Create a `Writable` store that allows both updating and reading by subscription. * @param {*=}value initial value * @param {StartStopNotifier=}start start and stop notifications for subscriptions */ function writable(value, start = noop) { let stop; const subscribers = []; function set(new_value) { if (safe_not_equal(value, new_value)) { value = new_value; if (stop) { // store is ready const run_queue = !subscriber_queue.length; for (let i = 0; i < subscribers.length; i += 1) { const s = subscribers[i]; s[1](); subscriber_queue.push(s, value); } if (run_queue) { for (let i = 0; i < subscriber_queue.length; i += 2) { subscriber_queue[i][0](subscriber_queue[i + 1]); } subscriber_queue.length = 0; } } } } function update(fn) { set(fn(value)); } function subscribe(run, invalidate = noop) { const subscriber = [run, invalidate]; subscribers.push(subscriber); if (subscribers.length === 1) { stop = start(set) || noop; } run(value); return () => { const index = subscribers.indexOf(subscriber); if (index !== -1) { subscribers.splice(index, 1); } if (subscribers.length === 0) { stop(); stop = null; } }; } return { set, update, subscribe }; } /* * Copyright MacFJA * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** * Create a store with undo/redo feature * @param {*} initial The initial value of the store * @param {number?} capacity The maximum number of entry to remember (any number lower than 2 is considered as infinite size) * @param {function(*: newValue): boolean} accept The validation function to accept a value to be save in memory.<br/> * Take the new store value as parameter, should return a boolean (`true` to save the value, `false` to dismiss it).<br/> * If ignore, it will accept all value * @return {UndoableStore<*>} */ function undoable(initial, capacity, accept) { let states = [initial]; let inAction = true; let index = 0; const store = writable(initial); if (typeof accept !== "function") { accept = () => true; } store.subscribe((newValue) => { if (!inAction && accept(newValue)) { if (states.length > index + 1) { states = states.slice(0, index + 1); } states.push(newValue); index++; if (capacity > 1 && states.length > capacity) { states.shift(); index = states.length; } } }); inAction = false; /** * @internal * Change the value to the previous saved state */ const undo = () => { inAction = true; if (index > 0) { index--; } store.set(states[index]); inAction = false; }; /** * @internal * Change the value to the next saved state */ const redo = () => { inAction = true; if (index < states.length - 1) { index++; } store.set(states[index]); inAction = false; }; /** * @internal * Indicate if the store value can be revert to a previous state * @return {boolean} */ const canUndo = () => { return index > 0; }; /** * @internal * Indicate if the store value can be change to a next state * @return {boolean} */ const canRedo = () => { return index < states.length - 1; }; /** * @internal * Revert the value of the store to the oldest state. * If the parameter is `true`, then the store state history is cleared * @param {boolean?} clear If `true` the history is cleared */ const reset = (clear) => { inAction = true; index = 0; store.set(states[index]); if (clear) { states = states.slice(0, 1); } inAction = false; }; /** * @internal * Get the number of saved states * @return {number} */ const length = () => { return states.length; }; return { subscribe: store.subscribe, set: store.set, update: store.update, undo, redo, canUndo, canRedo, reset, length, }; } /** * Change the value to the previous saved state * @param {UndoableStore<*>} undoableStore The store to use */ function undo(undoableStore) { undoableStore.undo(); } /** * Change the value to the next saved state * @param {UndoableStore<*>} undoableStore The store to use */ function redo(undoableStore) { undoableStore.redo(); } /** * Indicate if the store value can be revert to a previous state * @param {UndoableStore<*>} undoableStore The store to use * @return {boolean} */ function canUndo(undoableStore) { return undoableStore.canUndo(); } /** * Indicate if the store value can be change to a next state * @param {UndoableStore<*>} undoableStore The store to use * @return {boolean} */ function canRedo(undoableStore) { return undoableStore.canRedo(); } /** * Revert the value of the store to the oldest state. * If the second parameter is `true`, then the store state history is cleared * @param {UndoableStore<*>} undoableStore The store to use * @param {boolean} clear If `true` the history is cleared */ function reset(undoableStore, clear) { undoableStore.reset(clear); } exports.canRedo = canRedo; exports.canUndo = canUndo; exports.redo = redo; exports.reset = reset; exports.undo = undo; exports.undoable = undoable; Object.defineProperty(exports, '__esModule', { value: true }); })));