handsontable 0.0.0-next-8dc7078-20240322 → 0.0.0-next-28fc088-20240325

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of handsontable might be problematic. Click here for more details.

Files changed (98) hide show
  1. package/3rdparty/walkontable/src/cell/range.d.ts +2 -1
  2. package/3rdparty/walkontable/src/cell/range.js +22 -5
  3. package/3rdparty/walkontable/src/cell/range.mjs +22 -5
  4. package/base.js +2 -2
  5. package/base.mjs +2 -2
  6. package/core/viewportScroll/index.js +4 -1
  7. package/core/viewportScroll/index.mjs +4 -1
  8. package/core/viewportScroll/scrollStrategies/focusScroll.js +15 -0
  9. package/core/viewportScroll/scrollStrategies/focusScroll.mjs +11 -0
  10. package/core.d.ts +4 -3
  11. package/core.js +76 -21
  12. package/core.mjs +76 -21
  13. package/dist/handsontable.css +2 -2
  14. package/dist/handsontable.full.css +2 -2
  15. package/dist/handsontable.full.js +2649 -1389
  16. package/dist/handsontable.full.min.css +2 -2
  17. package/dist/handsontable.full.min.js +71 -71
  18. package/dist/handsontable.js +2653 -1393
  19. package/dist/handsontable.min.css +2 -2
  20. package/dist/handsontable.min.js +19 -19
  21. package/editorManager.js +12 -8
  22. package/editorManager.mjs +12 -8
  23. package/focusManager.js +7 -1
  24. package/focusManager.mjs +7 -1
  25. package/helpers/mixed.js +1 -1
  26. package/helpers/mixed.mjs +1 -1
  27. package/package.json +1 -1
  28. package/pluginHooks.d.ts +4 -0
  29. package/pluginHooks.js +98 -37
  30. package/pluginHooks.mjs +98 -37
  31. package/plugins/collapsibleColumns/collapsibleColumns.js +9 -3
  32. package/plugins/collapsibleColumns/collapsibleColumns.mjs +9 -3
  33. package/plugins/columnSorting/columnSorting.js +8 -2
  34. package/plugins/columnSorting/columnSorting.mjs +8 -2
  35. package/plugins/contextMenu/menu/defaultShortcutsList.js +26 -10
  36. package/plugins/contextMenu/menu/defaultShortcutsList.mjs +26 -10
  37. package/plugins/mergeCells/calculations/selection.js +1 -70
  38. package/plugins/mergeCells/calculations/selection.mjs +1 -70
  39. package/plugins/mergeCells/cellsCollection.js +116 -0
  40. package/plugins/mergeCells/cellsCollection.mjs +116 -0
  41. package/plugins/mergeCells/contextMenuItem/toggleMerge.js +11 -1
  42. package/plugins/mergeCells/contextMenuItem/toggleMerge.mjs +11 -1
  43. package/plugins/mergeCells/focusOrder.js +305 -0
  44. package/plugins/mergeCells/focusOrder.mjs +300 -0
  45. package/plugins/mergeCells/mergeCells.js +336 -192
  46. package/plugins/mergeCells/mergeCells.mjs +336 -192
  47. package/plugins/multiColumnSorting/multiColumnSorting.js +8 -2
  48. package/plugins/multiColumnSorting/multiColumnSorting.mjs +8 -2
  49. package/plugins/nestedHeaders/nestedHeaders.js +1 -0
  50. package/plugins/nestedHeaders/nestedHeaders.mjs +1 -0
  51. package/plugins/nestedRows/nestedRows.js +9 -3
  52. package/plugins/nestedRows/nestedRows.mjs +9 -3
  53. package/renderers/checkboxRenderer/checkboxRenderer.js +8 -5
  54. package/renderers/checkboxRenderer/checkboxRenderer.mjs +8 -5
  55. package/selection/highlight/visualSelection.js +2 -0
  56. package/selection/highlight/visualSelection.mjs +2 -0
  57. package/selection/selection.js +209 -40
  58. package/selection/selection.mjs +208 -39
  59. package/selection/transformation.js +83 -32
  60. package/selection/transformation.mjs +83 -32
  61. package/shortcutContexts/commands/editor/closeAndSave.js +2 -2
  62. package/shortcutContexts/commands/editor/closeAndSave.mjs +2 -2
  63. package/shortcutContexts/commands/editor/open.js +18 -3
  64. package/shortcutContexts/commands/editor/open.mjs +18 -3
  65. package/shortcutContexts/commands/extendCellsSelection/down.js +1 -1
  66. package/shortcutContexts/commands/extendCellsSelection/down.mjs +1 -1
  67. package/shortcutContexts/commands/extendCellsSelection/left.js +1 -1
  68. package/shortcutContexts/commands/extendCellsSelection/left.mjs +1 -1
  69. package/shortcutContexts/commands/extendCellsSelection/right.js +1 -1
  70. package/shortcutContexts/commands/extendCellsSelection/right.mjs +1 -1
  71. package/shortcutContexts/commands/extendCellsSelection/toColumns.js +1 -1
  72. package/shortcutContexts/commands/extendCellsSelection/toColumns.mjs +1 -1
  73. package/shortcutContexts/commands/extendCellsSelection/toMostBottom.js +3 -1
  74. package/shortcutContexts/commands/extendCellsSelection/toMostBottom.mjs +3 -1
  75. package/shortcutContexts/commands/extendCellsSelection/toMostInlineEnd.js +9 -3
  76. package/shortcutContexts/commands/extendCellsSelection/toMostInlineEnd.mjs +9 -3
  77. package/shortcutContexts/commands/extendCellsSelection/toMostInlineStart.js +10 -3
  78. package/shortcutContexts/commands/extendCellsSelection/toMostInlineStart.mjs +10 -3
  79. package/shortcutContexts/commands/extendCellsSelection/toMostLeft.js +3 -1
  80. package/shortcutContexts/commands/extendCellsSelection/toMostLeft.mjs +3 -1
  81. package/shortcutContexts/commands/extendCellsSelection/toMostRight.js +3 -1
  82. package/shortcutContexts/commands/extendCellsSelection/toMostRight.mjs +3 -1
  83. package/shortcutContexts/commands/extendCellsSelection/toMostTop.js +3 -1
  84. package/shortcutContexts/commands/extendCellsSelection/toMostTop.mjs +3 -1
  85. package/shortcutContexts/commands/extendCellsSelection/toRows.js +1 -1
  86. package/shortcutContexts/commands/extendCellsSelection/toRows.mjs +1 -1
  87. package/shortcutContexts/commands/extendCellsSelection/up.js +1 -1
  88. package/shortcutContexts/commands/extendCellsSelection/up.mjs +1 -1
  89. package/shortcutContexts/commands/moveCellSelection/inlineEnd.js +6 -1
  90. package/shortcutContexts/commands/moveCellSelection/inlineEnd.mjs +6 -1
  91. package/shortcutContexts/commands/moveCellSelection/inlineStart.js +6 -1
  92. package/shortcutContexts/commands/moveCellSelection/inlineStart.mjs +6 -1
  93. package/shortcutContexts/grid.js +2 -2
  94. package/shortcutContexts/grid.mjs +2 -2
  95. package/shortcuts/context.js +2 -1
  96. package/shortcuts/context.mjs +2 -1
  97. package/utils/dataStructures/linkedList.js +6 -1
  98. package/utils/dataStructures/linkedList.mjs +6 -1
@@ -22,6 +22,9 @@ import { A11Y_SELECTED } from "../helpers/a11y.mjs";
22
22
  * @class Selection
23
23
  * @util
24
24
  */
25
+ var _transformation = /*#__PURE__*/new WeakMap();
26
+ var _focusTransformation = /*#__PURE__*/new WeakMap();
27
+ var _isFocusSelectionChanged = /*#__PURE__*/new WeakMap();
25
28
  var _disableHeadersHighlight = /*#__PURE__*/new WeakMap();
26
29
  var _selectionSource = /*#__PURE__*/new WeakMap();
27
30
  var _expectedLayersCount = /*#__PURE__*/new WeakMap();
@@ -61,11 +64,17 @@ class Selection {
61
64
  */
62
65
  _defineProperty(this, "highlight", void 0);
63
66
  /**
64
- * The module for modifying coordinates.
67
+ * The module for modifying coordinates of the start and end selection.
65
68
  *
66
69
  * @type {Transformation}
67
70
  */
68
- _defineProperty(this, "transformation", void 0);
71
+ _classPrivateFieldInitSpec(this, _transformation, void 0);
72
+ /**
73
+ * The module for modifying coordinates of the focus selection.
74
+ *
75
+ * @type {Transformation}
76
+ */
77
+ _classPrivateFieldInitSpec(this, _focusTransformation, void 0);
69
78
  /**
70
79
  * The collection of the selection layer levels where the whole row was selected using the row header or
71
80
  * the corner header.
@@ -80,6 +89,12 @@ class Selection {
80
89
  * @type {Set<number>}
81
90
  */
82
91
  _defineProperty(this, "selectedByColumnHeader", new Set());
92
+ /**
93
+ * The flag which determines if the focus selection was changed.
94
+ *
95
+ * @type {boolean}
96
+ */
97
+ _classPrivateFieldInitSpec(this, _isFocusSelectionChanged, false);
83
98
  /**
84
99
  * When sets disable highlighting the headers even when the logical coordinates points on them.
85
100
  *
@@ -121,71 +136,112 @@ class Selection {
121
136
  createCellCoords: (row, column) => this.tableProps.createCellCoords(row, column),
122
137
  createCellRange: (highlight, from, to) => this.tableProps.createCellRange(highlight, from, to)
123
138
  });
124
- this.transformation = new Transformation(this.selectedRange, {
139
+ _classPrivateFieldSet(_transformation, this, new Transformation(this.selectedRange, {
125
140
  rowIndexMapper: this.tableProps.rowIndexMapper,
126
141
  columnIndexMapper: this.tableProps.columnIndexMapper,
127
142
  countRenderableRows: () => this.tableProps.countRenderableRows(),
128
143
  countRenderableColumns: () => this.tableProps.countRenderableColumns(),
129
- countRowHeaders: () => this.tableProps.countRowHeaders(),
130
- countColHeaders: () => this.tableProps.countColHeaders(),
131
144
  visualToRenderableCoords: coords => this.tableProps.visualToRenderableCoords(coords),
132
145
  renderableToVisualCoords: coords => this.tableProps.renderableToVisualCoords(coords),
146
+ findFirstNonHiddenRenderableRow: function () {
147
+ return _this.tableProps.findFirstNonHiddenRenderableRow(...arguments);
148
+ },
149
+ findFirstNonHiddenRenderableColumn: function () {
150
+ return _this.tableProps.findFirstNonHiddenRenderableColumn(...arguments);
151
+ },
133
152
  createCellCoords: (row, column) => this.tableProps.createCellCoords(row, column),
134
- navigableHeaders: () => settings.navigableHeaders,
135
153
  fixedRowsBottom: () => settings.fixedRowsBottom,
136
154
  minSpareRows: () => settings.minSpareRows,
137
155
  minSpareCols: () => settings.minSpareCols,
138
156
  autoWrapRow: () => settings.autoWrapRow,
139
157
  autoWrapCol: () => settings.autoWrapCol
140
- });
141
- this.transformation.addLocalHook('beforeTransformStart', function () {
158
+ }));
159
+ _classPrivateFieldSet(_focusTransformation, this, new Transformation(this.selectedRange, {
160
+ rowIndexMapper: this.tableProps.rowIndexMapper,
161
+ columnIndexMapper: this.tableProps.columnIndexMapper,
162
+ countRenderableRows: () => {
163
+ const range = this.selectedRange.current();
164
+ return this.tableProps.countRenderableRowsInRange(0, range.getOuterBottomEndCorner().row);
165
+ },
166
+ countRenderableColumns: () => {
167
+ const range = this.selectedRange.current();
168
+ return this.tableProps.countRenderableColumnsInRange(0, range.getOuterBottomEndCorner().col);
169
+ },
170
+ visualToRenderableCoords: coords => this.tableProps.visualToRenderableCoords(coords),
171
+ renderableToVisualCoords: coords => this.tableProps.renderableToVisualCoords(coords),
172
+ findFirstNonHiddenRenderableRow: function () {
173
+ return _this.tableProps.findFirstNonHiddenRenderableRow(...arguments);
174
+ },
175
+ findFirstNonHiddenRenderableColumn: function () {
176
+ return _this.tableProps.findFirstNonHiddenRenderableColumn(...arguments);
177
+ },
178
+ createCellCoords: (row, column) => this.tableProps.createCellCoords(row, column),
179
+ fixedRowsBottom: () => 0,
180
+ minSpareRows: () => 0,
181
+ minSpareCols: () => 0,
182
+ autoWrapRow: () => true,
183
+ autoWrapCol: () => true
184
+ }));
185
+ _classPrivateFieldGet(_transformation, this).addLocalHook('beforeTransformStart', function () {
142
186
  for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
143
187
  args[_key] = arguments[_key];
144
188
  }
145
189
  return _this.runLocalHooks('beforeModifyTransformStart', ...args);
146
190
  });
147
- this.transformation.addLocalHook('afterTransformStart', function () {
191
+ _classPrivateFieldGet(_transformation, this).addLocalHook('afterTransformStart', function () {
148
192
  for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
149
193
  args[_key2] = arguments[_key2];
150
194
  }
151
195
  return _this.runLocalHooks('afterModifyTransformStart', ...args);
152
196
  });
153
- this.transformation.addLocalHook('beforeTransformEnd', function () {
197
+ _classPrivateFieldGet(_transformation, this).addLocalHook('beforeTransformEnd', function () {
154
198
  for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
155
199
  args[_key3] = arguments[_key3];
156
200
  }
157
201
  return _this.runLocalHooks('beforeModifyTransformEnd', ...args);
158
202
  });
159
- this.transformation.addLocalHook('afterTransformEnd', function () {
203
+ _classPrivateFieldGet(_transformation, this).addLocalHook('afterTransformEnd', function () {
160
204
  for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
161
205
  args[_key4] = arguments[_key4];
162
206
  }
163
207
  return _this.runLocalHooks('afterModifyTransformEnd', ...args);
164
208
  });
165
- this.transformation.addLocalHook('insertRowRequire', function () {
209
+ _classPrivateFieldGet(_transformation, this).addLocalHook('insertRowRequire', function () {
166
210
  for (var _len5 = arguments.length, args = new Array(_len5), _key5 = 0; _key5 < _len5; _key5++) {
167
211
  args[_key5] = arguments[_key5];
168
212
  }
169
213
  return _this.runLocalHooks('insertRowRequire', ...args);
170
214
  });
171
- this.transformation.addLocalHook('insertColRequire', function () {
215
+ _classPrivateFieldGet(_transformation, this).addLocalHook('insertColRequire', function () {
172
216
  for (var _len6 = arguments.length, args = new Array(_len6), _key6 = 0; _key6 < _len6; _key6++) {
173
217
  args[_key6] = arguments[_key6];
174
218
  }
175
219
  return _this.runLocalHooks('insertColRequire', ...args);
176
220
  });
177
- this.transformation.addLocalHook('beforeRowWrap', function () {
221
+ _classPrivateFieldGet(_transformation, this).addLocalHook('beforeRowWrap', function () {
178
222
  for (var _len7 = arguments.length, args = new Array(_len7), _key7 = 0; _key7 < _len7; _key7++) {
179
223
  args[_key7] = arguments[_key7];
180
224
  }
181
225
  return _this.runLocalHooks('beforeRowWrap', ...args);
182
226
  });
183
- this.transformation.addLocalHook('beforeColumnWrap', function () {
227
+ _classPrivateFieldGet(_transformation, this).addLocalHook('beforeColumnWrap', function () {
184
228
  for (var _len8 = arguments.length, args = new Array(_len8), _key8 = 0; _key8 < _len8; _key8++) {
185
229
  args[_key8] = arguments[_key8];
186
230
  }
187
231
  return _this.runLocalHooks('beforeColumnWrap', ...args);
188
232
  });
233
+ _classPrivateFieldGet(_focusTransformation, this).addLocalHook('beforeTransformStart', function () {
234
+ for (var _len9 = arguments.length, args = new Array(_len9), _key9 = 0; _key9 < _len9; _key9++) {
235
+ args[_key9] = arguments[_key9];
236
+ }
237
+ return _this.runLocalHooks('beforeModifyTransformFocus', ...args);
238
+ });
239
+ _classPrivateFieldGet(_focusTransformation, this).addLocalHook('afterTransformStart', function () {
240
+ for (var _len10 = arguments.length, args = new Array(_len10), _key10 = 0; _key10 < _len10; _key10++) {
241
+ args[_key10] = arguments[_key10];
242
+ }
243
+ return _this.runLocalHooks('afterModifyTransformFocus', ...args);
244
+ });
189
245
  }
190
246
 
191
247
  /**
@@ -276,6 +332,7 @@ class Selection {
276
332
  // We are creating copy. We would like to modify just the start of the selection by below hook. Then original coords
277
333
  // should be handled by next methods.
278
334
  const coordsClone = coords.clone();
335
+ _classPrivateFieldSet(_isFocusSelectionChanged, this, false);
279
336
  this.runLocalHooks(`beforeSetRangeStart${fragment ? 'Only' : ''}`, coordsClone);
280
337
  if (!isMultipleMode || isMultipleMode && !isMultipleSelection && isUndefined(multipleSelection)) {
281
338
  this.selectedRange.clear();
@@ -333,7 +390,20 @@ class Selection {
333
390
  cellRange.setFrom(cellRange.highlight);
334
391
  cellRange.setTo(cellRange.highlight);
335
392
  } else {
393
+ const horizontalDir = cellRange.getHorizontalDirection();
394
+ const verticalDir = cellRange.getVerticalDirection();
395
+ const isMultiple = this.isMultiple();
336
396
  cellRange.setTo(coordsClone);
397
+ if (isMultiple && (horizontalDir !== cellRange.getHorizontalDirection() || cellRange.getWidth() === 1 && !cellRange.includes(cellRange.highlight))) {
398
+ cellRange.from.assign({
399
+ col: cellRange.highlight.col
400
+ });
401
+ }
402
+ if (isMultiple && (verticalDir !== cellRange.getVerticalDirection() || cellRange.getHeight() === 1 && !cellRange.includes(cellRange.highlight))) {
403
+ cellRange.from.assign({
404
+ row: cellRange.highlight.row
405
+ });
406
+ }
337
407
  }
338
408
 
339
409
  // Prevent creating "area" selection that overlaps headers.
@@ -343,11 +413,7 @@ class Selection {
343
413
  }
344
414
  }
345
415
  this.runLocalHooks('beforeHighlightSet');
346
- const focusHighlight = this.highlight.getFocus();
347
- focusHighlight.clear();
348
- if (this.highlight.isEnabledFor(FOCUS_TYPE, cellRange.highlight)) {
349
- focusHighlight.add(this.selectedRange.current().highlight).commit().syncWith(cellRange);
350
- }
416
+ this.setRangeFocus(this.selectedRange.current().highlight);
351
417
  const layerLevel = this.getLayerLevel();
352
418
 
353
419
  // If the next layer level is lower than previous then clear all area and header highlights. This is the
@@ -435,15 +501,31 @@ class Selection {
435
501
  }
436
502
 
437
503
  /**
438
- * Returns information if we have a multiselection. This method check multiselection only on the latest layer of
439
- * the selection.
504
+ * Sets the selection focus position at the specified coordinates.
440
505
  *
441
- * @returns {boolean}
506
+ * @param {CellCoords} coords The CellCoords instance with defined visual coordinates.
442
507
  */
443
- isMultiple() {
444
- const isMultipleListener = createObjectPropListener(!this.selectedRange.current().isSingle());
445
- this.runLocalHooks('afterIsMultipleSelection', isMultipleListener);
446
- return isMultipleListener.value;
508
+ setRangeFocus(coords) {
509
+ if (this.selectedRange.isEmpty()) {
510
+ return;
511
+ }
512
+ const cellRange = this.selectedRange.current();
513
+ if (!this.inProgress) {
514
+ this.runLocalHooks('beforeSetFocus', coords);
515
+ }
516
+ const focusHighlight = this.highlight.getFocus();
517
+ focusHighlight.clear();
518
+ cellRange.setHighlight(coords);
519
+ if (!this.inProgress) {
520
+ this.runLocalHooks('beforeHighlightSet');
521
+ }
522
+ if (this.highlight.isEnabledFor(FOCUS_TYPE, cellRange.highlight)) {
523
+ focusHighlight.add(cellRange.highlight).commit().syncWith(cellRange);
524
+ }
525
+ if (!this.inProgress) {
526
+ _classPrivateFieldSet(_isFocusSelectionChanged, this, true);
527
+ this.runLocalHooks('afterSetFocus', cellRange.highlight);
528
+ }
447
529
  }
448
530
 
449
531
  /**
@@ -456,7 +538,13 @@ class Selection {
456
538
  */
457
539
  transformStart(rowDelta, colDelta) {
458
540
  let createMissingRecords = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
459
- this.setRangeStart(this.transformation.transformStart(rowDelta, colDelta, createMissingRecords));
541
+ if (this.settings.navigableHeaders) {
542
+ _classPrivateFieldGet(_transformation, this).setOffsetSize({
543
+ x: this.tableProps.countRowHeaders(),
544
+ y: this.tableProps.countColHeaders()
545
+ });
546
+ }
547
+ this.setRangeStart(_classPrivateFieldGet(_transformation, this).transformStart(rowDelta, colDelta, createMissingRecords));
460
548
  }
461
549
 
462
550
  /**
@@ -466,7 +554,44 @@ class Selection {
466
554
  * @param {number} colDelta Columns number to move, value can be passed as negative number.
467
555
  */
468
556
  transformEnd(rowDelta, colDelta) {
469
- this.setRangeEnd(this.transformation.transformEnd(rowDelta, colDelta));
557
+ if (this.settings.navigableHeaders) {
558
+ _classPrivateFieldGet(_transformation, this).setOffsetSize({
559
+ x: this.tableProps.countRowHeaders(),
560
+ y: this.tableProps.countColHeaders()
561
+ });
562
+ }
563
+ this.setRangeEnd(_classPrivateFieldGet(_transformation, this).transformEnd(rowDelta, colDelta));
564
+ }
565
+
566
+ /**
567
+ * Transforms the focus cell selection relative to the current focus position.
568
+ *
569
+ * @param {number} rowDelta Rows number to move, value can be passed as negative number.
570
+ * @param {number} colDelta Columns number to move, value can be passed as negative number.
571
+ */
572
+ transformFocus(rowDelta, colDelta) {
573
+ const range = this.selectedRange.current();
574
+ const {
575
+ row,
576
+ col
577
+ } = range.getOuterTopStartCorner();
578
+ const columnsInRange = this.tableProps.countRenderableColumnsInRange(0, col - 1);
579
+ const rowsInRange = this.tableProps.countRenderableRowsInRange(0, row - 1);
580
+ if (range.highlight.isHeader()) {
581
+ // for header focus selection calculate the new coords based on the selection including headers
582
+ _classPrivateFieldGet(_focusTransformation, this).setOffsetSize({
583
+ x: col < 0 ? Math.abs(col) : -columnsInRange,
584
+ y: row < 0 ? Math.abs(row) : -rowsInRange
585
+ });
586
+ } else {
587
+ // for focus selection in cells calculate the new coords only based on the selected cells
588
+ _classPrivateFieldGet(_focusTransformation, this).setOffsetSize({
589
+ x: col < 0 ? 0 : -columnsInRange,
590
+ y: row < 0 ? 0 : -rowsInRange
591
+ });
592
+ }
593
+ const focusCoords = _classPrivateFieldGet(_focusTransformation, this).transformStart(rowDelta, colDelta);
594
+ this.setRangeFocus(focusCoords.normalize());
470
595
  }
471
596
 
472
597
  /**
@@ -487,6 +612,30 @@ class Selection {
487
612
  return !this.selectedRange.isEmpty();
488
613
  }
489
614
 
615
+ /**
616
+ * Returns information if we have a multi-selection. This method check multi-selection only on the latest layer of
617
+ * the selection.
618
+ *
619
+ * @returns {boolean}
620
+ */
621
+ isMultiple() {
622
+ if (!this.isSelected()) {
623
+ return false;
624
+ }
625
+ const isMultipleListener = createObjectPropListener(!this.selectedRange.current().isSingle());
626
+ this.runLocalHooks('afterIsMultipleSelection', isMultipleListener);
627
+ return isMultipleListener.value;
628
+ }
629
+
630
+ /**
631
+ * Checks if the last selection involves changing the focus cell position only.
632
+ *
633
+ * @returns {boolean}
634
+ */
635
+ isFocusSelectionChanged() {
636
+ return this.isSelected() && _classPrivateFieldGet(_isFocusSelectionChanged, this);
637
+ }
638
+
490
639
  /**
491
640
  * Returns `true` if the selection was applied by clicking to the row header. If the `layerLevel`
492
641
  * argument is passed then only that layer will be checked. Otherwise, it checks if any row header
@@ -769,9 +918,10 @@ class Selection {
769
918
  *
770
919
  * @param {number|string} startColumn Visual column index or column property from which the selection starts.
771
920
  * @param {number|string} [endColumn] Visual column index or column property from to the selection finishes.
772
- * @param {number} [focusPosition=0] The argument allows changing the cell/header focus position.
773
- * The value can take visual row index from -N to N, where negative values
774
- * point to the headers and positive values point to the cell range.
921
+ * @param {number | { row: number, col: number }} [focusPosition=0] The argument allows changing the cell/header focus
922
+ * position. The value can take visual row index from -N to N, where negative values point to the headers and positive
923
+ * values point to the cell range. An object with `row` and `col` properties also can be passed to change the focus
924
+ * position horizontally.
775
925
  * @returns {boolean} Returns `true` if selection was successful, `false` otherwise.
776
926
  */
777
927
  selectColumns(startColumn) {
@@ -792,11 +942,20 @@ class Selection {
792
942
  countColHeaders
793
943
  });
794
944
  if (isValid) {
795
- const fromRow = countColHeaders === 0 ? 0 : clamp(focusPosition, columnHeaderLastIndex, -1);
945
+ let highlightRow = 0;
946
+ let highlightColumn = 0;
947
+ if (Number.isInteger(focusPosition === null || focusPosition === void 0 ? void 0 : focusPosition.row) && Number.isInteger(focusPosition === null || focusPosition === void 0 ? void 0 : focusPosition.col)) {
948
+ highlightRow = clamp(focusPosition.row, columnHeaderLastIndex, countRows - 1);
949
+ highlightColumn = clamp(focusPosition.col, Math.min(start, end), Math.max(start, end));
950
+ } else {
951
+ highlightRow = clamp(focusPosition, columnHeaderLastIndex, countRows - 1);
952
+ highlightColumn = start;
953
+ }
954
+ const highlight = this.tableProps.createCellCoords(highlightRow, highlightColumn);
955
+ const fromRow = countColHeaders === 0 ? 0 : clamp(highlight.row, columnHeaderLastIndex, -1);
796
956
  const toRow = countRows - 1;
797
957
  const from = this.tableProps.createCellCoords(fromRow, start);
798
958
  const to = this.tableProps.createCellCoords(toRow, end);
799
- const highlight = this.tableProps.createCellCoords(clamp(focusPosition, columnHeaderLastIndex, countRows - 1), start);
800
959
  this.runLocalHooks('beforeSelectColumns', from, to, highlight);
801
960
 
802
961
  // disallow modifying row axis for that hooks
@@ -816,9 +975,10 @@ class Selection {
816
975
  *
817
976
  * @param {number} startRow Visual row index from which the selection starts.
818
977
  * @param {number} [endRow] Visual row index from to the selection finishes.
819
- * @param {number} [focusPosition=0] The argument allows changing the cell/header focus position.
820
- * The value can take visual column index from -N to N, where negative values
821
- * point to the headers and positive values point to the cell range.
978
+ * @param {number | { row: number, col: number }} [focusPosition=0] The argument allows changing the cell/header focus
979
+ * position. The value can take visual row index from -N to N, where negative values point to the headers and positive
980
+ * values point to the cell range. An object with `row` and `col` properties also can be passed to change the focus
981
+ * position horizontally.
822
982
  * @returns {boolean} Returns `true` if selection was successful, `false` otherwise.
823
983
  */
824
984
  selectRows(startRow) {
@@ -837,11 +997,20 @@ class Selection {
837
997
  countColHeaders: 0
838
998
  });
839
999
  if (isValid) {
840
- const fromColumn = countRowHeaders === 0 ? 0 : clamp(focusPosition, rowHeaderLastIndex, -1);
1000
+ let highlightRow = 0;
1001
+ let highlightColumn = 0;
1002
+ if (Number.isInteger(focusPosition === null || focusPosition === void 0 ? void 0 : focusPosition.row) && Number.isInteger(focusPosition === null || focusPosition === void 0 ? void 0 : focusPosition.col)) {
1003
+ highlightRow = clamp(focusPosition.row, Math.min(startRow, endRow), Math.max(startRow, endRow));
1004
+ highlightColumn = clamp(focusPosition.col, rowHeaderLastIndex, countCols - 1);
1005
+ } else {
1006
+ highlightRow = startRow;
1007
+ highlightColumn = clamp(focusPosition, rowHeaderLastIndex, countCols - 1);
1008
+ }
1009
+ const highlight = this.tableProps.createCellCoords(highlightRow, highlightColumn);
1010
+ const fromColumn = countRowHeaders === 0 ? 0 : clamp(highlight.col, rowHeaderLastIndex, -1);
841
1011
  const toColumn = countCols - 1;
842
1012
  const from = this.tableProps.createCellCoords(startRow, fromColumn);
843
1013
  const to = this.tableProps.createCellCoords(endRow, toColumn);
844
- const highlight = this.tableProps.createCellCoords(startRow, clamp(focusPosition, rowHeaderLastIndex, countCols - 1));
845
1014
  this.runLocalHooks('beforeSelectRows', from, to, highlight);
846
1015
 
847
1016
  // disallow modifying column axis for that hooks
@@ -33,10 +33,10 @@ var _Transformation_brand = /*#__PURE__*/new WeakSet();
33
33
  class Transformation {
34
34
  constructor(range, options) {
35
35
  /**
36
- * Sets the additional offset in table size that may occur when the `navigableHeaders` option
37
- * is enabled.
36
+ * Clamps the coords to make sure they points to the cell (or header) in the table range.
38
37
  *
39
- * @param {{x: number, y: number}} offset Offset as x and y properties.
38
+ * @param {CellCoords} zeroBasedCoords The coords object to clamp.
39
+ * @returns {{rowDir: 1|0|-1, colDir: 1|0|-1}}
40
40
  */
41
41
  _classPrivateMethodInitSpec(this, _Transformation_brand);
42
42
  /**
@@ -77,10 +77,6 @@ class Transformation {
77
77
  */
78
78
  transformStart(rowDelta, colDelta) {
79
79
  let createMissingRecords = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
80
- _assertClassBrand(_Transformation_brand, this, _setOffsetSize).call(this, {
81
- x: _classPrivateFieldGet(_options, this).navigableHeaders() ? _classPrivateFieldGet(_options, this).countRowHeaders() : 0,
82
- y: _classPrivateFieldGet(_options, this).navigableHeaders() ? _classPrivateFieldGet(_options, this).countColHeaders() : 0
83
- });
84
80
  const delta = _classPrivateFieldGet(_options, this).createCellCoords(rowDelta, colDelta);
85
81
  let visualCoords = _classPrivateFieldGet(_range, this).current().highlight;
86
82
  const highlightRenderableCoords = _classPrivateFieldGet(_options, this).visualToRenderableCoords(visualCoords);
@@ -160,23 +156,46 @@ class Transformation {
160
156
  * @returns {CellCoords} Visual coordinates after transformation.
161
157
  */
162
158
  transformEnd(rowDelta, colDelta) {
163
- _assertClassBrand(_Transformation_brand, this, _setOffsetSize).call(this, {
164
- x: _classPrivateFieldGet(_options, this).navigableHeaders() ? _classPrivateFieldGet(_options, this).countRowHeaders() : 0,
165
- y: _classPrivateFieldGet(_options, this).navigableHeaders() ? _classPrivateFieldGet(_options, this).countColHeaders() : 0
166
- });
167
159
  const delta = _classPrivateFieldGet(_options, this).createCellCoords(rowDelta, colDelta);
168
160
  const cellRange = _classPrivateFieldGet(_range, this).current();
169
161
  const highlightRenderableCoords = _classPrivateFieldGet(_options, this).visualToRenderableCoords(cellRange.highlight);
162
+ const toRow = _assertClassBrand(_Transformation_brand, this, _findFirstNonHiddenZeroBasedRow).call(this, cellRange.to.row, cellRange.from.row);
163
+ const toColumn = _assertClassBrand(_Transformation_brand, this, _findFirstNonHiddenZeroBasedColumn).call(this, cellRange.to.col, cellRange.from.col);
170
164
  const visualCoords = cellRange.to.clone();
171
165
  let rowTransformDir = 0;
172
166
  let colTransformDir = 0;
173
167
  this.runLocalHooks('beforeTransformEnd', delta);
174
- if (highlightRenderableCoords.row !== null && highlightRenderableCoords.col !== null) {
168
+ if (highlightRenderableCoords.row !== null && highlightRenderableCoords.col !== null && toRow !== null && toColumn !== null) {
175
169
  const {
176
- row,
177
- col
178
- } = _assertClassBrand(_Transformation_brand, this, _visualToZeroBasedCoords).call(this, cellRange.to);
179
- const coords = _classPrivateFieldGet(_options, this).createCellCoords(row + delta.row, col + delta.col);
170
+ row: highlightRow,
171
+ col: highlightColumn
172
+ } = _assertClassBrand(_Transformation_brand, this, _visualToZeroBasedCoords).call(this, cellRange.highlight);
173
+ const coords = _classPrivateFieldGet(_options, this).createCellCoords(toRow + delta.row, toColumn + delta.col);
174
+ const topStartCorner = cellRange.getTopStartCorner();
175
+ const topEndCorner = cellRange.getTopEndCorner();
176
+ const bottomEndCorner = cellRange.getBottomEndCorner();
177
+ const restDelta = {
178
+ row: coords.row - highlightRow,
179
+ col: coords.col - highlightColumn
180
+ };
181
+ if (delta.col < 0) {
182
+ if (toColumn >= highlightColumn && coords.col < highlightColumn) {
183
+ coords.col = _assertClassBrand(_Transformation_brand, this, _findFirstNonHiddenZeroBasedColumn).call(this, topStartCorner.col, topEndCorner.col) + restDelta.col;
184
+ }
185
+ } else if (delta.col > 0) {
186
+ if (toColumn <= highlightColumn && coords.col > highlightColumn) {
187
+ coords.col = _assertClassBrand(_Transformation_brand, this, _findFirstNonHiddenZeroBasedColumn).call(this, topEndCorner.col, topStartCorner.col) + restDelta.col;
188
+ }
189
+ }
190
+ if (delta.row < 0) {
191
+ if (toRow >= highlightRow && coords.row < highlightRow) {
192
+ coords.row = _assertClassBrand(_Transformation_brand, this, _findFirstNonHiddenZeroBasedRow).call(this, topStartCorner.row, bottomEndCorner.row) + restDelta.row;
193
+ }
194
+ } else if (delta.row > 0) {
195
+ if (toRow <= highlightRow && coords.row > highlightRow) {
196
+ coords.row = _assertClassBrand(_Transformation_brand, this, _findFirstNonHiddenZeroBasedRow).call(this, bottomEndCorner.row, topStartCorner.row) + restDelta.row;
197
+ }
198
+ }
180
199
  const {
181
200
  rowDir,
182
201
  colDir
@@ -196,23 +215,24 @@ class Transformation {
196
215
  this.runLocalHooks('afterTransformEnd', visualCoords, rowTransformDir, colTransformDir);
197
216
  return visualCoords;
198
217
  }
218
+
219
+ /**
220
+ * Sets the additional offset in table size that may occur when the `navigableHeaders` option
221
+ * is enabled.
222
+ *
223
+ * @param {{x: number, y: number}} offset Offset as x and y properties.
224
+ */
225
+ setOffsetSize(_ref) {
226
+ let {
227
+ x,
228
+ y
229
+ } = _ref;
230
+ _classPrivateFieldSet(_offset, this, {
231
+ x,
232
+ y
233
+ });
234
+ }
199
235
  }
200
- function _setOffsetSize(_ref) {
201
- let {
202
- x,
203
- y
204
- } = _ref;
205
- _classPrivateFieldSet(_offset, this, {
206
- x,
207
- y
208
- });
209
- }
210
- /**
211
- * Clamps the coords to make sure they points to the cell (or header) in the table range.
212
- *
213
- * @param {CellCoords} zeroBasedCoords The coords object to clamp.
214
- * @returns {{rowDir: 1|0|-1, colDir: 1|0|-1}}
215
- */
216
236
  function _clampCoords(zeroBasedCoords) {
217
237
  const {
218
238
  width,
@@ -251,6 +271,34 @@ function _getTableSize() {
251
271
  height: _classPrivateFieldGet(_offset, this).y + _classPrivateFieldGet(_options, this).countRenderableRows()
252
272
  };
253
273
  }
274
+ /**
275
+ * Finds the first non-hidden zero-based row in the table range.
276
+ *
277
+ * @param {number} visualRowFrom The visual row from which the search should start.
278
+ * @param {number} visualRowTo The visual row to which the search should end.
279
+ * @returns {number | null}
280
+ */
281
+ function _findFirstNonHiddenZeroBasedRow(visualRowFrom, visualRowTo) {
282
+ const row = _classPrivateFieldGet(_options, this).findFirstNonHiddenRenderableRow(visualRowFrom, visualRowTo);
283
+ if (row === null) {
284
+ return null;
285
+ }
286
+ return _classPrivateFieldGet(_offset, this).y + row;
287
+ }
288
+ /**
289
+ * Finds the first non-hidden zero-based column in the table range.
290
+ *
291
+ * @param {number} visualColumnFrom The visual column from which the search should start.
292
+ * @param {number} visualColumnTo The visual column to which the search should end.
293
+ * @returns {number | null}
294
+ */
295
+ function _findFirstNonHiddenZeroBasedColumn(visualColumnFrom, visualColumnTo) {
296
+ const column = _classPrivateFieldGet(_options, this).findFirstNonHiddenRenderableColumn(visualColumnFrom, visualColumnTo);
297
+ if (column === null) {
298
+ return null;
299
+ }
300
+ return _classPrivateFieldGet(_offset, this).x + column;
301
+ }
254
302
  /**
255
303
  * Translates the visual coordinates to zero-based ones.
256
304
  *
@@ -262,6 +310,9 @@ function _visualToZeroBasedCoords(visualCoords) {
262
310
  row,
263
311
  col
264
312
  } = _classPrivateFieldGet(_options, this).visualToRenderableCoords(visualCoords);
313
+ if (row === null || col === null) {
314
+ throw new Error('Renderable coords are not visible.');
315
+ }
265
316
  return _classPrivateFieldGet(_options, this).createCellCoords(_classPrivateFieldGet(_offset, this).y + row, _classPrivateFieldGet(_offset, this).x + col);
266
317
  }
267
318
  /**