UNPKG

handsontable

Version:

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

282 lines (271 loc) • 12.3 kB
"use strict"; exports.__esModule = true; require("core-js/modules/es.error.cause.js"); var _console = require("./helpers/console"); var _element = require("./helpers/dom/element"); var _function = require("./helpers/function"); function _classPrivateMethodInitSpec(e, a) { _checkPrivateRedeclaration(e, a), a.add(e); } function _classPrivateFieldInitSpec(e, t, a) { _checkPrivateRedeclaration(e, t), t.set(e, a); } function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); } function _classPrivateFieldGet(s, a) { return s.get(_assertClassBrand(s, a)); } function _classPrivateFieldSet(s, a, r) { return s.set(_assertClassBrand(s, a), r), r; } 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"); } /** * Possible focus modes. * - CELL - The browser's focus stays on the lastly selected cell element. * - MIXED - The browser's focus switches from the lastly selected cell element to the currently active editor's * `TEXTAREA` element after a delay defined in the manager. * * @type {{CELL: string, MIXED: string}} */ const FOCUS_MODES = Object.freeze({ CELL: 'cell', MIXED: 'mixed' }); /** * Manages the browser's focus in the table. */ var _hot = /*#__PURE__*/new WeakMap(); var _focusMode = /*#__PURE__*/new WeakMap(); var _refocusDelay = /*#__PURE__*/new WeakMap(); var _refocusElementGetter = /*#__PURE__*/new WeakMap(); var _debouncedSelect = /*#__PURE__*/new WeakMap(); var _FocusManager_brand = /*#__PURE__*/new WeakSet(); class FocusManager { constructor(hotInstance) { var _this = this; /** * Get and return the currently selected and highlighted cell/header element. * * @param {Function} callback Callback function to be called after the cell element is retrieved. */ _classPrivateMethodInitSpec(this, _FocusManager_brand); /** * The Handsontable instance. */ _classPrivateFieldInitSpec(this, _hot, void 0); /** * The currently enabled focus mode. * Can be either: * * - 'cell' - The browser's focus stays on the lastly selected cell element. * - 'mixed' - The browser's focus switches from the lastly selected cell element to the currently active editor's * `TEXTAREA` element after a delay defined in the manager. * * @type {'cell' | 'mixed'} */ _classPrivateFieldInitSpec(this, _focusMode, void 0); /** * The delay after which the focus switches from the lastly selected cell to the active editor's `TEXTAREA` * element if the focus mode is set to 'mixed'. * * @type {number} */ _classPrivateFieldInitSpec(this, _refocusDelay, 1); /** * Getter function for the element to be used when refocusing the browser after a delay. If `null`, the active * editor's `TEXTAREA` element will be used. * * @type {null|Function} */ _classPrivateFieldInitSpec(this, _refocusElementGetter, null); /** * Map of the debounced `select` functions. * * @type {Map<number, Function>} */ _classPrivateFieldInitSpec(this, _debouncedSelect, new Map()); const hotSettings = hotInstance.getSettings(); _classPrivateFieldSet(_hot, this, hotInstance); _classPrivateFieldSet(_focusMode, this, hotSettings.imeFastEdit ? FOCUS_MODES.MIXED : FOCUS_MODES.CELL); _classPrivateFieldGet(_hot, this).addHook('afterUpdateSettings', function () { for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } return _assertClassBrand(_FocusManager_brand, _this, _onUpdateSettings).call(_this, ...args); }); _classPrivateFieldGet(_hot, this).addHook('afterSelection', function () { for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { args[_key2] = arguments[_key2]; } return _assertClassBrand(_FocusManager_brand, _this, _focusCell).call(_this, ...args); }); _classPrivateFieldGet(_hot, this).addHook('afterSelectionFocusSet', function () { for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { args[_key3] = arguments[_key3]; } return _assertClassBrand(_FocusManager_brand, _this, _focusCell).call(_this, ...args); }); _classPrivateFieldGet(_hot, this).addHook('afterSelectionEnd', function () { for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) { args[_key4] = arguments[_key4]; } return _assertClassBrand(_FocusManager_brand, _this, _focusEditorElement).call(_this, ...args); }); } /** * Get the current focus mode. * * @returns {'cell' | 'mixed'} */ getFocusMode() { return _classPrivateFieldGet(_focusMode, this); } /** * Set the focus mode. * * @param {'cell' | 'mixed'} focusMode The new focus mode. */ setFocusMode(focusMode) { if (Object.values(FOCUS_MODES).includes(focusMode)) { _classPrivateFieldSet(_focusMode, this, focusMode); } else { (0, _console.warn)(`"${focusMode}" is not a valid focus mode.`); } } /** * Get the delay after which the focus will change from the cell elements to the active editor's `TEXTAREA` * element if the focus mode is set to 'mixed'. * * @returns {number} Delay in milliseconds. */ getRefocusDelay() { return _classPrivateFieldGet(_refocusDelay, this); } /** * Set the delay after which the focus will change from the cell elements to the active editor's `TEXTAREA` * element if the focus mode is set to 'mixed'. * * @param {number} delay Delay in milliseconds. */ setRefocusDelay(delay) { _classPrivateFieldSet(_refocusDelay, this, delay); } /** * Set the function to be used as the "refocus element" getter. It should return a focusable HTML element. * * @param {Function} getRefocusElementFunction The refocus element getter. */ setRefocusElementGetter(getRefocusElementFunction) { _classPrivateFieldSet(_refocusElementGetter, this, getRefocusElementFunction); } /** * Get the element to be used when refocusing the browser after a delay in case of the focus mode being 'mixed'. * * @returns {HTMLTextAreaElement|HTMLElement|undefined} */ getRefocusElement() { var _classPrivateFieldGet2; if (typeof _classPrivateFieldGet(_refocusElementGetter, this) === 'function') { return _classPrivateFieldGet(_refocusElementGetter, this).call(this); } return (_classPrivateFieldGet2 = _classPrivateFieldGet(_hot, this).getActiveEditor()) === null || _classPrivateFieldGet2 === void 0 ? void 0 : _classPrivateFieldGet2.TEXTAREA; } /** * Set the browser's focus to the highlighted cell of the last selection. * * @param {HTMLTableCellElement} [selectedCell] The highlighted cell/header element. */ focusOnHighlightedCell(selectedCell) { const focusElement = element => { var _classPrivateFieldGet3, _classPrivateFieldGet4; const currentHighlightCoords = (_classPrivateFieldGet3 = _classPrivateFieldGet(_hot, this).getSelectedRangeActive()) === null || _classPrivateFieldGet3 === void 0 ? void 0 : _classPrivateFieldGet3.highlight; if (!currentHighlightCoords) { return; } let elementToBeFocused = _classPrivateFieldGet(_hot, this).runHooks('modifyFocusedElement', currentHighlightCoords.row, currentHighlightCoords.col, element); if (!(0, _element.isHTMLElement)(elementToBeFocused)) { elementToBeFocused = element; } if (elementToBeFocused && !((_classPrivateFieldGet4 = _classPrivateFieldGet(_hot, this).getActiveEditor()) !== null && _classPrivateFieldGet4 !== void 0 && _classPrivateFieldGet4.isOpened())) { elementToBeFocused.focus({ preventScroll: true }); } }; if (selectedCell) { focusElement(selectedCell); } else { _assertClassBrand(_FocusManager_brand, this, _getSelectedCell).call(this, element => focusElement(element)); } } /** * Set the focus to the active editor's `TEXTAREA` element after the provided delay. If no delay is provided, it * will be taken from the manager's configuration. * * @param {number} [delay] Delay in milliseconds. */ refocusToEditorTextarea() { var _classPrivateFieldGet5; let delay = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _classPrivateFieldGet(_refocusDelay, this); // Re-focus on the editor's `TEXTAREA` element (or a predefined element) if the `imeFastEdit` option is enabled. if (_classPrivateFieldGet(_hot, this).getSettings().imeFastEdit && !((_classPrivateFieldGet5 = _classPrivateFieldGet(_hot, this).getActiveEditor()) !== null && _classPrivateFieldGet5 !== void 0 && _classPrivateFieldGet5.isOpened())) { var _classPrivateFieldGet6, _classPrivateFieldGet7; (_classPrivateFieldGet6 = _classPrivateFieldGet(_hot, this).getActiveEditor()) === null || _classPrivateFieldGet6 === void 0 || (_classPrivateFieldGet7 = _classPrivateFieldGet6.refreshValue) === null || _classPrivateFieldGet7 === void 0 || _classPrivateFieldGet7.call(_classPrivateFieldGet6); if (!_classPrivateFieldGet(_debouncedSelect, this).has(delay)) { _classPrivateFieldGet(_debouncedSelect, this).set(delay, (0, _function.debounce)(() => { if (!_classPrivateFieldGet(_hot, this).isDestroyed) { var _this$getRefocusEleme; (_this$getRefocusEleme = this.getRefocusElement()) === null || _this$getRefocusEleme === void 0 || _this$getRefocusEleme.select(); } }, delay)); } _classPrivateFieldGet(_debouncedSelect, this).get(delay)(); } } } exports.FocusManager = FocusManager; function _getSelectedCell(callback) { var _classPrivateFieldGet8; const highlight = (_classPrivateFieldGet8 = _classPrivateFieldGet(_hot, this).getSelectedRangeActive()) === null || _classPrivateFieldGet8 === void 0 ? void 0 : _classPrivateFieldGet8.highlight; if (!highlight || !_classPrivateFieldGet(_hot, this).selection.isCellVisible(highlight)) { callback(null); return; } const cell = _classPrivateFieldGet(_hot, this).getCell(highlight.row, highlight.col, true); if (cell === null) { _classPrivateFieldGet(_hot, this).addHookOnce('afterScroll', () => { callback(_classPrivateFieldGet(_hot, this).getCell(highlight.row, highlight.col, true)); }); } else { callback(cell); } } /** * Manage the browser's focus after each cell selection change. */ function _focusCell() { _assertClassBrand(_FocusManager_brand, this, _getSelectedCell).call(this, selectedCell => { const { activeElement } = _classPrivateFieldGet(_hot, this).rootDocument; // Blurring the `activeElement` removes the unwanted border around the focusable element (#6877) // and resets the `document.activeElement` property. The blurring should happen only when the // previously selected input element has not belonged to the Handsontable editor. If blurring is // triggered for all elements, there is a problem with the disappearing IME editor (#9672). if (activeElement && (0, _element.isOutsideInput)(activeElement)) { activeElement.blur(); } this.focusOnHighlightedCell(selectedCell); }); } /** * Manage the browser's focus after cell selection end. */ function _focusEditorElement() { _assertClassBrand(_FocusManager_brand, this, _getSelectedCell).call(this, selectedCell => { if (this.getFocusMode() === FOCUS_MODES.MIXED && (selectedCell === null || selectedCell === void 0 ? void 0 : selectedCell.nodeName) === 'TD') { this.refocusToEditorTextarea(); } }); } /** * Update the manager configuration after calling `updateSettings`. * * @param {object} newSettings The new settings passed to the `updateSettings` method. */ function _onUpdateSettings(newSettings) { if (typeof newSettings.imeFastEdit === 'boolean') { this.setFocusMode(newSettings.imeFastEdit ? FOCUS_MODES.MIXED : FOCUS_MODES.CELL); } }