handsontable 18.1.0-rc8 → 18.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/3rdparty/walkontable/src/event.d.ts +17 -0
  2. package/3rdparty/walkontable/src/event.js +188 -21
  3. package/3rdparty/walkontable/src/event.mjs +188 -21
  4. package/CHANGELOG.md +7 -30
  5. package/base.js +2 -2
  6. package/base.mjs +2 -2
  7. package/dist/handsontable.full.js +2263 -2011
  8. package/dist/handsontable.full.min.js +117 -117
  9. package/dist/handsontable.js +2262 -2011
  10. package/dist/handsontable.min.js +117 -117
  11. package/dist/themes/classic.js +2 -2
  12. package/dist/themes/classic.min.js +2 -2
  13. package/dist/themes/horizon.js +2 -2
  14. package/dist/themes/horizon.min.js +2 -2
  15. package/dist/themes/main.js +2 -2
  16. package/dist/themes/main.min.js +2 -2
  17. package/dist/themes/static/variables/colors/ant.js +2 -2
  18. package/dist/themes/static/variables/colors/ant.min.js +2 -2
  19. package/dist/themes/static/variables/colors/classic.js +2 -2
  20. package/dist/themes/static/variables/colors/classic.min.js +2 -2
  21. package/dist/themes/static/variables/colors/horizon.js +2 -2
  22. package/dist/themes/static/variables/colors/horizon.min.js +2 -2
  23. package/dist/themes/static/variables/colors/main.js +2 -2
  24. package/dist/themes/static/variables/colors/main.min.js +2 -2
  25. package/dist/themes/static/variables/colors/material.js +2 -2
  26. package/dist/themes/static/variables/colors/material.min.js +2 -2
  27. package/dist/themes/static/variables/colors/shadcn.js +2 -2
  28. package/dist/themes/static/variables/colors/shadcn.min.js +2 -2
  29. package/dist/themes/static/variables/density.js +2 -2
  30. package/dist/themes/static/variables/density.min.js +2 -2
  31. package/dist/themes/static/variables/helpers/iconsMap.js +2 -2
  32. package/dist/themes/static/variables/helpers/iconsMap.min.js +2 -2
  33. package/dist/themes/static/variables/icons/horizon.js +2 -2
  34. package/dist/themes/static/variables/icons/horizon.min.js +2 -2
  35. package/dist/themes/static/variables/icons/main.js +2 -2
  36. package/dist/themes/static/variables/icons/main.min.js +2 -2
  37. package/dist/themes/static/variables/sizing.js +2 -2
  38. package/dist/themes/static/variables/sizing.min.js +2 -2
  39. package/dist/themes/static/variables/tokens/classic.js +2 -2
  40. package/dist/themes/static/variables/tokens/classic.min.js +2 -2
  41. package/dist/themes/static/variables/tokens/horizon.js +2 -2
  42. package/dist/themes/static/variables/tokens/horizon.min.js +2 -2
  43. package/dist/themes/static/variables/tokens/main.js +2 -2
  44. package/dist/themes/static/variables/tokens/main.min.js +2 -2
  45. package/helpers/dom/inputOrigin.d.mts +1 -0
  46. package/helpers/dom/inputOrigin.d.ts +38 -0
  47. package/helpers/dom/inputOrigin.js +54 -0
  48. package/helpers/dom/inputOrigin.mjs +48 -0
  49. package/helpers/mixed.js +20 -5
  50. package/helpers/mixed.mjs +20 -5
  51. package/package.json +1 -1
  52. package/styles/handsontable.css +2 -2
  53. package/styles/handsontable.min.css +2 -2
  54. package/styles/handsontableStyles.js +1 -1
  55. package/styles/handsontableStyles.mjs +1 -1
  56. package/styles/ht-icons-horizon.min.css +2 -2
  57. package/styles/ht-icons-main.min.css +2 -2
  58. package/styles/ht-theme-classic-no-icons.min.css +2 -2
  59. package/styles/ht-theme-classic.min.css +2 -2
  60. package/styles/ht-theme-horizon-no-icons.min.css +2 -2
  61. package/styles/ht-theme-horizon.min.css +2 -2
  62. package/styles/ht-theme-main-no-icons.min.css +2 -2
  63. package/styles/ht-theme-main.min.css +2 -2
  64. package/tableView.js +16 -12
  65. package/tableView.mjs +16 -12
  66. package/utils/entitlementLicenseKey/constants.d.ts +9 -3
  67. package/utils/entitlementLicenseKey/constants.mjs +9 -3
  68. package/utils/entitlementLicenseKey/types.d.ts +9 -3
  69. package/utils/licenseBranding/index.js +22 -7
  70. package/utils/licenseBranding/index.mjs +22 -7
  71. package/utils/licenseNotification.js +2 -2
  72. package/utils/licenseNotification.mjs +2 -2
  73. package/styles/handsontableStyles.d.mts +0 -2
  74. package/styles/handsontableStyles.d.ts +0 -2
@@ -138,6 +138,23 @@ declare class Event {
138
138
  * @param {TouchEvent} event The touch event object.
139
139
  */
140
140
  onTouchEnd(event: TouchEvent): void;
141
+ /**
142
+ * OnTouchCancel callback. Browsers cancel a gesture instead of ending it whenever the system
143
+ * claims the touch (an edge-swipe, a dialog, a context callout) — `touchend` then never fires.
144
+ * Without this reset `touchApplied` stays `true` and every REAL mouse `mouseup` is routed into
145
+ * the touch tap detector, which has no button filter, so two right-clicks or two drag-selections
146
+ * ending on the same cell within the double-tap window would fire a phantom double-click.
147
+ * It also releases the mouse-down flag: a long-press fires `onMouseDown` from its timer, and
148
+ * after a cancel no `mouseup` ever follows, which would leave mouse-move selection dragging
149
+ * armed until the next click. The pending synthesized-pair flag is cleared for the same reason
150
+ * the scroll branch of `onTouchEnd` clears it: a cancelled gesture armed nothing, so a
151
+ * still-pending flag belongs to an earlier gesture whose pair never came, and left in place it
152
+ * would drop the next real mouse pair inside the ceiling on engines that do not report the
153
+ * input origin.
154
+ *
155
+ * @private
156
+ */
157
+ onTouchCancel(): void;
141
158
  /**
142
159
  * Holder `scroll` callback. Cancels the long-press timer and runs the momentum-scroll
143
160
  * bookkeeping. When called during an active touch sequence it also marks the gesture
@@ -20,6 +20,7 @@ const _element = require("../../../helpers/dom/element");
20
20
  const _pointerToCoords = require("./utils/pointerToCoords");
21
21
  const _feature = require("../../../helpers/feature");
22
22
  const _browser = require("../../../helpers/browser");
23
+ const _inputOrigin = require("../../../helpers/dom/inputOrigin");
23
24
  function _check_private_redeclaration(obj, privateCollection) {
24
25
  if (privateCollection.has(obj)) {
25
26
  throw new TypeError("Cannot initialize the same private elements twice on an object");
@@ -72,6 +73,20 @@ function _class_private_method_init(obj, privateSet) {
72
73
  }
73
74
  const LONG_PRESS_DELAY = 500;
74
75
  const LONG_PRESS_MOVE_THRESHOLD = 10;
76
+ /**
77
+ * How long (ms) after a mousedown the double-click detector keeps waiting for the matching
78
+ * second click. Mirrors the OS double-click tolerance with margin.
79
+ */ const DBLCLICK_MOUSEDOWN_TIMEOUT = 1000;
80
+ /**
81
+ * How long (ms) after a mouseup the double-click detector keeps the first click "armed"
82
+ * for a mouse-driven double-click.
83
+ */ const DBLCLICK_MOUSEUP_TIMEOUT = 500;
84
+ /**
85
+ * How long (ms) after a touch tap a second tap on the same cell counts as a double-tap. Touch
86
+ * taps are paired by `#handleTouchTap()`, independently of the mouse double-click slots, so a
87
+ * real mouse click after a tap never pairs with it. iOS drops taps under ~250 ms and humans
88
+ * double-tap at 300–600 ms (DEV-2687).
89
+ */ const TOUCH_DBLTAP_TIMEOUT = 1000;
75
90
  function createEventDeps(ctx) {
76
91
  return {
77
92
  facadeGetter: ctx.getFacade(),
@@ -99,6 +114,17 @@ var /**
99
114
  */ _dblClickTimeout = /*#__PURE__*/ new WeakMap(), /**
100
115
  * @type {number[]}
101
116
  */ _dblClickOrigin = /*#__PURE__*/ new WeakMap(), /**
117
+ * Coordinates of the most recent touch tap, kept for double-tap detection. Coordinates, not the
118
+ * resolved TD: Walkontable recycles TD elements across scrolls and re-renders, so element
119
+ * identity can pair two taps that landed on different cells (DEV-2687 review). Touch taps never
120
+ * arm the mouse double-click slots, so a mouse click after a tap cannot pair with a tap.
121
+ *
122
+ * @type {CellCoords|null}
123
+ */ _lastTapCoords = /*#__PURE__*/ new WeakMap(), /**
124
+ * Timestamp (ms) of the most recent touch tap.
125
+ *
126
+ * @type {number}
127
+ */ _lastTapAt = /*#__PURE__*/ new WeakMap(), /**
102
128
  * Timer ID for the long-press gesture detection.
103
129
  *
104
130
  * @type {number|null}
@@ -123,12 +149,25 @@ var /**
123
149
  * @type {TouchEvent|null}
124
150
  */ _deferredTouchStartEvent = /*#__PURE__*/ new WeakMap(), /**
125
151
  * Timestamp (ms) of the most recent `onMouseUp` call that originated from a touch gesture.
126
- * Used to suppress the browser-synthesized `mouseup` event that fires after `touchend` on
127
- * devices that register both touch and mouse listeners (e.g. iPad Safari), which would
128
- * otherwise trigger `onCellMouseUp` and context-menu commands a second time.
152
+ * On devices that register both touch and mouse listeners (iPad with a desktop UA, Windows
153
+ * touchscreens) the browser synthesizes a `mousedown`/`mouseup`/`click` sequence after
154
+ * `touchend`. The whole sequence must be ignored dropping only one half fires
155
+ * `onCellMouseDown` a second time per tap, and the leaked pair would act as a phantom mouse
156
+ * click (DEV-2687).
157
+ * Used as the fallback for engines that do not expose `sourceCapabilities`
158
+ * (see `#isTouchSynthesizedMouseEvent`).
129
159
  *
130
160
  * @type {number}
131
161
  */ _lastTouchMouseUpAt = /*#__PURE__*/ new WeakMap(), /**
162
+ * `true` between a touch-driven `onMouseUp` and the browser-synthesized `mouseup` that follows it.
163
+ * Only that first pair is dropped; once it is consumed, real mouse events pass even inside the
164
+ * `TOUCH_SYNTHESIZED_MOUSE_WINDOW` ceiling, so a fill-handle drag or a drag-selection started with
165
+ * a mouse right after a tap works on engines that do not report the input origin (DEV-2687). The
166
+ * flag is re-armed by every touch-driven `onMouseUp` and cleared when a scroll-classified gesture
167
+ * ends, so a tap that synthesized nothing cannot swallow that gesture's own compatibility pair.
168
+ *
169
+ * @type {boolean}
170
+ */ _synthesizedPairPending = /*#__PURE__*/ new WeakMap(), /**
132
171
  * @type {boolean}
133
172
  */ _mouseDown = /*#__PURE__*/ new WeakMap(), /**
134
173
  * The last renderable coords seen in `onMouseMove` while the mouse was outside the viewport.
@@ -137,6 +176,18 @@ var /**
137
176
  *
138
177
  * @type {{ row: number, col: number } | null}
139
178
  */ _mouseOverOutsideLastCoords = /*#__PURE__*/ new WeakMap(), /**
179
+ * Decides whether a mouse event caught by the mouse listeners is one half of the browser-synthesized
180
+ * `mousedown`/`mouseup` pair that follows a touch tap, and must therefore be dropped. Order of the
181
+ * checks: an engine that reports the input origin and says "not touch" wins (Blink, a real mouse or
182
+ * pen is never dropped); otherwise the event is synthesized only if a touch-driven `onMouseUp` just
183
+ * armed the pair (`#synthesizedPairPending`) and the ceiling has not passed. The `mouseup` half
184
+ * consumes the pair, so any later mouse event inside the ceiling is treated as real. Dropping both
185
+ * halves keeps `onCellMouseDown` at one call per tap and context-menu commands at one execution
186
+ * (#12803); a tap that left no stamp (a gesture treated as a scroll) has its pair processed.
187
+ *
188
+ * @param {MouseEvent} event The mouse event object.
189
+ * @returns {boolean}
190
+ */ _isTouchSynthesizedMouseEvent = /*#__PURE__*/ new WeakSet(), /**
140
191
  * Returns the cell coordinates for the given mouse position and whether the mouse is
141
192
  * outside the visible viewport. When the mouse is outside, the nearest edge cell is returned.
142
193
  *
@@ -145,6 +196,19 @@ var /**
145
196
  * @param {number} mouseY Client Y coordinate of the mouse.
146
197
  * @returns {{ coords: CellCoords, isOutside: boolean }}
147
198
  */ _getCellCoordsFromMousePosition = /*#__PURE__*/ new WeakSet(), /**
199
+ * Pairs touch taps into double-taps. Called from `onMouseUp` while `touchApplied` is `true`,
200
+ * i.e. for the `onMouseUp` that `onTouchEnd` drives. Two taps on the same coordinates within
201
+ * `TOUCH_DBLTAP_TIMEOUT` fire the double-click callbacks; a long-press, a tap outside the cells,
202
+ * or a tap on different coordinates resets the detector.
203
+ *
204
+ * @param {MouseEvent|TouchEvent} event The event that ended the tap.
205
+ * @param {ParentCell} cell The tapped cell, as returned by `parentCell()`.
206
+ */ _handleTouchTap = /*#__PURE__*/ new WeakSet(), /**
207
+ * Fires the corner or the cell double-click callback for the given cell.
208
+ *
209
+ * @param {MouseEvent|TouchEvent} event The event that completed the double-click.
210
+ * @param {ParentCell} cell The double-clicked cell.
211
+ */ _fireDblClick = /*#__PURE__*/ new WeakSet(), /**
148
212
  * Starts the long-press timer. When the timer fires, a synthetic `contextmenu` event is
149
213
  * dispatched on the original touch target so that the existing contextmenu hook chain
150
214
  * (and ContextMenu plugin) work without changes.
@@ -178,20 +242,27 @@ var /**
178
242
  _class_private_field_get(this, _deps).eventManager.addEventListener(_class_private_field_get(this, _deps).wtTable.holder, 'touchstart', (event)=>this.onTouchStart(event));
179
243
  _class_private_field_get(this, _deps).eventManager.addEventListener(_class_private_field_get(this, _deps).wtTable.holder, 'touchend', (event)=>this.onTouchEnd(event));
180
244
  _class_private_field_get(this, _deps).eventManager.addEventListener(_class_private_field_get(this, _deps).wtTable.holder, 'touchmove', (event)=>this.onTouchMove(event));
245
+ _class_private_field_get(this, _deps).eventManager.addEventListener(_class_private_field_get(this, _deps).wtTable.holder, 'touchcancel', ()=>this.onTouchCancel());
181
246
  _class_private_field_get(this, _deps).eventManager.addEventListener(_class_private_field_get(this, _deps).wtTable.holder, 'scroll', ()=>this.onHolderScroll());
182
247
  };
183
248
  const initMouseEvents = ()=>{
249
+ // On devices that register both touch and mouse listeners (e.g. iPad Safari with a
250
+ // desktop UA), `touchend` already drove `onMouseDown`/`onMouseUp`. The browser then
251
+ // synthesizes a `mousedown`/`mouseup` pair ~0-50 ms later; drop BOTH halves when the
252
+ // touch path handled the tap, so `onCellMouseDown` fires once per tap (DEV-2687) and
253
+ // context-menu commands do not execute twice (#12803).
184
254
  _class_private_field_get(this, _deps).eventManager.addEventListener(_class_private_field_get(this, _deps).wtTable.holder, 'mouseup', (event)=>{
185
- // On devices that register both touch and mouse listeners (e.g. iPad Safari),
186
- // `touchend` fires first and calls `onMouseUp` directly. The browser then
187
- // synthesizes a `mouseup` event ~0-50 ms later; suppress it to prevent
188
- // context-menu commands from executing twice.
189
- if (Date.now() - _class_private_field_get(this, _lastTouchMouseUpAt) < 500) {
255
+ if (_class_private_method_get(this, _isTouchSynthesizedMouseEvent, isTouchSynthesizedMouseEvent).call(this, event)) {
190
256
  return;
191
257
  }
192
258
  this.onMouseUp(event);
193
259
  });
194
- _class_private_field_get(this, _deps).eventManager.addEventListener(_class_private_field_get(this, _deps).wtTable.holder, 'mousedown', (event)=>this.onMouseDown(event));
260
+ _class_private_field_get(this, _deps).eventManager.addEventListener(_class_private_field_get(this, _deps).wtTable.holder, 'mousedown', (event)=>{
261
+ if (_class_private_method_get(this, _isTouchSynthesizedMouseEvent, isTouchSynthesizedMouseEvent).call(this, event)) {
262
+ return;
263
+ }
264
+ this.onMouseDown(event);
265
+ });
195
266
  };
196
267
  if ((0, _browser.isMobileBrowser)()) {
197
268
  initTouchEvents();
@@ -288,15 +359,16 @@ var /**
288
359
  this.callListener('onCellMouseDown', event, cell.coords, cell.TD);
289
360
  }
290
361
  }
291
- // doubleclick reacts only for left mouse button or from touch events
292
- if ((event.button === 0 || this.touchApplied) && cell.TD) {
362
+ // The mouse double-click slots are armed by mouse clicks only; touch taps are paired by
363
+ // #handleTouchTap() in onMouseUp (DEV-2687).
364
+ if (!this.touchApplied && event.button === 0 && cell.TD) {
293
365
  _class_private_field_get(this, _dblClickOrigin)[0] = cell.TD;
294
366
  if (_class_private_field_get(this, _dblClickTimeout)[0] !== null) {
295
367
  clearTimeout(_class_private_field_get(this, _dblClickTimeout)[0]);
296
368
  }
297
369
  _class_private_field_get(this, _dblClickTimeout)[0] = setTimeout(()=>{
298
370
  _class_private_field_get(this, _dblClickOrigin)[0] = null;
299
- }, 1000);
371
+ }, DBLCLICK_MOUSEDOWN_TIMEOUT);
300
372
  }
301
373
  }
302
374
  /**
@@ -405,16 +477,16 @@ var /**
405
477
  if (cell.TD && _class_private_field_get(this, _deps).wtSettings.has('onCellMouseUp')) {
406
478
  this.callListener('onCellMouseUp', event, cell.coords, cell.TD);
407
479
  }
408
- // if not left mouse button, and the origin event is not comes from touch
409
- if (event.button !== 0 && !this.touchApplied) {
480
+ if (this.touchApplied) {
481
+ _class_private_method_get(this, _handleTouchTap, handleTouchTap).call(this, event, cell);
482
+ return;
483
+ }
484
+ // ignore non-left mouse buttons
485
+ if (event.button !== 0) {
410
486
  return;
411
487
  }
412
488
  if (cell.TD && cell.TD === _class_private_field_get(this, _dblClickOrigin)[0] && cell.TD === _class_private_field_get(this, _dblClickOrigin)[1]) {
413
- if ((0, _element.hasClass)((0, _element.eventTargetEl)(event), 'corner')) {
414
- this.callListener('onCellCornerDblClick', event, cell.coords, cell.TD);
415
- } else {
416
- this.callListener('onCellDblClick', event, cell.coords, cell.TD);
417
- }
489
+ _class_private_method_get(this, _fireDblClick, fireDblClick).call(this, event, cell);
418
490
  _class_private_field_get(this, _dblClickOrigin)[0] = null;
419
491
  _class_private_field_get(this, _dblClickOrigin)[1] = null;
420
492
  } else if (cell.TD && cell.TD === _class_private_field_get(this, _dblClickOrigin)[0]) {
@@ -424,7 +496,7 @@ var /**
424
496
  }
425
497
  _class_private_field_get(this, _dblClickTimeout)[1] = setTimeout(()=>{
426
498
  _class_private_field_get(this, _dblClickOrigin)[1] = null;
427
- }, 500);
499
+ }, DBLCLICK_MOUSEUP_TIMEOUT);
428
500
  }
429
501
  }
430
502
  /**
@@ -494,14 +566,51 @@ var /**
494
566
  // before/after hooks (e.g. nestedHeaders, ContextMenu close logic).
495
567
  // Suppress only for pure scroll gestures, where onMouseDown was never fired.
496
568
  if (!wasScrolled || _class_private_field_get(this, _longPressFired)) {
497
- _class_private_field_set(this, _lastTouchMouseUpAt, Date.now());
569
+ // The stamp is taken AFTER the tap is handled, so the selection/render work onMouseUp does
570
+ // is not charged against the TOUCH_SYNTHESIZED_MOUSE_WINDOW ceiling. Everything onMouseUp
571
+ // triggers is synchronous; the browser only dispatches the synthesized mousedown/mouseup
572
+ // sequence after this touchend handler returns.
498
573
  this.onMouseUp(event);
574
+ _class_private_field_set(this, _lastTouchMouseUpAt, Date.now());
575
+ _class_private_field_set(this, _synthesizedPairPending, true);
576
+ } else {
577
+ // A pure scroll gesture calls neither onMouseDown nor onMouseUp, so #handleTouchTap never
578
+ // runs to reset the tap detector. Reset it here so a scroll between two taps can't pair them.
579
+ _class_private_field_set(this, _lastTapCoords, null);
580
+ // This gesture armed nothing, so a still-pending flag belongs to an earlier gesture whose
581
+ // pair never came; clear it so THIS gesture's compatibility pair is processed.
582
+ _class_private_field_set(this, _synthesizedPairPending, false);
499
583
  }
500
584
  this.touchApplied = false;
501
585
  _class_private_field_set(this, _touchWasMoved, false);
502
586
  _class_private_field_set(this, _longPressFired, false);
503
587
  }
504
588
  /**
589
+ * OnTouchCancel callback. Browsers cancel a gesture instead of ending it whenever the system
590
+ * claims the touch (an edge-swipe, a dialog, a context callout) — `touchend` then never fires.
591
+ * Without this reset `touchApplied` stays `true` and every REAL mouse `mouseup` is routed into
592
+ * the touch tap detector, which has no button filter, so two right-clicks or two drag-selections
593
+ * ending on the same cell within the double-tap window would fire a phantom double-click.
594
+ * It also releases the mouse-down flag: a long-press fires `onMouseDown` from its timer, and
595
+ * after a cancel no `mouseup` ever follows, which would leave mouse-move selection dragging
596
+ * armed until the next click. The pending synthesized-pair flag is cleared for the same reason
597
+ * the scroll branch of `onTouchEnd` clears it: a cancelled gesture armed nothing, so a
598
+ * still-pending flag belongs to an earlier gesture whose pair never came, and left in place it
599
+ * would drop the next real mouse pair inside the ceiling on engines that do not report the
600
+ * input origin.
601
+ *
602
+ * @private
603
+ */ onTouchCancel() {
604
+ _class_private_method_get(this, _cancelLongPressTimer, cancelLongPressTimer).call(this);
605
+ this.touchApplied = false;
606
+ _class_private_field_set(this, _mouseDown, false);
607
+ _class_private_field_set(this, _touchWasMoved, false);
608
+ _class_private_field_set(this, _longPressFired, false);
609
+ _class_private_field_set(this, _deferredTouchStartEvent, null);
610
+ _class_private_field_set(this, _lastTapCoords, null);
611
+ _class_private_field_set(this, _synthesizedPairPending, false);
612
+ }
613
+ /**
505
614
  * Holder `scroll` callback. Cancels the long-press timer and runs the momentum-scroll
506
615
  * bookkeeping. When called during an active touch sequence it also marks the gesture
507
616
  * as a scroll so the deferred mousedown is not fired on `touchend` - native scrolling
@@ -585,7 +694,10 @@ var /**
585
694
  * @param {EventDeps} deps The Event module dependencies.
586
695
  * @param {Event} [parent=null] The main Event instance.
587
696
  */ constructor(deps, parent = null){
697
+ _class_private_method_init(this, _isTouchSynthesizedMouseEvent);
588
698
  _class_private_method_init(this, _getCellCoordsFromMousePosition);
699
+ _class_private_method_init(this, _handleTouchTap);
700
+ _class_private_method_init(this, _fireDblClick);
589
701
  _class_private_method_init(this, _startLongPressTimer);
590
702
  _class_private_method_init(this, _cancelLongPressTimer);
591
703
  _class_private_field_init(this, _deps, {
@@ -608,6 +720,14 @@ var /**
608
720
  writable: true,
609
721
  value: void 0
610
722
  });
723
+ _class_private_field_init(this, _lastTapCoords, {
724
+ writable: true,
725
+ value: void 0
726
+ });
727
+ _class_private_field_init(this, _lastTapAt, {
728
+ writable: true,
729
+ value: void 0
730
+ });
611
731
  _class_private_field_init(this, _longPressTimeout, {
612
732
  writable: true,
613
733
  value: void 0
@@ -632,6 +752,10 @@ var /**
632
752
  writable: true,
633
753
  value: void 0
634
754
  });
755
+ _class_private_field_init(this, _synthesizedPairPending, {
756
+ writable: true,
757
+ value: void 0
758
+ });
635
759
  _class_private_field_init(this, _mouseDown, {
636
760
  writable: true,
637
761
  value: void 0
@@ -649,12 +773,15 @@ var /**
649
773
  null,
650
774
  null
651
775
  ]);
776
+ _class_private_field_set(this, _lastTapCoords, null);
777
+ _class_private_field_set(this, _lastTapAt, 0);
652
778
  _class_private_field_set(this, _longPressTimeout, null);
653
779
  _class_private_field_set(this, _longPressFired, false);
654
780
  _class_private_field_set(this, _touchStartCoords, null);
655
781
  _class_private_field_set(this, _touchWasMoved, false);
656
782
  _class_private_field_set(this, _deferredTouchStartEvent, null);
657
783
  _class_private_field_set(this, _lastTouchMouseUpAt, 0);
784
+ _class_private_field_set(this, _synthesizedPairPending, false);
658
785
  _class_private_field_set(this, _mouseDown, false);
659
786
  _class_private_field_set(this, _mouseOverOutsideLastCoords, null);
660
787
  _class_private_field_set(this, _deps, deps);
@@ -662,9 +789,48 @@ var /**
662
789
  this.registerEvents();
663
790
  }
664
791
  }
792
+ function isTouchSynthesizedMouseEvent(event) {
793
+ if ((0, _inputOrigin.getMouseEventTouchOrigin)(event) === false) {
794
+ return false;
795
+ }
796
+ const withinCeiling = Date.now() - _class_private_field_get(this, _lastTouchMouseUpAt) < _inputOrigin.TOUCH_SYNTHESIZED_MOUSE_WINDOW;
797
+ if (!_class_private_field_get(this, _synthesizedPairPending) || !withinCeiling) {
798
+ _class_private_field_set(this, _synthesizedPairPending, false);
799
+ return false;
800
+ }
801
+ if (event.type === 'mouseup') {
802
+ _class_private_field_set(this, _synthesizedPairPending, false);
803
+ }
804
+ return true;
805
+ }
665
806
  function getCellCoordsFromMousePosition(mouseX, mouseY) {
666
807
  return (0, _pointerToCoords.getCellCoordsFromMousePosition)(_class_private_field_get(this, _deps), mouseX, mouseY);
667
808
  }
809
+ function handleTouchTap(event, cell) {
810
+ if (_class_private_field_get(this, _longPressFired) || !cell.TD || !cell.coords) {
811
+ _class_private_field_set(this, _lastTapCoords, null);
812
+ return;
813
+ }
814
+ const now = Date.now();
815
+ const isSameCell = _class_private_field_get(this, _lastTapCoords) !== null && _class_private_field_get(this, _lastTapCoords).row === cell.coords.row && _class_private_field_get(this, _lastTapCoords).col === cell.coords.col;
816
+ if (isSameCell && now - _class_private_field_get(this, _lastTapAt) < TOUCH_DBLTAP_TIMEOUT) {
817
+ _class_private_method_get(this, _fireDblClick, fireDblClick).call(this, event, cell);
818
+ _class_private_field_set(this, _lastTapCoords, null);
819
+ return;
820
+ }
821
+ // Clone: parentCell()'s border branches return references into the live selection CellRange,
822
+ // which normalize() mutates in place — a stored alias could silently shift within the
823
+ // double-tap window.
824
+ _class_private_field_set(this, _lastTapCoords, cell.coords.clone());
825
+ _class_private_field_set(this, _lastTapAt, now);
826
+ }
827
+ function fireDblClick(event, cell) {
828
+ if ((0, _element.hasClass)((0, _element.eventTargetEl)(event), 'corner')) {
829
+ this.callListener('onCellCornerDblClick', event, cell.coords, cell.TD);
830
+ } else {
831
+ this.callListener('onCellDblClick', event, cell.coords, cell.TD);
832
+ }
833
+ }
668
834
  function startLongPressTimer(event) {
669
835
  _class_private_method_get(this, _cancelLongPressTimer, cancelLongPressTimer).call(this);
670
836
  const touch = event.touches[0];
@@ -685,6 +851,7 @@ function startLongPressTimer(event) {
685
851
  this.onMouseDown(event);
686
852
  _class_private_field_get(this, _dblClickOrigin)[0] = null;
687
853
  _class_private_field_get(this, _dblClickOrigin)[1] = null;
854
+ _class_private_field_set(this, _lastTapCoords, null);
688
855
  if (_class_private_field_get(this, _dblClickTimeout)[0] !== null) {
689
856
  clearTimeout(_class_private_field_get(this, _dblClickTimeout)[0]);
690
857
  }