handsontable 0.0.0-next-827c165-20240604 → 0.0.0-next-7230ce6-20240605

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.

@@ -47,9 +47,15 @@ class MergedCellsCollection {
47
47
  /**
48
48
  * Array of merged cells.
49
49
  *
50
- * @type {Array}
50
+ * @type {MergedCellCoords[]}
51
51
  */
52
52
  _defineProperty(this, "mergedCells", []);
53
+ /**
54
+ * Matrix of cells (row, col) that points to the instances of the MergedCellCoords objects.
55
+ *
56
+ * @type {Array}
57
+ */
58
+ _defineProperty(this, "mergedCellsMatrix", new Map());
53
59
  /**
54
60
  * The Handsontable instance.
55
61
  *
@@ -81,16 +87,11 @@ class MergedCellsCollection {
81
87
  * @returns {MergedCellCoords|boolean} Returns a wanted merged cell on success and `false` on failure.
82
88
  */
83
89
  get(row, column) {
84
- const mergedCells = this.mergedCells;
85
- let result = false;
86
- arrayEach(mergedCells, mergedCell => {
87
- if (mergedCell.row <= row && mergedCell.row + mergedCell.rowspan - 1 >= row && mergedCell.col <= column && mergedCell.col + mergedCell.colspan - 1 >= column) {
88
- result = mergedCell;
89
- return false;
90
- }
91
- return true;
92
- });
93
- return result;
90
+ var _this$mergedCellsMatr;
91
+ if (!this.mergedCellsMatrix.has(row)) {
92
+ return false;
93
+ }
94
+ return (_this$mergedCellsMatr = this.mergedCellsMatrix.get(row).get(column)) !== null && _this$mergedCellsMatr !== void 0 ? _this$mergedCellsMatr : false;
94
95
  }
95
96
 
96
97
  /**
@@ -100,9 +101,8 @@ class MergedCellsCollection {
100
101
  * @returns {MergedCellCoords|boolean}
101
102
  */
102
103
  getByRange(range) {
103
- const mergedCells = this.mergedCells;
104
104
  let result = false;
105
- arrayEach(mergedCells, mergedCell => {
105
+ arrayEach(this.mergedCells, mergedCell => {
106
106
  if (mergedCell.row <= range.from.row && mergedCell.row + mergedCell.rowspan - 1 >= range.to.row && mergedCell.col <= range.from.col && mergedCell.col + mergedCell.colspan - 1 >= range.to.col) {
107
107
  result = mergedCell;
108
108
  return result;
@@ -112,6 +112,59 @@ class MergedCellsCollection {
112
112
  return result;
113
113
  }
114
114
 
115
+ /**
116
+ * Filters merge cells objects provided by users from overlapping cells.
117
+ *
118
+ * @param {{ row: number, col: number, rowspan: number, colspan: number }} mergedCellsInfo The merged cell information object.
119
+ * Has to contain `row`, `col`, `colspan` and `rowspan` properties.
120
+ * @returns {Array<{ row: number, col: number, rowspan: number, colspan: number }>}
121
+ */
122
+ filterOverlappingMergeCells(mergedCellsInfo) {
123
+ const occupiedCells = new Set();
124
+ this.mergedCells.forEach(mergedCell => {
125
+ const {
126
+ row,
127
+ col,
128
+ colspan,
129
+ rowspan
130
+ } = mergedCell;
131
+ for (let r = row; r < row + rowspan; r++) {
132
+ for (let c = col; c < col + colspan; c++) {
133
+ occupiedCells.add(`r${r},c${c}`);
134
+ }
135
+ }
136
+ });
137
+ const filteredMergeCells = mergedCellsInfo.filter(mergedCell => {
138
+ const {
139
+ row,
140
+ col,
141
+ colspan,
142
+ rowspan
143
+ } = mergedCell;
144
+ const localOccupiedCells = new Set();
145
+ let isOverlapping = false;
146
+ for (let r = row; r < row + rowspan; r++) {
147
+ for (let c = col; c < col + colspan; c++) {
148
+ const cellId = `r${r},c${c}`;
149
+ if (occupiedCells.has(cellId)) {
150
+ warn(MergedCellsCollection.IS_OVERLAPPING_WARNING(mergedCell));
151
+ isOverlapping = true;
152
+ break;
153
+ }
154
+ localOccupiedCells.add(cellId);
155
+ }
156
+ if (isOverlapping) {
157
+ break;
158
+ }
159
+ }
160
+ if (!isOverlapping) {
161
+ occupiedCells.add(...localOccupiedCells);
162
+ }
163
+ return !isOverlapping;
164
+ });
165
+ return filteredMergeCells;
166
+ }
167
+
115
168
  /**
116
169
  * Get a merged cell contained in the provided range.
117
170
  *
@@ -121,7 +174,6 @@ class MergedCellsCollection {
121
174
  */
122
175
  getWithinRange(range) {
123
176
  let countPartials = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
124
- const mergedCells = this.mergedCells;
125
177
  const foundMergedCells = [];
126
178
  let testedRange = range;
127
179
  if (!testedRange.includesRange) {
@@ -129,7 +181,7 @@ class MergedCellsCollection {
129
181
  const to = this.hot._createCellCoords(testedRange.to.row, testedRange.to.col);
130
182
  testedRange = this.hot._createCellRange(from, from, to);
131
183
  }
132
- arrayEach(mergedCells, mergedCell => {
184
+ arrayEach(this.mergedCells, mergedCell => {
133
185
  const mergedCellTopLeft = this.hot._createCellCoords(mergedCell.row, mergedCell.col);
134
186
  const mergedCellBottomRight = this.hot._createCellCoords(mergedCell.row + mergedCell.rowspan - 1, mergedCell.col + mergedCell.colspan - 1);
135
187
  const mergedCellRange = this.hot._createCellRange(mergedCellTopLeft, mergedCellTopLeft, mergedCellBottomRight);
@@ -148,22 +200,24 @@ class MergedCellsCollection {
148
200
  * Add a merged cell to the container.
149
201
  *
150
202
  * @param {object} mergedCellInfo The merged cell information object. Has to contain `row`, `col`, `colspan` and `rowspan` properties.
203
+ * @param {boolean} [auto=false] `true` if called internally by the plugin (usually in batch).
151
204
  * @returns {MergedCellCoords|boolean} Returns the new merged cell on success and `false` on failure.
152
205
  */
153
206
  add(mergedCellInfo) {
154
- const mergedCells = this.mergedCells;
207
+ let auto = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
155
208
  const row = mergedCellInfo.row;
156
209
  const column = mergedCellInfo.col;
157
210
  const rowspan = mergedCellInfo.rowspan;
158
211
  const colspan = mergedCellInfo.colspan;
159
212
  const newMergedCell = new MergedCellCoords(row, column, rowspan, colspan, this.hot._createCellCoords, this.hot._createCellRange);
160
213
  const alreadyExists = this.get(row, column);
161
- const isOverlapping = this.isOverlapping(newMergedCell);
214
+ const isOverlapping = auto ? false : this.isOverlapping(newMergedCell);
162
215
  if (!alreadyExists && !isOverlapping) {
163
216
  if (this.hot) {
164
217
  newMergedCell.normalize(this.hot);
165
218
  }
166
- mergedCells.push(newMergedCell);
219
+ this.mergedCells.push(newMergedCell);
220
+ _assertClassBrand(_MergedCellsCollection_brand, this, _addMergedCellToMatrix).call(this, newMergedCell);
167
221
  return newMergedCell;
168
222
  }
169
223
  warn(MergedCellsCollection.IS_OVERLAPPING_WARNING(newMergedCell));
@@ -179,12 +233,12 @@ class MergedCellsCollection {
179
233
  * @returns {MergedCellCoords|boolean} Returns the removed merged cell on success and `false` on failure.
180
234
  */
181
235
  remove(row, column) {
182
- const mergedCells = this.mergedCells;
183
- const wantedCollection = this.get(row, column);
184
- const wantedCollectionIndex = wantedCollection ? this.mergedCells.indexOf(wantedCollection) : -1;
185
- if (wantedCollection && wantedCollectionIndex !== -1) {
186
- mergedCells.splice(wantedCollectionIndex, 1);
187
- return wantedCollection;
236
+ const mergedCell = this.get(row, column);
237
+ const mergedCellIndex = mergedCell ? this.mergedCells.indexOf(mergedCell) : -1;
238
+ if (mergedCell && mergedCellIndex !== -1) {
239
+ this.mergedCells.splice(mergedCellIndex, 1);
240
+ _assertClassBrand(_MergedCellsCollection_brand, this, _removeMergedCellFromMatrix).call(this, mergedCell);
241
+ return mergedCell;
188
242
  }
189
243
  return false;
190
244
  }
@@ -193,16 +247,16 @@ class MergedCellsCollection {
193
247
  * Clear all the merged cells.
194
248
  */
195
249
  clear() {
196
- const mergedCells = this.mergedCells;
197
250
  const mergedCellParentsToClear = [];
198
251
  const hiddenCollectionElements = [];
199
- arrayEach(mergedCells, mergedCell => {
252
+ arrayEach(this.mergedCells, mergedCell => {
200
253
  const TD = this.hot.getCell(mergedCell.row, mergedCell.col);
201
254
  if (TD) {
202
255
  mergedCellParentsToClear.push([TD, this.get(mergedCell.row, mergedCell.col), mergedCell.row, mergedCell.col]);
203
256
  }
204
257
  });
205
258
  this.mergedCells.length = 0;
259
+ this.mergedCellsMatrix = new Map();
206
260
  arrayEach(mergedCellParentsToClear, (mergedCell, i) => {
207
261
  rangeEach(0, mergedCell.rowspan - 1, j => {
208
262
  rangeEach(0, mergedCell.colspan - 1, k => {
@@ -225,23 +279,21 @@ class MergedCellsCollection {
225
279
  }
226
280
 
227
281
  /**
228
- * Check if the provided merged cell overlaps with the others in the container.
282
+ * Check if the provided merged cell overlaps with the others already added.
229
283
  *
230
284
  * @param {MergedCellCoords} mergedCell The merged cell to check against all others in the container.
231
285
  * @returns {boolean} `true` if the provided merged cell overlaps with the others, `false` otherwise.
232
286
  */
233
287
  isOverlapping(mergedCell) {
234
- const mergedCellRange = this.hot._createCellRange(this.hot._createCellCoords(0, 0), this.hot._createCellCoords(mergedCell.row, mergedCell.col), this.hot._createCellCoords(mergedCell.row + mergedCell.rowspan - 1, mergedCell.col + mergedCell.colspan - 1));
235
- let result = false;
236
- arrayEach(this.mergedCells, col => {
237
- const currentRange = this.hot._createCellRange(this.hot._createCellCoords(0, 0), this.hot._createCellCoords(col.row, col.col), this.hot._createCellCoords(col.row + col.rowspan - 1, col.col + col.colspan - 1));
238
- if (currentRange.overlaps(mergedCellRange)) {
239
- result = true;
240
- return false;
288
+ const mergedCellRange = mergedCell.getRange();
289
+ for (let i = 0; i < this.mergedCells.length; i++) {
290
+ const otherMergedCell = this.mergedCells[i];
291
+ const otherMergedCellRange = otherMergedCell.getRange();
292
+ if (otherMergedCellRange.overlaps(mergedCellRange)) {
293
+ return true;
241
294
  }
242
- return true;
243
- });
244
- return result;
295
+ }
296
+ return false;
245
297
  }
246
298
 
247
299
  /**
@@ -376,15 +428,24 @@ class MergedCellsCollection {
376
428
  default:
377
429
  }
378
430
  arrayEach(this.mergedCells, currentMerge => {
431
+ _assertClassBrand(_MergedCellsCollection_brand, this, _removeMergedCellFromMatrix).call(this, currentMerge);
379
432
  currentMerge.shift(shiftVector, index);
433
+ _assertClassBrand(_MergedCellsCollection_brand, this, _addMergedCellToMatrix).call(this, currentMerge);
380
434
  });
381
435
  rangeEachReverse(this.mergedCells.length - 1, 0, i => {
382
436
  const currentMerge = this.mergedCells[i];
383
437
  if (currentMerge && currentMerge.removed) {
384
438
  this.mergedCells.splice(this.mergedCells.indexOf(currentMerge), 1);
439
+ _assertClassBrand(_MergedCellsCollection_brand, this, _removeMergedCellFromMatrix).call(this, currentMerge);
385
440
  }
386
441
  });
387
442
  }
443
+
444
+ /**
445
+ * Adds a merged cell to the matrix.
446
+ *
447
+ * @param {MergedCellCoords} mergedCell The merged cell to add.
448
+ */
388
449
  }
389
450
  function _getNonIntersectingIndexes(range, axis) {
390
451
  let scanDirection = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
@@ -413,4 +474,26 @@ function _getNonIntersectingIndexes(range, axis) {
413
474
  return Array.from(set);
414
475
  })));
415
476
  }
477
+ function _addMergedCellToMatrix(mergedCell) {
478
+ for (let row = mergedCell.row; row < mergedCell.row + mergedCell.rowspan; row++) {
479
+ for (let col = mergedCell.col; col < mergedCell.col + mergedCell.colspan; col++) {
480
+ if (!this.mergedCellsMatrix.has(row)) {
481
+ this.mergedCellsMatrix.set(row, new Map());
482
+ }
483
+ this.mergedCellsMatrix.get(row).set(col, mergedCell);
484
+ }
485
+ }
486
+ }
487
+ /**
488
+ * Removes a merged cell from the matrix.
489
+ *
490
+ * @param {MergedCellCoords} mergedCell The merged cell to remove.
491
+ */
492
+ function _removeMergedCellFromMatrix(mergedCell) {
493
+ for (let row = mergedCell.row; row < mergedCell.row + mergedCell.rowspan; row++) {
494
+ for (let col = mergedCell.col; col < mergedCell.col + mergedCell.colspan; col++) {
495
+ this.mergedCellsMatrix.get(row).delete(col);
496
+ }
497
+ }
498
+ }
416
499
  export default MergedCellsCollection;
@@ -445,36 +445,42 @@ class MergeCells extends _base.BasePlugin {
445
445
  * @param {Array|boolean} settings The settings provided to the plugin.
446
446
  */
447
447
  generateFromSettings(settings) {
448
- if (Array.isArray(settings)) {
449
- const populatedNulls = [];
450
- (0, _array.arrayEach)(settings, setting => {
451
- if (!this.validateSetting(setting)) {
452
- return;
453
- }
454
- const highlight = this.hot._createCellCoords(setting.row, setting.col);
455
- const rangeEnd = this.hot._createCellCoords(setting.row + setting.rowspan - 1, setting.col + setting.colspan - 1);
456
- const mergeRange = this.hot._createCellRange(highlight, highlight, rangeEnd);
457
-
458
- // Merging without data population.
459
- this.mergeRange(mergeRange, true, true);
460
- (0, _number.rangeEach)(setting.row, setting.row + setting.rowspan - 1, rowIndex => {
461
- (0, _number.rangeEach)(setting.col, setting.col + setting.colspan - 1, columnIndex => {
462
- // Not resetting a cell representing a merge area's value.
463
- if ((rowIndex === setting.row && columnIndex === setting.col) === false) {
464
- populatedNulls.push([rowIndex, columnIndex, null]);
465
- }
466
- });
467
- });
468
- });
448
+ if (!Array.isArray(settings)) {
449
+ return;
450
+ }
451
+ const validSettings = settings.filter(mergeCellInfo => this.validateSetting(mergeCellInfo));
452
+ const nonOverlappingSettings = this.mergedCellsCollection.filterOverlappingMergeCells(validSettings);
453
+ const populatedNulls = [];
454
+ nonOverlappingSettings.forEach(mergeCellInfo => {
455
+ const {
456
+ row,
457
+ col,
458
+ rowspan,
459
+ colspan
460
+ } = mergeCellInfo;
461
+ const from = this.hot._createCellCoords(row, col);
462
+ const to = this.hot._createCellCoords(row + rowspan - 1, col + colspan - 1);
463
+ const mergeRange = this.hot._createCellRange(from, from, to);
469
464
 
470
- // There are no merged cells. Thus, no data population is needed.
471
- if (populatedNulls.length === 0) {
472
- return;
465
+ // Merging without data population.
466
+ this.mergeRange(mergeRange, true, true);
467
+ for (let r = row; r < row + rowspan; r++) {
468
+ for (let c = col; c < col + colspan; c++) {
469
+ // Not resetting a cell representing a merge area's value.
470
+ if (r !== row || c !== col) {
471
+ populatedNulls.push([r, c, null]);
472
+ }
473
+ }
473
474
  }
475
+ });
474
476
 
475
- // TODO: Change the `source` argument to a more meaningful value, e.g. `${this.pluginName}.clearCells`.
476
- this.hot.setDataAtCell(populatedNulls, undefined, undefined, this.pluginName);
477
+ // There are no merged cells. Thus, no data population is needed.
478
+ if (populatedNulls.length === 0) {
479
+ return;
477
480
  }
481
+
482
+ // TODO: Change the `source` argument to a more meaningful value, e.g. `${this.pluginName}.clearCells`.
483
+ this.hot.setDataAtCell(populatedNulls, undefined, undefined, this.pluginName);
478
484
  }
479
485
 
480
486
  /**
@@ -580,7 +586,7 @@ class MergeCells extends _base.BasePlugin {
580
586
  });
581
587
  });
582
588
  this.hot.setCellMeta(mergeParent.row, mergeParent.col, 'spanned', true);
583
- const mergedCellAdded = this.mergedCellsCollection.add(mergeParent);
589
+ const mergedCellAdded = this.mergedCellsCollection.add(mergeParent, auto);
584
590
  if (mergedCellAdded) {
585
591
  if (preventPopulation) {
586
592
  populationInfo = [mergeParent.row, mergeParent.col, clearedData];
@@ -1124,9 +1130,9 @@ function _addMergeActionsToContextMenu(defaultOptions) {
1124
1130
  */
1125
1131
  function _onAfterRenderer(TD, row, col) {
1126
1132
  const mergedCell = this.mergedCellsCollection.get(row, col);
1127
- // We shouldn't override data in the collection.
1128
- const mergedCellCopy = (0, _object.isObject)(mergedCell) ? (0, _object.clone)(mergedCell) : undefined;
1129
- if ((0, _object.isObject)(mergedCellCopy)) {
1133
+ if ((0, _object.isObject)(mergedCell)) {
1134
+ // We shouldn't override data in the collection.
1135
+ const mergedCellCopy = (0, _object.clone)(mergedCell);
1130
1136
  const {
1131
1137
  rowIndexMapper: rowMapper,
1132
1138
  columnIndexMapper: columnMapper
@@ -1151,8 +1157,10 @@ function _onAfterRenderer(TD, row, col) {
1151
1157
  mergedCellCopy.rowspan = Math.min(mergedCellCopy.rowspan, maxRowSpan);
1152
1158
  // The `colSpan` property for a `TD` element should be at most equal to number of rendered columns in the merge area.
1153
1159
  mergedCellCopy.colspan = Math.min(mergedCellCopy.colspan, maxColSpan);
1160
+ (0, _utils.applySpanProperties)(TD, mergedCellCopy, row, col);
1161
+ } else {
1162
+ (0, _utils.applySpanProperties)(TD, null, row, col);
1154
1163
  }
1155
- (0, _utils.applySpanProperties)(TD, mergedCellCopy, row, col);
1156
1164
  }
1157
1165
  /**
1158
1166
  * Clears the last selected coordinates before setting a new selection range.
@@ -441,36 +441,42 @@ export class MergeCells extends BasePlugin {
441
441
  * @param {Array|boolean} settings The settings provided to the plugin.
442
442
  */
443
443
  generateFromSettings(settings) {
444
- if (Array.isArray(settings)) {
445
- const populatedNulls = [];
446
- arrayEach(settings, setting => {
447
- if (!this.validateSetting(setting)) {
448
- return;
449
- }
450
- const highlight = this.hot._createCellCoords(setting.row, setting.col);
451
- const rangeEnd = this.hot._createCellCoords(setting.row + setting.rowspan - 1, setting.col + setting.colspan - 1);
452
- const mergeRange = this.hot._createCellRange(highlight, highlight, rangeEnd);
453
-
454
- // Merging without data population.
455
- this.mergeRange(mergeRange, true, true);
456
- rangeEach(setting.row, setting.row + setting.rowspan - 1, rowIndex => {
457
- rangeEach(setting.col, setting.col + setting.colspan - 1, columnIndex => {
458
- // Not resetting a cell representing a merge area's value.
459
- if ((rowIndex === setting.row && columnIndex === setting.col) === false) {
460
- populatedNulls.push([rowIndex, columnIndex, null]);
461
- }
462
- });
463
- });
464
- });
444
+ if (!Array.isArray(settings)) {
445
+ return;
446
+ }
447
+ const validSettings = settings.filter(mergeCellInfo => this.validateSetting(mergeCellInfo));
448
+ const nonOverlappingSettings = this.mergedCellsCollection.filterOverlappingMergeCells(validSettings);
449
+ const populatedNulls = [];
450
+ nonOverlappingSettings.forEach(mergeCellInfo => {
451
+ const {
452
+ row,
453
+ col,
454
+ rowspan,
455
+ colspan
456
+ } = mergeCellInfo;
457
+ const from = this.hot._createCellCoords(row, col);
458
+ const to = this.hot._createCellCoords(row + rowspan - 1, col + colspan - 1);
459
+ const mergeRange = this.hot._createCellRange(from, from, to);
465
460
 
466
- // There are no merged cells. Thus, no data population is needed.
467
- if (populatedNulls.length === 0) {
468
- return;
461
+ // Merging without data population.
462
+ this.mergeRange(mergeRange, true, true);
463
+ for (let r = row; r < row + rowspan; r++) {
464
+ for (let c = col; c < col + colspan; c++) {
465
+ // Not resetting a cell representing a merge area's value.
466
+ if (r !== row || c !== col) {
467
+ populatedNulls.push([r, c, null]);
468
+ }
469
+ }
469
470
  }
471
+ });
470
472
 
471
- // TODO: Change the `source` argument to a more meaningful value, e.g. `${this.pluginName}.clearCells`.
472
- this.hot.setDataAtCell(populatedNulls, undefined, undefined, this.pluginName);
473
+ // There are no merged cells. Thus, no data population is needed.
474
+ if (populatedNulls.length === 0) {
475
+ return;
473
476
  }
477
+
478
+ // TODO: Change the `source` argument to a more meaningful value, e.g. `${this.pluginName}.clearCells`.
479
+ this.hot.setDataAtCell(populatedNulls, undefined, undefined, this.pluginName);
474
480
  }
475
481
 
476
482
  /**
@@ -576,7 +582,7 @@ export class MergeCells extends BasePlugin {
576
582
  });
577
583
  });
578
584
  this.hot.setCellMeta(mergeParent.row, mergeParent.col, 'spanned', true);
579
- const mergedCellAdded = this.mergedCellsCollection.add(mergeParent);
585
+ const mergedCellAdded = this.mergedCellsCollection.add(mergeParent, auto);
580
586
  if (mergedCellAdded) {
581
587
  if (preventPopulation) {
582
588
  populationInfo = [mergeParent.row, mergeParent.col, clearedData];
@@ -1119,9 +1125,9 @@ function _addMergeActionsToContextMenu(defaultOptions) {
1119
1125
  */
1120
1126
  function _onAfterRenderer(TD, row, col) {
1121
1127
  const mergedCell = this.mergedCellsCollection.get(row, col);
1122
- // We shouldn't override data in the collection.
1123
- const mergedCellCopy = isObject(mergedCell) ? clone(mergedCell) : undefined;
1124
- if (isObject(mergedCellCopy)) {
1128
+ if (isObject(mergedCell)) {
1129
+ // We shouldn't override data in the collection.
1130
+ const mergedCellCopy = clone(mergedCell);
1125
1131
  const {
1126
1132
  rowIndexMapper: rowMapper,
1127
1133
  columnIndexMapper: columnMapper
@@ -1146,8 +1152,10 @@ function _onAfterRenderer(TD, row, col) {
1146
1152
  mergedCellCopy.rowspan = Math.min(mergedCellCopy.rowspan, maxRowSpan);
1147
1153
  // The `colSpan` property for a `TD` element should be at most equal to number of rendered columns in the merge area.
1148
1154
  mergedCellCopy.colspan = Math.min(mergedCellCopy.colspan, maxColSpan);
1155
+ applySpanProperties(TD, mergedCellCopy, row, col);
1156
+ } else {
1157
+ applySpanProperties(TD, null, row, col);
1149
1158
  }
1150
- applySpanProperties(TD, mergedCellCopy, row, col);
1151
1159
  }
1152
1160
  /**
1153
1161
  * Clears the last selected coordinates before setting a new selection range.
@@ -14,7 +14,7 @@ interface Shortcut {
14
14
  export interface Context {
15
15
  addShortcut(shortcut: Shortcut): void;
16
16
  addShortcuts(shortcuts: Shortcut[]): void;
17
- getShortcuts(): Shortcut[];
17
+ getShortcuts(keys: string[]): Shortcut[];
18
18
  hasShortcut(keys: string[]): boolean;
19
19
  removeShortcutsByKeys(keys: string[]): void;
20
20
  removeShortcutsByGroup(group: string): void;