@lexical/table 0.20.1-nightly.20241122.0 → 0.20.1-nightly.20241126.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.
- package/LexicalTable.dev.js +663 -413
- package/LexicalTable.dev.mjs +664 -414
- package/LexicalTable.js.flow +5 -5
- package/LexicalTable.prod.js +106 -96
- package/LexicalTable.prod.mjs +1 -1
- package/LexicalTableCellNode.d.ts +2 -2
- package/LexicalTableObserver.d.ts +35 -8
- package/LexicalTableSelection.d.ts +16 -1
- package/LexicalTableSelectionHelpers.d.ts +3 -1
- package/LexicalTableUtils.d.ts +20 -2
- package/package.json +4 -4
package/LexicalTable.dev.js
CHANGED
@@ -106,31 +106,24 @@ class TableCellNode extends lexical.ElementNode {
|
|
106
106
|
return element;
|
107
107
|
}
|
108
108
|
exportDOM(editor) {
|
109
|
-
const
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
const element_ = element;
|
114
|
-
element_.style.border = '1px solid black';
|
109
|
+
const output = super.exportDOM(editor);
|
110
|
+
if (output.element) {
|
111
|
+
const element = output.element;
|
112
|
+
element.style.border = '1px solid black';
|
115
113
|
if (this.__colSpan > 1) {
|
116
|
-
|
114
|
+
element.colSpan = this.__colSpan;
|
117
115
|
}
|
118
116
|
if (this.__rowSpan > 1) {
|
119
|
-
|
117
|
+
element.rowSpan = this.__rowSpan;
|
120
118
|
}
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
element_.style.backgroundColor = backgroundColor;
|
127
|
-
} else if (this.hasHeader()) {
|
128
|
-
element_.style.backgroundColor = '#f2f3f5';
|
119
|
+
element.style.width = `${this.getWidth() || COLUMN_WIDTH}px`;
|
120
|
+
element.style.verticalAlign = 'top';
|
121
|
+
element.style.textAlign = 'start';
|
122
|
+
if (this.__backgroundColor === null && this.hasHeader()) {
|
123
|
+
element.style.backgroundColor = '#f2f3f5';
|
129
124
|
}
|
130
125
|
}
|
131
|
-
return
|
132
|
-
element
|
133
|
-
};
|
126
|
+
return output;
|
134
127
|
}
|
135
128
|
exportJSON() {
|
136
129
|
return {
|
@@ -298,6 +291,18 @@ const INSERT_TABLE_COMMAND = lexical.createCommand('INSERT_TABLE_COMMAND');
|
|
298
291
|
|
299
292
|
const CAN_USE_DOM = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined';
|
300
293
|
|
294
|
+
/**
|
295
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
296
|
+
*
|
297
|
+
* This source code is licensed under the MIT license found in the
|
298
|
+
* LICENSE file in the root directory of this source tree.
|
299
|
+
*
|
300
|
+
*/
|
301
|
+
|
302
|
+
const documentMode = CAN_USE_DOM && 'documentMode' in document ? document.documentMode : null;
|
303
|
+
const IS_FIREFOX = CAN_USE_DOM && /^(?!.*Seamonkey)(?=.*Firefox).*/i.test(navigator.userAgent);
|
304
|
+
CAN_USE_DOM && 'InputEvent' in window && !documentMode ? 'getTargetRanges' in new window.InputEvent('input') : false;
|
305
|
+
|
301
306
|
/**
|
302
307
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
303
308
|
*
|
@@ -982,8 +987,8 @@ function $unmergeCell() {
|
|
982
987
|
cell.setRowSpan(1);
|
983
988
|
}
|
984
989
|
}
|
985
|
-
function $computeTableMap(
|
986
|
-
const [tableMap, cellAValue, cellBValue] = $computeTableMapSkipCellCheck(
|
990
|
+
function $computeTableMap(tableNode, cellA, cellB) {
|
991
|
+
const [tableMap, cellAValue, cellBValue] = $computeTableMapSkipCellCheck(tableNode, cellA, cellB);
|
987
992
|
if (!(cellAValue !== null)) {
|
988
993
|
throw Error(`Anchor not found in Table`);
|
989
994
|
}
|
@@ -992,7 +997,7 @@ function $computeTableMap(grid, cellA, cellB) {
|
|
992
997
|
}
|
993
998
|
return [tableMap, cellAValue, cellBValue];
|
994
999
|
}
|
995
|
-
function $computeTableMapSkipCellCheck(
|
1000
|
+
function $computeTableMapSkipCellCheck(tableNode, cellA, cellB) {
|
996
1001
|
const tableMap = [];
|
997
1002
|
let cellAValue = null;
|
998
1003
|
let cellBValue = null;
|
@@ -1003,7 +1008,7 @@ function $computeTableMapSkipCellCheck(grid, cellA, cellB) {
|
|
1003
1008
|
}
|
1004
1009
|
return row;
|
1005
1010
|
}
|
1006
|
-
const gridChildren =
|
1011
|
+
const gridChildren = tableNode.getChildren();
|
1007
1012
|
for (let rowIdx = 0; rowIdx < gridChildren.length; rowIdx++) {
|
1008
1013
|
const row = gridChildren[rowIdx];
|
1009
1014
|
if (!$isTableRowNode(row)) {
|
@@ -1079,6 +1084,99 @@ function $getNodeTriplet(source) {
|
|
1079
1084
|
}
|
1080
1085
|
return [cell, row, grid];
|
1081
1086
|
}
|
1087
|
+
function $computeTableCellRectSpans(map, boundary) {
|
1088
|
+
const {
|
1089
|
+
minColumn,
|
1090
|
+
maxColumn,
|
1091
|
+
minRow,
|
1092
|
+
maxRow
|
1093
|
+
} = boundary;
|
1094
|
+
let topSpan = 1;
|
1095
|
+
let leftSpan = 1;
|
1096
|
+
let rightSpan = 1;
|
1097
|
+
let bottomSpan = 1;
|
1098
|
+
const topRow = map[minRow];
|
1099
|
+
const bottomRow = map[maxRow];
|
1100
|
+
for (let col = minColumn; col <= maxColumn; col++) {
|
1101
|
+
topSpan = Math.max(topSpan, topRow[col].cell.__rowSpan);
|
1102
|
+
bottomSpan = Math.max(bottomSpan, bottomRow[col].cell.__rowSpan);
|
1103
|
+
}
|
1104
|
+
for (let row = minRow; row <= maxRow; row++) {
|
1105
|
+
leftSpan = Math.max(leftSpan, map[row][minColumn].cell.__colSpan);
|
1106
|
+
rightSpan = Math.max(rightSpan, map[row][maxColumn].cell.__colSpan);
|
1107
|
+
}
|
1108
|
+
return {
|
1109
|
+
bottomSpan,
|
1110
|
+
leftSpan,
|
1111
|
+
rightSpan,
|
1112
|
+
topSpan
|
1113
|
+
};
|
1114
|
+
}
|
1115
|
+
function $computeTableCellRectBoundary(map, cellAMap, cellBMap) {
|
1116
|
+
let minColumn = Math.min(cellAMap.startColumn, cellBMap.startColumn);
|
1117
|
+
let minRow = Math.min(cellAMap.startRow, cellBMap.startRow);
|
1118
|
+
let maxColumn = Math.max(cellAMap.startColumn + cellAMap.cell.__colSpan - 1, cellBMap.startColumn + cellBMap.cell.__colSpan - 1);
|
1119
|
+
let maxRow = Math.max(cellAMap.startRow + cellAMap.cell.__rowSpan - 1, cellBMap.startRow + cellBMap.cell.__rowSpan - 1);
|
1120
|
+
let exploredMinColumn = minColumn;
|
1121
|
+
let exploredMinRow = minRow;
|
1122
|
+
let exploredMaxColumn = minColumn;
|
1123
|
+
let exploredMaxRow = minRow;
|
1124
|
+
function expandBoundary(mapValue) {
|
1125
|
+
const {
|
1126
|
+
cell,
|
1127
|
+
startColumn: cellStartColumn,
|
1128
|
+
startRow: cellStartRow
|
1129
|
+
} = mapValue;
|
1130
|
+
minColumn = Math.min(minColumn, cellStartColumn);
|
1131
|
+
minRow = Math.min(minRow, cellStartRow);
|
1132
|
+
maxColumn = Math.max(maxColumn, cellStartColumn + cell.__colSpan - 1);
|
1133
|
+
maxRow = Math.max(maxRow, cellStartRow + cell.__rowSpan - 1);
|
1134
|
+
}
|
1135
|
+
while (minColumn < exploredMinColumn || minRow < exploredMinRow || maxColumn > exploredMaxColumn || maxRow > exploredMaxRow) {
|
1136
|
+
if (minColumn < exploredMinColumn) {
|
1137
|
+
// Expand on the left
|
1138
|
+
const rowDiff = exploredMaxRow - exploredMinRow;
|
1139
|
+
const previousColumn = exploredMinColumn - 1;
|
1140
|
+
for (let i = 0; i <= rowDiff; i++) {
|
1141
|
+
expandBoundary(map[exploredMinRow + i][previousColumn]);
|
1142
|
+
}
|
1143
|
+
exploredMinColumn = previousColumn;
|
1144
|
+
}
|
1145
|
+
if (minRow < exploredMinRow) {
|
1146
|
+
// Expand on top
|
1147
|
+
const columnDiff = exploredMaxColumn - exploredMinColumn;
|
1148
|
+
const previousRow = exploredMinRow - 1;
|
1149
|
+
for (let i = 0; i <= columnDiff; i++) {
|
1150
|
+
expandBoundary(map[previousRow][exploredMinColumn + i]);
|
1151
|
+
}
|
1152
|
+
exploredMinRow = previousRow;
|
1153
|
+
}
|
1154
|
+
if (maxColumn > exploredMaxColumn) {
|
1155
|
+
// Expand on the right
|
1156
|
+
const rowDiff = exploredMaxRow - exploredMinRow;
|
1157
|
+
const nextColumn = exploredMaxColumn + 1;
|
1158
|
+
for (let i = 0; i <= rowDiff; i++) {
|
1159
|
+
expandBoundary(map[exploredMinRow + i][nextColumn]);
|
1160
|
+
}
|
1161
|
+
exploredMaxColumn = nextColumn;
|
1162
|
+
}
|
1163
|
+
if (maxRow > exploredMaxRow) {
|
1164
|
+
// Expand on the bottom
|
1165
|
+
const columnDiff = exploredMaxColumn - exploredMinColumn;
|
1166
|
+
const nextRow = exploredMaxRow + 1;
|
1167
|
+
for (let i = 0; i <= columnDiff; i++) {
|
1168
|
+
expandBoundary(map[nextRow][exploredMinColumn + i]);
|
1169
|
+
}
|
1170
|
+
exploredMaxRow = nextRow;
|
1171
|
+
}
|
1172
|
+
}
|
1173
|
+
return {
|
1174
|
+
maxColumn,
|
1175
|
+
maxRow,
|
1176
|
+
minColumn,
|
1177
|
+
minRow
|
1178
|
+
};
|
1179
|
+
}
|
1082
1180
|
function $getTableCellNodeRect(tableCellNode) {
|
1083
1181
|
const [cellNode,, gridNode] = $getNodeTriplet(tableCellNode);
|
1084
1182
|
const rows = gridNode.getChildren();
|
@@ -1133,6 +1231,38 @@ function $getTableCellNodeRect(tableCellNode) {
|
|
1133
1231
|
*
|
1134
1232
|
*/
|
1135
1233
|
|
1234
|
+
function $getCellNodes(tableSelection) {
|
1235
|
+
const [[anchorNode, anchorCell, anchorRow, anchorTable], [focusNode, focusCell, focusRow, focusTable]] = ['anchor', 'focus'].map(k => {
|
1236
|
+
const node = tableSelection[k].getNode();
|
1237
|
+
const cellNode = utils.$findMatchingParent(node, $isTableCellNode);
|
1238
|
+
if (!$isTableCellNode(cellNode)) {
|
1239
|
+
throw Error(`Expected TableSelection ${k} to be (or a child of) TableCellNode, got key ${node.getKey()} of type ${node.getType()}`);
|
1240
|
+
}
|
1241
|
+
const rowNode = cellNode.getParent();
|
1242
|
+
if (!$isTableRowNode(rowNode)) {
|
1243
|
+
throw Error(`Expected TableSelection ${k} cell parent to be a TableRowNode`);
|
1244
|
+
}
|
1245
|
+
const tableNode = rowNode.getParent();
|
1246
|
+
if (!$isTableNode(tableNode)) {
|
1247
|
+
throw Error(`Expected TableSelection ${k} row parent to be a TableNode`);
|
1248
|
+
}
|
1249
|
+
return [node, cellNode, rowNode, tableNode];
|
1250
|
+
});
|
1251
|
+
// TODO: nested tables may violate this
|
1252
|
+
if (!anchorTable.is(focusTable)) {
|
1253
|
+
throw Error(`Expected TableSelection anchor and focus to be in the same table`);
|
1254
|
+
}
|
1255
|
+
return {
|
1256
|
+
anchorCell,
|
1257
|
+
anchorNode,
|
1258
|
+
anchorRow,
|
1259
|
+
anchorTable,
|
1260
|
+
focusCell,
|
1261
|
+
focusNode,
|
1262
|
+
focusRow,
|
1263
|
+
focusTable
|
1264
|
+
};
|
1265
|
+
}
|
1136
1266
|
class TableSelection {
|
1137
1267
|
constructor(tableKey, anchor, focus) {
|
1138
1268
|
this.anchor = anchor;
|
@@ -1147,6 +1277,17 @@ class TableSelection {
|
|
1147
1277
|
return [this.anchor, this.focus];
|
1148
1278
|
}
|
1149
1279
|
|
1280
|
+
/**
|
1281
|
+
* {@link $createTableSelection} unfortunately makes it very easy to create
|
1282
|
+
* nonsense selections, so we have a method to see if the selection probably
|
1283
|
+
* makes sense.
|
1284
|
+
*
|
1285
|
+
* @returns true if the TableSelection is (probably) valid
|
1286
|
+
*/
|
1287
|
+
isValid() {
|
1288
|
+
return this.tableKey !== 'root' && this.anchor.key !== 'root' && this.anchor.type === 'element' && this.focus.key !== 'root' && this.focus.type === 'element';
|
1289
|
+
}
|
1290
|
+
|
1150
1291
|
/**
|
1151
1292
|
* Returns whether the Selection is "backwards", meaning the focus
|
1152
1293
|
* logically precedes the anchor in the EditorState.
|
@@ -1162,20 +1303,18 @@ class TableSelection {
|
|
1162
1303
|
this._cachedNodes = nodes;
|
1163
1304
|
}
|
1164
1305
|
is(selection) {
|
1165
|
-
|
1166
|
-
return false;
|
1167
|
-
}
|
1168
|
-
return this.tableKey === selection.tableKey && this.anchor.is(selection.anchor) && this.focus.is(selection.focus);
|
1306
|
+
return $isTableSelection(selection) && this.tableKey === selection.tableKey && this.anchor.is(selection.anchor) && this.focus.is(selection.focus);
|
1169
1307
|
}
|
1170
1308
|
set(tableKey, anchorCellKey, focusCellKey) {
|
1171
|
-
|
1309
|
+
// note: closure compiler's acorn does not support ||=
|
1310
|
+
this.dirty = this.dirty || tableKey !== this.tableKey || anchorCellKey !== this.anchor.key || focusCellKey !== this.focus.key;
|
1172
1311
|
this.tableKey = tableKey;
|
1173
1312
|
this.anchor.key = anchorCellKey;
|
1174
1313
|
this.focus.key = focusCellKey;
|
1175
1314
|
this._cachedNodes = null;
|
1176
1315
|
}
|
1177
1316
|
clone() {
|
1178
|
-
return new TableSelection(this.tableKey, this.anchor, this.focus);
|
1317
|
+
return new TableSelection(this.tableKey, lexical.$createPoint(this.anchor.key, this.anchor.offset, this.anchor.type), lexical.$createPoint(this.focus.key, this.focus.offset, this.focus.type));
|
1179
1318
|
}
|
1180
1319
|
isCollapsed() {
|
1181
1320
|
return false;
|
@@ -1220,19 +1359,15 @@ class TableSelection {
|
|
1220
1359
|
|
1221
1360
|
// TODO Deprecate this method. It's confusing when used with colspan|rowspan
|
1222
1361
|
getShape() {
|
1223
|
-
const
|
1224
|
-
|
1225
|
-
|
1226
|
-
}
|
1227
|
-
const anchorCellNodeRect = $getTableCellNodeRect(
|
1362
|
+
const {
|
1363
|
+
anchorCell,
|
1364
|
+
focusCell
|
1365
|
+
} = $getCellNodes(this);
|
1366
|
+
const anchorCellNodeRect = $getTableCellNodeRect(anchorCell);
|
1228
1367
|
if (!(anchorCellNodeRect !== null)) {
|
1229
1368
|
throw Error(`getCellRect: expected to find AnchorNode`);
|
1230
1369
|
}
|
1231
|
-
const
|
1232
|
-
if (!$isTableCellNode(focusCellNode)) {
|
1233
|
-
throw Error(`Expected TableSelection focus to be (or a child of) TableCellNode`);
|
1234
|
-
}
|
1235
|
-
const focusCellNodeRect = $getTableCellNodeRect(focusCellNode);
|
1370
|
+
const focusCellNodeRect = $getTableCellNodeRect(focusCell);
|
1236
1371
|
if (!(focusCellNodeRect !== null)) {
|
1237
1372
|
throw Error(`getCellRect: expected to find focusCellNode`);
|
1238
1373
|
}
|
@@ -1248,29 +1383,18 @@ class TableSelection {
|
|
1248
1383
|
};
|
1249
1384
|
}
|
1250
1385
|
getNodes() {
|
1386
|
+
if (!this.isValid()) {
|
1387
|
+
return [];
|
1388
|
+
}
|
1251
1389
|
const cachedNodes = this._cachedNodes;
|
1252
1390
|
if (cachedNodes !== null) {
|
1253
1391
|
return cachedNodes;
|
1254
1392
|
}
|
1255
|
-
const
|
1256
|
-
|
1257
|
-
|
1258
|
-
|
1259
|
-
|
1260
|
-
if (!$isTableCellNode(anchorCell)) {
|
1261
|
-
throw Error(`Expected TableSelection anchor to be (or a child of) TableCellNode`);
|
1262
|
-
}
|
1263
|
-
if (!$isTableCellNode(focusCell)) {
|
1264
|
-
throw Error(`Expected TableSelection focus to be (or a child of) TableCellNode`);
|
1265
|
-
}
|
1266
|
-
const anchorRow = anchorCell.getParent();
|
1267
|
-
if (!$isTableRowNode(anchorRow)) {
|
1268
|
-
throw Error(`Expected anchorCell to have a parent TableRowNode`);
|
1269
|
-
}
|
1270
|
-
const tableNode = anchorRow.getParent();
|
1271
|
-
if (!$isTableNode(tableNode)) {
|
1272
|
-
throw Error(`Expected tableNode to have a parent TableNode`);
|
1273
|
-
}
|
1393
|
+
const {
|
1394
|
+
anchorTable: tableNode,
|
1395
|
+
anchorCell,
|
1396
|
+
focusCell
|
1397
|
+
} = $getCellNodes(this);
|
1274
1398
|
const focusCellGrid = focusCell.getParents()[1];
|
1275
1399
|
if (focusCellGrid !== tableNode) {
|
1276
1400
|
if (!tableNode.isParentOf(focusCell)) {
|
@@ -1296,63 +1420,12 @@ class TableSelection {
|
|
1296
1420
|
// ability to store a state. Killing TableSelection and moving the logic to the plugin would make
|
1297
1421
|
// this possible.
|
1298
1422
|
const [map, cellAMap, cellBMap] = $computeTableMap(tableNode, anchorCell, focusCell);
|
1299
|
-
|
1300
|
-
|
1301
|
-
|
1302
|
-
|
1303
|
-
|
1304
|
-
|
1305
|
-
let exploredMaxColumn = minColumn;
|
1306
|
-
let exploredMaxRow = minRow;
|
1307
|
-
function expandBoundary(mapValue) {
|
1308
|
-
const {
|
1309
|
-
cell,
|
1310
|
-
startColumn: cellStartColumn,
|
1311
|
-
startRow: cellStartRow
|
1312
|
-
} = mapValue;
|
1313
|
-
minColumn = Math.min(minColumn, cellStartColumn);
|
1314
|
-
minRow = Math.min(minRow, cellStartRow);
|
1315
|
-
maxColumn = Math.max(maxColumn, cellStartColumn + cell.__colSpan - 1);
|
1316
|
-
maxRow = Math.max(maxRow, cellStartRow + cell.__rowSpan - 1);
|
1317
|
-
}
|
1318
|
-
while (minColumn < exploredMinColumn || minRow < exploredMinRow || maxColumn > exploredMaxColumn || maxRow > exploredMaxRow) {
|
1319
|
-
if (minColumn < exploredMinColumn) {
|
1320
|
-
// Expand on the left
|
1321
|
-
const rowDiff = exploredMaxRow - exploredMinRow;
|
1322
|
-
const previousColumn = exploredMinColumn - 1;
|
1323
|
-
for (let i = 0; i <= rowDiff; i++) {
|
1324
|
-
expandBoundary(map[exploredMinRow + i][previousColumn]);
|
1325
|
-
}
|
1326
|
-
exploredMinColumn = previousColumn;
|
1327
|
-
}
|
1328
|
-
if (minRow < exploredMinRow) {
|
1329
|
-
// Expand on top
|
1330
|
-
const columnDiff = exploredMaxColumn - exploredMinColumn;
|
1331
|
-
const previousRow = exploredMinRow - 1;
|
1332
|
-
for (let i = 0; i <= columnDiff; i++) {
|
1333
|
-
expandBoundary(map[previousRow][exploredMinColumn + i]);
|
1334
|
-
}
|
1335
|
-
exploredMinRow = previousRow;
|
1336
|
-
}
|
1337
|
-
if (maxColumn > exploredMaxColumn) {
|
1338
|
-
// Expand on the right
|
1339
|
-
const rowDiff = exploredMaxRow - exploredMinRow;
|
1340
|
-
const nextColumn = exploredMaxColumn + 1;
|
1341
|
-
for (let i = 0; i <= rowDiff; i++) {
|
1342
|
-
expandBoundary(map[exploredMinRow + i][nextColumn]);
|
1343
|
-
}
|
1344
|
-
exploredMaxColumn = nextColumn;
|
1345
|
-
}
|
1346
|
-
if (maxRow > exploredMaxRow) {
|
1347
|
-
// Expand on the bottom
|
1348
|
-
const columnDiff = exploredMaxColumn - exploredMinColumn;
|
1349
|
-
const nextRow = exploredMaxRow + 1;
|
1350
|
-
for (let i = 0; i <= columnDiff; i++) {
|
1351
|
-
expandBoundary(map[nextRow][exploredMinColumn + i]);
|
1352
|
-
}
|
1353
|
-
exploredMaxRow = nextRow;
|
1354
|
-
}
|
1355
|
-
}
|
1423
|
+
const {
|
1424
|
+
minColumn,
|
1425
|
+
maxColumn,
|
1426
|
+
minRow,
|
1427
|
+
maxRow
|
1428
|
+
} = $computeTableCellRectBoundary(map, cellAMap, cellBMap);
|
1356
1429
|
|
1357
1430
|
// We use a Map here because merged cells in the grid would otherwise
|
1358
1431
|
// show up multiple times in the nodes array
|
@@ -1369,12 +1442,13 @@ class TableSelection {
|
|
1369
1442
|
}
|
1370
1443
|
if (currentRow !== lastRow) {
|
1371
1444
|
nodeMap.set(currentRow.getKey(), currentRow);
|
1445
|
+
lastRow = currentRow;
|
1372
1446
|
}
|
1373
|
-
nodeMap.
|
1374
|
-
|
1375
|
-
|
1447
|
+
if (!nodeMap.has(cell.getKey())) {
|
1448
|
+
$visitRecursively(cell, childNode => {
|
1449
|
+
nodeMap.set(childNode.getKey(), childNode);
|
1450
|
+
});
|
1376
1451
|
}
|
1377
|
-
lastRow = currentRow;
|
1378
1452
|
}
|
1379
1453
|
}
|
1380
1454
|
const nodes = Array.from(nodeMap.values());
|
@@ -1399,26 +1473,51 @@ function $isTableSelection(x) {
|
|
1399
1473
|
return x instanceof TableSelection;
|
1400
1474
|
}
|
1401
1475
|
function $createTableSelection() {
|
1476
|
+
// TODO this is a suboptimal design, it doesn't make sense to have
|
1477
|
+
// a table selection that isn't associated with a table. This
|
1478
|
+
// constructor should have required argumnets and in true we
|
1479
|
+
// should check that they point to a table and are element points to
|
1480
|
+
// cell nodes of that table.
|
1402
1481
|
const anchor = lexical.$createPoint('root', 0, 'element');
|
1403
1482
|
const focus = lexical.$createPoint('root', 0, 'element');
|
1404
1483
|
return new TableSelection('root', anchor, focus);
|
1405
1484
|
}
|
1406
|
-
function $
|
1407
|
-
const
|
1408
|
-
const
|
1409
|
-
|
1410
|
-
|
1411
|
-
if (!(
|
1412
|
-
throw Error(
|
1485
|
+
function $createTableSelectionFrom(tableNode, anchorCell, focusCell) {
|
1486
|
+
const tableNodeKey = tableNode.getKey();
|
1487
|
+
const anchorCellKey = anchorCell.getKey();
|
1488
|
+
const focusCellKey = focusCell.getKey();
|
1489
|
+
{
|
1490
|
+
if (!tableNode.isAttached()) {
|
1491
|
+
throw Error(`$createTableSelectionFrom: tableNode ${tableNodeKey} is not attached`);
|
1413
1492
|
}
|
1414
|
-
if (
|
1415
|
-
|
1493
|
+
if (!tableNode.is($findTableNode(anchorCell))) {
|
1494
|
+
throw Error(`$createTableSelectionFrom: anchorCell ${anchorCellKey} is not in table ${tableNodeKey}`);
|
1416
1495
|
}
|
1417
|
-
if (
|
1418
|
-
|
1496
|
+
if (!tableNode.is($findTableNode(focusCell))) {
|
1497
|
+
throw Error(`$createTableSelectionFrom: focusCell ${focusCellKey} is not in table ${tableNodeKey}`);
|
1498
|
+
} // TODO: Check for rectangular grid
|
1499
|
+
}
|
1500
|
+
const prevSelection = lexical.$getSelection();
|
1501
|
+
const nextSelection = $isTableSelection(prevSelection) ? prevSelection.clone() : $createTableSelection();
|
1502
|
+
nextSelection.set(tableNode.getKey(), anchorCell.getKey(), focusCell.getKey());
|
1503
|
+
return nextSelection;
|
1504
|
+
}
|
1505
|
+
|
1506
|
+
/**
|
1507
|
+
* Depth first visitor
|
1508
|
+
* @param node The starting node
|
1509
|
+
* @param $visit The function to call for each node. If the function returns false, then children of this node will not be explored
|
1510
|
+
*/
|
1511
|
+
function $visitRecursively(node, $visit) {
|
1512
|
+
const stack = [[node]];
|
1513
|
+
for (let currentArray = stack.at(-1); currentArray !== undefined && stack.length > 0; currentArray = stack.at(-1)) {
|
1514
|
+
const currentNode = currentArray.pop();
|
1515
|
+
if (currentNode === undefined) {
|
1516
|
+
stack.pop();
|
1517
|
+
} else if ($visit(currentNode) !== false && lexical.$isElementNode(currentNode)) {
|
1518
|
+
stack.push(currentNode.getChildren());
|
1419
1519
|
}
|
1420
1520
|
}
|
1421
|
-
return nodes;
|
1422
1521
|
}
|
1423
1522
|
|
1424
1523
|
/**
|
@@ -1470,6 +1569,7 @@ class TableObserver {
|
|
1470
1569
|
this.listenerOptions = {
|
1471
1570
|
signal: this.abortController.signal
|
1472
1571
|
};
|
1572
|
+
this.nextFocus = null;
|
1473
1573
|
this.trackTable();
|
1474
1574
|
}
|
1475
1575
|
getTable() {
|
@@ -1523,7 +1623,7 @@ class TableObserver {
|
|
1523
1623
|
editor: this.editor
|
1524
1624
|
});
|
1525
1625
|
}
|
1526
|
-
clearHighlight() {
|
1626
|
+
$clearHighlight() {
|
1527
1627
|
const editor = this.editor;
|
1528
1628
|
this.isHighlightingCells = false;
|
1529
1629
|
this.anchorX = -1;
|
@@ -1536,55 +1636,47 @@ class TableObserver {
|
|
1536
1636
|
this.anchorCell = null;
|
1537
1637
|
this.focusCell = null;
|
1538
1638
|
this.hasHijackedSelectionStyles = false;
|
1539
|
-
this
|
1540
|
-
|
1541
|
-
|
1542
|
-
|
1543
|
-
|
1544
|
-
|
1545
|
-
|
1546
|
-
|
1639
|
+
this.$enableHighlightStyle();
|
1640
|
+
const {
|
1641
|
+
tableNode,
|
1642
|
+
tableElement
|
1643
|
+
} = this.$lookup();
|
1644
|
+
const grid = getTable(tableNode, tableElement);
|
1645
|
+
$updateDOMForSelection(editor, grid, null);
|
1646
|
+
if (lexical.$getSelection() !== null) {
|
1547
1647
|
lexical.$setSelection(null);
|
1548
1648
|
editor.dispatchCommand(lexical.SELECTION_CHANGE_COMMAND, undefined);
|
1549
|
-
}
|
1649
|
+
}
|
1550
1650
|
}
|
1551
|
-
enableHighlightStyle() {
|
1651
|
+
$enableHighlightStyle() {
|
1552
1652
|
const editor = this.editor;
|
1553
|
-
|
1554
|
-
|
1555
|
-
|
1556
|
-
|
1557
|
-
|
1558
|
-
|
1559
|
-
this.hasHijackedSelectionStyles = false;
|
1560
|
-
}, {
|
1561
|
-
editor
|
1562
|
-
});
|
1653
|
+
const {
|
1654
|
+
tableElement
|
1655
|
+
} = this.$lookup();
|
1656
|
+
utils.removeClassNamesFromElement(tableElement, editor._config.theme.tableSelection);
|
1657
|
+
tableElement.classList.remove('disable-selection');
|
1658
|
+
this.hasHijackedSelectionStyles = false;
|
1563
1659
|
}
|
1564
|
-
disableHighlightStyle() {
|
1565
|
-
const
|
1566
|
-
|
1567
|
-
|
1568
|
-
|
1569
|
-
|
1570
|
-
utils.addClassNamesToElement(tableElement, editor._config.theme.tableSelection);
|
1571
|
-
this.hasHijackedSelectionStyles = true;
|
1572
|
-
}, {
|
1573
|
-
editor
|
1574
|
-
});
|
1660
|
+
$disableHighlightStyle() {
|
1661
|
+
const {
|
1662
|
+
tableElement
|
1663
|
+
} = this.$lookup();
|
1664
|
+
utils.addClassNamesToElement(tableElement, this.editor._config.theme.tableSelection);
|
1665
|
+
this.hasHijackedSelectionStyles = true;
|
1575
1666
|
}
|
1576
|
-
updateTableTableSelection(selection) {
|
1577
|
-
if (selection !== null
|
1667
|
+
$updateTableTableSelection(selection) {
|
1668
|
+
if (selection !== null) {
|
1669
|
+
if (!(selection.tableKey === this.tableNodeKey)) {
|
1670
|
+
throw Error(`TableObserver.$updateTableTableSelection: selection.tableKey !== this.tableNodeKey ('${selection.tableKey}' !== '${this.tableNodeKey}')`);
|
1671
|
+
}
|
1578
1672
|
const editor = this.editor;
|
1579
1673
|
this.tableSelection = selection;
|
1580
1674
|
this.isHighlightingCells = true;
|
1581
|
-
this
|
1675
|
+
this.$disableHighlightStyle();
|
1676
|
+
this.updateDOMSelection();
|
1582
1677
|
$updateDOMForSelection(editor, this.table, this.tableSelection);
|
1583
|
-
} else if (selection == null) {
|
1584
|
-
this.clearHighlight();
|
1585
1678
|
} else {
|
1586
|
-
this
|
1587
|
-
this.updateTableTableSelection(selection);
|
1679
|
+
this.$clearHighlight();
|
1588
1680
|
}
|
1589
1681
|
}
|
1590
1682
|
|
@@ -1610,120 +1702,165 @@ class TableObserver {
|
|
1610
1702
|
}
|
1611
1703
|
return false;
|
1612
1704
|
}
|
1613
|
-
|
1614
|
-
|
1615
|
-
|
1616
|
-
|
1617
|
-
|
1618
|
-
|
1619
|
-
|
1620
|
-
|
1621
|
-
|
1622
|
-
|
1623
|
-
|
1624
|
-
|
1625
|
-
|
1626
|
-
|
1627
|
-
|
1628
|
-
|
1629
|
-
|
1630
|
-
|
1631
|
-
|
1632
|
-
|
1633
|
-
|
1705
|
+
|
1706
|
+
/**
|
1707
|
+
* @internal
|
1708
|
+
* When handling mousemove events we track what the focus cell should be, but
|
1709
|
+
* the DOM selection may end up somewhere else entirely. We don't have an elegant
|
1710
|
+
* way to handle this after the DOM selection has been resolved in a
|
1711
|
+
* SELECTION_CHANGE_COMMAND callback.
|
1712
|
+
*/
|
1713
|
+
setNextFocus(nextFocus) {
|
1714
|
+
this.nextFocus = nextFocus;
|
1715
|
+
}
|
1716
|
+
|
1717
|
+
/** @internal */
|
1718
|
+
getAndClearNextFocus() {
|
1719
|
+
const {
|
1720
|
+
nextFocus
|
1721
|
+
} = this;
|
1722
|
+
if (nextFocus !== null) {
|
1723
|
+
this.nextFocus = null;
|
1724
|
+
}
|
1725
|
+
return nextFocus;
|
1726
|
+
}
|
1727
|
+
|
1728
|
+
/** @internal */
|
1729
|
+
updateDOMSelection() {
|
1730
|
+
if (this.anchorCell !== null && this.focusCell !== null) {
|
1731
|
+
const domSelection = lexical.getDOMSelection(this.editor._window);
|
1732
|
+
// We are not using a native selection for tables, and if we
|
1733
|
+
// set one then the reconciler will undo it.
|
1734
|
+
// TODO - it would make sense to have one so that native
|
1735
|
+
// copy/paste worked. Right now we have to emulate with
|
1736
|
+
// keyboard events but it won't fire if trigged from the menu
|
1737
|
+
if (domSelection && domSelection.rangeCount > 0) {
|
1738
|
+
domSelection.removeAllRanges();
|
1634
1739
|
}
|
1635
|
-
|
1636
|
-
|
1637
|
-
|
1638
|
-
|
1639
|
-
|
1640
|
-
|
1641
|
-
|
1642
|
-
|
1643
|
-
|
1644
|
-
|
1645
|
-
|
1646
|
-
|
1647
|
-
|
1740
|
+
}
|
1741
|
+
}
|
1742
|
+
$setFocusCellForSelection(cell, ignoreStart = false) {
|
1743
|
+
const editor = this.editor;
|
1744
|
+
const {
|
1745
|
+
tableNode
|
1746
|
+
} = this.$lookup();
|
1747
|
+
const cellX = cell.x;
|
1748
|
+
const cellY = cell.y;
|
1749
|
+
this.focusCell = cell;
|
1750
|
+
if (!this.isHighlightingCells && (this.anchorX !== cellX || this.anchorY !== cellY || ignoreStart)) {
|
1751
|
+
this.isHighlightingCells = true;
|
1752
|
+
this.$disableHighlightStyle();
|
1753
|
+
} else if (cellX === this.focusX && cellY === this.focusY) {
|
1754
|
+
return false;
|
1755
|
+
}
|
1756
|
+
this.focusX = cellX;
|
1757
|
+
this.focusY = cellY;
|
1758
|
+
if (this.isHighlightingCells) {
|
1759
|
+
const focusTableCellNode = lexical.$getNearestNodeFromDOMNode(cell.elem);
|
1760
|
+
if (this.tableSelection != null && this.anchorCellNodeKey != null && $isTableCellNode(focusTableCellNode) && tableNode.is($findTableNode(focusTableCellNode))) {
|
1761
|
+
this.focusCellNodeKey = focusTableCellNode.getKey();
|
1762
|
+
this.tableSelection = $createTableSelectionFrom(tableNode, this.$getAnchorTableCellOrThrow(), focusTableCellNode);
|
1763
|
+
lexical.$setSelection(this.tableSelection);
|
1764
|
+
editor.dispatchCommand(lexical.SELECTION_CHANGE_COMMAND, undefined);
|
1765
|
+
$updateDOMForSelection(editor, this.table, this.tableSelection);
|
1766
|
+
return true;
|
1648
1767
|
}
|
1649
|
-
}
|
1768
|
+
}
|
1769
|
+
return false;
|
1770
|
+
}
|
1771
|
+
$getAnchorTableCell() {
|
1772
|
+
return this.anchorCellNodeKey ? lexical.$getNodeByKey(this.anchorCellNodeKey) : null;
|
1773
|
+
}
|
1774
|
+
$getAnchorTableCellOrThrow() {
|
1775
|
+
const anchorTableCell = this.$getAnchorTableCell();
|
1776
|
+
if (!(anchorTableCell !== null)) {
|
1777
|
+
throw Error(`TableObserver anchorTableCell is null`);
|
1778
|
+
}
|
1779
|
+
return anchorTableCell;
|
1780
|
+
}
|
1781
|
+
$getFocusTableCell() {
|
1782
|
+
return this.focusCellNodeKey ? lexical.$getNodeByKey(this.focusCellNodeKey) : null;
|
1783
|
+
}
|
1784
|
+
$getFocusTableCellOrThrow() {
|
1785
|
+
const focusTableCell = this.$getFocusTableCell();
|
1786
|
+
if (!(focusTableCell !== null)) {
|
1787
|
+
throw Error(`TableObserver focusTableCell is null`);
|
1788
|
+
}
|
1789
|
+
return focusTableCell;
|
1650
1790
|
}
|
1651
|
-
setAnchorCellForSelection(cell) {
|
1791
|
+
$setAnchorCellForSelection(cell) {
|
1652
1792
|
this.isHighlightingCells = false;
|
1653
1793
|
this.anchorCell = cell;
|
1654
1794
|
this.anchorX = cell.x;
|
1655
1795
|
this.anchorY = cell.y;
|
1656
|
-
|
1657
|
-
|
1658
|
-
|
1659
|
-
|
1660
|
-
|
1661
|
-
|
1662
|
-
}
|
1663
|
-
});
|
1796
|
+
const anchorTableCellNode = lexical.$getNearestNodeFromDOMNode(cell.elem);
|
1797
|
+
if ($isTableCellNode(anchorTableCellNode)) {
|
1798
|
+
const anchorNodeKey = anchorTableCellNode.getKey();
|
1799
|
+
this.tableSelection = this.tableSelection != null ? this.tableSelection.clone() : $createTableSelection();
|
1800
|
+
this.anchorCellNodeKey = anchorNodeKey;
|
1801
|
+
}
|
1664
1802
|
}
|
1665
|
-
formatCells(type) {
|
1666
|
-
|
1667
|
-
|
1668
|
-
|
1669
|
-
|
1670
|
-
|
1671
|
-
|
1672
|
-
|
1673
|
-
|
1674
|
-
|
1675
|
-
|
1676
|
-
|
1677
|
-
|
1678
|
-
|
1679
|
-
|
1680
|
-
|
1681
|
-
|
1682
|
-
|
1683
|
-
});
|
1684
|
-
lexical.$setSelection(selection);
|
1685
|
-
this.editor.dispatchCommand(lexical.SELECTION_CHANGE_COMMAND, undefined);
|
1803
|
+
$formatCells(type) {
|
1804
|
+
const selection = lexical.$getSelection();
|
1805
|
+
if (!$isTableSelection(selection)) {
|
1806
|
+
throw Error(`Expected Table selection`);
|
1807
|
+
}
|
1808
|
+
const formatSelection = lexical.$createRangeSelection();
|
1809
|
+
const anchor = formatSelection.anchor;
|
1810
|
+
const focus = formatSelection.focus;
|
1811
|
+
const cellNodes = selection.getNodes().filter($isTableCellNode);
|
1812
|
+
if (!(cellNodes.length > 0)) {
|
1813
|
+
throw Error(`No table cells present`);
|
1814
|
+
}
|
1815
|
+
const paragraph = cellNodes[0].getFirstChild();
|
1816
|
+
const alignFormatWith = lexical.$isParagraphNode(paragraph) ? paragraph.getFormatFlags(type, null) : null;
|
1817
|
+
cellNodes.forEach(cellNode => {
|
1818
|
+
anchor.set(cellNode.getKey(), 0, 'element');
|
1819
|
+
focus.set(cellNode.getKey(), cellNode.getChildrenSize(), 'element');
|
1820
|
+
formatSelection.formatText(type, alignFormatWith);
|
1686
1821
|
});
|
1822
|
+
lexical.$setSelection(selection);
|
1823
|
+
this.editor.dispatchCommand(lexical.SELECTION_CHANGE_COMMAND, undefined);
|
1687
1824
|
}
|
1688
|
-
clearText() {
|
1689
|
-
const
|
1690
|
-
|
1691
|
-
|
1692
|
-
|
1693
|
-
|
1694
|
-
|
1695
|
-
|
1696
|
-
|
1697
|
-
|
1698
|
-
|
1699
|
-
|
1825
|
+
$clearText() {
|
1826
|
+
const {
|
1827
|
+
editor
|
1828
|
+
} = this;
|
1829
|
+
const tableNode = lexical.$getNodeByKey(this.tableNodeKey);
|
1830
|
+
if (!$isTableNode(tableNode)) {
|
1831
|
+
throw new Error('Expected TableNode.');
|
1832
|
+
}
|
1833
|
+
const selection = lexical.$getSelection();
|
1834
|
+
if (!$isTableSelection(selection)) {
|
1835
|
+
{
|
1836
|
+
throw Error(`Expected grid selection`);
|
1700
1837
|
}
|
1701
|
-
|
1702
|
-
|
1703
|
-
|
1704
|
-
|
1705
|
-
|
1706
|
-
|
1707
|
-
|
1708
|
-
|
1838
|
+
}
|
1839
|
+
const selectedNodes = selection.getNodes().filter($isTableCellNode);
|
1840
|
+
if (selectedNodes.length === this.table.columns * this.table.rows) {
|
1841
|
+
tableNode.selectPrevious();
|
1842
|
+
// Delete entire table
|
1843
|
+
tableNode.remove();
|
1844
|
+
const rootNode = lexical.$getRoot();
|
1845
|
+
rootNode.selectStart();
|
1846
|
+
return;
|
1847
|
+
}
|
1848
|
+
selectedNodes.forEach(cellNode => {
|
1849
|
+
if (lexical.$isElementNode(cellNode)) {
|
1850
|
+
const paragraphNode = lexical.$createParagraphNode();
|
1851
|
+
const textNode = lexical.$createTextNode();
|
1852
|
+
paragraphNode.append(textNode);
|
1853
|
+
cellNode.append(paragraphNode);
|
1854
|
+
cellNode.getChildren().forEach(child => {
|
1855
|
+
if (child !== paragraphNode) {
|
1856
|
+
child.remove();
|
1857
|
+
}
|
1858
|
+
});
|
1709
1859
|
}
|
1710
|
-
selectedNodes.forEach(cellNode => {
|
1711
|
-
if (lexical.$isElementNode(cellNode)) {
|
1712
|
-
const paragraphNode = lexical.$createParagraphNode();
|
1713
|
-
const textNode = lexical.$createTextNode();
|
1714
|
-
paragraphNode.append(textNode);
|
1715
|
-
cellNode.append(paragraphNode);
|
1716
|
-
cellNode.getChildren().forEach(child => {
|
1717
|
-
if (child !== paragraphNode) {
|
1718
|
-
child.remove();
|
1719
|
-
}
|
1720
|
-
});
|
1721
|
-
}
|
1722
|
-
});
|
1723
|
-
$updateDOMForSelection(editor, this.table, null);
|
1724
|
-
lexical.$setSelection(null);
|
1725
|
-
editor.dispatchCommand(lexical.SELECTION_CHANGE_COMMAND, undefined);
|
1726
1860
|
});
|
1861
|
+
$updateDOMForSelection(editor, this.table, null);
|
1862
|
+
lexical.$setSelection(null);
|
1863
|
+
editor.dispatchCommand(lexical.SELECTION_CHANGE_COMMAND, undefined);
|
1727
1864
|
}
|
1728
1865
|
}
|
1729
1866
|
|
@@ -1736,7 +1873,6 @@ class TableObserver {
|
|
1736
1873
|
*/
|
1737
1874
|
|
1738
1875
|
const LEXICAL_ELEMENT_KEY = '__lexicalTableSelection';
|
1739
|
-
const getDOMSelection = targetWindow => CAN_USE_DOM ? (targetWindow || window).getSelection() : null;
|
1740
1876
|
const isMouseDownOnEvent = event => {
|
1741
1877
|
return (event.buttons & 1) === 1;
|
1742
1878
|
};
|
@@ -1750,64 +1886,103 @@ function getTableElement(tableNode, dom) {
|
|
1750
1886
|
}
|
1751
1887
|
return element;
|
1752
1888
|
}
|
1889
|
+
function getEditorWindow(editor) {
|
1890
|
+
return editor._window;
|
1891
|
+
}
|
1892
|
+
function $findParentTableCellNodeInTable(tableNode, node) {
|
1893
|
+
for (let currentNode = node, lastTableCellNode = null; currentNode !== null; currentNode = currentNode.getParent()) {
|
1894
|
+
if (tableNode.is(currentNode)) {
|
1895
|
+
return lastTableCellNode;
|
1896
|
+
} else if ($isTableCellNode(currentNode)) {
|
1897
|
+
lastTableCellNode = currentNode;
|
1898
|
+
}
|
1899
|
+
}
|
1900
|
+
return null;
|
1901
|
+
}
|
1902
|
+
const ARROW_KEY_COMMANDS_WITH_DIRECTION = [[lexical.KEY_ARROW_DOWN_COMMAND, 'down'], [lexical.KEY_ARROW_UP_COMMAND, 'up'], [lexical.KEY_ARROW_LEFT_COMMAND, 'backward'], [lexical.KEY_ARROW_RIGHT_COMMAND, 'forward']];
|
1903
|
+
const DELETE_TEXT_COMMANDS = [lexical.DELETE_WORD_COMMAND, lexical.DELETE_LINE_COMMAND, lexical.DELETE_CHARACTER_COMMAND];
|
1904
|
+
const DELETE_KEY_COMMANDS = [lexical.KEY_BACKSPACE_COMMAND, lexical.KEY_DELETE_COMMAND];
|
1753
1905
|
function applyTableHandlers(tableNode, element, editor, hasTabHandler) {
|
1754
1906
|
const rootElement = editor.getRootElement();
|
1755
|
-
|
1756
|
-
|
1907
|
+
const editorWindow = getEditorWindow(editor);
|
1908
|
+
if (!(rootElement !== null && editorWindow !== null)) {
|
1909
|
+
throw Error(`applyTableHandlers: editor has no root element set`);
|
1757
1910
|
}
|
1758
1911
|
const tableObserver = new TableObserver(editor, tableNode.getKey());
|
1759
|
-
const editorWindow = editor._window || window;
|
1760
1912
|
const tableElement = getTableElement(tableNode, element);
|
1761
1913
|
attachTableObserverToTableElement(tableElement, tableObserver);
|
1762
1914
|
tableObserver.listenersToRemove.add(() => detatchTableObserverFromTableElement(tableElement, tableObserver));
|
1763
1915
|
const createMouseHandlers = () => {
|
1916
|
+
if (tableObserver.isSelecting) {
|
1917
|
+
return;
|
1918
|
+
}
|
1764
1919
|
const onMouseUp = () => {
|
1765
1920
|
tableObserver.isSelecting = false;
|
1766
1921
|
editorWindow.removeEventListener('mouseup', onMouseUp);
|
1767
1922
|
editorWindow.removeEventListener('mousemove', onMouseMove);
|
1768
1923
|
};
|
1769
1924
|
const onMouseMove = moveEvent => {
|
1770
|
-
|
1771
|
-
|
1772
|
-
|
1773
|
-
|
1774
|
-
editorWindow.removeEventListener('mouseup', onMouseUp);
|
1775
|
-
editorWindow.removeEventListener('mousemove', onMouseMove);
|
1776
|
-
return;
|
1777
|
-
}
|
1778
|
-
const focusCell = getDOMCellFromTarget(moveEvent.target);
|
1779
|
-
if (focusCell !== null && (tableObserver.anchorX !== focusCell.x || tableObserver.anchorY !== focusCell.y)) {
|
1780
|
-
moveEvent.preventDefault();
|
1781
|
-
tableObserver.setFocusCellForSelection(focusCell);
|
1782
|
-
}
|
1783
|
-
}, 0);
|
1784
|
-
};
|
1785
|
-
return {
|
1786
|
-
onMouseMove,
|
1787
|
-
onMouseUp
|
1788
|
-
};
|
1789
|
-
};
|
1790
|
-
const onMouseDown = event => {
|
1791
|
-
setTimeout(() => {
|
1792
|
-
if (event.button !== 0) {
|
1925
|
+
if (!isMouseDownOnEvent(moveEvent) && tableObserver.isSelecting) {
|
1926
|
+
tableObserver.isSelecting = false;
|
1927
|
+
editorWindow.removeEventListener('mouseup', onMouseUp);
|
1928
|
+
editorWindow.removeEventListener('mousemove', onMouseMove);
|
1793
1929
|
return;
|
1794
1930
|
}
|
1795
|
-
|
1796
|
-
|
1931
|
+
const override = !tableElement.contains(moveEvent.target);
|
1932
|
+
let focusCell = null;
|
1933
|
+
if (!override) {
|
1934
|
+
focusCell = getDOMCellFromTarget(moveEvent.target);
|
1935
|
+
} else {
|
1936
|
+
for (const el of document.elementsFromPoint(moveEvent.clientX, moveEvent.clientY)) {
|
1937
|
+
focusCell = tableElement.contains(el) ? getDOMCellFromTarget(el) : null;
|
1938
|
+
if (focusCell) {
|
1939
|
+
break;
|
1940
|
+
}
|
1941
|
+
}
|
1797
1942
|
}
|
1798
|
-
|
1799
|
-
|
1800
|
-
|
1801
|
-
|
1943
|
+
if (focusCell && (tableObserver.focusCell === null || focusCell.elem !== tableObserver.focusCell.elem)) {
|
1944
|
+
tableObserver.setNextFocus({
|
1945
|
+
focusCell,
|
1946
|
+
override
|
1947
|
+
});
|
1948
|
+
editor.dispatchCommand(lexical.SELECTION_CHANGE_COMMAND, undefined);
|
1802
1949
|
}
|
1803
|
-
|
1804
|
-
|
1805
|
-
|
1806
|
-
|
1807
|
-
|
1808
|
-
|
1809
|
-
|
1810
|
-
|
1950
|
+
};
|
1951
|
+
tableObserver.isSelecting = true;
|
1952
|
+
editorWindow.addEventListener('mouseup', onMouseUp, tableObserver.listenerOptions);
|
1953
|
+
editorWindow.addEventListener('mousemove', onMouseMove, tableObserver.listenerOptions);
|
1954
|
+
};
|
1955
|
+
const onMouseDown = event => {
|
1956
|
+
if (event.button !== 0) {
|
1957
|
+
return;
|
1958
|
+
}
|
1959
|
+
if (!editorWindow) {
|
1960
|
+
return;
|
1961
|
+
}
|
1962
|
+
const targetCell = getDOMCellFromTarget(event.target);
|
1963
|
+
if (targetCell !== null) {
|
1964
|
+
editor.update(() => {
|
1965
|
+
const prevSelection = lexical.$getPreviousSelection();
|
1966
|
+
// We can't trust Firefox to do the right thing with the selection and
|
1967
|
+
// we don't have a proper state machine to do this "correctly" but
|
1968
|
+
// if we go ahead and make the table selection now it will work
|
1969
|
+
if (IS_FIREFOX && event.shiftKey && $isSelectionInTable(prevSelection, tableNode) && (lexical.$isRangeSelection(prevSelection) || $isTableSelection(prevSelection))) {
|
1970
|
+
const prevAnchorNode = prevSelection.anchor.getNode();
|
1971
|
+
const prevAnchorCell = $findParentTableCellNodeInTable(tableNode, prevSelection.anchor.getNode());
|
1972
|
+
if (prevAnchorCell) {
|
1973
|
+
tableObserver.$setAnchorCellForSelection($getObserverCellFromCellNodeOrThrow(tableObserver, prevAnchorCell));
|
1974
|
+
tableObserver.$setFocusCellForSelection(targetCell);
|
1975
|
+
stopEvent(event);
|
1976
|
+
} else {
|
1977
|
+
const newSelection = tableNode.isBefore(prevAnchorNode) ? tableNode.selectStart() : tableNode.selectEnd();
|
1978
|
+
newSelection.anchor.set(prevSelection.anchor.key, prevSelection.anchor.offset, prevSelection.anchor.type);
|
1979
|
+
}
|
1980
|
+
} else {
|
1981
|
+
tableObserver.$setAnchorCellForSelection(targetCell);
|
1982
|
+
}
|
1983
|
+
});
|
1984
|
+
}
|
1985
|
+
createMouseHandlers();
|
1811
1986
|
};
|
1812
1987
|
tableElement.addEventListener('mousedown', onMouseDown, tableObserver.listenerOptions);
|
1813
1988
|
|
@@ -1820,20 +1995,19 @@ function applyTableHandlers(tableNode, element, editor, hasTabHandler) {
|
|
1820
1995
|
const selection = lexical.$getSelection();
|
1821
1996
|
const target = event.target;
|
1822
1997
|
if ($isTableSelection(selection) && selection.tableKey === tableObserver.tableNodeKey && rootElement.contains(target)) {
|
1823
|
-
tableObserver
|
1998
|
+
tableObserver.$clearHighlight();
|
1824
1999
|
}
|
1825
2000
|
});
|
1826
2001
|
};
|
1827
2002
|
editorWindow.addEventListener('mousedown', mouseDownCallback, tableObserver.listenerOptions);
|
1828
|
-
|
1829
|
-
|
1830
|
-
|
1831
|
-
tableObserver.listenersToRemove.add(editor.registerCommand(lexical.KEY_ARROW_RIGHT_COMMAND, event => $handleArrowKey(editor, event, 'forward', tableNode, tableObserver), lexical.COMMAND_PRIORITY_HIGH));
|
2003
|
+
for (const [command, direction] of ARROW_KEY_COMMANDS_WITH_DIRECTION) {
|
2004
|
+
tableObserver.listenersToRemove.add(editor.registerCommand(command, event => $handleArrowKey(editor, event, direction, tableNode, tableObserver), lexical.COMMAND_PRIORITY_HIGH));
|
2005
|
+
}
|
1832
2006
|
tableObserver.listenersToRemove.add(editor.registerCommand(lexical.KEY_ESCAPE_COMMAND, event => {
|
1833
2007
|
const selection = lexical.$getSelection();
|
1834
2008
|
if ($isTableSelection(selection)) {
|
1835
|
-
const focusCellNode =
|
1836
|
-
if (
|
2009
|
+
const focusCellNode = $findParentTableCellNodeInTable(tableNode, selection.focus.getNode());
|
2010
|
+
if (focusCellNode !== null) {
|
1837
2011
|
stopEvent(event);
|
1838
2012
|
focusCellNode.selectEnd();
|
1839
2013
|
return true;
|
@@ -1847,10 +2021,10 @@ function applyTableHandlers(tableNode, element, editor, hasTabHandler) {
|
|
1847
2021
|
return false;
|
1848
2022
|
}
|
1849
2023
|
if ($isTableSelection(selection)) {
|
1850
|
-
tableObserver
|
2024
|
+
tableObserver.$clearText();
|
1851
2025
|
return true;
|
1852
2026
|
} else if (lexical.$isRangeSelection(selection)) {
|
1853
|
-
const tableCellNode =
|
2027
|
+
const tableCellNode = $findParentTableCellNodeInTable(tableNode, selection.anchor.getNode());
|
1854
2028
|
if (!$isTableCellNode(tableCellNode)) {
|
1855
2029
|
return false;
|
1856
2030
|
}
|
@@ -1860,7 +2034,7 @@ function applyTableHandlers(tableNode, element, editor, hasTabHandler) {
|
|
1860
2034
|
const isFocusInside = tableNode.isParentOf(focusNode);
|
1861
2035
|
const selectionContainsPartialTable = isAnchorInside && !isFocusInside || isFocusInside && !isAnchorInside;
|
1862
2036
|
if (selectionContainsPartialTable) {
|
1863
|
-
tableObserver
|
2037
|
+
tableObserver.$clearText();
|
1864
2038
|
return true;
|
1865
2039
|
}
|
1866
2040
|
const nearestElementNode = utils.$findMatchingParent(selection.anchor.getNode(), n => lexical.$isElementNode(n));
|
@@ -1875,9 +2049,9 @@ function applyTableHandlers(tableNode, element, editor, hasTabHandler) {
|
|
1875
2049
|
}
|
1876
2050
|
return false;
|
1877
2051
|
};
|
1878
|
-
|
2052
|
+
for (const command of DELETE_TEXT_COMMANDS) {
|
1879
2053
|
tableObserver.listenersToRemove.add(editor.registerCommand(command, deleteTextHandler(command), lexical.COMMAND_PRIORITY_CRITICAL));
|
1880
|
-
}
|
2054
|
+
}
|
1881
2055
|
const $deleteCellHandler = event => {
|
1882
2056
|
const selection = lexical.$getSelection();
|
1883
2057
|
if (!($isTableSelection(selection) || lexical.$isRangeSelection(selection))) {
|
@@ -1910,13 +2084,14 @@ function applyTableHandlers(tableNode, element, editor, hasTabHandler) {
|
|
1910
2084
|
event.preventDefault();
|
1911
2085
|
event.stopPropagation();
|
1912
2086
|
}
|
1913
|
-
tableObserver
|
2087
|
+
tableObserver.$clearText();
|
1914
2088
|
return true;
|
1915
2089
|
}
|
1916
2090
|
return false;
|
1917
2091
|
};
|
1918
|
-
|
1919
|
-
|
2092
|
+
for (const command of DELETE_KEY_COMMANDS) {
|
2093
|
+
tableObserver.listenersToRemove.add(editor.registerCommand(command, $deleteCellHandler, lexical.COMMAND_PRIORITY_CRITICAL));
|
2094
|
+
}
|
1920
2095
|
tableObserver.listenersToRemove.add(editor.registerCommand(lexical.CUT_COMMAND, event => {
|
1921
2096
|
const selection = lexical.$getSelection();
|
1922
2097
|
if (selection) {
|
@@ -1941,7 +2116,7 @@ function applyTableHandlers(tableNode, element, editor, hasTabHandler) {
|
|
1941
2116
|
return false;
|
1942
2117
|
}
|
1943
2118
|
if ($isTableSelection(selection)) {
|
1944
|
-
tableObserver
|
2119
|
+
tableObserver.$formatCells(payload);
|
1945
2120
|
return true;
|
1946
2121
|
} else if (lexical.$isRangeSelection(selection)) {
|
1947
2122
|
const tableCellNode = utils.$findMatchingParent(selection.anchor.getNode(), n => $isTableCellNode(n));
|
@@ -1962,13 +2137,18 @@ function applyTableHandlers(tableNode, element, editor, hasTabHandler) {
|
|
1962
2137
|
return false;
|
1963
2138
|
}
|
1964
2139
|
const [tableMap, anchorCell, focusCell] = $computeTableMap(tableNode, anchorNode, focusNode);
|
1965
|
-
const maxRow = Math.max(anchorCell.startRow, focusCell.startRow);
|
1966
|
-
const maxColumn = Math.max(anchorCell.startColumn, focusCell.startColumn);
|
2140
|
+
const maxRow = Math.max(anchorCell.startRow + anchorCell.cell.__rowSpan - 1, focusCell.startRow + focusCell.cell.__rowSpan - 1);
|
2141
|
+
const maxColumn = Math.max(anchorCell.startColumn + anchorCell.cell.__colSpan - 1, focusCell.startColumn + focusCell.cell.__colSpan - 1);
|
1967
2142
|
const minRow = Math.min(anchorCell.startRow, focusCell.startRow);
|
1968
2143
|
const minColumn = Math.min(anchorCell.startColumn, focusCell.startColumn);
|
2144
|
+
const visited = new Set();
|
1969
2145
|
for (let i = minRow; i <= maxRow; i++) {
|
1970
2146
|
for (let j = minColumn; j <= maxColumn; j++) {
|
1971
2147
|
const cell = tableMap[i][j].cell;
|
2148
|
+
if (visited.has(cell)) {
|
2149
|
+
continue;
|
2150
|
+
}
|
2151
|
+
visited.add(cell);
|
1972
2152
|
cell.setFormat(formatType);
|
1973
2153
|
const cellChildren = cell.getChildren();
|
1974
2154
|
for (let k = 0; k < cellChildren.length; k++) {
|
@@ -1987,7 +2167,7 @@ function applyTableHandlers(tableNode, element, editor, hasTabHandler) {
|
|
1987
2167
|
return false;
|
1988
2168
|
}
|
1989
2169
|
if ($isTableSelection(selection)) {
|
1990
|
-
tableObserver
|
2170
|
+
tableObserver.$clearHighlight();
|
1991
2171
|
return false;
|
1992
2172
|
} else if (lexical.$isRangeSelection(selection)) {
|
1993
2173
|
const tableCellNode = utils.$findMatchingParent(selection.anchor.getNode(), n => $isTableCellNode(n));
|
@@ -2023,10 +2203,6 @@ function applyTableHandlers(tableNode, element, editor, hasTabHandler) {
|
|
2023
2203
|
tableObserver.listenersToRemove.add(editor.registerCommand(lexical.FOCUS_COMMAND, payload => {
|
2024
2204
|
return tableNode.isSelected();
|
2025
2205
|
}, lexical.COMMAND_PRIORITY_HIGH));
|
2026
|
-
function getObserverCellFromCellNode(tableCellNode) {
|
2027
|
-
const currentCords = tableNode.getCordsFromCellNode(tableCellNode, tableObserver.table);
|
2028
|
-
return tableNode.getDOMCellFromCordsOrThrow(currentCords.x, currentCords.y, tableObserver.table);
|
2029
|
-
}
|
2030
2206
|
tableObserver.listenersToRemove.add(editor.registerCommand(lexical.SELECTION_INSERT_CLIPBOARD_NODES_COMMAND, selectionPayload => {
|
2031
2207
|
const {
|
2032
2208
|
nodes,
|
@@ -2101,10 +2277,30 @@ function applyTableHandlers(tableNode, element, editor, hasTabHandler) {
|
|
2101
2277
|
tableObserver.listenersToRemove.add(editor.registerCommand(lexical.SELECTION_CHANGE_COMMAND, () => {
|
2102
2278
|
const selection = lexical.$getSelection();
|
2103
2279
|
const prevSelection = lexical.$getPreviousSelection();
|
2280
|
+
const nextFocus = tableObserver.getAndClearNextFocus();
|
2281
|
+
if (nextFocus !== null) {
|
2282
|
+
const {
|
2283
|
+
focusCell
|
2284
|
+
} = nextFocus;
|
2285
|
+
if ($isTableSelection(selection) && selection.tableKey === tableObserver.tableNodeKey) {
|
2286
|
+
if (focusCell.x === tableObserver.focusX && focusCell.y === tableObserver.focusY) {
|
2287
|
+
// The selection is already the correct table selection
|
2288
|
+
return false;
|
2289
|
+
} else {
|
2290
|
+
tableObserver.$setFocusCellForSelection(focusCell);
|
2291
|
+
return true;
|
2292
|
+
}
|
2293
|
+
} else if (focusCell !== tableObserver.anchorCell && $isSelectionInTable(selection, tableNode)) {
|
2294
|
+
// The selection has crossed cells
|
2295
|
+
tableObserver.$setFocusCellForSelection(focusCell);
|
2296
|
+
return true;
|
2297
|
+
}
|
2298
|
+
}
|
2299
|
+
const shouldCheckSelection = tableObserver.getAndClearShouldCheckSelection();
|
2104
2300
|
// If they pressed the down arrow with the selection outside of the
|
2105
2301
|
// table, and then the selection ends up in the table but not in the
|
2106
2302
|
// first cell, then move the selection to the first cell.
|
2107
|
-
if (
|
2303
|
+
if (shouldCheckSelection && lexical.$isRangeSelection(prevSelection) && lexical.$isRangeSelection(selection) && selection.isCollapsed()) {
|
2108
2304
|
const anchor = selection.anchor.getNode();
|
2109
2305
|
const firstRow = tableNode.getFirstChild();
|
2110
2306
|
const anchorCell = $findCellNode(anchor);
|
@@ -2130,10 +2326,10 @@ function applyTableHandlers(tableNode, element, editor, hasTabHandler) {
|
|
2130
2326
|
const focusCellNode = $findCellNode(focusNode);
|
2131
2327
|
const isAnchorInside = !!(anchorCellNode && tableNode.is($findTableNode(anchorCellNode)));
|
2132
2328
|
const isFocusInside = !!(focusCellNode && tableNode.is($findTableNode(focusCellNode)));
|
2133
|
-
const
|
2329
|
+
const isPartiallyWithinTable = isAnchorInside !== isFocusInside;
|
2134
2330
|
const isWithinTable = isAnchorInside && isFocusInside;
|
2135
2331
|
const isBackward = selection.isBackward();
|
2136
|
-
if (
|
2332
|
+
if (isPartiallyWithinTable) {
|
2137
2333
|
const newSelection = selection.clone();
|
2138
2334
|
if (isFocusInside) {
|
2139
2335
|
const [tableMap] = $computeTableMap(tableNode, focusCellNode, focusCellNode);
|
@@ -2156,26 +2352,15 @@ function applyTableHandlers(tableNode, element, editor, hasTabHandler) {
|
|
2156
2352
|
$addHighlightStyleToTable(editor, tableObserver);
|
2157
2353
|
} else if (isWithinTable) {
|
2158
2354
|
// Handle case when selection spans across multiple cells but still
|
2159
|
-
// has range selection, then we convert it into
|
2355
|
+
// has range selection, then we convert it into table selection
|
2160
2356
|
if (!anchorCellNode.is(focusCellNode)) {
|
2161
|
-
tableObserver
|
2162
|
-
tableObserver
|
2163
|
-
if (!tableObserver.isSelecting) {
|
2164
|
-
setTimeout(() => {
|
2165
|
-
const {
|
2166
|
-
onMouseUp,
|
2167
|
-
onMouseMove
|
2168
|
-
} = createMouseHandlers();
|
2169
|
-
tableObserver.isSelecting = true;
|
2170
|
-
editorWindow.addEventListener('mouseup', onMouseUp);
|
2171
|
-
editorWindow.addEventListener('mousemove', onMouseMove);
|
2172
|
-
}, 0);
|
2173
|
-
}
|
2357
|
+
tableObserver.$setAnchorCellForSelection($getObserverCellFromCellNodeOrThrow(tableObserver, anchorCellNode));
|
2358
|
+
tableObserver.$setFocusCellForSelection($getObserverCellFromCellNodeOrThrow(tableObserver, focusCellNode), true);
|
2174
2359
|
}
|
2175
2360
|
}
|
2176
2361
|
} else if (selection && $isTableSelection(selection) && selection.is(prevSelection) && selection.tableKey === tableNode.getKey()) {
|
2177
2362
|
// if selection goes outside of the table we need to change it to Range selection
|
2178
|
-
const domSelection = getDOMSelection(
|
2363
|
+
const domSelection = lexical.getDOMSelection(editorWindow);
|
2179
2364
|
if (domSelection && domSelection.anchorNode && domSelection.focusNode) {
|
2180
2365
|
const focusNode = lexical.$getNearestNodeFromDOMNode(domSelection.focusNode);
|
2181
2366
|
const isFocusOutside = focusNode && !tableNode.is($findTableNode(focusNode));
|
@@ -2193,9 +2378,9 @@ function applyTableHandlers(tableNode, element, editor, hasTabHandler) {
|
|
2193
2378
|
}
|
2194
2379
|
if (selection && !selection.is(prevSelection) && ($isTableSelection(selection) || $isTableSelection(prevSelection)) && tableObserver.tableSelection && !tableObserver.tableSelection.is(prevSelection)) {
|
2195
2380
|
if ($isTableSelection(selection) && selection.tableKey === tableObserver.tableNodeKey) {
|
2196
|
-
tableObserver
|
2381
|
+
tableObserver.$updateTableTableSelection(selection);
|
2197
2382
|
} else if (!$isTableSelection(selection) && $isTableSelection(prevSelection) && prevSelection.tableKey === tableObserver.tableNodeKey) {
|
2198
|
-
tableObserver
|
2383
|
+
tableObserver.$updateTableTableSelection(null);
|
2199
2384
|
}
|
2200
2385
|
return false;
|
2201
2386
|
}
|
@@ -2350,14 +2535,14 @@ function $forEachTableCell(grid, cb) {
|
|
2350
2535
|
}
|
2351
2536
|
}
|
2352
2537
|
function $addHighlightStyleToTable(editor, tableSelection) {
|
2353
|
-
tableSelection
|
2538
|
+
tableSelection.$disableHighlightStyle();
|
2354
2539
|
$forEachTableCell(tableSelection.table, cell => {
|
2355
2540
|
cell.highlighted = true;
|
2356
2541
|
$addHighlightToDOM(editor, cell);
|
2357
2542
|
});
|
2358
2543
|
}
|
2359
2544
|
function $removeHighlightStyleToTable(editor, tableObserver) {
|
2360
|
-
tableObserver
|
2545
|
+
tableObserver.$enableHighlightStyle();
|
2361
2546
|
$forEachTableCell(tableObserver.table, cell => {
|
2362
2547
|
const elem = cell.elem;
|
2363
2548
|
cell.highlighted = false;
|
@@ -2402,33 +2587,105 @@ const selectTableNodeInDirection = (tableObserver, tableNode, x, y, direction) =
|
|
2402
2587
|
return false;
|
2403
2588
|
}
|
2404
2589
|
};
|
2405
|
-
|
2406
|
-
|
2407
|
-
|
2408
|
-
|
2409
|
-
|
2410
|
-
|
2411
|
-
|
2412
|
-
|
2413
|
-
|
2414
|
-
case 'up':
|
2415
|
-
if (y !== 0) {
|
2416
|
-
tableObserver.setFocusCellForSelection(tableNode.getDOMCellFromCordsOrThrow(x, y - 1, tableObserver.table));
|
2417
|
-
return true;
|
2418
|
-
} else {
|
2419
|
-
return false;
|
2420
|
-
}
|
2421
|
-
case 'down':
|
2422
|
-
if (y !== tableObserver.table.rows - 1) {
|
2423
|
-
tableObserver.setFocusCellForSelection(tableNode.getDOMCellFromCordsOrThrow(x, y + 1, tableObserver.table));
|
2424
|
-
return true;
|
2425
|
-
} else {
|
2426
|
-
return false;
|
2427
|
-
}
|
2428
|
-
default:
|
2429
|
-
return false;
|
2590
|
+
function getCorner(rect, cellValue) {
|
2591
|
+
let colName;
|
2592
|
+
let rowName;
|
2593
|
+
if (cellValue.startColumn === rect.minColumn) {
|
2594
|
+
colName = 'minColumn';
|
2595
|
+
} else if (cellValue.startColumn + cellValue.cell.__colSpan - 1 === rect.maxColumn) {
|
2596
|
+
colName = 'maxColumn';
|
2597
|
+
} else {
|
2598
|
+
return null;
|
2430
2599
|
}
|
2431
|
-
|
2600
|
+
if (cellValue.startRow === rect.minRow) {
|
2601
|
+
rowName = 'minRow';
|
2602
|
+
} else if (cellValue.startRow + cellValue.cell.__rowSpan - 1 === rect.maxRow) {
|
2603
|
+
rowName = 'maxRow';
|
2604
|
+
} else {
|
2605
|
+
return null;
|
2606
|
+
}
|
2607
|
+
return [colName, rowName];
|
2608
|
+
}
|
2609
|
+
function getCornerOrThrow(rect, cellValue) {
|
2610
|
+
const corner = getCorner(rect, cellValue);
|
2611
|
+
if (!(corner !== null)) {
|
2612
|
+
throw Error(`getCornerOrThrow: cell ${cellValue.cell.getKey()} is not at a corner of rect`);
|
2613
|
+
}
|
2614
|
+
return corner;
|
2615
|
+
}
|
2616
|
+
function oppositeCorner([colName, rowName]) {
|
2617
|
+
return [colName === 'minColumn' ? 'maxColumn' : 'minColumn', rowName === 'minRow' ? 'maxRow' : 'minRow'];
|
2618
|
+
}
|
2619
|
+
function cellAtCornerOrThrow(tableMap, rect, [colName, rowName]) {
|
2620
|
+
const rowNum = rect[rowName];
|
2621
|
+
const rowMap = tableMap[rowNum];
|
2622
|
+
if (!(rowMap !== undefined)) {
|
2623
|
+
throw Error(`cellAtCornerOrThrow: ${rowName} = ${String(rowNum)} missing in tableMap`);
|
2624
|
+
}
|
2625
|
+
const colNum = rect[colName];
|
2626
|
+
const cell = rowMap[colNum];
|
2627
|
+
if (!(cell !== undefined)) {
|
2628
|
+
throw Error(`cellAtCornerOrThrow: ${colName} = ${String(colNum)} missing in tableMap`);
|
2629
|
+
}
|
2630
|
+
return cell;
|
2631
|
+
}
|
2632
|
+
function $extractRectCorners(tableMap, anchorCellValue, newFocusCellValue) {
|
2633
|
+
// We are sure that the focus now either contracts or expands the rect
|
2634
|
+
// but both the anchor and focus might be moved to ensure a rectangle
|
2635
|
+
// given a potentially ragged merge shape
|
2636
|
+
const rect = $computeTableCellRectBoundary(tableMap, anchorCellValue, newFocusCellValue);
|
2637
|
+
const anchorCorner = getCorner(rect, anchorCellValue);
|
2638
|
+
if (anchorCorner) {
|
2639
|
+
return [cellAtCornerOrThrow(tableMap, rect, anchorCorner), cellAtCornerOrThrow(tableMap, rect, oppositeCorner(anchorCorner))];
|
2640
|
+
}
|
2641
|
+
const newFocusCorner = getCorner(rect, newFocusCellValue);
|
2642
|
+
if (newFocusCorner) {
|
2643
|
+
return [cellAtCornerOrThrow(tableMap, rect, oppositeCorner(newFocusCorner)), cellAtCornerOrThrow(tableMap, rect, newFocusCorner)];
|
2644
|
+
}
|
2645
|
+
// TODO this doesn't have to be arbitrary, use the closest corner instead
|
2646
|
+
const newAnchorCorner = ['minColumn', 'minRow'];
|
2647
|
+
return [cellAtCornerOrThrow(tableMap, rect, newAnchorCorner), cellAtCornerOrThrow(tableMap, rect, oppositeCorner(newAnchorCorner))];
|
2648
|
+
}
|
2649
|
+
function $adjustFocusInDirection(tableObserver, tableMap, anchorCellValue, focusCellValue, direction) {
|
2650
|
+
const rect = $computeTableCellRectBoundary(tableMap, anchorCellValue, focusCellValue);
|
2651
|
+
const spans = $computeTableCellRectSpans(tableMap, rect);
|
2652
|
+
const {
|
2653
|
+
topSpan,
|
2654
|
+
leftSpan,
|
2655
|
+
bottomSpan,
|
2656
|
+
rightSpan
|
2657
|
+
} = spans;
|
2658
|
+
const anchorCorner = getCornerOrThrow(rect, anchorCellValue);
|
2659
|
+
const [focusColumn, focusRow] = oppositeCorner(anchorCorner);
|
2660
|
+
let fCol = rect[focusColumn];
|
2661
|
+
let fRow = rect[focusRow];
|
2662
|
+
if (direction === 'forward') {
|
2663
|
+
fCol += focusColumn === 'maxColumn' ? 1 : leftSpan;
|
2664
|
+
} else if (direction === 'backward') {
|
2665
|
+
fCol -= focusColumn === 'minColumn' ? 1 : rightSpan;
|
2666
|
+
} else if (direction === 'down') {
|
2667
|
+
fRow += focusRow === 'maxRow' ? 1 : topSpan;
|
2668
|
+
} else if (direction === 'up') {
|
2669
|
+
fRow -= focusRow === 'minRow' ? 1 : bottomSpan;
|
2670
|
+
}
|
2671
|
+
const targetRowMap = tableMap[fRow];
|
2672
|
+
if (targetRowMap === undefined) {
|
2673
|
+
return false;
|
2674
|
+
}
|
2675
|
+
const newFocusCellValue = targetRowMap[fCol];
|
2676
|
+
if (newFocusCellValue === undefined) {
|
2677
|
+
return false;
|
2678
|
+
}
|
2679
|
+
// We can be certain that anchorCellValue and newFocusCellValue are
|
2680
|
+
// contained within the desired selection, but we are not certain if
|
2681
|
+
// they need to be expanded or not to maintain a rectangular shape
|
2682
|
+
const [finalAnchorCell, finalFocusCell] = $extractRectCorners(tableMap, anchorCellValue, newFocusCellValue);
|
2683
|
+
const anchorDOM = $getObserverCellFromCellNodeOrThrow(tableObserver, finalAnchorCell.cell);
|
2684
|
+
const focusDOM = $getObserverCellFromCellNodeOrThrow(tableObserver, finalFocusCell.cell);
|
2685
|
+
tableObserver.$setAnchorCellForSelection(anchorDOM);
|
2686
|
+
tableObserver.$setFocusCellForSelection(focusDOM, true);
|
2687
|
+
return true;
|
2688
|
+
}
|
2432
2689
|
function $isSelectionInTable(selection, tableNode) {
|
2433
2690
|
if (lexical.$isRangeSelection(selection) || $isTableSelection(selection)) {
|
2434
2691
|
const isAnchorInside = tableNode.isParentOf(selection.anchor.getNode());
|
@@ -2444,20 +2701,14 @@ function selectTableCellNode(tableCell, fromStart) {
|
|
2444
2701
|
tableCell.selectEnd();
|
2445
2702
|
}
|
2446
2703
|
}
|
2447
|
-
const BROWSER_BLUE_RGB = '172,206,247';
|
2448
2704
|
function $addHighlightToDOM(editor, cell) {
|
2449
2705
|
const element = cell.elem;
|
2706
|
+
const editorThemeClasses = editor._config.theme;
|
2450
2707
|
const node = lexical.$getNearestNodeFromDOMNode(element);
|
2451
2708
|
if (!$isTableCellNode(node)) {
|
2452
2709
|
throw Error(`Expected to find LexicalNode from Table Cell DOMNode`);
|
2453
2710
|
}
|
2454
|
-
|
2455
|
-
if (backgroundColor === null) {
|
2456
|
-
element.style.setProperty('background-color', `rgb(${BROWSER_BLUE_RGB})`);
|
2457
|
-
} else {
|
2458
|
-
element.style.setProperty('background-image', `linear-gradient(to right, rgba(${BROWSER_BLUE_RGB},0.85), rgba(${BROWSER_BLUE_RGB},0.85))`);
|
2459
|
-
}
|
2460
|
-
element.style.setProperty('caret-color', 'transparent');
|
2711
|
+
utils.addClassNamesToElement(element, editorThemeClasses.tableCellSelected);
|
2461
2712
|
}
|
2462
2713
|
function $removeHighlightFromDOM(editor, cell) {
|
2463
2714
|
const element = cell.elem;
|
@@ -2465,12 +2716,8 @@ function $removeHighlightFromDOM(editor, cell) {
|
|
2465
2716
|
if (!$isTableCellNode(node)) {
|
2466
2717
|
throw Error(`Expected to find LexicalNode from Table Cell DOMNode`);
|
2467
2718
|
}
|
2468
|
-
const
|
2469
|
-
|
2470
|
-
element.style.removeProperty('background-color');
|
2471
|
-
}
|
2472
|
-
element.style.removeProperty('background-image');
|
2473
|
-
element.style.removeProperty('caret-color');
|
2719
|
+
const editorThemeClasses = editor._config.theme;
|
2720
|
+
utils.removeClassNamesFromElement(element, editorThemeClasses.tableCellSelected);
|
2474
2721
|
}
|
2475
2722
|
function $findCellNode(node) {
|
2476
2723
|
const cellNode = utils.$findMatchingParent(node, $isTableCellNode);
|
@@ -2561,8 +2808,8 @@ function $handleArrowKey(editor, event, direction, tableNode, tableObserver) {
|
|
2561
2808
|
const lastCellCoords = tableNode.getCordsFromCellNode(lastCellNode, tableObserver.table);
|
2562
2809
|
const firstCellDOM = tableNode.getDOMCellFromCordsOrThrow(firstCellCoords.x, firstCellCoords.y, tableObserver.table);
|
2563
2810
|
const lastCellDOM = tableNode.getDOMCellFromCordsOrThrow(lastCellCoords.x, lastCellCoords.y, tableObserver.table);
|
2564
|
-
tableObserver
|
2565
|
-
tableObserver
|
2811
|
+
tableObserver.$setAnchorCellForSelection(firstCellDOM);
|
2812
|
+
tableObserver.$setFocusCellForSelection(lastCellDOM, true);
|
2566
2813
|
return true;
|
2567
2814
|
}
|
2568
2815
|
}
|
@@ -2629,7 +2876,7 @@ function $handleArrowKey(editor, event, direction, tableNode, tableObserver) {
|
|
2629
2876
|
return false;
|
2630
2877
|
}
|
2631
2878
|
if (isExitingTableAnchor(anchorType, anchorOffset, anchorNode, direction)) {
|
2632
|
-
return $handleTableExit(event, anchorNode, tableNode, direction);
|
2879
|
+
return $handleTableExit(event, anchorNode, anchorCellNode, tableNode, direction);
|
2633
2880
|
}
|
2634
2881
|
return false;
|
2635
2882
|
}
|
@@ -2642,7 +2889,7 @@ function $handleArrowKey(editor, event, direction, tableNode, tableObserver) {
|
|
2642
2889
|
if (anchor.type === 'element') {
|
2643
2890
|
edgeSelectionRect = anchorDOM.getBoundingClientRect();
|
2644
2891
|
} else {
|
2645
|
-
const domSelection =
|
2892
|
+
const domSelection = lexical.getDOMSelection(getEditorWindow(editor));
|
2646
2893
|
if (domSelection === null || domSelection.rangeCount === 0) {
|
2647
2894
|
return false;
|
2648
2895
|
}
|
@@ -2664,8 +2911,8 @@ function $handleArrowKey(editor, event, direction, tableNode, tableObserver) {
|
|
2664
2911
|
const cords = tableNode.getCordsFromCellNode(anchorCellNode, tableObserver.table);
|
2665
2912
|
if (event.shiftKey) {
|
2666
2913
|
const cell = tableNode.getDOMCellFromCordsOrThrow(cords.x, cords.y, tableObserver.table);
|
2667
|
-
tableObserver
|
2668
|
-
tableObserver
|
2914
|
+
tableObserver.$setAnchorCellForSelection(cell);
|
2915
|
+
tableObserver.$setFocusCellForSelection(cell, true);
|
2669
2916
|
} else {
|
2670
2917
|
return selectTableNodeInDirection(tableObserver, tableNode, cords.x, cords.y, direction);
|
2671
2918
|
}
|
@@ -2686,15 +2933,15 @@ function $handleArrowKey(editor, event, direction, tableNode, tableObserver) {
|
|
2686
2933
|
if (!$isTableCellNode(anchorCellNode) || !$isTableCellNode(focusCellNode) || !$isTableNode(tableNodeFromSelection) || tableElement == null) {
|
2687
2934
|
return false;
|
2688
2935
|
}
|
2689
|
-
tableObserver
|
2936
|
+
tableObserver.$updateTableTableSelection(selection);
|
2690
2937
|
const grid = getTable(tableNodeFromSelection, tableElement);
|
2691
2938
|
const cordsAnchor = tableNode.getCordsFromCellNode(anchorCellNode, grid);
|
2692
2939
|
const anchorCell = tableNode.getDOMCellFromCordsOrThrow(cordsAnchor.x, cordsAnchor.y, grid);
|
2693
|
-
tableObserver
|
2940
|
+
tableObserver.$setAnchorCellForSelection(anchorCell);
|
2694
2941
|
stopEvent(event);
|
2695
2942
|
if (event.shiftKey) {
|
2696
|
-
const
|
2697
|
-
return
|
2943
|
+
const [tableMap, anchorValue, focusValue] = $computeTableMap(tableNode, anchorCellNode, focusCellNode);
|
2944
|
+
return $adjustFocusInDirection(tableObserver, tableMap, anchorValue, focusValue, direction);
|
2698
2945
|
} else {
|
2699
2946
|
focusCellNode.selectEnd();
|
2700
2947
|
}
|
@@ -2730,11 +2977,7 @@ function $isExitingTableTextAnchor(type, offset, anchorNode, direction) {
|
|
2730
2977
|
const hasValidOffset = direction === 'backward' ? offset === 0 : offset === anchorNode.getTextContentSize();
|
2731
2978
|
return type === 'text' && hasValidOffset && (direction === 'backward' ? parentNode.getPreviousSibling() === null : parentNode.getNextSibling() === null);
|
2732
2979
|
}
|
2733
|
-
function $handleTableExit(event, anchorNode, tableNode, direction) {
|
2734
|
-
const anchorCellNode = utils.$findMatchingParent(anchorNode, $isTableCellNode);
|
2735
|
-
if (!$isTableCellNode(anchorCellNode)) {
|
2736
|
-
return false;
|
2737
|
-
}
|
2980
|
+
function $handleTableExit(event, anchorNode, anchorCellNode, tableNode, direction) {
|
2738
2981
|
const [tableMap, cellValue] = $computeTableMap(tableNode, anchorCellNode, anchorCellNode);
|
2739
2982
|
if (!isExitingCell(tableMap, cellValue, direction)) {
|
2740
2983
|
return false;
|
@@ -2785,7 +3028,7 @@ function $getTableEdgeCursorPosition(editor, selection, tableNode) {
|
|
2785
3028
|
}
|
2786
3029
|
|
2787
3030
|
// TODO: Add support for nested tables
|
2788
|
-
const domSelection =
|
3031
|
+
const domSelection = lexical.getDOMSelection(getEditorWindow(editor));
|
2789
3032
|
if (!domSelection) {
|
2790
3033
|
return undefined;
|
2791
3034
|
}
|
@@ -2825,6 +3068,13 @@ function $getTableEdgeCursorPosition(editor, selection, tableNode) {
|
|
2825
3068
|
return undefined;
|
2826
3069
|
}
|
2827
3070
|
}
|
3071
|
+
function $getObserverCellFromCellNodeOrThrow(tableObserver, tableCellNode) {
|
3072
|
+
const {
|
3073
|
+
tableNode
|
3074
|
+
} = tableObserver.$lookup();
|
3075
|
+
const currentCords = tableNode.getCordsFromCellNode(tableCellNode, tableObserver.table);
|
3076
|
+
return tableNode.getDOMCellFromCordsOrThrow(currentCords.x, currentCords.y, tableObserver.table);
|
3077
|
+
}
|
2828
3078
|
|
2829
3079
|
/**
|
2830
3080
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|