UNPKG

12 kBJavaScriptView Raw
1import "core-js/modules/es.error.cause.js";
2function _classPrivateMethodInitSpec(e, a) { _checkPrivateRedeclaration(e, a), a.add(e); }
3function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); }
4function _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; }
5function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
6function _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); }
7function _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"); }
8import { isFunctionKey, isCtrlMetaKey } from "./helpers/unicode.mjs";
9import { isImmediatePropagationStopped } from "./helpers/dom/event.mjs";
10import { getEditorInstance } from "./editors/registry.mjs";
11import EventManager from "./eventManager.mjs";
12var _EditorManager_brand = /*#__PURE__*/new WeakSet();
13class EditorManager {
14 /**
15 * @param {Core} hotInstance The Handsontable instance.
16 * @param {TableMeta} tableMeta The table meta instance.
17 * @param {Selection} selection The selection instance.
18 */
19 constructor(hotInstance, tableMeta, _selection) {
20 /**
21 * OnAfterDocumentKeyDown callback.
22 *
23 * @param {KeyboardEvent} event The keyboard event object.
24 */
25 _classPrivateMethodInitSpec(this, _EditorManager_brand);
26 /**
27 * Instance of {@link Handsontable}.
28 *
29 * @private
30 * @type {Handsontable}
31 */
32 _defineProperty(this, "hot", void 0);
33 /**
34 * Reference to an instance's private GridSettings object.
35 *
36 * @private
37 * @type {GridSettings}
38 */
39 _defineProperty(this, "tableMeta", void 0);
40 /**
41 * Instance of {@link Selection}.
42 *
43 * @private
44 * @type {Selection}
45 */
46 _defineProperty(this, "selection", void 0);
47 /**
48 * Instance of {@link EventManager}.
49 *
50 * @private
51 * @type {EventManager}
52 */
53 _defineProperty(this, "eventManager", void 0);
54 /**
55 * Determines if EditorManager is destroyed.
56 *
57 * @private
58 * @type {boolean}
59 */
60 _defineProperty(this, "destroyed", false);
61 /**
62 * A reference to an instance of the activeEditor.
63 *
64 * @private
65 * @type {BaseEditor}
66 */
67 _defineProperty(this, "activeEditor", void 0);
68 /**
69 * Keeps a reference to the cell's properties object.
70 *
71 * @type {object}
72 */
73 _defineProperty(this, "cellProperties", void 0);
74 this.hot = hotInstance;
75 this.tableMeta = tableMeta;
76 this.selection = _selection;
77 this.eventManager = new EventManager(hotInstance);
78 this.hot.addHook('afterDocumentKeyDown', event => _assertClassBrand(_EditorManager_brand, this, _onAfterDocumentKeyDown).call(this, event));
79
80 // Open editor when text composition is started (IME editor)
81 this.eventManager.addEventListener(this.hot.rootDocument.documentElement, 'compositionstart', event => {
82 if (!this.destroyed && this.hot.isListening()) {
83 this.openEditor('', event);
84 }
85 });
86 this.hot.view._wt.update('onCellDblClick', (event, coords, elem) => _assertClassBrand(_EditorManager_brand, this, _onCellDblClick).call(this, event, coords, elem));
87 }
88
89 /**
90 * Get active editor.
91 *
92 * @returns {BaseEditor}
93 */
94 getActiveEditor() {
95 return this.activeEditor;
96 }
97
98 /**
99 * Prepare text input to be displayed at given grid cell.
100 */
101 prepareEditor() {
102 var _this$hot$getSelected;
103 if (this.activeEditor && this.activeEditor.isWaiting()) {
104 this.closeEditor(false, false, dataSaved => {
105 if (dataSaved) {
106 this.prepareEditor();
107 }
108 });
109 return;
110 }
111 const highlight = (_this$hot$getSelected = this.hot.getSelectedRangeLast()) === null || _this$hot$getSelected === void 0 ? void 0 : _this$hot$getSelected.highlight;
112 if (!highlight || highlight.isHeader()) {
113 return;
114 }
115 const {
116 row,
117 col
118 } = highlight;
119 const modifiedCellCoords = this.hot.runHooks('modifyGetCellCoords', row, col);
120 let visualRowToCheck = row;
121 let visualColumnToCheck = col;
122 if (Array.isArray(modifiedCellCoords)) {
123 [visualRowToCheck, visualColumnToCheck] = modifiedCellCoords;
124 }
125
126 // Getting values using the modified coordinates.
127 this.cellProperties = this.hot.getCellMeta(visualRowToCheck, visualColumnToCheck);
128 if (!this.isCellEditable()) {
129 this.clearActiveEditor();
130 return;
131 }
132 const td = this.hot.getCell(row, col, true);
133
134 // Skip the preparation when the cell is not rendered in the DOM. The cell is scrolled out of
135 // the table's viewport.
136 if (td) {
137 const editorClass = this.hot.getCellEditor(this.cellProperties);
138 const prop = this.hot.colToProp(visualColumnToCheck);
139 const originalValue = this.hot.getSourceDataAtCell(this.hot.toPhysicalRow(visualRowToCheck), visualColumnToCheck);
140 this.activeEditor = getEditorInstance(editorClass, this.hot);
141 // Using not modified coordinates, as we need to get the table element using selection coordinates.
142 // There is an extra translation in the editor for saving value.
143 this.activeEditor.prepare(row, col, prop, td, originalValue, this.cellProperties);
144 }
145 }
146
147 /**
148 * Check is editor is opened/showed.
149 *
150 * @returns {boolean}
151 */
152 isEditorOpened() {
153 return this.activeEditor && this.activeEditor.isOpened();
154 }
155
156 /**
157 * Open editor with initial value.
158 *
159 * @param {null|string} newInitialValue New value from which editor will start if handled property it's not the `null`.
160 * @param {Event} event The event object.
161 * @param {boolean} [enableFullEditMode=false] When true, an editor works in full editing mode. Mode disallows closing an editor
162 * when arrow keys are pressed.
163 */
164 openEditor(newInitialValue, event) {
165 let enableFullEditMode = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
166 if (!this.isCellEditable()) {
167 this.clearActiveEditor();
168 return;
169 }
170 const selection = this.hot.getSelectedRangeLast();
171 let allowOpening = this.hot.runHooks('beforeBeginEditing', selection.highlight.row, selection.highlight.col, newInitialValue, event, enableFullEditMode);
172
173 // If the above hook does not return boolean apply default behavior which disallows opening
174 // an editor after double mouse click for non-contiguous selection (while pressing Ctrl/Cmd) and
175 // for multiple selected cells (while pressing SHIFT).
176 if (event instanceof MouseEvent && typeof allowOpening !== 'boolean') {
177 allowOpening = this.hot.selection.getLayerLevel() === 0 && selection.isSingle();
178 }
179 if (allowOpening === false) {
180 this.clearActiveEditor();
181 return;
182 }
183 if (!this.activeEditor) {
184 this.hot.scrollToFocusedCell();
185 this.prepareEditor();
186 }
187 if (this.activeEditor) {
188 if (enableFullEditMode) {
189 this.activeEditor.enableFullEditMode();
190 }
191 this.activeEditor.beginEditing(newInitialValue, event);
192 }
193 }
194
195 /**
196 * Close editor, finish editing cell.
197 *
198 * @param {boolean} restoreOriginalValue If `true`, then closes editor without saving value from the editor into a cell.
199 * @param {boolean} isCtrlPressed If `true`, then editor will save value to each cell in the last selected range.
200 * @param {Function} callback The callback function, fired after editor closing.
201 */
202 closeEditor(restoreOriginalValue, isCtrlPressed, callback) {
203 if (this.activeEditor) {
204 this.activeEditor.finishEditing(restoreOriginalValue, isCtrlPressed, callback);
205 } else if (callback) {
206 callback(false);
207 }
208 }
209
210 /**
211 * Close editor and save changes.
212 *
213 * @param {boolean} isCtrlPressed If `true`, then editor will save value to each cell in the last selected range.
214 */
215 closeEditorAndSaveChanges(isCtrlPressed) {
216 this.closeEditor(false, isCtrlPressed);
217 }
218
219 /**
220 * Close editor and restore original value.
221 *
222 * @param {boolean} isCtrlPressed Indication of whether the CTRL button is pressed.
223 */
224 closeEditorAndRestoreOriginalValue(isCtrlPressed) {
225 this.closeEditor(true, isCtrlPressed);
226 }
227
228 /**
229 * Clears reference to an instance of the active editor.
230 *
231 * @private
232 */
233 clearActiveEditor() {
234 this.activeEditor = undefined;
235 }
236
237 /**
238 * Checks if the currently selected cell (pointed by selection highlight coords) is editable.
239 * Editable cell is when:
240 * - the cell has defined an editor type;
241 * - the cell is not marked as read-only;
242 * - the cell is not hidden.
243 *
244 * @private
245 * @returns {boolean}
246 */
247 isCellEditable() {
248 const selection = this.hot.getSelectedRangeLast();
249 if (!selection) {
250 return false;
251 }
252 const editorClass = this.hot.getCellEditor(this.cellProperties);
253 const {
254 row,
255 col
256 } = selection.highlight;
257 const {
258 rowIndexMapper,
259 columnIndexMapper
260 } = this.hot;
261 const isCellHidden = rowIndexMapper.isHidden(this.hot.toPhysicalRow(row)) || columnIndexMapper.isHidden(this.hot.toPhysicalColumn(col));
262 if (this.cellProperties.readOnly || !editorClass || isCellHidden) {
263 return false;
264 }
265 return true;
266 }
267
268 /**
269 * Controls selection's behavior after clicking `Enter`.
270 *
271 * @private
272 * @param {KeyboardEvent} event The keyboard event object.
273 */
274 moveSelectionAfterEnter(event) {
275 const enterMoves = {
276 ...(typeof this.tableMeta.enterMoves === 'function' ? this.tableMeta.enterMoves(event) : this.tableMeta.enterMoves)
277 };
278 if (event.shiftKey) {
279 enterMoves.row = -enterMoves.row;
280 enterMoves.col = -enterMoves.col;
281 }
282 if (this.hot.selection.isMultiple()) {
283 this.selection.transformFocus(enterMoves.row, enterMoves.col);
284 } else {
285 this.selection.transformStart(enterMoves.row, enterMoves.col, true);
286 }
287 }
288 /**
289 * Destroy the instance.
290 */
291 destroy() {
292 this.destroyed = true;
293 this.eventManager.destroy();
294 }
295}
296function _onAfterDocumentKeyDown(event) {
297 const selection = this.hot.getSelectedRangeLast();
298 if (!this.hot.isListening() || !selection || selection.highlight.isHeader() || isImmediatePropagationStopped(event)) {
299 return;
300 }
301 const {
302 keyCode
303 } = event;
304
305 // catch CTRL but not right ALT (which in some systems triggers ALT+CTRL)
306 const isCtrlPressed = (event.ctrlKey || event.metaKey) && !event.altKey;
307 if (!this.activeEditor || this.activeEditor && !this.activeEditor.isWaiting()) {
308 if (!isFunctionKey(keyCode) && !isCtrlMetaKey(keyCode) && !isCtrlPressed && !this.isEditorOpened()) {
309 this.openEditor('', event);
310 }
311 }
312}
313/**
314 * OnCellDblClick callback.
315 *
316 * @param {MouseEvent} event The mouse event object.
317 * @param {object} coords The cell coordinates.
318 */
319function _onCellDblClick(event, coords) {
320 if (coords.isCell()) {
321 this.openEditor(null, event, true);
322 }
323}
324const instances = new WeakMap();
325
326/**
327 * @param {Core} hotInstance The Handsontable instance.
328 * @param {TableMeta} tableMeta The table meta class instance.
329 * @param {Selection} selection The selection instance.
330 * @returns {EditorManager}
331 */
332EditorManager.getInstance = function (hotInstance, tableMeta, selection) {
333 let editorManager = instances.get(hotInstance);
334 if (!editorManager) {
335 editorManager = new EditorManager(hotInstance, tableMeta, selection);
336 instances.set(hotInstance, editorManager);
337 }
338 return editorManager;
339};
340export default EditorManager;
\No newline at end of file