handsontable 0.0.0-next-221e46b-20240307 → 0.0.0-next-2becb78-20240313

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.

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 +1 -0
  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 +81 -21
  12. package/core.mjs +81 -21
  13. package/dist/handsontable.css +2 -2
  14. package/dist/handsontable.full.css +2 -2
  15. package/dist/handsontable.full.js +2721 -1361
  16. package/dist/handsontable.full.min.css +2 -2
  17. package/dist/handsontable.full.min.js +152 -152
  18. package/dist/handsontable.js +2721 -1361
  19. package/dist/handsontable.min.css +2 -2
  20. package/dist/handsontable.min.js +32 -32
  21. package/editorManager.js +12 -8
  22. package/editorManager.mjs +12 -8
  23. package/helpers/mixed.js +1 -1
  24. package/helpers/mixed.mjs +1 -1
  25. package/package.json +1 -1
  26. package/pluginHooks.d.ts +1 -0
  27. package/pluginHooks.js +43 -1
  28. package/pluginHooks.mjs +43 -1
  29. package/plugins/collapsibleColumns/collapsibleColumns.js +7 -1
  30. package/plugins/collapsibleColumns/collapsibleColumns.mjs +7 -1
  31. package/plugins/columnSorting/columnSorting.js +6 -0
  32. package/plugins/columnSorting/columnSorting.mjs +6 -0
  33. package/plugins/contextMenu/menu/defaultShortcutsList.js +26 -10
  34. package/plugins/contextMenu/menu/defaultShortcutsList.mjs +26 -10
  35. package/plugins/mergeCells/calculations/selection.js +1 -70
  36. package/plugins/mergeCells/calculations/selection.mjs +1 -70
  37. package/plugins/mergeCells/cellsCollection.js +116 -0
  38. package/plugins/mergeCells/cellsCollection.mjs +116 -0
  39. package/plugins/mergeCells/contextMenuItem/toggleMerge.js +11 -1
  40. package/plugins/mergeCells/contextMenuItem/toggleMerge.mjs +11 -1
  41. package/plugins/mergeCells/focusOrder.js +327 -0
  42. package/plugins/mergeCells/focusOrder.mjs +322 -0
  43. package/plugins/mergeCells/mergeCells.js +392 -197
  44. package/plugins/mergeCells/mergeCells.mjs +392 -197
  45. package/plugins/multiColumnSorting/multiColumnSorting.js +6 -0
  46. package/plugins/multiColumnSorting/multiColumnSorting.mjs +6 -0
  47. package/plugins/nestedHeaders/nestedHeaders.js +1 -0
  48. package/plugins/nestedHeaders/nestedHeaders.mjs +1 -0
  49. package/plugins/nestedRows/nestedRows.js +7 -1
  50. package/plugins/nestedRows/nestedRows.mjs +7 -1
  51. package/renderers/checkboxRenderer/checkboxRenderer.js +8 -5
  52. package/renderers/checkboxRenderer/checkboxRenderer.mjs +8 -5
  53. package/selection/highlight/visualSelection.js +16 -2
  54. package/selection/highlight/visualSelection.mjs +16 -2
  55. package/selection/selection.js +228 -44
  56. package/selection/selection.mjs +227 -43
  57. package/selection/transformation.js +86 -32
  58. package/selection/transformation.mjs +86 -32
  59. package/shortcutContexts/commands/editor/closeAndSave.js +2 -2
  60. package/shortcutContexts/commands/editor/closeAndSave.mjs +2 -2
  61. package/shortcutContexts/commands/editor/open.js +18 -3
  62. package/shortcutContexts/commands/editor/open.mjs +18 -3
  63. package/shortcutContexts/commands/extendCellsSelection/down.js +1 -1
  64. package/shortcutContexts/commands/extendCellsSelection/down.mjs +1 -1
  65. package/shortcutContexts/commands/extendCellsSelection/left.js +1 -1
  66. package/shortcutContexts/commands/extendCellsSelection/left.mjs +1 -1
  67. package/shortcutContexts/commands/extendCellsSelection/right.js +1 -1
  68. package/shortcutContexts/commands/extendCellsSelection/right.mjs +1 -1
  69. package/shortcutContexts/commands/extendCellsSelection/toColumns.js +1 -1
  70. package/shortcutContexts/commands/extendCellsSelection/toColumns.mjs +1 -1
  71. package/shortcutContexts/commands/extendCellsSelection/toMostBottom.js +3 -1
  72. package/shortcutContexts/commands/extendCellsSelection/toMostBottom.mjs +3 -1
  73. package/shortcutContexts/commands/extendCellsSelection/toMostInlineEnd.js +9 -3
  74. package/shortcutContexts/commands/extendCellsSelection/toMostInlineEnd.mjs +9 -3
  75. package/shortcutContexts/commands/extendCellsSelection/toMostInlineStart.js +10 -3
  76. package/shortcutContexts/commands/extendCellsSelection/toMostInlineStart.mjs +10 -3
  77. package/shortcutContexts/commands/extendCellsSelection/toMostLeft.js +3 -1
  78. package/shortcutContexts/commands/extendCellsSelection/toMostLeft.mjs +3 -1
  79. package/shortcutContexts/commands/extendCellsSelection/toMostRight.js +3 -1
  80. package/shortcutContexts/commands/extendCellsSelection/toMostRight.mjs +3 -1
  81. package/shortcutContexts/commands/extendCellsSelection/toMostTop.js +3 -1
  82. package/shortcutContexts/commands/extendCellsSelection/toMostTop.mjs +3 -1
  83. package/shortcutContexts/commands/extendCellsSelection/toRows.js +1 -1
  84. package/shortcutContexts/commands/extendCellsSelection/toRows.mjs +1 -1
  85. package/shortcutContexts/commands/extendCellsSelection/up.js +1 -1
  86. package/shortcutContexts/commands/extendCellsSelection/up.mjs +1 -1
  87. package/shortcutContexts/commands/moveCellSelection/inlineEnd.js +6 -1
  88. package/shortcutContexts/commands/moveCellSelection/inlineEnd.mjs +6 -1
  89. package/shortcutContexts/commands/moveCellSelection/inlineStart.js +6 -1
  90. package/shortcutContexts/commands/moveCellSelection/inlineStart.mjs +6 -1
  91. package/shortcutContexts/grid.js +2 -2
  92. package/shortcutContexts/grid.mjs +2 -2
  93. package/shortcuts/context.js +2 -1
  94. package/shortcuts/context.mjs +2 -1
  95. package/tableView.js +20 -0
  96. package/tableView.mjs +20 -0
  97. package/utils/dataStructures/linkedList.js +6 -1
  98. package/utils/dataStructures/linkedList.mjs +6 -1
@@ -24,6 +24,9 @@ import { A11Y_SELECTED } from "../helpers/a11y.mjs";
24
24
  * @class Selection
25
25
  * @util
26
26
  */
27
+ var _transformation = /*#__PURE__*/new WeakMap();
28
+ var _focusTransformation = /*#__PURE__*/new WeakMap();
29
+ var _isFocusSelectionChanged = /*#__PURE__*/new WeakMap();
27
30
  var _disableHeadersHighlight = /*#__PURE__*/new WeakMap();
28
31
  var _selectionSource = /*#__PURE__*/new WeakMap();
29
32
  var _expectedLayersCount = /*#__PURE__*/new WeakMap();
@@ -63,11 +66,23 @@ class Selection {
63
66
  */
64
67
  _defineProperty(this, "highlight", void 0);
65
68
  /**
66
- * The module for modifying coordinates.
69
+ * The module for modifying coordinates of the start and end selection.
67
70
  *
68
71
  * @type {Transformation}
69
72
  */
70
- _defineProperty(this, "transformation", void 0);
73
+ _classPrivateFieldInitSpec(this, _transformation, {
74
+ writable: true,
75
+ value: void 0
76
+ });
77
+ /**
78
+ * The module for modifying coordinates of the focus selection.
79
+ *
80
+ * @type {Transformation}
81
+ */
82
+ _classPrivateFieldInitSpec(this, _focusTransformation, {
83
+ writable: true,
84
+ value: void 0
85
+ });
71
86
  /**
72
87
  * The collection of the selection layer levels where the whole row was selected using the row header or
73
88
  * the corner header.
@@ -82,6 +97,15 @@ class Selection {
82
97
  * @type {Set<number>}
83
98
  */
84
99
  _defineProperty(this, "selectedByColumnHeader", new Set());
100
+ /**
101
+ * The flag which determines if the focus selection was changed.
102
+ *
103
+ * @type {boolean}
104
+ */
105
+ _classPrivateFieldInitSpec(this, _isFocusSelectionChanged, {
106
+ writable: true,
107
+ value: false
108
+ });
85
109
  /**
86
110
  * When sets disable highlighting the headers even when the logical coordinates points on them.
87
111
  *
@@ -132,71 +156,118 @@ class Selection {
132
156
  createCellCoords: (row, column) => this.tableProps.createCellCoords(row, column),
133
157
  createCellRange: (highlight, from, to) => this.tableProps.createCellRange(highlight, from, to)
134
158
  });
135
- this.transformation = new Transformation(this.selectedRange, {
159
+ _classPrivateFieldSet(this, _transformation, new Transformation(this.selectedRange, {
136
160
  rowIndexMapper: this.tableProps.rowIndexMapper,
137
161
  columnIndexMapper: this.tableProps.columnIndexMapper,
138
162
  countRenderableRows: () => this.tableProps.countRenderableRows(),
139
163
  countRenderableColumns: () => this.tableProps.countRenderableColumns(),
140
- countRowHeaders: () => this.tableProps.countRowHeaders(),
141
- countColHeaders: () => this.tableProps.countColHeaders(),
142
164
  visualToRenderableCoords: coords => this.tableProps.visualToRenderableCoords(coords),
143
165
  renderableToVisualCoords: coords => this.tableProps.renderableToVisualCoords(coords),
166
+ findFirstNonHiddenRenderableRow: function () {
167
+ return _this.tableProps.findFirstNonHiddenRenderableRow(...arguments);
168
+ },
169
+ findFirstNonHiddenRenderableColumn: function () {
170
+ return _this.tableProps.findFirstNonHiddenRenderableColumn(...arguments);
171
+ },
144
172
  createCellCoords: (row, column) => this.tableProps.createCellCoords(row, column),
145
- navigableHeaders: () => settings.navigableHeaders,
146
173
  fixedRowsBottom: () => settings.fixedRowsBottom,
147
174
  minSpareRows: () => settings.minSpareRows,
148
175
  minSpareCols: () => settings.minSpareCols,
149
176
  autoWrapRow: () => settings.autoWrapRow,
150
177
  autoWrapCol: () => settings.autoWrapCol
151
- });
152
- this.transformation.addLocalHook('beforeTransformStart', function () {
178
+ }));
179
+ _classPrivateFieldSet(this, _focusTransformation, new Transformation(this.selectedRange, {
180
+ rowIndexMapper: this.tableProps.rowIndexMapper,
181
+ columnIndexMapper: this.tableProps.columnIndexMapper,
182
+ countRenderableRows: () => {
183
+ const range = this.selectedRange.current();
184
+ return this.tableProps.countRenderableRowsInRange(0, range.getOuterBottomEndCorner().row);
185
+ },
186
+ countRenderableColumns: () => {
187
+ const range = this.selectedRange.current();
188
+ return this.tableProps.countRenderableColumnsInRange(0, range.getOuterBottomEndCorner().col);
189
+ },
190
+ visualToRenderableCoords: coords => this.tableProps.visualToRenderableCoords(coords),
191
+ renderableToVisualCoords: coords => this.tableProps.renderableToVisualCoords(coords),
192
+ findFirstNonHiddenRenderableRow: function () {
193
+ return _this.tableProps.findFirstNonHiddenRenderableRow(...arguments);
194
+ },
195
+ findFirstNonHiddenRenderableColumn: function () {
196
+ return _this.tableProps.findFirstNonHiddenRenderableColumn(...arguments);
197
+ },
198
+ createCellCoords: (row, column) => this.tableProps.createCellCoords(row, column),
199
+ fixedRowsBottom: () => 0,
200
+ minSpareRows: () => 0,
201
+ minSpareCols: () => 0,
202
+ autoWrapRow: () => true,
203
+ autoWrapCol: () => true
204
+ }));
205
+ _classPrivateFieldGet(this, _transformation).addLocalHook('beforeTransformStart', function () {
153
206
  for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
154
207
  args[_key] = arguments[_key];
155
208
  }
156
209
  return _this.runLocalHooks('beforeModifyTransformStart', ...args);
157
210
  });
158
- this.transformation.addLocalHook('afterTransformStart', function () {
211
+ _classPrivateFieldGet(this, _transformation).addLocalHook('afterTransformStart', function () {
159
212
  for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
160
213
  args[_key2] = arguments[_key2];
161
214
  }
162
215
  return _this.runLocalHooks('afterModifyTransformStart', ...args);
163
216
  });
164
- this.transformation.addLocalHook('beforeTransformEnd', function () {
217
+ _classPrivateFieldGet(this, _transformation).addLocalHook('beforeTransformEnd', function () {
165
218
  for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
166
219
  args[_key3] = arguments[_key3];
167
220
  }
168
221
  return _this.runLocalHooks('beforeModifyTransformEnd', ...args);
169
222
  });
170
- this.transformation.addLocalHook('afterTransformEnd', function () {
223
+ _classPrivateFieldGet(this, _transformation).addLocalHook('modifyTransformEndRestDelta', function () {
171
224
  for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
172
225
  args[_key4] = arguments[_key4];
173
226
  }
174
- return _this.runLocalHooks('afterModifyTransformEnd', ...args);
227
+ return _this.runLocalHooks('modifyTransformEndRestDelta', ...args);
175
228
  });
176
- this.transformation.addLocalHook('insertRowRequire', function () {
229
+ _classPrivateFieldGet(this, _transformation).addLocalHook('afterTransformEnd', function () {
177
230
  for (var _len5 = arguments.length, args = new Array(_len5), _key5 = 0; _key5 < _len5; _key5++) {
178
231
  args[_key5] = arguments[_key5];
179
232
  }
180
- return _this.runLocalHooks('insertRowRequire', ...args);
233
+ return _this.runLocalHooks('afterModifyTransformEnd', ...args);
181
234
  });
182
- this.transformation.addLocalHook('insertColRequire', function () {
235
+ _classPrivateFieldGet(this, _transformation).addLocalHook('insertRowRequire', function () {
183
236
  for (var _len6 = arguments.length, args = new Array(_len6), _key6 = 0; _key6 < _len6; _key6++) {
184
237
  args[_key6] = arguments[_key6];
185
238
  }
186
- return _this.runLocalHooks('insertColRequire', ...args);
239
+ return _this.runLocalHooks('insertRowRequire', ...args);
187
240
  });
188
- this.transformation.addLocalHook('beforeRowWrap', function () {
241
+ _classPrivateFieldGet(this, _transformation).addLocalHook('insertColRequire', function () {
189
242
  for (var _len7 = arguments.length, args = new Array(_len7), _key7 = 0; _key7 < _len7; _key7++) {
190
243
  args[_key7] = arguments[_key7];
191
244
  }
192
- return _this.runLocalHooks('beforeRowWrap', ...args);
245
+ return _this.runLocalHooks('insertColRequire', ...args);
193
246
  });
194
- this.transformation.addLocalHook('beforeColumnWrap', function () {
247
+ _classPrivateFieldGet(this, _transformation).addLocalHook('beforeRowWrap', function () {
195
248
  for (var _len8 = arguments.length, args = new Array(_len8), _key8 = 0; _key8 < _len8; _key8++) {
196
249
  args[_key8] = arguments[_key8];
197
250
  }
251
+ return _this.runLocalHooks('beforeRowWrap', ...args);
252
+ });
253
+ _classPrivateFieldGet(this, _transformation).addLocalHook('beforeColumnWrap', function () {
254
+ for (var _len9 = arguments.length, args = new Array(_len9), _key9 = 0; _key9 < _len9; _key9++) {
255
+ args[_key9] = arguments[_key9];
256
+ }
198
257
  return _this.runLocalHooks('beforeColumnWrap', ...args);
199
258
  });
259
+ _classPrivateFieldGet(this, _focusTransformation).addLocalHook('beforeTransformStart', function () {
260
+ for (var _len10 = arguments.length, args = new Array(_len10), _key10 = 0; _key10 < _len10; _key10++) {
261
+ args[_key10] = arguments[_key10];
262
+ }
263
+ return _this.runLocalHooks('beforeModifyTransformFocus', ...args);
264
+ });
265
+ _classPrivateFieldGet(this, _focusTransformation).addLocalHook('afterTransformStart', function () {
266
+ for (var _len11 = arguments.length, args = new Array(_len11), _key11 = 0; _key11 < _len11; _key11++) {
267
+ args[_key11] = arguments[_key11];
268
+ }
269
+ return _this.runLocalHooks('afterModifyTransformFocus', ...args);
270
+ });
200
271
  }
201
272
 
202
273
  /**
@@ -287,6 +358,7 @@ class Selection {
287
358
  // We are creating copy. We would like to modify just the start of the selection by below hook. Then original coords
288
359
  // should be handled by next methods.
289
360
  const coordsClone = coords.clone();
361
+ _classPrivateFieldSet(this, _isFocusSelectionChanged, false);
290
362
  this.runLocalHooks(`beforeSetRangeStart${fragment ? 'Only' : ''}`, coordsClone);
291
363
  if (!isMultipleMode || isMultipleMode && !isMultipleSelection && isUndefined(multipleSelection)) {
292
364
  this.selectedRange.clear();
@@ -344,7 +416,20 @@ class Selection {
344
416
  cellRange.setFrom(cellRange.highlight);
345
417
  cellRange.setTo(cellRange.highlight);
346
418
  } else {
419
+ const horizontalDir = cellRange.getHorizontalDirection();
420
+ const verticalDir = cellRange.getVerticalDirection();
421
+ const isMultiple = this.isMultiple();
347
422
  cellRange.setTo(coordsClone);
423
+ if (isMultiple && horizontalDir !== cellRange.getHorizontalDirection()) {
424
+ cellRange.from.assign({
425
+ col: cellRange.highlight.col
426
+ });
427
+ }
428
+ if (isMultiple && verticalDir !== cellRange.getVerticalDirection()) {
429
+ cellRange.from.assign({
430
+ row: cellRange.highlight.row
431
+ });
432
+ }
348
433
  }
349
434
 
350
435
  // Prevent creating "area" selection that overlaps headers.
@@ -354,11 +439,7 @@ class Selection {
354
439
  }
355
440
  }
356
441
  this.runLocalHooks('beforeHighlightSet');
357
- const focusHighlight = this.highlight.getFocus();
358
- focusHighlight.clear();
359
- if (this.highlight.isEnabledFor(FOCUS_TYPE, cellRange.highlight)) {
360
- focusHighlight.add(this.selectedRange.current().highlight).commit().syncWith(cellRange);
361
- }
442
+ this.setRangeFocus(this.selectedRange.current().highlight);
362
443
  const layerLevel = this.getLayerLevel();
363
444
 
364
445
  // If the next layer level is lower than previous then clear all area and header highlights. This is the
@@ -446,15 +527,31 @@ class Selection {
446
527
  }
447
528
 
448
529
  /**
449
- * Returns information if we have a multiselection. This method check multiselection only on the latest layer of
450
- * the selection.
530
+ * Sets the selection focus position at the specified coordinates.
451
531
  *
452
- * @returns {boolean}
532
+ * @param {CellCoords} coords The CellCoords instance with defined visual coordinates.
453
533
  */
454
- isMultiple() {
455
- const isMultipleListener = createObjectPropListener(!this.selectedRange.current().isSingle());
456
- this.runLocalHooks('afterIsMultipleSelection', isMultipleListener);
457
- return isMultipleListener.value;
534
+ setRangeFocus(coords) {
535
+ if (this.selectedRange.isEmpty()) {
536
+ return;
537
+ }
538
+ const cellRange = this.selectedRange.current();
539
+ if (!this.inProgress) {
540
+ this.runLocalHooks('beforeSetFocus', coords);
541
+ }
542
+ const focusHighlight = this.highlight.getFocus();
543
+ focusHighlight.clear();
544
+ cellRange.setHighlight(coords);
545
+ if (!this.inProgress) {
546
+ this.runLocalHooks('beforeHighlightSet');
547
+ }
548
+ if (this.highlight.isEnabledFor(FOCUS_TYPE, cellRange.highlight)) {
549
+ focusHighlight.add(cellRange.highlight).commit().syncWith(cellRange);
550
+ }
551
+ if (!this.inProgress) {
552
+ _classPrivateFieldSet(this, _isFocusSelectionChanged, true);
553
+ this.runLocalHooks('afterSetFocus', cellRange.highlight);
554
+ }
458
555
  }
459
556
 
460
557
  /**
@@ -467,7 +564,13 @@ class Selection {
467
564
  */
468
565
  transformStart(rowDelta, colDelta) {
469
566
  let createMissingRecords = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
470
- this.setRangeStart(this.transformation.transformStart(rowDelta, colDelta, createMissingRecords));
567
+ if (this.settings.navigableHeaders) {
568
+ _classPrivateFieldGet(this, _transformation).setOffsetSize({
569
+ x: this.tableProps.countRowHeaders(),
570
+ y: this.tableProps.countColHeaders()
571
+ });
572
+ }
573
+ this.setRangeStart(_classPrivateFieldGet(this, _transformation).transformStart(rowDelta, colDelta, createMissingRecords));
471
574
  }
472
575
 
473
576
  /**
@@ -477,7 +580,44 @@ class Selection {
477
580
  * @param {number} colDelta Columns number to move, value can be passed as negative number.
478
581
  */
479
582
  transformEnd(rowDelta, colDelta) {
480
- this.setRangeEnd(this.transformation.transformEnd(rowDelta, colDelta));
583
+ if (this.settings.navigableHeaders) {
584
+ _classPrivateFieldGet(this, _transformation).setOffsetSize({
585
+ x: this.tableProps.countRowHeaders(),
586
+ y: this.tableProps.countColHeaders()
587
+ });
588
+ }
589
+ this.setRangeEnd(_classPrivateFieldGet(this, _transformation).transformEnd(rowDelta, colDelta));
590
+ }
591
+
592
+ /**
593
+ * Transforms the focus cell selection relative to the current focus position.
594
+ *
595
+ * @param {number} rowDelta Rows number to move, value can be passed as negative number.
596
+ * @param {number} colDelta Columns number to move, value can be passed as negative number.
597
+ */
598
+ transformFocus(rowDelta, colDelta) {
599
+ const range = this.selectedRange.current();
600
+ const {
601
+ row,
602
+ col
603
+ } = range.getOuterTopStartCorner();
604
+ const columnsInRange = this.tableProps.countRenderableColumnsInRange(0, col - 1);
605
+ const rowsInRange = this.tableProps.countRenderableRowsInRange(0, row - 1);
606
+ if (range.highlight.isHeader()) {
607
+ // for header focus selection calculate the new coords based on the selection including headers
608
+ _classPrivateFieldGet(this, _focusTransformation).setOffsetSize({
609
+ x: col < 0 ? Math.abs(col) : -columnsInRange,
610
+ y: row < 0 ? Math.abs(row) : -rowsInRange
611
+ });
612
+ } else {
613
+ // for focus selection in cells calculate the new coords only based on the selected cells
614
+ _classPrivateFieldGet(this, _focusTransformation).setOffsetSize({
615
+ x: col < 0 ? 0 : -columnsInRange,
616
+ y: row < 0 ? 0 : -rowsInRange
617
+ });
618
+ }
619
+ const focusCoords = _classPrivateFieldGet(this, _focusTransformation).transformStart(rowDelta, colDelta);
620
+ this.setRangeFocus(focusCoords.normalize());
481
621
  }
482
622
 
483
623
  /**
@@ -498,6 +638,30 @@ class Selection {
498
638
  return !this.selectedRange.isEmpty();
499
639
  }
500
640
 
641
+ /**
642
+ * Returns information if we have a multi-selection. This method check multi-selection only on the latest layer of
643
+ * the selection.
644
+ *
645
+ * @returns {boolean}
646
+ */
647
+ isMultiple() {
648
+ if (!this.isSelected()) {
649
+ return false;
650
+ }
651
+ const isMultipleListener = createObjectPropListener(!this.selectedRange.current().isSingle());
652
+ this.runLocalHooks('afterIsMultipleSelection', isMultipleListener);
653
+ return isMultipleListener.value;
654
+ }
655
+
656
+ /**
657
+ * Checks if the last selection involves changing the focus cell position only.
658
+ *
659
+ * @returns {boolean}
660
+ */
661
+ isFocusSelectionChanged() {
662
+ return this.isSelected() && _classPrivateFieldGet(this, _isFocusSelectionChanged);
663
+ }
664
+
501
665
  /**
502
666
  * Returns `true` if the selection was applied by clicking to the row header. If the `layerLevel`
503
667
  * argument is passed then only that layer will be checked. Otherwise, it checks if any row header
@@ -780,9 +944,10 @@ class Selection {
780
944
  *
781
945
  * @param {number|string} startColumn Visual column index or column property from which the selection starts.
782
946
  * @param {number|string} [endColumn] Visual column index or column property from to the selection finishes.
783
- * @param {number} [focusPosition=0] The argument allows changing the cell/header focus position.
784
- * The value can take visual row index from -N to N, where negative values
785
- * point to the headers and positive values point to the cell range.
947
+ * @param {number | { row: number, col: number }} [focusPosition=0] The argument allows changing the cell/header focus
948
+ * position. The value can take visual row index from -N to N, where negative values point to the headers and positive
949
+ * values point to the cell range. An object with `row` and `col` properties also can be passed to change the focus
950
+ * position horizontally.
786
951
  * @returns {boolean} Returns `true` if selection was successful, `false` otherwise.
787
952
  */
788
953
  selectColumns(startColumn) {
@@ -803,11 +968,20 @@ class Selection {
803
968
  countColHeaders
804
969
  });
805
970
  if (isValid) {
806
- const fromRow = countColHeaders === 0 ? 0 : clamp(focusPosition, columnHeaderLastIndex, -1);
971
+ let highlightRow = 0;
972
+ let highlightColumn = 0;
973
+ if (Number.isInteger(focusPosition === null || focusPosition === void 0 ? void 0 : focusPosition.row) && Number.isInteger(focusPosition === null || focusPosition === void 0 ? void 0 : focusPosition.col)) {
974
+ highlightRow = clamp(focusPosition.row, columnHeaderLastIndex, countRows - 1);
975
+ highlightColumn = clamp(focusPosition.col, Math.min(start, end), Math.max(start, end));
976
+ } else {
977
+ highlightRow = clamp(focusPosition, columnHeaderLastIndex, countRows - 1);
978
+ highlightColumn = start;
979
+ }
980
+ const highlight = this.tableProps.createCellCoords(highlightRow, highlightColumn);
981
+ const fromRow = countColHeaders === 0 ? 0 : clamp(highlight.row, columnHeaderLastIndex, -1);
807
982
  const toRow = countRows - 1;
808
983
  const from = this.tableProps.createCellCoords(fromRow, start);
809
984
  const to = this.tableProps.createCellCoords(toRow, end);
810
- const highlight = this.tableProps.createCellCoords(clamp(focusPosition, columnHeaderLastIndex, countRows - 1), start);
811
985
  this.runLocalHooks('beforeSelectColumns', from, to, highlight);
812
986
 
813
987
  // disallow modifying row axis for that hooks
@@ -827,9 +1001,10 @@ class Selection {
827
1001
  *
828
1002
  * @param {number} startRow Visual row index from which the selection starts.
829
1003
  * @param {number} [endRow] Visual row index from to the selection finishes.
830
- * @param {number} [focusPosition=0] The argument allows changing the cell/header focus position.
831
- * The value can take visual column index from -N to N, where negative values
832
- * point to the headers and positive values point to the cell range.
1004
+ * @param {number | { row: number, col: number }} [focusPosition=0] The argument allows changing the cell/header focus
1005
+ * position. The value can take visual row index from -N to N, where negative values point to the headers and positive
1006
+ * values point to the cell range. An object with `row` and `col` properties also can be passed to change the focus
1007
+ * position horizontally.
833
1008
  * @returns {boolean} Returns `true` if selection was successful, `false` otherwise.
834
1009
  */
835
1010
  selectRows(startRow) {
@@ -848,11 +1023,20 @@ class Selection {
848
1023
  countColHeaders: 0
849
1024
  });
850
1025
  if (isValid) {
851
- const fromColumn = countRowHeaders === 0 ? 0 : clamp(focusPosition, rowHeaderLastIndex, -1);
1026
+ let highlightRow = 0;
1027
+ let highlightColumn = 0;
1028
+ if (Number.isInteger(focusPosition === null || focusPosition === void 0 ? void 0 : focusPosition.row) && Number.isInteger(focusPosition === null || focusPosition === void 0 ? void 0 : focusPosition.col)) {
1029
+ highlightRow = clamp(focusPosition.row, Math.min(startRow, endRow), Math.max(startRow, endRow));
1030
+ highlightColumn = clamp(focusPosition.col, rowHeaderLastIndex, countCols - 1);
1031
+ } else {
1032
+ highlightRow = startRow;
1033
+ highlightColumn = clamp(focusPosition, rowHeaderLastIndex, countCols - 1);
1034
+ }
1035
+ const highlight = this.tableProps.createCellCoords(highlightRow, highlightColumn);
1036
+ const fromColumn = countRowHeaders === 0 ? 0 : clamp(highlight.col, rowHeaderLastIndex, -1);
852
1037
  const toColumn = countCols - 1;
853
1038
  const from = this.tableProps.createCellCoords(startRow, fromColumn);
854
1039
  const to = this.tableProps.createCellCoords(endRow, toColumn);
855
- const highlight = this.tableProps.createCellCoords(startRow, clamp(focusPosition, rowHeaderLastIndex, countCols - 1));
856
1040
  this.runLocalHooks('beforeSelectRows', from, to, highlight);
857
1041
 
858
1042
  // disallow modifying column axis for that hooks
@@ -8,9 +8,9 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
8
8
  function _classPrivateMethodInitSpec(obj, privateSet) { _checkPrivateRedeclaration(obj, privateSet); privateSet.add(obj); }
9
9
  function _classPrivateFieldInitSpec(obj, privateMap, value) { _checkPrivateRedeclaration(obj, privateMap); privateMap.set(obj, value); }
10
10
  function _checkPrivateRedeclaration(obj, privateCollection) { if (privateCollection.has(obj)) { throw new TypeError("Cannot initialize the same private elements twice on an object"); } }
11
+ function _classPrivateMethodGet(receiver, privateSet, fn) { if (!privateSet.has(receiver)) { throw new TypeError("attempted to get private field on non-instance"); } return fn; }
11
12
  function _classPrivateFieldGet(receiver, privateMap) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "get"); return _classApplyDescriptorGet(receiver, descriptor); }
12
13
  function _classApplyDescriptorGet(receiver, descriptor) { if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; }
13
- function _classPrivateMethodGet(receiver, privateSet, fn) { if (!privateSet.has(receiver)) { throw new TypeError("attempted to get private field on non-instance"); } return fn; }
14
14
  function _classPrivateFieldSet(receiver, privateMap, value) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "set"); _classApplyDescriptorSet(receiver, descriptor, value); return value; }
15
15
  function _classExtractFieldDescriptor(receiver, privateMap, action) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to " + action + " private field on non-instance"); } return privateMap.get(receiver); }
16
16
  function _classApplyDescriptorSet(receiver, descriptor, value) { if (descriptor.set) { descriptor.set.call(receiver, value); } else { if (!descriptor.writable) { throw new TypeError("attempted to set read only private field"); } descriptor.value = value; } }
@@ -32,9 +32,10 @@ function _classApplyDescriptorSet(receiver, descriptor, value) { if (descriptor.
32
32
  var _range = /*#__PURE__*/new WeakMap();
33
33
  var _options = /*#__PURE__*/new WeakMap();
34
34
  var _offset = /*#__PURE__*/new WeakMap();
35
- var _setOffsetSize = /*#__PURE__*/new WeakSet();
36
35
  var _clampCoords = /*#__PURE__*/new WeakSet();
37
36
  var _getTableSize = /*#__PURE__*/new WeakSet();
37
+ var _findFirstNonHiddenZeroBasedRow = /*#__PURE__*/new WeakSet();
38
+ var _findFirstNonHiddenZeroBasedColumn = /*#__PURE__*/new WeakSet();
38
39
  var _visualToZeroBasedCoords = /*#__PURE__*/new WeakSet();
39
40
  var _zeroBasedToVisualCoords = /*#__PURE__*/new WeakSet();
40
41
  class Transformation {
@@ -53,6 +54,22 @@ class Transformation {
53
54
  * @returns {CellCoords}
54
55
  */
55
56
  _classPrivateMethodInitSpec(this, _visualToZeroBasedCoords);
57
+ /**
58
+ * Finds the first non-hidden zero-based column in the table range.
59
+ *
60
+ * @param {number} visualColumnFrom The visual column from which the search should start.
61
+ * @param {number} visualColumnTo The visual column to which the search should end.
62
+ * @returns {number | null}
63
+ */
64
+ _classPrivateMethodInitSpec(this, _findFirstNonHiddenZeroBasedColumn);
65
+ /**
66
+ * Finds the first non-hidden zero-based row in the table range.
67
+ *
68
+ * @param {number} visualRowFrom The visual row from which the search should start.
69
+ * @param {number} visualRowTo The visual row to which the search should end.
70
+ * @returns {number | null}
71
+ */
72
+ _classPrivateMethodInitSpec(this, _findFirstNonHiddenZeroBasedRow);
56
73
  /**
57
74
  * Gets the table size in number of rows with headers as "height" and number of columns with
58
75
  * headers as "width".
@@ -67,13 +84,6 @@ class Transformation {
67
84
  * @returns {{rowDir: 1|0|-1, colDir: 1|0|-1}}
68
85
  */
69
86
  _classPrivateMethodInitSpec(this, _clampCoords);
70
- /**
71
- * Sets the additional offset in table size that may occur when the `navigableHeaders` option
72
- * is enabled.
73
- *
74
- * @param {{x: number, y: number}} offset Offset as x and y properties.
75
- */
76
- _classPrivateMethodInitSpec(this, _setOffsetSize);
77
87
  /**
78
88
  * Instance of the SelectionRange, holder for visual coordinates applied to the table.
79
89
  *
@@ -121,10 +131,6 @@ class Transformation {
121
131
  */
122
132
  transformStart(rowDelta, colDelta) {
123
133
  let createMissingRecords = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
124
- _classPrivateMethodGet(this, _setOffsetSize, _setOffsetSize2).call(this, {
125
- x: _classPrivateFieldGet(this, _options).navigableHeaders() ? _classPrivateFieldGet(this, _options).countRowHeaders() : 0,
126
- y: _classPrivateFieldGet(this, _options).navigableHeaders() ? _classPrivateFieldGet(this, _options).countColHeaders() : 0
127
- });
128
134
  const delta = _classPrivateFieldGet(this, _options).createCellCoords(rowDelta, colDelta);
129
135
  let visualCoords = _classPrivateFieldGet(this, _range).current().highlight;
130
136
  const highlightRenderableCoords = _classPrivateFieldGet(this, _options).visualToRenderableCoords(visualCoords);
@@ -204,23 +210,47 @@ class Transformation {
204
210
  * @returns {CellCoords} Visual coordinates after transformation.
205
211
  */
206
212
  transformEnd(rowDelta, colDelta) {
207
- _classPrivateMethodGet(this, _setOffsetSize, _setOffsetSize2).call(this, {
208
- x: _classPrivateFieldGet(this, _options).navigableHeaders() ? _classPrivateFieldGet(this, _options).countRowHeaders() : 0,
209
- y: _classPrivateFieldGet(this, _options).navigableHeaders() ? _classPrivateFieldGet(this, _options).countColHeaders() : 0
210
- });
211
213
  const delta = _classPrivateFieldGet(this, _options).createCellCoords(rowDelta, colDelta);
212
214
  const cellRange = _classPrivateFieldGet(this, _range).current();
213
215
  const highlightRenderableCoords = _classPrivateFieldGet(this, _options).visualToRenderableCoords(cellRange.highlight);
216
+ const toRow = _classPrivateMethodGet(this, _findFirstNonHiddenZeroBasedRow, _findFirstNonHiddenZeroBasedRow2).call(this, cellRange.to.row, cellRange.from.row);
217
+ const toColumn = _classPrivateMethodGet(this, _findFirstNonHiddenZeroBasedColumn, _findFirstNonHiddenZeroBasedColumn2).call(this, cellRange.to.col, cellRange.from.col);
214
218
  const visualCoords = cellRange.to.clone();
215
219
  let rowTransformDir = 0;
216
220
  let colTransformDir = 0;
217
221
  this.runLocalHooks('beforeTransformEnd', delta);
218
- if (highlightRenderableCoords.row !== null && highlightRenderableCoords.col !== null) {
222
+ if (highlightRenderableCoords.row !== null && highlightRenderableCoords.col !== null && toRow !== null && toColumn !== null) {
219
223
  const {
220
- row,
221
- col
222
- } = _classPrivateMethodGet(this, _visualToZeroBasedCoords, _visualToZeroBasedCoords2).call(this, cellRange.to);
223
- const coords = _classPrivateFieldGet(this, _options).createCellCoords(row + delta.row, col + delta.col);
224
+ row: highlightRow,
225
+ col: highlightColumn
226
+ } = _classPrivateMethodGet(this, _visualToZeroBasedCoords, _visualToZeroBasedCoords2).call(this, cellRange.highlight);
227
+ const coords = _classPrivateFieldGet(this, _options).createCellCoords(toRow + delta.row, toColumn + delta.col);
228
+ const restDelta = {
229
+ row: coords.row - highlightRow,
230
+ col: coords.col - highlightColumn
231
+ };
232
+ this.runLocalHooks('modifyTransformEndRestDelta', restDelta, delta, _classPrivateMethodGet(this, _zeroBasedToVisualCoords, _zeroBasedToVisualCoords2).call(this, coords));
233
+ const topStartCorner = cellRange.getTopStartCorner();
234
+ const topEndCorner = cellRange.getTopEndCorner();
235
+ const bottomEndCorner = cellRange.getBottomEndCorner();
236
+ if (delta.col < 0) {
237
+ if (toColumn >= highlightColumn && coords.col < highlightColumn) {
238
+ coords.col = _classPrivateMethodGet(this, _findFirstNonHiddenZeroBasedColumn, _findFirstNonHiddenZeroBasedColumn2).call(this, topStartCorner.col, topEndCorner.col) + restDelta.col;
239
+ }
240
+ } else if (delta.col > 0) {
241
+ if (toColumn <= highlightColumn && coords.col > highlightColumn) {
242
+ coords.col = _classPrivateMethodGet(this, _findFirstNonHiddenZeroBasedColumn, _findFirstNonHiddenZeroBasedColumn2).call(this, topEndCorner.col, topStartCorner.col) + restDelta.col;
243
+ }
244
+ }
245
+ if (delta.row < 0) {
246
+ if (toRow >= highlightRow && coords.row < highlightRow) {
247
+ coords.row = _classPrivateMethodGet(this, _findFirstNonHiddenZeroBasedRow, _findFirstNonHiddenZeroBasedRow2).call(this, topStartCorner.row, bottomEndCorner.row) + restDelta.row;
248
+ }
249
+ } else if (delta.row > 0) {
250
+ if (toRow <= highlightRow && coords.row > highlightRow) {
251
+ coords.row = _classPrivateMethodGet(this, _findFirstNonHiddenZeroBasedRow, _findFirstNonHiddenZeroBasedRow2).call(this, bottomEndCorner.row, topStartCorner.row) + restDelta.row;
252
+ }
253
+ }
224
254
  const {
225
255
  rowDir,
226
256
  colDir
@@ -240,16 +270,23 @@ class Transformation {
240
270
  this.runLocalHooks('afterTransformEnd', visualCoords, rowTransformDir, colTransformDir);
241
271
  return visualCoords;
242
272
  }
243
- }
244
- function _setOffsetSize2(_ref) {
245
- let {
246
- x,
247
- y
248
- } = _ref;
249
- _classPrivateFieldSet(this, _offset, {
250
- x,
251
- y
252
- });
273
+
274
+ /**
275
+ * Sets the additional offset in table size that may occur when the `navigableHeaders` option
276
+ * is enabled.
277
+ *
278
+ * @param {{x: number, y: number}} offset Offset as x and y properties.
279
+ */
280
+ setOffsetSize(_ref) {
281
+ let {
282
+ x,
283
+ y
284
+ } = _ref;
285
+ _classPrivateFieldSet(this, _offset, {
286
+ x,
287
+ y
288
+ });
289
+ }
253
290
  }
254
291
  function _clampCoords2(zeroBasedCoords) {
255
292
  const {
@@ -283,11 +320,28 @@ function _getTableSize2() {
283
320
  height: _classPrivateFieldGet(this, _offset).y + _classPrivateFieldGet(this, _options).countRenderableRows()
284
321
  };
285
322
  }
323
+ function _findFirstNonHiddenZeroBasedRow2(visualRowFrom, visualRowTo) {
324
+ const row = _classPrivateFieldGet(this, _options).findFirstNonHiddenRenderableRow(visualRowFrom, visualRowTo);
325
+ if (row === null) {
326
+ return null;
327
+ }
328
+ return _classPrivateFieldGet(this, _offset).y + row;
329
+ }
330
+ function _findFirstNonHiddenZeroBasedColumn2(visualColumnFrom, visualColumnTo) {
331
+ const column = _classPrivateFieldGet(this, _options).findFirstNonHiddenRenderableColumn(visualColumnFrom, visualColumnTo);
332
+ if (column === null) {
333
+ return null;
334
+ }
335
+ return _classPrivateFieldGet(this, _offset).x + column;
336
+ }
286
337
  function _visualToZeroBasedCoords2(visualCoords) {
287
338
  const {
288
339
  row,
289
340
  col
290
341
  } = _classPrivateFieldGet(this, _options).visualToRenderableCoords(visualCoords);
342
+ if (row === null || col === null) {
343
+ throw new Error('Renderable coords are not visible.');
344
+ }
291
345
  return _classPrivateFieldGet(this, _options).createCellCoords(_classPrivateFieldGet(this, _offset).y + row, _classPrivateFieldGet(this, _offset).x + col);
292
346
  }
293
347
  function _zeroBasedToVisualCoords2(zeroBasedCoords) {