@deephaven/grid 0.5.2-beta.0 → 0.6.1-demo.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/CellInputField.js +40 -88
  2. package/dist/CellInputField.js.map +1 -1
  3. package/dist/Grid.js +1449 -1484
  4. package/dist/Grid.js.map +1 -1
  5. package/dist/GridColorUtils.js +18 -51
  6. package/dist/GridColorUtils.js.map +1 -1
  7. package/dist/GridMetricCalculator.d.ts +8 -1
  8. package/dist/GridMetricCalculator.d.ts.map +1 -1
  9. package/dist/GridMetricCalculator.js +1031 -994
  10. package/dist/GridMetricCalculator.js.map +1 -1
  11. package/dist/GridModel.d.ts +4 -1
  12. package/dist/GridModel.d.ts.map +1 -1
  13. package/dist/GridModel.js +175 -286
  14. package/dist/GridModel.js.map +1 -1
  15. package/dist/GridMouseHandler.js +39 -59
  16. package/dist/GridMouseHandler.js.map +1 -1
  17. package/dist/GridRange.js +572 -630
  18. package/dist/GridRange.js.map +1 -1
  19. package/dist/GridRenderer.js +1650 -1564
  20. package/dist/GridRenderer.js.map +1 -1
  21. package/dist/GridTestUtils.js +15 -29
  22. package/dist/GridTestUtils.js.map +1 -1
  23. package/dist/GridUtils.js +679 -717
  24. package/dist/GridUtils.js.map +1 -1
  25. package/dist/KeyHandler.js +6 -18
  26. package/dist/KeyHandler.js.map +1 -1
  27. package/dist/MockGridModel.js +105 -210
  28. package/dist/MockGridModel.js.map +1 -1
  29. package/dist/MockTreeGridModel.js +113 -183
  30. package/dist/MockTreeGridModel.js.map +1 -1
  31. package/dist/errors/PasteError.js +5 -44
  32. package/dist/errors/PasteError.js.map +1 -1
  33. package/dist/errors/index.js +1 -1
  34. package/dist/errors/index.js.map +1 -1
  35. package/dist/index.js +15 -15
  36. package/dist/index.js.map +1 -1
  37. package/dist/key-handlers/EditKeyHandler.js +42 -75
  38. package/dist/key-handlers/EditKeyHandler.js.map +1 -1
  39. package/dist/key-handlers/PasteKeyHandler.js +42 -78
  40. package/dist/key-handlers/PasteKeyHandler.js.map +1 -1
  41. package/dist/key-handlers/SelectionKeyHandler.d.ts.map +1 -1
  42. package/dist/key-handlers/SelectionKeyHandler.js +239 -229
  43. package/dist/key-handlers/SelectionKeyHandler.js.map +1 -1
  44. package/dist/key-handlers/TreeKeyHandler.js +42 -72
  45. package/dist/key-handlers/TreeKeyHandler.js.map +1 -1
  46. package/dist/key-handlers/index.js +4 -4
  47. package/dist/key-handlers/index.js.map +1 -1
  48. package/dist/memoizeClear.js +1 -1
  49. package/dist/memoizeClear.js.map +1 -1
  50. package/dist/mouse-handlers/EditMouseHandler.js +18 -50
  51. package/dist/mouse-handlers/EditMouseHandler.js.map +1 -1
  52. package/dist/mouse-handlers/GridColumnMoveMouseHandler.d.ts.map +1 -1
  53. package/dist/mouse-handlers/GridColumnMoveMouseHandler.js +141 -163
  54. package/dist/mouse-handlers/GridColumnMoveMouseHandler.js.map +1 -1
  55. package/dist/mouse-handlers/GridColumnSeparatorMouseHandler.js +47 -86
  56. package/dist/mouse-handlers/GridColumnSeparatorMouseHandler.js.map +1 -1
  57. package/dist/mouse-handlers/GridHorizontalScrollBarMouseHandler.js +145 -171
  58. package/dist/mouse-handlers/GridHorizontalScrollBarMouseHandler.js.map +1 -1
  59. package/dist/mouse-handlers/GridRowMoveMouseHandler.js +125 -147
  60. package/dist/mouse-handlers/GridRowMoveMouseHandler.js.map +1 -1
  61. package/dist/mouse-handlers/GridRowSeparatorMouseHandler.js +47 -86
  62. package/dist/mouse-handlers/GridRowSeparatorMouseHandler.js.map +1 -1
  63. package/dist/mouse-handlers/GridRowTreeMouseHandler.js +46 -76
  64. package/dist/mouse-handlers/GridRowTreeMouseHandler.js.map +1 -1
  65. package/dist/mouse-handlers/GridScrollBarCornerMouseHandler.js +31 -62
  66. package/dist/mouse-handlers/GridScrollBarCornerMouseHandler.js.map +1 -1
  67. package/dist/mouse-handlers/GridSelectionMouseHandler.js +200 -222
  68. package/dist/mouse-handlers/GridSelectionMouseHandler.js.map +1 -1
  69. package/dist/mouse-handlers/GridSeparatorMouseHandler.js +206 -253
  70. package/dist/mouse-handlers/GridSeparatorMouseHandler.js.map +1 -1
  71. package/dist/mouse-handlers/GridVerticalScrollBarMouseHandler.js +146 -172
  72. package/dist/mouse-handlers/GridVerticalScrollBarMouseHandler.js.map +1 -1
  73. package/dist/mouse-handlers/index.js +10 -10
  74. package/dist/mouse-handlers/index.js.map +1 -1
  75. package/dist/tsconfig.tsbuildinfo +1 -1
  76. package/package.json +6 -10
package/dist/GridRange.js CHANGED
@@ -1,752 +1,694 @@
1
- function _construct(Parent, args, Class) { if (_isNativeReflectConstruct()) { _construct = Reflect.construct; } else { _construct = function _construct(Parent, args, Class) { var a = [null]; a.push.apply(a, args); var Constructor = Function.bind.apply(Parent, a); var instance = new Constructor(); if (Class) _setPrototypeOf(instance, Class.prototype); return instance; }; } return _construct.apply(null, arguments); }
1
+ function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
2
2
 
3
- function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
3
+ class GridRange {
4
+ static normalize(startColumn, startRow, endColumn, endRow) {
5
+ var left = startColumn;
6
+ var top = startRow;
7
+ var right = endColumn;
8
+ var bottom = endRow;
4
9
 
5
- function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
10
+ if (left != null && right != null && right < left) {
11
+ left = right;
12
+ right = startColumn;
13
+ }
6
14
 
7
- function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
15
+ if (top != null && bottom != null && bottom < top) {
16
+ top = bottom;
17
+ bottom = startRow;
18
+ }
8
19
 
9
- function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
20
+ return [left, top, right, bottom];
21
+ }
22
+ /** Make a GridRange, but ensure startColumn <= endColumn, startRow <= endRow */
10
23
 
11
- function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
12
24
 
13
- function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); }
25
+ static makeNormalized() {
26
+ return new GridRange(...GridRange.normalize(...arguments));
27
+ }
14
28
 
15
- function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
29
+ static makeCell(column, row) {
30
+ return new GridRange(column, row, column, row);
31
+ }
16
32
 
17
- function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
33
+ static makeColumn(column) {
34
+ return new GridRange(column, null, column, null);
35
+ }
18
36
 
19
- function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
37
+ static makeRow(row) {
38
+ return new GridRange(null, row, null, row);
39
+ }
20
40
 
21
- function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
41
+ static minOrNull(value1, value2) {
42
+ if (value1 == null || value2 == null) {
43
+ return null;
44
+ }
22
45
 
23
- function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
46
+ return Math.min(value1, value2);
47
+ }
24
48
 
25
- function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
49
+ static maxOrNull(value1, value2) {
50
+ if (value1 == null || value2 == null) {
51
+ return null;
52
+ }
26
53
 
27
- var GridRange = /*#__PURE__*/function () {
28
- function GridRange(startColumn, startRow, endColumn, endRow) {
29
- _classCallCheck(this, GridRange);
54
+ return Math.max(value1, value2);
55
+ }
56
+ /**
57
+ * Consolidate the passed in ranges to the minimum set, merging overlapping ranges.
58
+ * @param {[GridRange]} ranges The ranges to consolidate
59
+ */
60
+
61
+
62
+ static consolidate(ranges) {
63
+ var result = ranges.slice();
64
+ var wasModified = true;
65
+
66
+ while (wasModified) {
67
+ wasModified = false;
68
+
69
+ for (var i = 0; i < result.length && !wasModified; i += 1) {
70
+ var range = result[i];
71
+
72
+ for (var j = result.length - 1; j > i; j -= 1) {
73
+ var other = result[j]; // If one contains the other, we can just keep the bigger one
74
+
75
+ if (range.contains(other)) {
76
+ result.splice(j, 1);
77
+ } else if (other.contains(range)) {
78
+ wasModified = true;
79
+ result[i] = other;
80
+ result.splice(j, 1);
81
+ break;
82
+ } else if (range.startRow === other.startRow && range.endRow === other.endRow) {
83
+ if (range.touches(other)) {
84
+ // If the start/end rows match, and columns touch, consolidate
85
+ var {
86
+ startRow,
87
+ endRow
88
+ } = range;
89
+ var startColumn = GridRange.minOrNull(range.startColumn, other.startColumn);
90
+ var endColumn = GridRange.maxOrNull(range.endColumn, other.endColumn);
91
+ wasModified = true;
92
+ result[i] = new GridRange(startColumn, startRow, endColumn, endRow);
93
+ result.splice(j, 1);
94
+ break;
95
+ }
96
+ } else if (range.startColumn === other.startColumn && range.endColumn === other.endColumn) {
97
+ if (range.touches(other)) {
98
+ // If the start/end rows match, and columns touch, consolidate
99
+ var {
100
+ startColumn: _startColumn,
101
+ endColumn: _endColumn
102
+ } = range;
30
103
 
31
- this.startColumn = startColumn;
32
- this.startRow = startRow;
33
- this.endColumn = endColumn;
34
- this.endRow = endRow;
104
+ var _startRow = GridRange.minOrNull(range.startRow, other.startRow);
105
+
106
+ var _endRow = GridRange.maxOrNull(range.endRow, other.endRow);
107
+
108
+ wasModified = true;
109
+ result[i] = new GridRange(_startColumn, _startRow, _endColumn, _endRow);
110
+ result.splice(j, 1);
111
+ break;
112
+ }
113
+ }
114
+ }
115
+ }
116
+ }
117
+
118
+ return result;
35
119
  }
36
120
 
37
- _createClass(GridRange, [{
38
- key: "equals",
39
- value: function equals(other) {
40
- return this.startColumn === other.startColumn && this.startRow === other.startRow && this.endColumn === other.endColumn && this.endRow === other.endRow;
121
+ static isAxisRangeTouching(start, end, otherStart, otherEnd) {
122
+ if (start == null) {
123
+ if (end == null) {
124
+ return true;
125
+ }
126
+
127
+ if (otherStart == null) {
128
+ return true;
129
+ }
130
+
131
+ return otherStart <= end + 1;
41
132
  }
42
- /** @returns {boolean} true if this GridRange completely contains `other` */
43
133
 
44
- }, {
45
- key: "contains",
46
- value: function contains(other) {
47
- return (this.startColumn == null || other.startColumn != null && this.startColumn <= other.startColumn) && (this.startRow == null || other.startRow != null && this.startRow <= other.startRow) && (this.endColumn == null || other.endColumn != null && this.endColumn >= other.endColumn) && (this.endRow == null || other.endRow != null && this.endRow >= other.endRow);
134
+ if (end == null) {
135
+ if (otherEnd == null) {
136
+ return true;
137
+ }
138
+
139
+ return otherEnd >= start - 1;
48
140
  }
49
- /**
50
- * Check if the provided cell is in this range
51
- * @param {number} column The column to check
52
- * @param {number} row The row to check
53
- * @returns {boolean} True if this cell is within this range
54
- */
55
-
56
- }, {
57
- key: "containsCell",
58
- value: function containsCell(column, row) {
59
- if (column == null || row == null) {
60
- return false;
141
+
142
+ if (otherStart == null) {
143
+ if (otherEnd == null) {
144
+ return true;
61
145
  }
62
146
 
63
- return (this.startColumn == null || this.startColumn <= column) && (this.endColumn == null || this.endColumn >= column) && (this.startRow == null || this.startRow <= row) && (this.endRow == null || this.endRow >= row);
147
+ return start <= otherEnd + 1;
64
148
  }
65
- /** @returns {boolean} true if this GridRange touches `other` */
66
149
 
67
- }, {
68
- key: "touches",
69
- value: function touches(other) {
70
- return GridRange.isAxisRangeTouching(this.startRow, this.endRow, other.startRow, other.endRow) && GridRange.isAxisRangeTouching(this.startColumn, this.endColumn, other.startColumn, other.endColumn);
150
+ if (otherEnd == null) {
151
+ return end >= otherStart - 1;
71
152
  }
72
- /**
73
- * @param {GridRange} other The range to deselect from within this range
74
- * @returns {[GridRange]} The ranges needed to represent the remaining
75
- */
76
-
77
- }, {
78
- key: "subtract",
79
- value: function subtract(other) {
80
- return GridRange.subtractFromRange(this, other);
153
+
154
+ if (otherStart >= start - 1) {
155
+ return otherStart <= end + 1;
81
156
  }
82
- /**
83
- * Get the first cell in this range. Throws if this range is unbounded.
84
- *
85
- * @param {GridRange.SELECTION_DIRECTION?} direction The direction to get the starting cell in. Defaults to DOWN
86
- * @returns {{column: number, row: number}} The first cell in this range in the direction specified
87
- */
88
-
89
- }, {
90
- key: "startCell",
91
- value: function startCell() {
92
- var direction = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : GridRange.SELECTION_DIRECTION.DOWN;
93
-
94
- if (!GridRange.isBounded(this)) {
95
- throw new Error('Cannot get the startCell of an unbounded range');
96
- }
97
157
 
98
- switch (direction) {
99
- case GridRange.SELECTION_DIRECTION.DOWN:
100
- case GridRange.SELECTION_DIRECTION.RIGHT:
101
- return {
102
- column: this.startColumn,
103
- row: this.startRow
104
- };
158
+ return otherEnd >= start - 1;
159
+ }
105
160
 
106
- case GridRange.SELECTION_DIRECTION.LEFT:
107
- case GridRange.SELECTION_DIRECTION.UP:
108
- {
109
- return {
110
- column: this.endColumn,
111
- row: this.endRow
112
- };
113
- }
161
+ static rangeArraysEqual(ranges1, ranges2) {
162
+ if (ranges1 === ranges2) {
163
+ return true;
164
+ }
114
165
 
115
- default:
116
- throw new Error("Invalid direction: ".concat(direction));
117
- }
166
+ if (ranges1 == null || ranges2 == null || ranges1.length !== ranges2.length) {
167
+ return false;
118
168
  }
119
- /**
120
- * Get the next cell in the direction specified. Throws if this range is unbounded.
121
- * If already at the bounds of the range in that direction, wrap to the next column or row
122
- * If at the end of the entire range, return null
123
- * If outside of the range, returns the next cell closest within this range.
124
- *
125
- * @param {number} column The cursor column
126
- * @param {number} row The cursor row
127
- * @param {SELECTION_DIRECTION} direction The direction to go in
128
- * @returns {GridCell|null} The next cell in the direction specified, or `null` if at the end of the range
129
- */
130
-
131
- }, {
132
- key: "nextCell",
133
- value: function nextCell(column, row, direction) {
134
- if (!GridRange.isBounded(this)) {
135
- throw new Error('Bounded range required');
136
- }
137
169
 
138
- if (column == null || row == null) {
139
- throw new Error('Require a non-null cursor');
170
+ for (var i = 0; i < ranges1.length; i += 1) {
171
+ if (!ranges1[i].equals(ranges2[i])) {
172
+ return false;
140
173
  }
174
+ }
141
175
 
142
- var startColumn = this.startColumn,
143
- endColumn = this.endColumn,
144
- startRow = this.startRow,
145
- endRow = this.endRow;
146
-
147
- switch (direction) {
148
- case GridRange.SELECTION_DIRECTION.DOWN:
149
- if (row < endRow) {
150
- return {
151
- column: column,
152
- row: Math.max(row + 1, startRow)
153
- };
154
- }
155
-
156
- if (column < endColumn) {
157
- return {
158
- column: Math.max(column + 1, startColumn),
159
- row: startRow
160
- };
161
- }
176
+ return true;
177
+ }
178
+ /**
179
+ * Get the intersection (overlapping area) of two ranges
180
+ * @param {GridRange} range One range to check for the intersection
181
+ * @param {GridRange} otherRange The other range to check for the intersection
182
+ * @returns {GridRange|null} Intersection of the two ranges. If they do not intersect, returns `null`.
183
+ */
162
184
 
163
- break;
164
185
 
165
- case GridRange.SELECTION_DIRECTION.UP:
166
- if (row > startRow) {
167
- return {
168
- column: column,
169
- row: Math.min(row - 1, endRow)
170
- };
171
- }
186
+ static intersection(range, otherRange) {
187
+ var _startColumn2, _endColumn2, _startRow2, _endRow2;
172
188
 
173
- if (column > startColumn) {
174
- return {
175
- column: Math.min(column - 1, endColumn),
176
- row: endRow
177
- };
178
- }
189
+ if (range.equals(otherRange)) {
190
+ return range;
191
+ }
179
192
 
180
- break;
193
+ var {
194
+ startColumn,
195
+ startRow,
196
+ endColumn,
197
+ endRow
198
+ } = range;
199
+ startColumn = startColumn != null && otherRange.startColumn != null ? Math.max(startColumn, otherRange.startColumn) : (_startColumn2 = startColumn) !== null && _startColumn2 !== void 0 ? _startColumn2 : otherRange.startColumn;
200
+ endColumn = endColumn != null && otherRange.endColumn != null ? Math.min(endColumn, otherRange.endColumn) : (_endColumn2 = endColumn) !== null && _endColumn2 !== void 0 ? _endColumn2 : otherRange.endColumn;
201
+ startRow = startRow != null && otherRange.startRow != null ? Math.max(startRow, otherRange.startRow) : (_startRow2 = startRow) !== null && _startRow2 !== void 0 ? _startRow2 : otherRange.startRow;
202
+ endRow = endRow != null && otherRange.endRow != null ? Math.min(endRow, otherRange.endRow) : (_endRow2 = endRow) !== null && _endRow2 !== void 0 ? _endRow2 : otherRange.endRow;
181
203
 
182
- case GridRange.SELECTION_DIRECTION.RIGHT:
183
- if (column < endColumn) {
184
- return {
185
- column: Math.max(column + 1, startColumn),
186
- row: row
187
- };
188
- }
204
+ if (startColumn != null && startColumn > endColumn || startRow != null && startRow > endRow) {
205
+ return null;
206
+ }
189
207
 
190
- if (row < endRow) {
191
- return {
192
- column: startColumn,
193
- row: Math.max(row + 1, startRow)
194
- };
195
- }
208
+ return new GridRange(startColumn, startRow, endColumn, endRow);
209
+ }
210
+ /**
211
+ * @param {GridRange} range The range to be subtracted from
212
+ * @param {GridRange} subtractRange The range to subtract from within this range
213
+ * @returns {GridRange[]} The ranges needed to represent the remaining
214
+ */
196
215
 
197
- break;
198
216
 
199
- case GridRange.SELECTION_DIRECTION.LEFT:
200
- if (column > startColumn) {
201
- return {
202
- column: Math.min(column - 1, endColumn),
203
- row: row
204
- };
205
- }
217
+ static subtractFromRange(range, subtractRange) {
218
+ var result = []; // Make it a little easier by finding only the part the subtraction range intersects
206
219
 
207
- if (row > startRow) {
208
- return {
209
- column: endColumn,
210
- row: Math.min(row - 1, endRow)
211
- };
212
- }
220
+ var subtract = GridRange.intersection(range, subtractRange);
213
221
 
214
- break;
222
+ if (subtract == null) {
223
+ return [range];
224
+ } // Go through each of the quadrants for deselection, there can be up to 4
225
+ // Top quadrant (above the subtracted area)
215
226
 
216
- default:
217
- throw new Error("Invalid direction: ".concat(direction));
218
- }
219
227
 
220
- return null;
221
- }
222
- /**
223
- * Iterate through each cell in the range
224
- * @param {(column:number, row:number, index:number) => void} callback Callback to execute. `index` is the index within this range
225
- * @param {GridRange.SELECTION_DIRECTION} direction The direction to iterate in
226
- */
228
+ if (subtract.startRow != null && (range.startRow == null || range.startRow < subtract.startRow)) {
229
+ result.push(new GridRange(range.startColumn, range.startRow, range.endColumn, subtract.startRow - 1));
230
+ } // middle left
227
231
 
228
- }, {
229
- key: "forEach",
230
- value: function forEach(callback) {
231
- var direction = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : GridRange.SELECTION_DIRECTION.RIGHT;
232
- var i = 0;
233
232
 
234
- var _this$startCell = this.startCell(direction),
235
- c = _this$startCell.column,
236
- r = _this$startCell.row;
233
+ if (subtract.startColumn != null && (range.startColumn == null || range.startColumn < subtract.startColumn)) {
234
+ result.push(new GridRange(range.startColumn, subtract.startRow, subtract.startColumn - 1, subtract.endRow));
235
+ } // middle right
237
236
 
238
- while (c != null && r != null) {
239
- var _this$nextCell;
240
237
 
241
- callback(c, r, i);
242
- i += 1;
238
+ if (subtract.endColumn != null && (range.endColumn == null || range.endColumn > subtract.endColumn)) {
239
+ result.push(new GridRange(subtract.endColumn + 1, subtract.startRow, range.endColumn, subtract.endRow));
240
+ } // Bottom quadrant
243
241
 
244
- var _ref = (_this$nextCell = this.nextCell(c, r, direction)) !== null && _this$nextCell !== void 0 ? _this$nextCell : {};
245
242
 
246
- c = _ref.column;
247
- r = _ref.row;
248
- }
243
+ if (subtract.endRow != null && (range.endRow == null || range.endRow > subtract.endRow)) {
244
+ result.push(new GridRange(range.startColumn, subtract.endRow + 1, range.endColumn, range.endRow));
249
245
  }
250
- }], [{
251
- key: "normalize",
252
- value: function normalize(startColumn, startRow, endColumn, endRow) {
253
- var left = startColumn;
254
- var top = startRow;
255
- var right = endColumn;
256
- var bottom = endRow;
257
-
258
- if (left != null && right != null && right < left) {
259
- left = right;
260
- right = startColumn;
261
- }
262
246
 
263
- if (top != null && bottom != null && bottom < top) {
264
- top = bottom;
265
- bottom = startRow;
266
- }
247
+ return result;
248
+ }
249
+ /**
250
+ * Subtract a range from multiple ranges
251
+ * @param {GridRange[]} ranges The ranges to be subtracted from
252
+ * @param {GridRange} subtractRange The range to subtract from within these ranges
253
+ * @returns {GridRange[]} The ranges needed to represent the remaining
254
+ */
267
255
 
268
- return [left, top, right, bottom];
269
- }
270
- /** Make a GridRange, but ensure startColumn <= endColumn, startRow <= endRow */
271
256
 
272
- }, {
273
- key: "makeNormalized",
274
- value: function makeNormalized() {
275
- return _construct(GridRange, _toConsumableArray(GridRange.normalize.apply(GridRange, arguments)));
276
- }
277
- }, {
278
- key: "makeCell",
279
- value: function makeCell(column, row) {
280
- return new GridRange(column, row, column, row);
281
- }
282
- }, {
283
- key: "makeColumn",
284
- value: function makeColumn(column) {
285
- return new GridRange(column, null, column, null);
257
+ static subtractFromRanges(ranges, subtractRange) {
258
+ var result = [];
259
+
260
+ for (var i = 0; i < ranges.length; i += 1) {
261
+ result.push(...GridRange.subtractFromRange(ranges[i], subtractRange));
286
262
  }
287
- }, {
288
- key: "makeRow",
289
- value: function makeRow(row) {
290
- return new GridRange(null, row, null, row);
263
+
264
+ return result;
265
+ }
266
+ /**
267
+ * Subtract multiple ranges from multiple ranges
268
+ * @param {GridRange[]} ranges The ranges to be subtracted from
269
+ * @param {GridRange[]} subtractRanges The ranges to subtract from within these ranges
270
+ * @returns {GridRange[]} The ranges needed to represent the remaining
271
+ */
272
+
273
+
274
+ static subtractRangesFromRanges(ranges, subtractRanges) {
275
+ if (!subtractRanges || subtractRanges.length === 0) {
276
+ return ranges;
291
277
  }
292
- }, {
293
- key: "minOrNull",
294
- value: function minOrNull(value1, value2) {
295
- if (value1 == null || value2 == null) {
296
- return null;
297
- }
298
278
 
299
- return Math.min(value1, value2);
279
+ var result = [...ranges];
280
+
281
+ for (var i = 0; i < subtractRanges.length; i += 1) {
282
+ result = GridRange.subtractFromRanges(result, subtractRanges[i]);
300
283
  }
301
- }, {
302
- key: "maxOrNull",
303
- value: function maxOrNull(value1, value2) {
304
- if (value1 == null || value2 == null) {
305
- return null;
306
- }
307
284
 
308
- return Math.max(value1, value2);
285
+ return result;
286
+ }
287
+ /**
288
+ * Test if a given range is bounded (all values are non-null)
289
+ * @param {GridRange} range The range to test
290
+ * @returns {boolean} True if this range is bounded, false otherwise
291
+ */
292
+
293
+
294
+ static isBounded(range) {
295
+ return range.startRow != null && range.startColumn != null && range.endRow != null && range.endColumn != null;
296
+ }
297
+ /**
298
+ * Converts any GridRange passed in that is a full row or column selection to be bound
299
+ * to the `columnCount` and `rowCount` passed in
300
+ *
301
+ * @param {GridRange} range The range to get the bounded range of
302
+ * @param {number} columnCount The number of columns
303
+ * @param {number} rowCount The number of rows
304
+ * @returns {GridRange} The passed in GridRange with any null values filled in
305
+ */
306
+
307
+
308
+ static boundedRange(range, columnCount, rowCount) {
309
+ var _range$startColumn, _range$startRow, _range$endColumn, _range$endRow;
310
+
311
+ if (GridRange.isBounded(range)) {
312
+ return range;
309
313
  }
310
- /**
311
- * Consolidate the passed in ranges to the minimum set, merging overlapping ranges.
312
- * @param {[GridRange]} ranges The ranges to consolidate
313
- */
314
314
 
315
- }, {
316
- key: "consolidate",
317
- value: function consolidate(ranges) {
318
- var result = ranges.slice();
319
- var wasModified = true;
315
+ return new GridRange((_range$startColumn = range.startColumn) !== null && _range$startColumn !== void 0 ? _range$startColumn : 0, (_range$startRow = range.startRow) !== null && _range$startRow !== void 0 ? _range$startRow : 0, (_range$endColumn = range.endColumn) !== null && _range$endColumn !== void 0 ? _range$endColumn : columnCount - 1, (_range$endRow = range.endRow) !== null && _range$endRow !== void 0 ? _range$endRow : rowCount - 1);
316
+ }
317
+ /**
318
+ * Converts the GridRanges passed in to be bound to the `columnCount` and `rowCount` passed in
319
+ *
320
+ * @param {GridRange[]} ranges The ranges to get the bounded ranges of
321
+ * @param {number} columnCount The number of columns
322
+ * @param {number} rowCount The number of rows
323
+ * @returns {GridRange} The passed in GridRange with any null values filled in
324
+ */
325
+
326
+
327
+ static boundedRanges(ranges, columnCount, rowCount) {
328
+ return ranges.map(r => GridRange.boundedRange(r, columnCount, rowCount));
329
+ }
330
+ /**
331
+ * Offsets a GridRange by the specified amount in the x and y directions
332
+ *
333
+ * @param {GridRange} range The range to offset
334
+ * @param {number} columnOffset The number of columns to offset
335
+ * @param {number} rowOffset The number of rows to offset
336
+ * @returns {GridRange} The new grid range offset from the original
337
+ */
338
+
339
+
340
+ static offset(range, columnOffset, rowOffset) {
341
+ return new GridRange(range.startColumn != null ? range.startColumn + columnOffset : null, range.startRow != null ? range.startRow + rowOffset : null, range.endColumn != null ? range.endColumn + columnOffset : null, range.endRow != null ? range.endRow + rowOffset : null);
342
+ }
343
+ /**
344
+ * Get the next cell given the selected ranges and the current cell
345
+ * @param {GridRange[]} ranges The selected bounded ranges within the grid
346
+ * @param {number|null} column The cursor column, or null if none focused
347
+ * @param {number|null} row The cursor row, or null if none focused
348
+ * @param {SELECTION_DIRECTION} direction The direction in which to select next
349
+ * @returns {Cell} The next cell to focus, or null if there should be no more focus
350
+ */
351
+
352
+
353
+ static nextCell(ranges) {
354
+ var column = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
355
+ var row = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
356
+ var direction = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : GridRange.SELECTION_DIRECTION.DOWN;
357
+
358
+ if (ranges.length === 0) {
359
+ return null;
360
+ }
320
361
 
321
- while (wasModified) {
322
- wasModified = false;
362
+ var rangeIndex = -1;
323
363
 
324
- for (var i = 0; i < result.length && !wasModified; i += 1) {
325
- var range = result[i];
364
+ if (column != null && row != null) {
365
+ rangeIndex = ranges.findIndex(r => r.containsCell(column, row));
326
366
 
327
- for (var j = result.length - 1; j > i; j -= 1) {
328
- var other = result[j]; // If one contains the other, we can just keep the bigger one
367
+ if (rangeIndex >= 0) {
368
+ var range = ranges[rangeIndex];
369
+ var nextCell = range.nextCell(column, row, direction);
329
370
 
330
- if (range.contains(other)) {
331
- result.splice(j, 1);
332
- } else if (other.contains(range)) {
333
- wasModified = true;
334
- result[i] = other;
335
- result.splice(j, 1);
336
- break;
337
- } else if (range.startRow === other.startRow && range.endRow === other.endRow) {
338
- if (range.touches(other)) {
339
- // If the start/end rows match, and columns touch, consolidate
340
- var startRow = range.startRow,
341
- endRow = range.endRow;
342
- var startColumn = GridRange.minOrNull(range.startColumn, other.startColumn);
343
- var endColumn = GridRange.maxOrNull(range.endColumn, other.endColumn);
344
- wasModified = true;
345
- result[i] = new GridRange(startColumn, startRow, endColumn, endRow);
346
- result.splice(j, 1);
347
- break;
348
- }
349
- } else if (range.startColumn === other.startColumn && range.endColumn === other.endColumn) {
350
- if (range.touches(other)) {
351
- // If the start/end rows match, and columns touch, consolidate
352
- var _startColumn = range.startColumn,
353
- _endColumn = range.endColumn;
354
-
355
- var _startRow = GridRange.minOrNull(range.startRow, other.startRow);
356
-
357
- var _endRow = GridRange.maxOrNull(range.endRow, other.endRow);
358
-
359
- wasModified = true;
360
- result[i] = new GridRange(_startColumn, _startRow, _endColumn, _endRow);
361
- result.splice(j, 1);
362
- break;
363
- }
364
- }
365
- }
371
+ if (nextCell != null) {
372
+ return nextCell;
366
373
  }
367
374
  }
375
+ } // Otherwise go to the start of the next range (could be same range if only one range)
368
376
 
369
- return result;
370
- }
371
- }, {
372
- key: "isAxisRangeTouching",
373
- value: function isAxisRangeTouching(start, end, otherStart, otherEnd) {
374
- if (start == null) {
375
- if (end == null) {
376
- return true;
377
- }
378
377
 
379
- if (otherStart == null) {
380
- return true;
378
+ switch (direction) {
379
+ case GridRange.SELECTION_DIRECTION.DOWN:
380
+ case GridRange.SELECTION_DIRECTION.RIGHT:
381
+ {
382
+ var nextRangeIndex = rangeIndex < ranges.length - 1 ? rangeIndex + 1 : 0;
383
+ var nextRange = ranges[nextRangeIndex];
384
+ return nextRange.startCell(direction);
381
385
  }
382
386
 
383
- return otherStart <= end + 1;
384
- }
387
+ case GridRange.SELECTION_DIRECTION.LEFT:
388
+ case GridRange.SELECTION_DIRECTION.UP:
389
+ {
390
+ var _nextRangeIndex = rangeIndex > 0 ? rangeIndex - 1 : ranges.length - 1;
385
391
 
386
- if (end == null) {
387
- if (otherEnd == null) {
388
- return true;
392
+ var _nextRange = ranges[_nextRangeIndex];
393
+ return _nextRange.startCell(direction);
389
394
  }
390
395
 
391
- return otherEnd >= start - 1;
392
- }
396
+ default:
397
+ throw new Error("Invalid direction: ".concat(direction));
398
+ }
399
+ }
400
+ /**
401
+ * Count the number of cells in the provided grid ranges
402
+ * @param {GridRange[]} ranges The ranges to count the rows of
403
+ * @returns {number|NaN} The number of cells in the ranges, or `NaN` if any of the ranges were unbounded
404
+ */
393
405
 
394
- if (otherStart == null) {
395
- if (otherEnd == null) {
396
- return true;
397
- }
398
406
 
399
- return start <= otherEnd + 1;
400
- }
407
+ static cellCount(ranges) {
408
+ return ranges.reduce((cellCount, range) => {
409
+ var _range$endRow2, _range$startRow2, _range$endColumn2, _range$startColumn2;
401
410
 
402
- if (otherEnd == null) {
403
- return end >= otherStart - 1;
404
- }
411
+ return cellCount + (((_range$endRow2 = range.endRow) !== null && _range$endRow2 !== void 0 ? _range$endRow2 : NaN) - ((_range$startRow2 = range.startRow) !== null && _range$startRow2 !== void 0 ? _range$startRow2 : NaN) + 1) * (((_range$endColumn2 = range.endColumn) !== null && _range$endColumn2 !== void 0 ? _range$endColumn2 : NaN) - ((_range$startColumn2 = range.startColumn) !== null && _range$startColumn2 !== void 0 ? _range$startColumn2 : NaN) + 1);
412
+ }, 0);
413
+ }
414
+ /**
415
+ * Count the number of rows in the provided grid ranges
416
+ * @param {GridRange[]} ranges The ranges to count the rows of
417
+ * @returns {number|NaN} The number of rows in the ranges, or `NaN` if any of the ranges were unbounded
418
+ */
405
419
 
406
- if (otherStart >= start - 1) {
407
- return otherStart <= end + 1;
408
- }
409
420
 
410
- return otherEnd >= start - 1;
411
- }
412
- }, {
413
- key: "rangeArraysEqual",
414
- value: function rangeArraysEqual(ranges1, ranges2) {
415
- if (ranges1 === ranges2) {
416
- return true;
417
- }
421
+ static rowCount(ranges) {
422
+ return ranges.reduce((rowCount, range) => {
423
+ var _range$endRow3, _range$startRow3;
418
424
 
419
- if (ranges1 == null || ranges2 == null || ranges1.length !== ranges2.length) {
420
- return false;
421
- }
425
+ return rowCount + ((_range$endRow3 = range.endRow) !== null && _range$endRow3 !== void 0 ? _range$endRow3 : NaN) - ((_range$startRow3 = range.startRow) !== null && _range$startRow3 !== void 0 ? _range$startRow3 : NaN) + 1;
426
+ }, 0);
427
+ }
428
+ /**
429
+ * Count the number of columns in the provided grid ranges
430
+ * @param {GridRange[]} ranges The ranges to count the columns of
431
+ * @returns {number|NaN} The number of columns in the ranges, or `NaN` if any of the ranges were unbounded
432
+ */
422
433
 
423
- for (var i = 0; i < ranges1.length; i += 1) {
424
- if (!ranges1[i].equals(ranges2[i])) {
425
- return false;
426
- }
427
- }
428
434
 
429
- return true;
430
- }
431
- /**
432
- * Get the intersection (overlapping area) of two ranges
433
- * @param {GridRange} range One range to check for the intersection
434
- * @param {GridRange} otherRange The other range to check for the intersection
435
- * @returns {GridRange|null} Intersection of the two ranges. If they do not intersect, returns `null`.
436
- */
437
-
438
- }, {
439
- key: "intersection",
440
- value: function intersection(range, otherRange) {
441
- var _startColumn2, _endColumn2, _startRow2, _endRow2;
442
-
443
- if (range.equals(otherRange)) {
444
- return range;
445
- }
435
+ static columnCount(ranges) {
436
+ return ranges.reduce((columnCount, range) => {
437
+ var _range$endColumn3, _range$startColumn3;
446
438
 
447
- var startColumn = range.startColumn,
448
- startRow = range.startRow,
449
- endColumn = range.endColumn,
450
- endRow = range.endRow;
451
- startColumn = startColumn != null && otherRange.startColumn != null ? Math.max(startColumn, otherRange.startColumn) : (_startColumn2 = startColumn) !== null && _startColumn2 !== void 0 ? _startColumn2 : otherRange.startColumn;
452
- endColumn = endColumn != null && otherRange.endColumn != null ? Math.min(endColumn, otherRange.endColumn) : (_endColumn2 = endColumn) !== null && _endColumn2 !== void 0 ? _endColumn2 : otherRange.endColumn;
453
- startRow = startRow != null && otherRange.startRow != null ? Math.max(startRow, otherRange.startRow) : (_startRow2 = startRow) !== null && _startRow2 !== void 0 ? _startRow2 : otherRange.startRow;
454
- endRow = endRow != null && otherRange.endRow != null ? Math.min(endRow, otherRange.endRow) : (_endRow2 = endRow) !== null && _endRow2 !== void 0 ? _endRow2 : otherRange.endRow;
455
-
456
- if (startColumn != null && startColumn > endColumn || startRow != null && startRow > endRow) {
457
- return null;
458
- }
439
+ return columnCount + ((_range$endColumn3 = range.endColumn) !== null && _range$endColumn3 !== void 0 ? _range$endColumn3 : NaN) - ((_range$startColumn3 = range.startColumn) !== null && _range$startColumn3 !== void 0 ? _range$startColumn3 : NaN) + 1;
440
+ }, 0);
441
+ }
442
+ /**
443
+ * Check if the provided ranges contain the provided cell
444
+ * @param {GridRange[]} ranges The ranges to check
445
+ * @param {number} column The column index
446
+ * @param {number} row The row index
447
+ * @returns {boolean} True if the cell is within the provided ranges, false otherwise.
448
+ */
459
449
 
460
- return new GridRange(startColumn, startRow, endColumn, endRow);
461
- }
462
- /**
463
- * @param {GridRange} range The range to be subtracted from
464
- * @param {GridRange} subtractRange The range to subtract from within this range
465
- * @returns {GridRange[]} The ranges needed to represent the remaining
466
- */
467
450
 
468
- }, {
469
- key: "subtractFromRange",
470
- value: function subtractFromRange(range, subtractRange) {
471
- var result = []; // Make it a little easier by finding only the part the subtraction range intersects
451
+ static containsCell(ranges, column, row) {
452
+ for (var i = 0; i < ranges.length; i += 1) {
453
+ var range = ranges[i];
472
454
 
473
- var subtract = GridRange.intersection(range, subtractRange);
455
+ if (range.containsCell(column, row)) {
456
+ return true;
457
+ }
458
+ }
474
459
 
475
- if (subtract == null) {
476
- return [range];
477
- } // Go through each of the quadrants for deselection, there can be up to 4
478
- // Top quadrant (above the subtracted area)
460
+ return false;
461
+ }
462
+ /**
463
+ * Iterate through each cell in the provided ranges
464
+ * @param {GridRange[]} ranges The ranges to iterate through
465
+ * @param {(column: number, row: number, index: number) => void} callback The callback to execute. `index` is the index within that range
466
+ * @param {GridRange.SELECTION_DIRECTION} direction The direction to iterate in
467
+ */
479
468
 
480
469
 
481
- if (subtract.startRow != null && (range.startRow == null || range.startRow < subtract.startRow)) {
482
- result.push(new GridRange(range.startColumn, range.startRow, range.endColumn, subtract.startRow - 1));
483
- } // middle left
470
+ static forEachCell(ranges, callback) {
471
+ var direction = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : GridRange.SELECTION_DIRECTION.RIGHT;
484
472
 
473
+ for (var i = 0; i < ranges.length; i += 1) {
474
+ ranges[i].forEach(callback, direction);
475
+ }
476
+ }
485
477
 
486
- if (subtract.startColumn != null && (range.startColumn == null || range.startColumn < subtract.startColumn)) {
487
- result.push(new GridRange(range.startColumn, subtract.startRow, subtract.startColumn - 1, subtract.endRow));
488
- } // middle right
478
+ constructor(startColumn, startRow, endColumn, endRow) {
479
+ this.startColumn = startColumn;
480
+ this.startRow = startRow;
481
+ this.endColumn = endColumn;
482
+ this.endRow = endRow;
483
+ }
489
484
 
485
+ equals(other) {
486
+ return this.startColumn === other.startColumn && this.startRow === other.startRow && this.endColumn === other.endColumn && this.endRow === other.endRow;
487
+ }
488
+ /** @returns {boolean} true if this GridRange completely contains `other` */
490
489
 
491
- if (subtract.endColumn != null && (range.endColumn == null || range.endColumn > subtract.endColumn)) {
492
- result.push(new GridRange(subtract.endColumn + 1, subtract.startRow, range.endColumn, subtract.endRow));
493
- } // Bottom quadrant
494
490
 
491
+ contains(other) {
492
+ return (this.startColumn == null || other.startColumn != null && this.startColumn <= other.startColumn) && (this.startRow == null || other.startRow != null && this.startRow <= other.startRow) && (this.endColumn == null || other.endColumn != null && this.endColumn >= other.endColumn) && (this.endRow == null || other.endRow != null && this.endRow >= other.endRow);
493
+ }
494
+ /**
495
+ * Check if the provided cell is in this range
496
+ * @param {number} column The column to check
497
+ * @param {number} row The row to check
498
+ * @returns {boolean} True if this cell is within this range
499
+ */
495
500
 
496
- if (subtract.endRow != null && (range.endRow == null || range.endRow > subtract.endRow)) {
497
- result.push(new GridRange(range.startColumn, subtract.endRow + 1, range.endColumn, range.endRow));
498
- }
499
501
 
500
- return result;
502
+ containsCell(column, row) {
503
+ if (column == null || row == null) {
504
+ return false;
501
505
  }
502
- /**
503
- * Subtract a range from multiple ranges
504
- * @param {GridRange[]} ranges The ranges to be subtracted from
505
- * @param {GridRange} subtractRange The range to subtract from within these ranges
506
- * @returns {GridRange[]} The ranges needed to represent the remaining
507
- */
508
-
509
- }, {
510
- key: "subtractFromRanges",
511
- value: function subtractFromRanges(ranges, subtractRange) {
512
- var result = [];
513
-
514
- for (var i = 0; i < ranges.length; i += 1) {
515
- result.push.apply(result, _toConsumableArray(GridRange.subtractFromRange(ranges[i], subtractRange)));
516
- }
517
506
 
518
- return result;
519
- }
520
- /**
521
- * Subtract multiple ranges from multiple ranges
522
- * @param {GridRange[]} ranges The ranges to be subtracted from
523
- * @param {GridRange[]} subtractRanges The ranges to subtract from within these ranges
524
- * @returns {GridRange[]} The ranges needed to represent the remaining
525
- */
526
-
527
- }, {
528
- key: "subtractRangesFromRanges",
529
- value: function subtractRangesFromRanges(ranges, subtractRanges) {
530
- if (!subtractRanges || subtractRanges.length === 0) {
531
- return ranges;
532
- }
507
+ return (this.startColumn == null || this.startColumn <= column) && (this.endColumn == null || this.endColumn >= column) && (this.startRow == null || this.startRow <= row) && (this.endRow == null || this.endRow >= row);
508
+ }
509
+ /** @returns {boolean} true if this GridRange touches `other` */
533
510
 
534
- var result = _toConsumableArray(ranges);
535
511
 
536
- for (var i = 0; i < subtractRanges.length; i += 1) {
537
- result = GridRange.subtractFromRanges(result, subtractRanges[i]);
538
- }
512
+ touches(other) {
513
+ return GridRange.isAxisRangeTouching(this.startRow, this.endRow, other.startRow, other.endRow) && GridRange.isAxisRangeTouching(this.startColumn, this.endColumn, other.startColumn, other.endColumn);
514
+ }
515
+ /**
516
+ * @param {GridRange} other The range to deselect from within this range
517
+ * @returns {[GridRange]} The ranges needed to represent the remaining
518
+ */
539
519
 
540
- return result;
541
- }
542
- /**
543
- * Test if a given range is bounded (all values are non-null)
544
- * @param {GridRange} range The range to test
545
- * @returns {boolean} True if this range is bounded, false otherwise
546
- */
547
-
548
- }, {
549
- key: "isBounded",
550
- value: function isBounded(range) {
551
- return range.startRow != null && range.startColumn != null && range.endRow != null && range.endColumn != null;
552
- }
553
- /**
554
- * Converts any GridRange passed in that is a full row or column selection to be bound
555
- * to the `columnCount` and `rowCount` passed in
556
- *
557
- * @param {GridRange} range The range to get the bounded range of
558
- * @param {number} columnCount The number of columns
559
- * @param {number} rowCount The number of rows
560
- * @returns {GridRange} The passed in GridRange with any null values filled in
561
- */
562
-
563
- }, {
564
- key: "boundedRange",
565
- value: function boundedRange(range, columnCount, rowCount) {
566
- var _range$startColumn, _range$startRow, _range$endColumn, _range$endRow;
567
-
568
- if (GridRange.isBounded(range)) {
569
- return range;
570
- }
571
520
 
572
- return new GridRange((_range$startColumn = range.startColumn) !== null && _range$startColumn !== void 0 ? _range$startColumn : 0, (_range$startRow = range.startRow) !== null && _range$startRow !== void 0 ? _range$startRow : 0, (_range$endColumn = range.endColumn) !== null && _range$endColumn !== void 0 ? _range$endColumn : columnCount - 1, (_range$endRow = range.endRow) !== null && _range$endRow !== void 0 ? _range$endRow : rowCount - 1);
573
- }
574
- /**
575
- * Converts the GridRanges passed in to be bound to the `columnCount` and `rowCount` passed in
576
- *
577
- * @param {GridRange[]} ranges The ranges to get the bounded ranges of
578
- * @param {number} columnCount The number of columns
579
- * @param {number} rowCount The number of rows
580
- * @returns {GridRange} The passed in GridRange with any null values filled in
581
- */
582
-
583
- }, {
584
- key: "boundedRanges",
585
- value: function boundedRanges(ranges, columnCount, rowCount) {
586
- return ranges.map(function (r) {
587
- return GridRange.boundedRange(r, columnCount, rowCount);
588
- });
521
+ subtract(other) {
522
+ return GridRange.subtractFromRange(this, other);
523
+ }
524
+ /**
525
+ * Get the first cell in this range. Throws if this range is unbounded.
526
+ *
527
+ * @param {GridRange.SELECTION_DIRECTION?} direction The direction to get the starting cell in. Defaults to DOWN
528
+ * @returns {{column: number, row: number}} The first cell in this range in the direction specified
529
+ */
530
+
531
+
532
+ startCell() {
533
+ var direction = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : GridRange.SELECTION_DIRECTION.DOWN;
534
+
535
+ if (!GridRange.isBounded(this)) {
536
+ throw new Error('Cannot get the startCell of an unbounded range');
589
537
  }
590
- /**
591
- * Offsets a GridRange by the specified amount in the x and y directions
592
- *
593
- * @param {GridRange} range The range to offset
594
- * @param {number} columnOffset The number of columns to offset
595
- * @param {number} rowOffset The number of rows to offset
596
- * @returns {GridRange} The new grid range offset from the original
597
- */
598
-
599
- }, {
600
- key: "offset",
601
- value: function offset(range, columnOffset, rowOffset) {
602
- return new GridRange(range.startColumn != null ? range.startColumn + columnOffset : null, range.startRow != null ? range.startRow + rowOffset : null, range.endColumn != null ? range.endColumn + columnOffset : null, range.endRow != null ? range.endRow + rowOffset : null);
538
+
539
+ switch (direction) {
540
+ case GridRange.SELECTION_DIRECTION.DOWN:
541
+ case GridRange.SELECTION_DIRECTION.RIGHT:
542
+ return {
543
+ column: this.startColumn,
544
+ row: this.startRow
545
+ };
546
+
547
+ case GridRange.SELECTION_DIRECTION.LEFT:
548
+ case GridRange.SELECTION_DIRECTION.UP:
549
+ {
550
+ return {
551
+ column: this.endColumn,
552
+ row: this.endRow
553
+ };
554
+ }
555
+
556
+ default:
557
+ throw new Error("Invalid direction: ".concat(direction));
603
558
  }
604
- /**
605
- * Get the next cell given the selected ranges and the current cell
606
- * @param {GridRange[]} ranges The selected bounded ranges within the grid
607
- * @param {number|null} column The cursor column, or null if none focused
608
- * @param {number|null} row The cursor row, or null if none focused
609
- * @param {SELECTION_DIRECTION} direction The direction in which to select next
610
- * @returns {Cell} The next cell to focus, or null if there should be no more focus
611
- */
612
-
613
- }, {
614
- key: "nextCell",
615
- value: function nextCell(ranges) {
616
- var column = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
617
- var row = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
618
- var direction = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : GridRange.SELECTION_DIRECTION.DOWN;
619
-
620
- if (ranges.length === 0) {
621
- return null;
622
- }
559
+ }
560
+ /**
561
+ * Get the next cell in the direction specified. Throws if this range is unbounded.
562
+ * If already at the bounds of the range in that direction, wrap to the next column or row
563
+ * If at the end of the entire range, return null
564
+ * If outside of the range, returns the next cell closest within this range.
565
+ *
566
+ * @param {number} column The cursor column
567
+ * @param {number} row The cursor row
568
+ * @param {SELECTION_DIRECTION} direction The direction to go in
569
+ * @returns {GridCell|null} The next cell in the direction specified, or `null` if at the end of the range
570
+ */
571
+
572
+
573
+ nextCell(column, row, direction) {
574
+ if (!GridRange.isBounded(this)) {
575
+ throw new Error('Bounded range required');
576
+ }
577
+
578
+ if (column == null || row == null) {
579
+ throw new Error('Require a non-null cursor');
580
+ }
581
+
582
+ var {
583
+ startColumn,
584
+ endColumn,
585
+ startRow,
586
+ endRow
587
+ } = this;
588
+
589
+ switch (direction) {
590
+ case GridRange.SELECTION_DIRECTION.DOWN:
591
+ if (row < endRow) {
592
+ return {
593
+ column,
594
+ row: Math.max(row + 1, startRow)
595
+ };
596
+ }
623
597
 
624
- var rangeIndex = -1;
598
+ if (column < endColumn) {
599
+ return {
600
+ column: Math.max(column + 1, startColumn),
601
+ row: startRow
602
+ };
603
+ }
625
604
 
626
- if (column != null && row != null) {
627
- rangeIndex = ranges.findIndex(function (r) {
628
- return r.containsCell(column, row);
629
- });
605
+ break;
630
606
 
631
- if (rangeIndex >= 0) {
632
- var range = ranges[rangeIndex];
633
- var nextCell = range.nextCell(column, row, direction);
607
+ case GridRange.SELECTION_DIRECTION.UP:
608
+ if (row > startRow) {
609
+ return {
610
+ column,
611
+ row: Math.min(row - 1, endRow)
612
+ };
613
+ }
634
614
 
635
- if (nextCell != null) {
636
- return nextCell;
637
- }
615
+ if (column > startColumn) {
616
+ return {
617
+ column: Math.min(column - 1, endColumn),
618
+ row: endRow
619
+ };
638
620
  }
639
- } // Otherwise go to the start of the next range (could be same range if only one range)
640
621
 
622
+ break;
641
623
 
642
- switch (direction) {
643
- case GridRange.SELECTION_DIRECTION.DOWN:
644
- case GridRange.SELECTION_DIRECTION.RIGHT:
645
- {
646
- var nextRangeIndex = rangeIndex < ranges.length - 1 ? rangeIndex + 1 : 0;
647
- var nextRange = ranges[nextRangeIndex];
648
- return nextRange.startCell(direction);
649
- }
624
+ case GridRange.SELECTION_DIRECTION.RIGHT:
625
+ if (column < endColumn) {
626
+ return {
627
+ column: Math.max(column + 1, startColumn),
628
+ row
629
+ };
630
+ }
650
631
 
651
- case GridRange.SELECTION_DIRECTION.LEFT:
652
- case GridRange.SELECTION_DIRECTION.UP:
653
- {
654
- var _nextRangeIndex = rangeIndex > 0 ? rangeIndex - 1 : ranges.length - 1;
632
+ if (row < endRow) {
633
+ return {
634
+ column: startColumn,
635
+ row: Math.max(row + 1, startRow)
636
+ };
637
+ }
655
638
 
656
- var _nextRange = ranges[_nextRangeIndex];
657
- return _nextRange.startCell(direction);
658
- }
639
+ break;
659
640
 
660
- default:
661
- throw new Error("Invalid direction: ".concat(direction));
662
- }
663
- }
664
- /**
665
- * Count the number of cells in the provided grid ranges
666
- * @param {GridRange[]} ranges The ranges to count the rows of
667
- * @returns {number|NaN} The number of cells in the ranges, or `NaN` if any of the ranges were unbounded
668
- */
669
-
670
- }, {
671
- key: "cellCount",
672
- value: function cellCount(ranges) {
673
- return ranges.reduce(function (cellCount, range) {
674
- var _range$endRow2, _range$startRow2, _range$endColumn2, _range$startColumn2;
675
-
676
- return cellCount + (((_range$endRow2 = range.endRow) !== null && _range$endRow2 !== void 0 ? _range$endRow2 : NaN) - ((_range$startRow2 = range.startRow) !== null && _range$startRow2 !== void 0 ? _range$startRow2 : NaN) + 1) * (((_range$endColumn2 = range.endColumn) !== null && _range$endColumn2 !== void 0 ? _range$endColumn2 : NaN) - ((_range$startColumn2 = range.startColumn) !== null && _range$startColumn2 !== void 0 ? _range$startColumn2 : NaN) + 1);
677
- }, 0);
678
- }
679
- /**
680
- * Count the number of rows in the provided grid ranges
681
- * @param {GridRange[]} ranges The ranges to count the rows of
682
- * @returns {number|NaN} The number of rows in the ranges, or `NaN` if any of the ranges were unbounded
683
- */
684
-
685
- }, {
686
- key: "rowCount",
687
- value: function rowCount(ranges) {
688
- return ranges.reduce(function (rowCount, range) {
689
- var _range$endRow3, _range$startRow3;
690
-
691
- return rowCount + ((_range$endRow3 = range.endRow) !== null && _range$endRow3 !== void 0 ? _range$endRow3 : NaN) - ((_range$startRow3 = range.startRow) !== null && _range$startRow3 !== void 0 ? _range$startRow3 : NaN) + 1;
692
- }, 0);
693
- }
694
- /**
695
- * Count the number of columns in the provided grid ranges
696
- * @param {GridRange[]} ranges The ranges to count the columns of
697
- * @returns {number|NaN} The number of columns in the ranges, or `NaN` if any of the ranges were unbounded
698
- */
699
-
700
- }, {
701
- key: "columnCount",
702
- value: function columnCount(ranges) {
703
- return ranges.reduce(function (columnCount, range) {
704
- var _range$endColumn3, _range$startColumn3;
705
-
706
- return columnCount + ((_range$endColumn3 = range.endColumn) !== null && _range$endColumn3 !== void 0 ? _range$endColumn3 : NaN) - ((_range$startColumn3 = range.startColumn) !== null && _range$startColumn3 !== void 0 ? _range$startColumn3 : NaN) + 1;
707
- }, 0);
708
- }
709
- /**
710
- * Check if the provided ranges contain the provided cell
711
- * @param {GridRange[]} ranges The ranges to check
712
- * @param {number} column The column index
713
- * @param {number} row The row index
714
- * @returns {boolean} True if the cell is within the provided ranges, false otherwise.
715
- */
716
-
717
- }, {
718
- key: "containsCell",
719
- value: function containsCell(ranges, column, row) {
720
- for (var i = 0; i < ranges.length; i += 1) {
721
- var range = ranges[i];
722
-
723
- if (range.containsCell(column, row)) {
724
- return true;
641
+ case GridRange.SELECTION_DIRECTION.LEFT:
642
+ if (column > startColumn) {
643
+ return {
644
+ column: Math.min(column - 1, endColumn),
645
+ row
646
+ };
725
647
  }
726
- }
727
648
 
728
- return false;
649
+ if (row > startRow) {
650
+ return {
651
+ column: endColumn,
652
+ row: Math.min(row - 1, endRow)
653
+ };
654
+ }
655
+
656
+ break;
657
+
658
+ default:
659
+ throw new Error("Invalid direction: ".concat(direction));
729
660
  }
730
- /**
731
- * Iterate through each cell in the provided ranges
732
- * @param {GridRange[]} ranges The ranges to iterate through
733
- * @param {(column: number, row: number, index: number) => void} callback The callback to execute. `index` is the index within that range
734
- * @param {GridRange.SELECTION_DIRECTION} direction The direction to iterate in
735
- */
736
-
737
- }, {
738
- key: "forEachCell",
739
- value: function forEachCell(ranges, callback) {
740
- var direction = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : GridRange.SELECTION_DIRECTION.RIGHT;
741
-
742
- for (var i = 0; i < ranges.length; i += 1) {
743
- ranges[i].forEach(callback, direction);
744
- }
661
+
662
+ return null;
663
+ }
664
+ /**
665
+ * Iterate through each cell in the range
666
+ * @param {(column:number, row:number, index:number) => void} callback Callback to execute. `index` is the index within this range
667
+ * @param {GridRange.SELECTION_DIRECTION} direction The direction to iterate in
668
+ */
669
+
670
+
671
+ forEach(callback) {
672
+ var direction = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : GridRange.SELECTION_DIRECTION.RIGHT;
673
+ var i = 0;
674
+ var {
675
+ column: c,
676
+ row: r
677
+ } = this.startCell(direction);
678
+
679
+ while (c != null && r != null) {
680
+ var _this$nextCell;
681
+
682
+ callback(c, r, i);
683
+ i += 1;
684
+ ({
685
+ column: c,
686
+ row: r
687
+ } = (_this$nextCell = this.nextCell(c, r, direction)) !== null && _this$nextCell !== void 0 ? _this$nextCell : {});
745
688
  }
746
- }]);
689
+ }
747
690
 
748
- return GridRange;
749
- }();
691
+ }
750
692
 
751
693
  _defineProperty(GridRange, "SELECTION_DIRECTION", Object.freeze({
752
694
  DOWN: 'DOWN',