UNPKG

11.7 kBJavaScriptView Raw
1"use strict";
2
3exports.__esModule = true;
4require("core-js/modules/es.error.cause.js");
5var _console = require("./helpers/console");
6var _element = require("./helpers/dom/element");
7var _function = require("./helpers/function");
8function _classPrivateMethodInitSpec(e, a) { _checkPrivateRedeclaration(e, a), a.add(e); }
9function _classPrivateFieldInitSpec(e, t, a) { _checkPrivateRedeclaration(e, t), t.set(e, a); }
10function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); }
11function _classPrivateFieldGet(s, a) { return s.get(_assertClassBrand(s, a)); }
12function _classPrivateFieldSet(s, a, r) { return s.set(_assertClassBrand(s, a), r), r; }
13function _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"); }
14/**
15 * Possible focus modes.
16 * - CELL - The browser's focus stays on the lastly selected cell element.
17 * - MIXED - The browser's focus switches from the lastly selected cell element to the currently active editor's
18 * `TEXTAREA` element after a delay defined in the manager.
19 *
20 * @type {{CELL: string, MIXED: string}}
21 */
22const FOCUS_MODES = Object.freeze({
23 CELL: 'cell',
24 MIXED: 'mixed'
25});
26
27/**
28 * Manages the browser's focus in the table.
29 */
30var _hot = /*#__PURE__*/new WeakMap();
31var _focusMode = /*#__PURE__*/new WeakMap();
32var _refocusDelay = /*#__PURE__*/new WeakMap();
33var _refocusElementGetter = /*#__PURE__*/new WeakMap();
34var _debouncedSelect = /*#__PURE__*/new WeakMap();
35var _FocusManager_brand = /*#__PURE__*/new WeakSet();
36class FocusManager {
37 constructor(hotInstance) {
38 var _this = this;
39 /**
40 * Get and return the currently selected and highlighted cell/header element.
41 *
42 * @param {Function} callback Callback function to be called after the cell element is retrieved.
43 */
44 _classPrivateMethodInitSpec(this, _FocusManager_brand);
45 /**
46 * The Handsontable instance.
47 */
48 _classPrivateFieldInitSpec(this, _hot, void 0);
49 /**
50 * The currently enabled focus mode.
51 * Can be either:
52 *
53 * - 'cell' - The browser's focus stays on the lastly selected cell element.
54 * - 'mixed' - The browser's focus switches from the lastly selected cell element to the currently active editor's
55 * `TEXTAREA` element after a delay defined in the manager.
56 *
57 * @type {'cell' | 'mixed'}
58 */
59 _classPrivateFieldInitSpec(this, _focusMode, void 0);
60 /**
61 * The delay after which the focus switches from the lastly selected cell to the active editor's `TEXTAREA`
62 * element if the focus mode is set to 'mixed'.
63 *
64 * @type {number}
65 */
66 _classPrivateFieldInitSpec(this, _refocusDelay, 1);
67 /**
68 * Getter function for the element to be used when refocusing the browser after a delay. If `null`, the active
69 * editor's `TEXTAREA` element will be used.
70 *
71 * @type {null|Function}
72 */
73 _classPrivateFieldInitSpec(this, _refocusElementGetter, null);
74 /**
75 * Map of the debounced `select` functions.
76 *
77 * @type {Map<number, Function>}
78 */
79 _classPrivateFieldInitSpec(this, _debouncedSelect, new Map());
80 const hotSettings = hotInstance.getSettings();
81 _classPrivateFieldSet(_hot, this, hotInstance);
82 _classPrivateFieldSet(_focusMode, this, hotSettings.imeFastEdit ? FOCUS_MODES.MIXED : FOCUS_MODES.CELL);
83 _classPrivateFieldGet(_hot, this).addHook('afterUpdateSettings', function () {
84 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
85 args[_key] = arguments[_key];
86 }
87 return _assertClassBrand(_FocusManager_brand, _this, _onUpdateSettings).call(_this, ...args);
88 });
89 _classPrivateFieldGet(_hot, this).addHook('afterSelection', function () {
90 for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
91 args[_key2] = arguments[_key2];
92 }
93 return _assertClassBrand(_FocusManager_brand, _this, _focusCell).call(_this, ...args);
94 });
95 _classPrivateFieldGet(_hot, this).addHook('afterSelectionFocusSet', function () {
96 for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
97 args[_key3] = arguments[_key3];
98 }
99 return _assertClassBrand(_FocusManager_brand, _this, _focusCell).call(_this, ...args);
100 });
101 _classPrivateFieldGet(_hot, this).addHook('afterSelectionEnd', function () {
102 for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
103 args[_key4] = arguments[_key4];
104 }
105 return _assertClassBrand(_FocusManager_brand, _this, _focusEditorElement).call(_this, ...args);
106 });
107 }
108
109 /**
110 * Get the current focus mode.
111 *
112 * @returns {'cell' | 'mixed'}
113 */
114 getFocusMode() {
115 return _classPrivateFieldGet(_focusMode, this);
116 }
117
118 /**
119 * Set the focus mode.
120 *
121 * @param {'cell' | 'mixed'} focusMode The new focus mode.
122 */
123 setFocusMode(focusMode) {
124 if (Object.values(FOCUS_MODES).includes(focusMode)) {
125 _classPrivateFieldSet(_focusMode, this, focusMode);
126 } else {
127 (0, _console.warn)(`"${focusMode}" is not a valid focus mode.`);
128 }
129 }
130
131 /**
132 * Get the delay after which the focus will change from the cell elements to the active editor's `TEXTAREA`
133 * element if the focus mode is set to 'mixed'.
134 *
135 * @returns {number} Delay in milliseconds.
136 */
137 getRefocusDelay() {
138 return _classPrivateFieldGet(_refocusDelay, this);
139 }
140
141 /**
142 * Set the delay after which the focus will change from the cell elements to the active editor's `TEXTAREA`
143 * element if the focus mode is set to 'mixed'.
144 *
145 * @param {number} delay Delay in milliseconds.
146 */
147 setRefocusDelay(delay) {
148 _classPrivateFieldSet(_refocusDelay, this, delay);
149 }
150
151 /**
152 * Set the function to be used as the "refocus element" getter. It should return a focusable HTML element.
153 *
154 * @param {Function} getRefocusElementFunction The refocus element getter.
155 */
156 setRefocusElementGetter(getRefocusElementFunction) {
157 _classPrivateFieldSet(_refocusElementGetter, this, getRefocusElementFunction);
158 }
159
160 /**
161 * Get the element to be used when refocusing the browser after a delay in case of the focus mode being 'mixed'.
162 *
163 * @returns {HTMLTextAreaElement|HTMLElement|undefined}
164 */
165 getRefocusElement() {
166 if (typeof _classPrivateFieldGet(_refocusElementGetter, this) === 'function') {
167 return _classPrivateFieldGet(_refocusElementGetter, this).call(this);
168 } else {
169 var _classPrivateFieldGet2;
170 return (_classPrivateFieldGet2 = _classPrivateFieldGet(_hot, this).getActiveEditor()) === null || _classPrivateFieldGet2 === void 0 ? void 0 : _classPrivateFieldGet2.TEXTAREA;
171 }
172 }
173
174 /**
175 * Set the browser's focus to the highlighted cell of the last selection.
176 *
177 * @param {HTMLTableCellElement} [selectedCell] The highlighted cell/header element.
178 */
179 focusOnHighlightedCell(selectedCell) {
180 const focusElement = element => {
181 var _classPrivateFieldGet3, _classPrivateFieldGet4;
182 const currentHighlightCoords = (_classPrivateFieldGet3 = _classPrivateFieldGet(_hot, this).getSelectedRangeLast()) === null || _classPrivateFieldGet3 === void 0 ? void 0 : _classPrivateFieldGet3.highlight;
183 if (!currentHighlightCoords) {
184 return;
185 }
186 let elementToBeFocused = _classPrivateFieldGet(_hot, this).runHooks('modifyFocusedElement', currentHighlightCoords.row, currentHighlightCoords.col, element);
187 if (!(elementToBeFocused instanceof HTMLElement)) {
188 elementToBeFocused = element;
189 }
190 if (elementToBeFocused && !((_classPrivateFieldGet4 = _classPrivateFieldGet(_hot, this).getActiveEditor()) !== null && _classPrivateFieldGet4 !== void 0 && _classPrivateFieldGet4.isOpened())) {
191 elementToBeFocused.focus({
192 preventScroll: true
193 });
194 }
195 };
196 if (selectedCell) {
197 focusElement(selectedCell);
198 } else {
199 _assertClassBrand(_FocusManager_brand, this, _getSelectedCell).call(this, element => focusElement(element));
200 }
201 }
202
203 /**
204 * Set the focus to the active editor's `TEXTAREA` element after the provided delay. If no delay is provided, it
205 * will be taken from the manager's configuration.
206 *
207 * @param {number} [delay] Delay in milliseconds.
208 */
209 refocusToEditorTextarea() {
210 var _classPrivateFieldGet5;
211 let delay = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _classPrivateFieldGet(_refocusDelay, this);
212 const refocusElement = this.getRefocusElement();
213
214 // Re-focus on the editor's `TEXTAREA` element (or a predefined element) if the `imeFastEdit` option is enabled.
215 if (_classPrivateFieldGet(_hot, this).getSettings().imeFastEdit && !((_classPrivateFieldGet5 = _classPrivateFieldGet(_hot, this).getActiveEditor()) !== null && _classPrivateFieldGet5 !== void 0 && _classPrivateFieldGet5.isOpened()) && !!refocusElement) {
216 if (!_classPrivateFieldGet(_debouncedSelect, this).has(delay)) {
217 _classPrivateFieldGet(_debouncedSelect, this).set(delay, (0, _function.debounce)(() => {
218 refocusElement.select();
219 }, delay));
220 }
221 _classPrivateFieldGet(_debouncedSelect, this).get(delay)();
222 }
223 }
224}
225exports.FocusManager = FocusManager;
226function _getSelectedCell(callback) {
227 var _classPrivateFieldGet6;
228 const highlight = (_classPrivateFieldGet6 = _classPrivateFieldGet(_hot, this).getSelectedRangeLast()) === null || _classPrivateFieldGet6 === void 0 ? void 0 : _classPrivateFieldGet6.highlight;
229 if (!highlight || !_classPrivateFieldGet(_hot, this).selection.isCellVisible(highlight)) {
230 callback(null);
231 return;
232 }
233 const cell = _classPrivateFieldGet(_hot, this).getCell(highlight.row, highlight.col, true);
234 if (cell === null) {
235 _classPrivateFieldGet(_hot, this).addHookOnce('afterScroll', () => {
236 callback(_classPrivateFieldGet(_hot, this).getCell(highlight.row, highlight.col, true));
237 });
238 } else {
239 callback(cell);
240 }
241}
242/**
243 * Manage the browser's focus after each cell selection change.
244 */
245function _focusCell() {
246 _assertClassBrand(_FocusManager_brand, this, _getSelectedCell).call(this, selectedCell => {
247 const {
248 activeElement
249 } = _classPrivateFieldGet(_hot, this).rootDocument;
250
251 // Blurring the `activeElement` removes the unwanted border around the focusable element (#6877)
252 // and resets the `document.activeElement` property. The blurring should happen only when the
253 // previously selected input element has not belonged to the Handsontable editor. If blurring is
254 // triggered for all elements, there is a problem with the disappearing IME editor (#9672).
255 if (activeElement && (0, _element.isOutsideInput)(activeElement)) {
256 activeElement.blur();
257 }
258 this.focusOnHighlightedCell(selectedCell);
259 });
260}
261/**
262 * Manage the browser's focus after cell selection end.
263 */
264function _focusEditorElement() {
265 _assertClassBrand(_FocusManager_brand, this, _getSelectedCell).call(this, selectedCell => {
266 if (this.getFocusMode() === FOCUS_MODES.MIXED && selectedCell.nodeName === 'TD') {
267 this.refocusToEditorTextarea();
268 }
269 });
270}
271/**
272 * Update the manager configuration after calling `updateSettings`.
273 *
274 * @param {object} newSettings The new settings passed to the `updateSettings` method.
275 */
276function _onUpdateSettings(newSettings) {
277 if (typeof newSettings.imeFastEdit === 'boolean') {
278 this.setFocusMode(newSettings.imeFastEdit ? FOCUS_MODES.MIXED : FOCUS_MODES.CELL);
279 }
280}
\No newline at end of file