@atlaskit/editor-tables 2.3.18 → 2.5.0

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 (38) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/cjs/table-map.js +28 -3
  3. package/dist/cjs/utils/analyse-table.js +38 -0
  4. package/dist/cjs/utils/get-selection-range-in-column.js +6 -5
  5. package/dist/cjs/utils/get-selection-range-in-row.js +6 -5
  6. package/dist/cjs/utils/move-column.js +110 -7
  7. package/dist/cjs/utils/move-row.js +97 -9
  8. package/dist/cjs/utils/normalize-direction.js +15 -0
  9. package/dist/es2019/table-map.js +22 -1
  10. package/dist/es2019/utils/analyse-table.js +32 -0
  11. package/dist/es2019/utils/get-selection-range-in-column.js +5 -5
  12. package/dist/es2019/utils/get-selection-range-in-row.js +5 -5
  13. package/dist/es2019/utils/move-column.js +109 -8
  14. package/dist/es2019/utils/move-row.js +85 -9
  15. package/dist/es2019/utils/normalize-direction.js +8 -0
  16. package/dist/esm/table-map.js +28 -3
  17. package/dist/esm/utils/analyse-table.js +32 -0
  18. package/dist/esm/utils/get-selection-range-in-column.js +6 -5
  19. package/dist/esm/utils/get-selection-range-in-row.js +6 -5
  20. package/dist/esm/utils/move-column.js +110 -9
  21. package/dist/esm/utils/move-row.js +97 -10
  22. package/dist/esm/utils/normalize-direction.js +9 -0
  23. package/dist/types/table-map.d.ts +3 -0
  24. package/dist/types/utils/analyse-table.d.ts +7 -0
  25. package/dist/types/utils/get-selection-range-in-column.d.ts +3 -3
  26. package/dist/types/utils/get-selection-range-in-row.d.ts +3 -3
  27. package/dist/types/utils/move-column.d.ts +2 -2
  28. package/dist/types/utils/move-row.d.ts +2 -2
  29. package/dist/types/utils/normalize-direction.d.ts +4 -0
  30. package/dist/types-ts4.5/table-map.d.ts +3 -0
  31. package/dist/types-ts4.5/utils/analyse-table.d.ts +7 -0
  32. package/dist/types-ts4.5/utils/get-selection-range-in-column.d.ts +3 -3
  33. package/dist/types-ts4.5/utils/get-selection-range-in-row.d.ts +3 -3
  34. package/dist/types-ts4.5/utils/move-column.d.ts +2 -2
  35. package/dist/types-ts4.5/utils/move-row.d.ts +2 -2
  36. package/dist/types-ts4.5/utils/normalize-direction.d.ts +4 -0
  37. package/package.json +2 -2
  38. package/tsconfig.json +32 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @atlaskit/editor-tables
2
2
 
3
+ ## 2.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#69232](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/69232) [`93c8f231aa82`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/93c8f231aa82) - Optimized the table move column logic to perform individual insert/deletes per row
8
+
9
+ ## 2.4.0
10
+
11
+ ### Minor Changes
12
+
13
+ - [#67400](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/67400) [`191436e36f93`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/191436e36f93) - Optimised the table move row logic to perform insert/delete steps rather than an entire table replacement
14
+
3
15
  ## 2.3.18
4
16
 
5
17
  ### Patch Changes
@@ -143,9 +143,34 @@ var TableMap = exports.TableMap = /*#__PURE__*/function () {
143
143
  }, {
144
144
  key: "isPosMerged",
145
145
  value: function isPosMerged(pos) {
146
- return this.map.filter(function (cellPos) {
147
- return cellPos === pos;
148
- }).length > 1;
146
+ return this.map.includes(pos, this.map.indexOf(pos) + 1);
147
+ }
148
+ }, {
149
+ key: "isCellMerged",
150
+ value: function isCellMerged(row, col) {
151
+ return this.isCellMergedTopLeft(row, col) || this.isCellMergedBottomRight(row, col);
152
+ }
153
+ }, {
154
+ key: "isCellMergedTopLeft",
155
+ value: function isCellMergedTopLeft(row, col) {
156
+ var pos = this.map[row * this.width + col];
157
+ return (
158
+ // top
159
+ row > 0 && pos === this.map[(row - 1) * this.width + col] ||
160
+ // left
161
+ col > 0 && pos === this.map[row * this.width + (col - 1)]
162
+ );
163
+ }
164
+ }, {
165
+ key: "isCellMergedBottomRight",
166
+ value: function isCellMergedBottomRight(row, col) {
167
+ var pos = this.map[row * this.width + col];
168
+ return (
169
+ // bottom
170
+ row < this.height - 1 && pos === this.map[(row + 1) * this.width + col] ||
171
+ // right
172
+ col < this.width - 1 && pos === this.map[row * this.width + (col + 1)]
173
+ );
149
174
  }
150
175
 
151
176
  // :: (number, string, number) → ?number
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.determineTableHeaderStateFromTableNode = determineTableHeaderStateFromTableNode;
7
+ function determineTableHeaderStateFromTableNode(table, tableMap, types) {
8
+ // We only really need to check the 2nd cell in the row/col if it's a header, since we only support a single full row/col header on the top & left
9
+ // of a table. We can assume that if the 2nd cell is a header then the entire row/col is a header.
10
+ // Be carefull though! when checking the 1st cell as it shares its header state with both row/cols.
11
+ // This means we wont be able to reliably identify header state on tables smaller the 2x2, however we can do best guess.
12
+
13
+ // This is a 3 bit mask;
14
+ // bit: 0 = Identifies if the cell at (0, 0) (row, col - 0-based) is a header cell or not
15
+ // bit: 1 = Identifies if the cell at (0, 1) is a header cell or not
16
+ // bit: 2 = Identifies if the cell at (1, 0) is a header cell or not
17
+ var mask = 0;
18
+
19
+ // At minimum we should have 1 cell in the table.
20
+ var topLeftCell = table.nodeAt(tableMap.map[0]);
21
+ // If this cell is a header that could indicate
22
+ mask |= topLeftCell && topLeftCell.type === types.header_cell ? 1 : 0;
23
+ if (tableMap.width > 1) {
24
+ var cell = table.nodeAt(tableMap.map[1]);
25
+ // If the cell at (0, 1) is a header then we set the bit flag to indicate row headers are enabled, otherwise if it's
26
+ // not then we will set the col headers enabled flag (and vice versa in the branch below) only if the cell at (0,0)
27
+ // was a header cell.
28
+ mask |= cell && cell.type === types.header_cell ? 2 : 4 * (mask & 1);
29
+ }
30
+ if (tableMap.height > 1) {
31
+ var _cell = table.nodeAt(tableMap.map[tableMap.width]);
32
+ mask |= _cell && _cell.type === types.header_cell ? 4 : 2 * (mask & 1);
33
+ }
34
+ return {
35
+ rowHeaderEnabled: mask === 7 || mask === 3,
36
+ columnHeaderEnabled: mask === 7 || mask === 5
37
+ };
38
+ }
@@ -7,10 +7,11 @@ exports.getSelectionRangeInColumn = void 0;
7
7
  var _getCellsInColumn = require("./get-cells-in-column");
8
8
  var _getCellsInRow = require("./get-cells-in-row");
9
9
  // Returns a range of rectangular selection spanning all merged cells around a column at index `columnIndex`.
10
- var getSelectionRangeInColumn = exports.getSelectionRangeInColumn = function getSelectionRangeInColumn(columnIndex) {
10
+ var getSelectionRangeInColumn = exports.getSelectionRangeInColumn = function getSelectionRangeInColumn(startColIndex) {
11
+ var endColIndex = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : startColIndex;
11
12
  return function (tr) {
12
- var startIndex = columnIndex;
13
- var endIndex = columnIndex;
13
+ var startIndex = startColIndex;
14
+ var endIndex = endColIndex;
14
15
 
15
16
  // looking for selection start column (startIndex)
16
17
  var _loop = function _loop(i) {
@@ -27,7 +28,7 @@ var getSelectionRangeInColumn = exports.getSelectionRangeInColumn = function get
27
28
  });
28
29
  }
29
30
  };
30
- for (var i = columnIndex; i >= 0; i--) {
31
+ for (var i = startColIndex; i >= 0; i--) {
31
32
  _loop(i);
32
33
  }
33
34
  // looking for selection end column (endIndex)
@@ -42,7 +43,7 @@ var getSelectionRangeInColumn = exports.getSelectionRangeInColumn = function get
42
43
  });
43
44
  }
44
45
  };
45
- for (var _i = columnIndex; _i <= endIndex; _i++) {
46
+ for (var _i = startColIndex; _i <= endIndex; _i++) {
46
47
  _loop2(_i);
47
48
  }
48
49
 
@@ -7,10 +7,11 @@ exports.getSelectionRangeInRow = void 0;
7
7
  var _getCellsInColumn = require("./get-cells-in-column");
8
8
  var _getCellsInRow = require("./get-cells-in-row");
9
9
  // Returns a range of rectangular selection spanning all merged cells around a row at index `rowIndex`.
10
- var getSelectionRangeInRow = exports.getSelectionRangeInRow = function getSelectionRangeInRow(rowIndex) {
10
+ var getSelectionRangeInRow = exports.getSelectionRangeInRow = function getSelectionRangeInRow(startRowIndex) {
11
+ var endRowIndex = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : startRowIndex;
11
12
  return function (tr) {
12
- var startIndex = rowIndex;
13
- var endIndex = rowIndex;
13
+ var startIndex = startRowIndex;
14
+ var endIndex = endRowIndex;
14
15
 
15
16
  // looking for selection start row (startIndex)
16
17
  var _loop = function _loop(i) {
@@ -27,7 +28,7 @@ var getSelectionRangeInRow = exports.getSelectionRangeInRow = function getSelect
27
28
  });
28
29
  }
29
30
  };
30
- for (var i = rowIndex; i >= 0; i--) {
31
+ for (var i = startRowIndex; i >= 0; i--) {
31
32
  _loop(i);
32
33
  }
33
34
  // looking for selection end row (endIndex)
@@ -42,7 +43,7 @@ var getSelectionRangeInRow = exports.getSelectionRangeInRow = function getSelect
42
43
  });
43
44
  }
44
45
  };
45
- for (var _i = rowIndex; _i <= endIndex; _i++) {
46
+ for (var _i = startRowIndex; _i <= endIndex; _i++) {
46
47
  _loop2(_i);
47
48
  }
48
49
 
@@ -1,13 +1,19 @@
1
1
  "use strict";
2
2
 
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
3
4
  Object.defineProperty(exports, "__esModule", {
4
5
  value: true
5
6
  });
6
7
  exports.moveColumn = void 0;
8
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
9
+ var _tableMap = require("../table-map");
10
+ var _analyseTable = require("./analyse-table");
7
11
  var _cloneTr = require("./clone-tr");
8
12
  var _find = require("./find");
9
13
  var _getSelectionRangeInColumn = require("./get-selection-range-in-column");
14
+ var _normalizeDirection = require("./normalize-direction");
10
15
  var _reorderUtils = require("./reorder-utils");
16
+ var _tableNodeTypes = require("./table-node-types");
11
17
  // :: (originColumnIndex: number, targetColumnIndex: targetColumnIndex, options?: MovementOptions) → (tr: Transaction) → Transaction
12
18
  // Returns a new transaction that moves the origin column to the target index;
13
19
  //
@@ -136,8 +142,8 @@ var _reorderUtils = require("./reorder-utils");
136
142
  // moveColumn(x, y, options)(state.tr)
137
143
  // );
138
144
  // ```
139
- var moveColumn = exports.moveColumn = function moveColumn(originColumnIndex, targetColumnIndex) {
140
- var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {
145
+ var moveColumn = exports.moveColumn = function moveColumn(state, originColumnIndex, targetColumnIndex) {
146
+ var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {
141
147
  tryToFit: false,
142
148
  direction: 0
143
149
  };
@@ -147,18 +153,115 @@ var moveColumn = exports.moveColumn = function moveColumn(originColumnIndex, tar
147
153
  if (!table) {
148
154
  return tr;
149
155
  }
150
- var originalColumnRanges = (0, _getSelectionRangeInColumn.getSelectionRangeInColumn)(originColumnIndex)(tr);
156
+
157
+ // normalize the origin index to an array since this supports moving both a single & multiple cols in a single action.
158
+ if (!Array.isArray(originColumnIndex)) {
159
+ originColumnIndex = [originColumnIndex];
160
+ }
161
+ var tableMap = _tableMap.TableMap.get(table.node);
162
+ var originalColumnRanges = (0, _getSelectionRangeInColumn.getSelectionRangeInColumn)(Math.min.apply(Math, (0, _toConsumableArray2.default)(originColumnIndex)), Math.max.apply(Math, (0, _toConsumableArray2.default)(originColumnIndex)))(tr);
151
163
  var targetColumnRanges = (0, _getSelectionRangeInColumn.getSelectionRangeInColumn)(targetColumnIndex)(tr);
152
164
  var indexesOriginColumn = (_originalColumnRanges = originalColumnRanges === null || originalColumnRanges === void 0 ? void 0 : originalColumnRanges.indexes) !== null && _originalColumnRanges !== void 0 ? _originalColumnRanges : [];
153
165
  var indexesTargetColumn = (_targetColumnRanges$i = targetColumnRanges === null || targetColumnRanges === void 0 ? void 0 : targetColumnRanges.indexes) !== null && _targetColumnRanges$i !== void 0 ? _targetColumnRanges$i : [];
166
+ var min = indexesOriginColumn[0];
167
+ var max = indexesOriginColumn[indexesOriginColumn.length - 1];
154
168
  if (indexesOriginColumn.includes(targetColumnIndex)) {
155
169
  return tr;
156
170
  }
157
171
  if (!options.tryToFit && indexesTargetColumn.length > 1) {
158
- (0, _reorderUtils.isValidReorder)(originColumnIndex, targetColumnIndex, indexesTargetColumn, 'column');
172
+ (0, _reorderUtils.isValidReorder)(originColumnIndex[0], targetColumnIndex, indexesTargetColumn, 'column');
173
+ }
174
+ var types = (0, _tableNodeTypes.tableNodeTypes)(state.schema);
175
+ var direction = (0, _normalizeDirection.normalizeDirection)(min, targetColumnIndex, options);
176
+ var actualTargetIndex = Math[direction === 'start' ? 'min' : 'max'].apply(Math, (0, _toConsumableArray2.default)(indexesTargetColumn));
177
+ var _determineTableHeader = (0, _analyseTable.determineTableHeaderStateFromTableNode)(table.node, tableMap, types),
178
+ rowHeaderEnabled = _determineTableHeader.rowHeaderEnabled,
179
+ columnHeaderEnabled = _determineTableHeader.columnHeaderEnabled;
180
+ var createContentNode = createContentNodeFactory(table);
181
+ var newTr = (0, _cloneTr.cloneTr)(tr);
182
+ var origins = [];
183
+ for (var y = 0; y < tableMap.height; y++) {
184
+ origins.push([]);
185
+ for (var x = min; x <= max; x++) {
186
+ if (tableMap.isCellMergedTopLeft(y, x)) {
187
+ continue;
188
+ }
189
+ var nodePos = tableMap.map[y * tableMap.width + x];
190
+ origins[y].push(createContentNode(nodePos));
191
+ }
192
+ if (columnHeaderEnabled && (min === 0 || actualTargetIndex === 0)) {
193
+ // This block is handling the situation where a col is moved in/out of the header position. If the header col option
194
+ // is enabled then;
195
+ // When a col is moved out, the col will be converted to a normal col and the col to the right will become the header.
196
+ // When a col is moved in, the old col header needs to be made normal, and the incoming col needs to be made a header.
197
+ // This section only manages what happens to the other col, not the one being moved.
198
+ var nearHeaderCol = min === 0 ? max + 1 : actualTargetIndex;
199
+ var _nodePos = tableMap.map[y * tableMap.width + nearHeaderCol];
200
+ var _createContentNode = createContentNode(_nodePos),
201
+ pos = _createContentNode.pos,
202
+ node = _createContentNode.node;
203
+ newTr.setNodeMarkup(pos, actualTargetIndex !== 0 || rowHeaderEnabled && y === 0 ? types.header_cell : types.cell, node.attrs);
204
+ }
159
205
  }
160
- var newTable = (0, _reorderUtils.moveTableColumn)(table, indexesOriginColumn, indexesTargetColumn, options.direction);
161
- var newTr = (0, _cloneTr.cloneTr)(tr).replaceWith(table.pos, table.pos + table.node.nodeSize, newTable);
206
+ origins.forEach(function (row, y) {
207
+ if (!row.length) {
208
+ // If the origin has no cells to be moved then we can skip moving for this row. This can occur when a cell above rowspans
209
+ // into the current row.
210
+ return;
211
+ }
212
+
213
+ // The actual target index needs to be translated per row, this is because row/col spans can affect the amount of
214
+ // cells each row contains.
215
+ var rowTargetPosition = translateTargetPosition(y, actualTargetIndex, tableMap);
216
+ var node = table.node.nodeAt(rowTargetPosition);
217
+ var pos = table.start + rowTargetPosition;
218
+ var insertPos = direction === 'end' ? newTr.mapping.map(pos + node.nodeSize, 1) : newTr.mapping.map(pos, -1);
219
+ newTr.insert(insertPos, row.map(function (_ref, x) {
220
+ var node = _ref.node;
221
+ return normalizeCellNode(node, rowHeaderEnabled && y === 0, columnHeaderEnabled && actualTargetIndex === 0 && x === 0, types);
222
+ }));
223
+
224
+ // NOTE: only consecutive cells can be moved, this means we can simplify the delete op into a single step which
225
+ // deletes the range of cells.
226
+ var first = row[0];
227
+ var last = row[row.length - 1];
228
+ return newTr.delete(newTr.mapping.map(first.pos, 1), newTr.mapping.map(last.pos + last.node.nodeSize, -1));
229
+ });
162
230
  return newTr;
163
231
  };
164
- };
232
+ };
233
+ function normalizeCellNode(cellNode, rowHeaderEnabled, columnHeaderEnabled, types) {
234
+ var newTargetType = rowHeaderEnabled || columnHeaderEnabled ? types.header_cell : types.cell;
235
+ return cellNode.type !== newTargetType ? newTargetType.create(cellNode.attrs, cellNode.content, cellNode.marks) : cellNode;
236
+ }
237
+ function createContentNodeFactory(table) {
238
+ return function (nodePos) {
239
+ var node = table.node.nodeAt(nodePos);
240
+ var pos = nodePos + table.start;
241
+ return {
242
+ pos: pos,
243
+ start: pos + 1,
244
+ node: node,
245
+ depth: table.depth + 2
246
+ };
247
+ };
248
+ }
249
+ function translateTargetPosition(row, startIndex, tableMap) {
250
+ if (tableMap.isCellMergedTopLeft(row, startIndex)) {
251
+ // find the closet unmerged position to the left of the target. We scan left first because merged cells will actually
252
+ // reduce the amount of cells in a row.
253
+ for (var x = startIndex - 1; x >= 0; x--) {
254
+ if (!tableMap.isCellMergedTopLeft(row, x)) {
255
+ return tableMap.map[row * tableMap.width + x];
256
+ }
257
+ }
258
+
259
+ // If no index found then we need to look to the right, this can occur when the first cell in the row is merged.
260
+ for (var _x = startIndex + 1; _x < tableMap.width; _x++) {
261
+ if (!tableMap.isCellMergedTopLeft(row, _x)) {
262
+ return tableMap.map[row * tableMap.width + _x];
263
+ }
264
+ }
265
+ }
266
+ return tableMap.map[row * tableMap.width + startIndex];
267
+ }
@@ -1,13 +1,20 @@
1
1
  "use strict";
2
2
 
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
3
4
  Object.defineProperty(exports, "__esModule", {
4
5
  value: true
5
6
  });
6
7
  exports.moveRow = void 0;
8
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
9
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
10
+ var _tableMap = require("../table-map");
11
+ var _analyseTable = require("./analyse-table");
7
12
  var _cloneTr = require("./clone-tr");
8
13
  var _find = require("./find");
9
14
  var _getSelectionRangeInRow = require("./get-selection-range-in-row");
15
+ var _normalizeDirection = require("./normalize-direction");
10
16
  var _reorderUtils = require("./reorder-utils");
17
+ var _tableNodeTypes = require("./table-node-types");
11
18
  // :: (originRowIndex: number, targetRowIndex: targetColumnIndex, options?: MovementOptions) → (tr: Transaction) → Transaction
12
19
  // Returns a new transaction that moves the origin row to the target index;
13
20
  //
@@ -151,8 +158,8 @@ var _reorderUtils = require("./reorder-utils");
151
158
  // moveRow(x, y, options)(state.tr)
152
159
  // );
153
160
  // ```
154
- var moveRow = exports.moveRow = function moveRow(originRowIndex, targetRowIndex) {
155
- var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {
161
+ var moveRow = exports.moveRow = function moveRow(state, originRowIndex, targetRowIndex) {
162
+ var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {
156
163
  tryToFit: false,
157
164
  direction: 0
158
165
  };
@@ -161,18 +168,99 @@ var moveRow = exports.moveRow = function moveRow(originRowIndex, targetRowIndex)
161
168
  if (!table) {
162
169
  return tr;
163
170
  }
164
- var originalRowRanges = (0, _getSelectionRangeInRow.getSelectionRangeInRow)(originRowIndex)(tr);
171
+
172
+ // normalize the origin index to an array since move row support moving both a single & multiple rows in a single action.
173
+ if (!Array.isArray(originRowIndex)) {
174
+ originRowIndex = [originRowIndex];
175
+ }
176
+ if (originRowIndex.includes(targetRowIndex)) {
177
+ return tr;
178
+ }
179
+ var tableMap = _tableMap.TableMap.get(table.node);
180
+ var _originRowIndex$reduc = originRowIndex.reduce(function (_ref, cur) {
181
+ var _ref2 = (0, _slicedToArray2.default)(_ref, 2),
182
+ min = _ref2[0],
183
+ max = _ref2[1];
184
+ return [Math.min(min, cur), Math.max(max, cur)];
185
+ }, [Number.MAX_SAFE_INTEGER, Number.MIN_SAFE_INTEGER]),
186
+ _originRowIndex$reduc2 = (0, _slicedToArray2.default)(_originRowIndex$reduc, 2),
187
+ originMin = _originRowIndex$reduc2[0],
188
+ originMax = _originRowIndex$reduc2[1];
189
+ var originalRowRanges = (0, _getSelectionRangeInRow.getSelectionRangeInRow)(originMin, originMax)(tr);
165
190
  var targetRowRanges = (0, _getSelectionRangeInRow.getSelectionRangeInRow)(targetRowIndex)(tr);
166
191
  var indexesOriginRow = (originalRowRanges === null || originalRowRanges === void 0 ? void 0 : originalRowRanges.indexes) || [];
167
192
  var indexesTargetRow = (targetRowRanges === null || targetRowRanges === void 0 ? void 0 : targetRowRanges.indexes) || [];
168
- if (indexesOriginRow.includes(targetRowIndex)) {
193
+ if (originMin < 0 || originMin === Number.MAX_SAFE_INTEGER || originMax >= tableMap.height || originMax === Number.MIN_SAFE_INTEGER ||
194
+ // The target index cannot be within the origin bounds
195
+ targetRowIndex >= originMin && targetRowIndex <= originMax) {
169
196
  return tr;
170
197
  }
171
198
  if (!options.tryToFit && indexesTargetRow.length > 1) {
172
- (0, _reorderUtils.isValidReorder)(originRowIndex, targetRowIndex, indexesTargetRow, 'row');
199
+ (0, _reorderUtils.isValidReorder)(originMin, targetRowIndex, indexesTargetRow, 'row');
200
+ }
201
+ var types = (0, _tableNodeTypes.tableNodeTypes)(state.schema);
202
+ var direction = (0, _normalizeDirection.normalizeDirection)(originMin, targetRowIndex, options);
203
+ var actualTargetIndex = Math[direction === 'start' ? 'min' : 'max'].apply(Math, (0, _toConsumableArray2.default)(indexesTargetRow));
204
+ var originPositions = indexesOriginRow.map(function (index) {
205
+ return tableMap.positionAt(index, 0, table.node) + table.pos;
206
+ });
207
+ var originNodes = originPositions.reduce(function (acc, pos) {
208
+ var node = tr.doc.nodeAt(tr.mapping.map(pos));
209
+ if (node) {
210
+ return [].concat((0, _toConsumableArray2.default)(acc), [{
211
+ pos: pos,
212
+ node: node
213
+ }]);
214
+ }
215
+ return acc;
216
+ }, []);
217
+ var targetPos = tableMap.positionAt(actualTargetIndex, 0, table.node) + table.pos;
218
+ var targetNode = tr.doc.nodeAt(tr.mapping.map(targetPos));
219
+ if (originNodes !== null && originNodes !== void 0 && originNodes.length && targetNode) {
220
+ var newTr = (0, _cloneTr.cloneTr)(tr);
221
+ var _determineTableHeader = (0, _analyseTable.determineTableHeaderStateFromTableNode)(table.node, tableMap, types),
222
+ rowHeaderEnabled = _determineTableHeader.rowHeaderEnabled,
223
+ columnHeaderEnabled = _determineTableHeader.columnHeaderEnabled;
224
+ if (rowHeaderEnabled && (originMin === 0 || actualTargetIndex === 0)) {
225
+ // This block is handling the situation where a row is moved in/out of the header position. If the header row option
226
+ // is enabled then;
227
+ // When a row is moved out, the row will be converted to a normal row and the row below it will become the header.
228
+ // When a row is moved in, the old row header needs to be made normal, and the incoming row needs to be made a header.
229
+ // This section only manages what happens to the other row, no the one being moved.
230
+ var nearHeaderPos = tableMap.positionAt(originMin === 0 ? originMax + 1 : actualTargetIndex, 0, table.node) + table.pos;
231
+ var nearHeaderNode = newTr.doc.nodeAt(newTr.mapping.map(nearHeaderPos));
232
+ if (nearHeaderNode) {
233
+ nearHeaderNode.forEach(function (node, offset, index) {
234
+ var start = newTr.mapping.map(nearHeaderPos + 1 + offset);
235
+ newTr.setNodeMarkup(start, actualTargetIndex !== 0 || columnHeaderEnabled && index === 0 ? types.header_cell : types.cell, node.attrs);
236
+ });
237
+ }
238
+ }
239
+ var insertPos = direction === 'end' ? newTr.mapping.map(targetPos + targetNode.nodeSize, 1) : newTr.mapping.map(targetPos, -1);
240
+ newTr.insert(insertPos, originNodes.map(function (_ref3, index) {
241
+ var node = _ref3.node;
242
+ return normalizeRowNode(node, rowHeaderEnabled && actualTargetIndex === 0 && index === 0, columnHeaderEnabled, types);
243
+ }));
244
+ originNodes.forEach(function (_ref4) {
245
+ var pos = _ref4.pos,
246
+ node = _ref4.node;
247
+ newTr.delete(newTr.mapping.map(pos, 1), newTr.mapping.map(pos + node.nodeSize, -1));
248
+ });
249
+ return newTr;
173
250
  }
174
- var newTable = (0, _reorderUtils.moveTableRow)(table, indexesOriginRow, indexesTargetRow, options.direction);
175
- var newTr = (0, _cloneTr.cloneTr)(tr).replaceWith(table.pos, table.pos + table.node.nodeSize, newTable);
176
- return newTr;
251
+ return tr;
177
252
  };
178
- };
253
+ };
254
+
255
+ /**
256
+ * This ensures the row node cell type correctly reflect what they should be.
257
+ * @returns A copy of the rowNode
258
+ */
259
+ function normalizeRowNode(rowNode, rowHeaderEnabled, columnHeaderEnabled, types) {
260
+ var content = [];
261
+ rowNode.forEach(function (node, offset, index) {
262
+ var newTargetType = rowHeaderEnabled || columnHeaderEnabled && index === 0 ? types.header_cell : types.cell;
263
+ content.push(node.type !== newTargetType ? newTargetType.create(node.attrs, node.content, node.marks) : node);
264
+ });
265
+ return rowNode.type.create(rowNode.attrs, content, rowNode.marks);
266
+ }
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.normalizeDirection = normalizeDirection;
7
+ function normalizeDirection(origin, target) {
8
+ var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {
9
+ tryToFit: false,
10
+ direction: 0
11
+ };
12
+ var dir = origin < target ? 'end' : 'start';
13
+ var override = options.direction < 0 ? 'start' : 'end';
14
+ return options.tryToFit && !!options.direction ? override : dir;
15
+ }
@@ -124,7 +124,28 @@ export class TableMap {
124
124
  throw new RangeError('No cell with offset ' + pos + ' found');
125
125
  }
126
126
  isPosMerged(pos) {
127
- return this.map.filter(cellPos => cellPos === pos).length > 1;
127
+ return this.map.includes(pos, this.map.indexOf(pos) + 1);
128
+ }
129
+ isCellMerged(row, col) {
130
+ return this.isCellMergedTopLeft(row, col) || this.isCellMergedBottomRight(row, col);
131
+ }
132
+ isCellMergedTopLeft(row, col) {
133
+ const pos = this.map[row * this.width + col];
134
+ return (
135
+ // top
136
+ row > 0 && pos === this.map[(row - 1) * this.width + col] ||
137
+ // left
138
+ col > 0 && pos === this.map[row * this.width + (col - 1)]
139
+ );
140
+ }
141
+ isCellMergedBottomRight(row, col) {
142
+ const pos = this.map[row * this.width + col];
143
+ return (
144
+ // bottom
145
+ row < this.height - 1 && pos === this.map[(row + 1) * this.width + col] ||
146
+ // right
147
+ col < this.width - 1 && pos === this.map[row * this.width + (col + 1)]
148
+ );
128
149
  }
129
150
 
130
151
  // :: (number, string, number) → ?number
@@ -0,0 +1,32 @@
1
+ export function determineTableHeaderStateFromTableNode(table, tableMap, types) {
2
+ // We only really need to check the 2nd cell in the row/col if it's a header, since we only support a single full row/col header on the top & left
3
+ // of a table. We can assume that if the 2nd cell is a header then the entire row/col is a header.
4
+ // Be carefull though! when checking the 1st cell as it shares its header state with both row/cols.
5
+ // This means we wont be able to reliably identify header state on tables smaller the 2x2, however we can do best guess.
6
+
7
+ // This is a 3 bit mask;
8
+ // bit: 0 = Identifies if the cell at (0, 0) (row, col - 0-based) is a header cell or not
9
+ // bit: 1 = Identifies if the cell at (0, 1) is a header cell or not
10
+ // bit: 2 = Identifies if the cell at (1, 0) is a header cell or not
11
+ let mask = 0;
12
+
13
+ // At minimum we should have 1 cell in the table.
14
+ const topLeftCell = table.nodeAt(tableMap.map[0]);
15
+ // If this cell is a header that could indicate
16
+ mask |= topLeftCell && topLeftCell.type === types.header_cell ? 1 : 0;
17
+ if (tableMap.width > 1) {
18
+ const cell = table.nodeAt(tableMap.map[1]);
19
+ // If the cell at (0, 1) is a header then we set the bit flag to indicate row headers are enabled, otherwise if it's
20
+ // not then we will set the col headers enabled flag (and vice versa in the branch below) only if the cell at (0,0)
21
+ // was a header cell.
22
+ mask |= cell && cell.type === types.header_cell ? 2 : 4 * (mask & 1);
23
+ }
24
+ if (tableMap.height > 1) {
25
+ const cell = table.nodeAt(tableMap.map[tableMap.width]);
26
+ mask |= cell && cell.type === types.header_cell ? 4 : 2 * (mask & 1);
27
+ }
28
+ return {
29
+ rowHeaderEnabled: mask === 7 || mask === 3,
30
+ columnHeaderEnabled: mask === 7 || mask === 5
31
+ };
32
+ }
@@ -2,12 +2,12 @@ import { getCellsInColumn } from './get-cells-in-column';
2
2
  import { getCellsInRow } from './get-cells-in-row';
3
3
 
4
4
  // Returns a range of rectangular selection spanning all merged cells around a column at index `columnIndex`.
5
- export const getSelectionRangeInColumn = columnIndex => tr => {
6
- let startIndex = columnIndex;
7
- let endIndex = columnIndex;
5
+ export const getSelectionRangeInColumn = (startColIndex, endColIndex = startColIndex) => tr => {
6
+ let startIndex = startColIndex;
7
+ let endIndex = endColIndex;
8
8
 
9
9
  // looking for selection start column (startIndex)
10
- for (let i = columnIndex; i >= 0; i--) {
10
+ for (let i = startColIndex; i >= 0; i--) {
11
11
  const cells = getCellsInColumn(i)(tr.selection);
12
12
  if (cells) {
13
13
  cells.forEach(cell => {
@@ -22,7 +22,7 @@ export const getSelectionRangeInColumn = columnIndex => tr => {
22
22
  }
23
23
  }
24
24
  // looking for selection end column (endIndex)
25
- for (let i = columnIndex; i <= endIndex; i++) {
25
+ for (let i = startColIndex; i <= endIndex; i++) {
26
26
  const cells = getCellsInColumn(i)(tr.selection);
27
27
  if (cells) {
28
28
  cells.forEach(cell => {
@@ -2,12 +2,12 @@ import { getCellsInColumn } from './get-cells-in-column';
2
2
  import { getCellsInRow } from './get-cells-in-row';
3
3
 
4
4
  // Returns a range of rectangular selection spanning all merged cells around a row at index `rowIndex`.
5
- export const getSelectionRangeInRow = rowIndex => tr => {
6
- let startIndex = rowIndex;
7
- let endIndex = rowIndex;
5
+ export const getSelectionRangeInRow = (startRowIndex, endRowIndex = startRowIndex) => tr => {
6
+ let startIndex = startRowIndex;
7
+ let endIndex = endRowIndex;
8
8
 
9
9
  // looking for selection start row (startIndex)
10
- for (let i = rowIndex; i >= 0; i--) {
10
+ for (let i = startRowIndex; i >= 0; i--) {
11
11
  const cells = getCellsInRow(i)(tr.selection);
12
12
  if (cells) {
13
13
  cells.forEach(cell => {
@@ -22,7 +22,7 @@ export const getSelectionRangeInRow = rowIndex => tr => {
22
22
  }
23
23
  }
24
24
  // looking for selection end row (endIndex)
25
- for (let i = rowIndex; i <= endIndex; i++) {
25
+ for (let i = startRowIndex; i <= endIndex; i++) {
26
26
  const cells = getCellsInRow(i)(tr.selection);
27
27
  if (cells) {
28
28
  cells.forEach(cell => {