UNPKG

handsontable

Version:

Handsontable is a JavaScript Data Grid available for React, Angular and Vue.

388 lines (372 loc) • 12.4 kB
"use strict"; require("core-js/modules/es.error.cause.js"); exports.__esModule = true; require("core-js/modules/es.array.push.js"); require("core-js/modules/es.set.difference.v2.js"); require("core-js/modules/es.set.intersection.v2.js"); require("core-js/modules/es.set.is-disjoint-from.v2.js"); require("core-js/modules/es.set.is-subset-of.v2.js"); require("core-js/modules/es.set.is-superset-of.v2.js"); require("core-js/modules/es.set.symmetric-difference.v2.js"); require("core-js/modules/es.set.union.v2.js"); var _base = require("../base"); var _hooks = require("../../core/hooks"); var _object = require("../../helpers/object"); var _templateLiteralTag = require("../../helpers/templateLiteralTag"); var _console = require("../../helpers/console"); var _actions = require("./actions"); function _classPrivateMethodInitSpec(e, a) { _checkPrivateRedeclaration(e, a), a.add(e); } function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); } function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } function _assertClassBrand(e, t, n) { if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n; throw new TypeError("Private element is not present on this object"); } const SHORTCUTS_GROUP = 'undoRedo'; const PLUGIN_KEY = exports.PLUGIN_KEY = 'undoRedo'; const PLUGIN_PRIORITY = exports.PLUGIN_PRIORITY = 1000; _hooks.Hooks.getSingleton().register('beforeUndo'); _hooks.Hooks.getSingleton().register('afterUndo'); _hooks.Hooks.getSingleton().register('beforeRedo'); _hooks.Hooks.getSingleton().register('afterRedo'); const deprecationWarns = new Set(); /** * @description * Handsontable UndoRedo plugin allows to undo and redo certain actions done in the table. * * __Note__, that not all actions are currently undo-able. The UndoRedo plugin is enabled by default. * @example * ```js * undo: true * ``` * @class UndoRedo * @plugin UndoRedo */ var _UndoRedo_brand = /*#__PURE__*/new WeakSet(); class UndoRedo extends _base.BasePlugin { static get PLUGIN_KEY() { return PLUGIN_KEY; } static get PLUGIN_PRIORITY() { return PLUGIN_PRIORITY; } static get SETTING_KEYS() { return true; } /** * The list of registered action do undo. * * @private * @type {Array} */ constructor(hotInstance) { super(hotInstance); /** * Listens to the data change and if the source is `loadData` then clears the undo and redo history. * * @param {Array} changes The data changes. * @param {string} source The source of the change. */ _classPrivateMethodInitSpec(this, _UndoRedo_brand); _defineProperty(this, "doneActions", []); /** * The list of registered action do redo. * * @private * @type {Array} */ _defineProperty(this, "undoneActions", []); /** * The flag that determines if new actions should be ignored. * * @private * @type {boolean} */ _defineProperty(this, "ignoreNewActions", false); (0, _actions.registerActions)(hotInstance, this); } /** * Checks if the plugin is enabled in the handsontable settings. This method is executed in {@link Hooks#beforeInit} * hook and if it returns `true` then the {@link UndoRedo#enablePlugin} method is called. * * @returns {boolean} */ isEnabled() { return !!this.hot.getSettings().undo; } /** * Enables the plugin functionality for this Handsontable instance. */ enablePlugin() { var _this = this; if (this.enabled) { return; } this.addHook('afterChange', function () { for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } return _assertClassBrand(_UndoRedo_brand, _this, _onAfterChange).call(_this, ...args); }); this.registerShortcuts(); _assertClassBrand(_UndoRedo_brand, this, _exposeAPIToCore).call(this); super.enablePlugin(); } /** * Disables the plugin functionality for this Handsontable instance. */ disablePlugin() { super.disablePlugin(); this.clear(); this.unregisterShortcuts(); _assertClassBrand(_UndoRedo_brand, this, _removeAPIFromCore).call(this); } /** * Registers shortcuts responsible for performing undo/redo. * * @private */ registerShortcuts() { const shortcutManager = this.hot.getShortcutManager(); const gridContext = shortcutManager.getContext('grid'); const runOnlyIf = event => { return !event.altKey; // right ALT in some systems triggers ALT+CTR }; const config = { runOnlyIf, group: SHORTCUTS_GROUP }; gridContext.addShortcuts([{ keys: [['Control/Meta', 'z']], callback: () => { this.undo(); } }, { keys: [['Control/Meta', 'y'], ['Control/Meta', 'Shift', 'z']], callback: () => { this.redo(); } }], config); } /** * Unregister shortcuts responsible for performing undo/redo. * * @private */ unregisterShortcuts() { const shortcutManager = this.hot.getShortcutManager(); const gridContext = shortcutManager.getContext('grid'); gridContext.removeShortcutsByGroup(SHORTCUTS_GROUP); } /** * Stash information about performed actions. * * @fires Hooks#beforeUndoStackChange * @fires Hooks#afterUndoStackChange * @fires Hooks#beforeRedoStackChange * @fires Hooks#afterRedoStackChange * @param {Function} wrappedAction The action descriptor wrapped in a closure. * @param {string} [source] Source of the action. It is defined just for more general actions (not related to plugins). */ done(wrappedAction, source) { if (this.ignoreNewActions) { return; } const isBlockedByDefault = source === 'UndoRedo.undo' || source === 'UndoRedo.redo' || source === 'auto'; if (isBlockedByDefault) { return; } const doneActionsCopy = this.doneActions.slice(); const continueAction = this.hot.runHooks('beforeUndoStackChange', doneActionsCopy, source); if (continueAction === false) { return; } const newAction = wrappedAction(); const undoneActionsCopy = this.undoneActions.slice(); this.doneActions.push(newAction); this.hot.runHooks('afterUndoStackChange', doneActionsCopy, this.doneActions.slice()); this.hot.runHooks('beforeRedoStackChange', undoneActionsCopy); this.undoneActions.length = 0; this.hot.runHooks('afterRedoStackChange', undoneActionsCopy, this.undoneActions.slice()); } /** * Undo the last action performed to the table. * * @fires Hooks#beforeUndoStackChange * @fires Hooks#afterUndoStackChange * @fires Hooks#beforeRedoStackChange * @fires Hooks#afterRedoStackChange * @fires Hooks#beforeUndo * @fires Hooks#afterUndo */ undo() { if (!this.isUndoAvailable()) { return; } const doneActionsCopy = this.doneActions.slice(); this.hot.runHooks('beforeUndoStackChange', doneActionsCopy); const action = this.doneActions.pop(); this.hot.runHooks('afterUndoStackChange', doneActionsCopy, this.doneActions.slice()); const actionClone = (0, _object.deepClone)(action); const continueAction = this.hot.runHooks('beforeUndo', actionClone); if (continueAction === false) { return; } this.ignoreNewActions = true; const undoneActionsCopy = this.undoneActions.slice(); this.hot.runHooks('beforeRedoStackChange', undoneActionsCopy); action.undo(this.hot, () => { this.ignoreNewActions = false; this.undoneActions.push(action); }); this.hot.runHooks('afterRedoStackChange', undoneActionsCopy, this.undoneActions.slice()); this.hot.runHooks('afterUndo', actionClone); } /** * Redo the previous action performed to the table (used to reverse an undo). * * @fires Hooks#beforeUndoStackChange * @fires Hooks#afterUndoStackChange * @fires Hooks#beforeRedoStackChange * @fires Hooks#afterRedoStackChange * @fires Hooks#beforeRedo * @fires Hooks#afterRedo */ redo() { if (!this.isRedoAvailable()) { return; } const undoneActionsCopy = this.undoneActions.slice(); this.hot.runHooks('beforeRedoStackChange', undoneActionsCopy); const action = this.undoneActions.pop(); this.hot.runHooks('afterRedoStackChange', undoneActionsCopy, this.undoneActions.slice()); const actionClone = (0, _object.deepClone)(action); const continueAction = this.hot.runHooks('beforeRedo', actionClone); if (continueAction === false) { return; } this.ignoreNewActions = true; const doneActionsCopy = this.doneActions.slice(); this.hot.runHooks('beforeUndoStackChange', doneActionsCopy); action.redo(this.hot, () => { this.ignoreNewActions = false; this.doneActions.push(action); }); this.hot.runHooks('afterUndoStackChange', doneActionsCopy, this.doneActions.slice()); this.hot.runHooks('afterRedo', actionClone); } /** * Checks if undo action is available. * * @returns {boolean} Return `true` if undo can be performed, `false` otherwise. */ isUndoAvailable() { return this.doneActions.length > 0; } /** * Checks if redo action is available. * * @returns {boolean} Return `true` if redo can be performed, `false` otherwise. */ isRedoAvailable() { return this.undoneActions.length > 0; } /** * Clears undo and redo history. */ clear() { this.doneActions.length = 0; this.undoneActions.length = 0; } /** * Destroys the plugin instance. */ destroy() { this.clear(); this.doneActions = null; this.undoneActions = null; super.destroy(); } } exports.UndoRedo = UndoRedo; function _onAfterChange(changes, source) { if (source === 'loadData') { this.clear(); } } /** * Expose the plugin API to the Core. It is for backward compatibility and it should be removed in the future. */ function _exposeAPIToCore() { const deprecatedWarn = methodName => { if (!deprecationWarns.has(methodName)) { (0, _console.warn)((0, _templateLiteralTag.toSingleLine)`The "${methodName}" method is deprecated and it will be removed\x20 from the Core API in the future. Please use the method from the UndoRedo plugin\x20 (e.g. \`hotInstance.getPlugin("undoRedo").${methodName}()\`).`); deprecationWarns.add(methodName); } }; /** * {@link UndoRedo#undo}. * * @alias undo * @memberof! Core# */ this.hot.undo = () => { deprecatedWarn('undo'); this.undo(); }; /** * {@link UndoRedo#redo}. * * @alias redo * @memberof! Core# */ this.hot.redo = () => { deprecatedWarn('redo'); this.redo(); }; /** * {@link UndoRedo#isUndoAvailable}. * * @alias isUndoAvailable * @memberof! Core# * @returns {boolean} */ this.hot.isUndoAvailable = () => { deprecatedWarn('isUndoAvailable'); return this.isUndoAvailable(); }; /** * {@link UndoRedo#isRedoAvailable}. * * @alias isRedoAvailable * @memberof! Core# * @returns {boolean} */ this.hot.isRedoAvailable = () => { deprecatedWarn('isRedoAvailable'); return this.isRedoAvailable(); }; /** * {@link UndoRedo#clear}. * * @alias clearUndo * @memberof! Core# */ this.hot.clearUndo = () => { deprecatedWarn('clear'); this.clear(); }; this.hot.undoRedo = this; } /** * Removes the plugin API from the Core. It is for backward compatibility and it should be removed in the future. */ function _removeAPIFromCore() { delete this.hot.undo; delete this.hot.redo; delete this.hot.isUndoAvailable; delete this.hot.isRedoAvailable; delete this.hot.clearUndo; delete this.hot.undoRedo; }