handsontable 0.0.0-next-221e46b-20240307 → 0.0.0-next-200799f-20240311

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 +78 -21
  12. package/core.mjs +78 -21
  13. package/dist/handsontable.css +2 -2
  14. package/dist/handsontable.full.css +2 -2
  15. package/dist/handsontable.full.js +2645 -1363
  16. package/dist/handsontable.full.min.css +2 -2
  17. package/dist/handsontable.full.min.js +152 -152
  18. package/dist/handsontable.js +2645 -1363
  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 +258 -0
  42. package/plugins/mergeCells/focusOrder.mjs +253 -0
  43. package/plugins/mergeCells/mergeCells.js +394 -199
  44. package/plugins/mergeCells/mergeCells.mjs +394 -199
  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 +4 -4
  52. package/renderers/checkboxRenderer/checkboxRenderer.mjs +4 -4
  53. package/selection/highlight/visualSelection.js +16 -2
  54. package/selection/highlight/visualSelection.mjs +16 -2
  55. package/selection/selection.js +225 -44
  56. package/selection/selection.mjs +224 -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,28 @@ 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
+ const focusHighlight = this.highlight.getFocus();
540
+ focusHighlight.clear();
541
+ cellRange.setHighlight(coords);
542
+ if (!this.inProgress) {
543
+ this.runLocalHooks('beforeHighlightSet');
544
+ }
545
+ if (this.highlight.isEnabledFor(FOCUS_TYPE, cellRange.highlight)) {
546
+ focusHighlight.add(cellRange.highlight).commit().syncWith(cellRange);
547
+ }
548
+ if (!this.inProgress) {
549
+ _classPrivateFieldSet(this, _isFocusSelectionChanged, true);
550
+ this.runLocalHooks('afterSetFocus', cellRange.highlight);
551
+ }
458
552
  }
459
553
 
460
554
  /**
@@ -467,7 +561,13 @@ class Selection {
467
561
  */
468
562
  transformStart(rowDelta, colDelta) {
469
563
  let createMissingRecords = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
470
- this.setRangeStart(this.transformation.transformStart(rowDelta, colDelta, createMissingRecords));
564
+ if (this.settings.navigableHeaders) {
565
+ _classPrivateFieldGet(this, _transformation).setOffsetSize({
566
+ x: this.tableProps.countRowHeaders(),
567
+ y: this.tableProps.countColHeaders()
568
+ });
569
+ }
570
+ this.setRangeStart(_classPrivateFieldGet(this, _transformation).transformStart(rowDelta, colDelta, createMissingRecords));
471
571
  }
472
572
 
473
573
  /**
@@ -477,7 +577,44 @@ class Selection {
477
577
  * @param {number} colDelta Columns number to move, value can be passed as negative number.
478
578
  */
479
579
  transformEnd(rowDelta, colDelta) {
480
- this.setRangeEnd(this.transformation.transformEnd(rowDelta, colDelta));
580
+ if (this.settings.navigableHeaders) {
581
+ _classPrivateFieldGet(this, _transformation).setOffsetSize({
582
+ x: this.tableProps.countRowHeaders(),
583
+ y: this.tableProps.countColHeaders()
584
+ });
585
+ }
586
+ this.setRangeEnd(_classPrivateFieldGet(this, _transformation).transformEnd(rowDelta, colDelta));
587
+ }
588
+
589
+ /**
590
+ * Transforms the focus cell selection relative to the current focus position.
591
+ *
592
+ * @param {number} rowDelta Rows number to move, value can be passed as negative number.
593
+ * @param {number} colDelta Columns number to move, value can be passed as negative number.
594
+ */
595
+ transformFocus(rowDelta, colDelta) {
596
+ const range = this.selectedRange.current();
597
+ const {
598
+ row,
599
+ col
600
+ } = range.getOuterTopStartCorner();
601
+ const columnsInRange = this.tableProps.countRenderableColumnsInRange(0, col - 1);
602
+ const rowsInRange = this.tableProps.countRenderableRowsInRange(0, row - 1);
603
+ if (range.highlight.isHeader()) {
604
+ // for header focus selection calculate the new coords based on the selection including headers
605
+ _classPrivateFieldGet(this, _focusTransformation).setOffsetSize({
606
+ x: col < 0 ? Math.abs(col) : -columnsInRange,
607
+ y: row < 0 ? Math.abs(row) : -rowsInRange
608
+ });
609
+ } else {
610
+ // for focus selection in cells calculate the new coords only based on the selected cells
611
+ _classPrivateFieldGet(this, _focusTransformation).setOffsetSize({
612
+ x: col < 0 ? 0 : -columnsInRange,
613
+ y: row < 0 ? 0 : -rowsInRange
614
+ });
615
+ }
616
+ const focusCoords = _classPrivateFieldGet(this, _focusTransformation).transformStart(rowDelta, colDelta);
617
+ this.setRangeFocus(focusCoords.normalize());
481
618
  }
482
619
 
483
620
  /**
@@ -498,6 +635,30 @@ class Selection {
498
635
  return !this.selectedRange.isEmpty();
499
636
  }
500
637
 
638
+ /**
639
+ * Returns information if we have a multi-selection. This method check multi-selection only on the latest layer of
640
+ * the selection.
641
+ *
642
+ * @returns {boolean}
643
+ */
644
+ isMultiple() {
645
+ if (!this.isSelected()) {
646
+ return false;
647
+ }
648
+ const isMultipleListener = createObjectPropListener(!this.selectedRange.current().isSingle());
649
+ this.runLocalHooks('afterIsMultipleSelection', isMultipleListener);
650
+ return isMultipleListener.value;
651
+ }
652
+
653
+ /**
654
+ * Checks if the last selection involves changing the focus cell position only.
655
+ *
656
+ * @returns {boolean}
657
+ */
658
+ isFocusSelectionChanged() {
659
+ return this.isSelected() && _classPrivateFieldGet(this, _isFocusSelectionChanged);
660
+ }
661
+
501
662
  /**
502
663
  * Returns `true` if the selection was applied by clicking to the row header. If the `layerLevel`
503
664
  * argument is passed then only that layer will be checked. Otherwise, it checks if any row header
@@ -780,9 +941,10 @@ class Selection {
780
941
  *
781
942
  * @param {number|string} startColumn Visual column index or column property from which the selection starts.
782
943
  * @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.
944
+ * @param {number | { row: number, col: number }} [focusPosition=0] The argument allows changing the cell/header focus
945
+ * position. The value can take visual row index from -N to N, where negative values point to the headers and positive
946
+ * values point to the cell range. An object with `row` and `col` properties also can be passed to change the focus
947
+ * position horizontally.
786
948
  * @returns {boolean} Returns `true` if selection was successful, `false` otherwise.
787
949
  */
788
950
  selectColumns(startColumn) {
@@ -803,11 +965,20 @@ class Selection {
803
965
  countColHeaders
804
966
  });
805
967
  if (isValid) {
806
- const fromRow = countColHeaders === 0 ? 0 : clamp(focusPosition, columnHeaderLastIndex, -1);
968
+ let highlightRow = 0;
969
+ let highlightColumn = 0;
970
+ if (Number.isInteger(focusPosition === null || focusPosition === void 0 ? void 0 : focusPosition.row) && Number.isInteger(focusPosition === null || focusPosition === void 0 ? void 0 : focusPosition.col)) {
971
+ highlightRow = clamp(focusPosition.row, columnHeaderLastIndex, countRows - 1);
972
+ highlightColumn = clamp(focusPosition.col, Math.min(start, end), Math.max(start, end));
973
+ } else {
974
+ highlightRow = clamp(focusPosition, columnHeaderLastIndex, countRows - 1);
975
+ highlightColumn = start;
976
+ }
977
+ const highlight = this.tableProps.createCellCoords(highlightRow, highlightColumn);
978
+ const fromRow = countColHeaders === 0 ? 0 : clamp(highlight.row, columnHeaderLastIndex, -1);
807
979
  const toRow = countRows - 1;
808
980
  const from = this.tableProps.createCellCoords(fromRow, start);
809
981
  const to = this.tableProps.createCellCoords(toRow, end);
810
- const highlight = this.tableProps.createCellCoords(clamp(focusPosition, columnHeaderLastIndex, countRows - 1), start);
811
982
  this.runLocalHooks('beforeSelectColumns', from, to, highlight);
812
983
 
813
984
  // disallow modifying row axis for that hooks
@@ -827,9 +998,10 @@ class Selection {
827
998
  *
828
999
  * @param {number} startRow Visual row index from which the selection starts.
829
1000
  * @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.
1001
+ * @param {number | { row: number, col: number }} [focusPosition=0] The argument allows changing the cell/header focus
1002
+ * position. The value can take visual row index from -N to N, where negative values point to the headers and positive
1003
+ * values point to the cell range. An object with `row` and `col` properties also can be passed to change the focus
1004
+ * position horizontally.
833
1005
  * @returns {boolean} Returns `true` if selection was successful, `false` otherwise.
834
1006
  */
835
1007
  selectRows(startRow) {
@@ -848,11 +1020,20 @@ class Selection {
848
1020
  countColHeaders: 0
849
1021
  });
850
1022
  if (isValid) {
851
- const fromColumn = countRowHeaders === 0 ? 0 : clamp(focusPosition, rowHeaderLastIndex, -1);
1023
+ let highlightRow = 0;
1024
+ let highlightColumn = 0;
1025
+ if (Number.isInteger(focusPosition === null || focusPosition === void 0 ? void 0 : focusPosition.row) && Number.isInteger(focusPosition === null || focusPosition === void 0 ? void 0 : focusPosition.col)) {
1026
+ highlightRow = clamp(focusPosition.row, Math.min(startRow, endRow), Math.max(startRow, endRow));
1027
+ highlightColumn = clamp(focusPosition.col, rowHeaderLastIndex, countCols - 1);
1028
+ } else {
1029
+ highlightRow = startRow;
1030
+ highlightColumn = clamp(focusPosition, rowHeaderLastIndex, countCols - 1);
1031
+ }
1032
+ const highlight = this.tableProps.createCellCoords(highlightRow, highlightColumn);
1033
+ const fromColumn = countRowHeaders === 0 ? 0 : clamp(highlight.col, rowHeaderLastIndex, -1);
852
1034
  const toColumn = countCols - 1;
853
1035
  const from = this.tableProps.createCellCoords(startRow, fromColumn);
854
1036
  const to = this.tableProps.createCellCoords(endRow, toColumn);
855
- const highlight = this.tableProps.createCellCoords(startRow, clamp(focusPosition, rowHeaderLastIndex, countCols - 1));
856
1037
  this.runLocalHooks('beforeSelectRows', from, to, highlight);
857
1038
 
858
1039
  // 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) {