@lexical/table 0.13.1 → 0.14.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.
@@ -0,0 +1,2433 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ import { addClassNamesToElement, $findMatchingParent, removeClassNamesFromElement, isHTMLElement } from '@lexical/utils';
8
+ import { ElementNode, $applyNodeReplacement, $createParagraphNode, $isElementNode, $isLineBreakNode, createCommand, $createTextNode, $getSelection, $isRangeSelection, $normalizeSelection__EXPERIMENTAL, $getNodeByKey, isCurrentlyReadOnlyMode, $createPoint, $setSelection, SELECTION_CHANGE_COMMAND, $getNearestNodeFromDOMNode, $createRangeSelection, $getRoot, KEY_ARROW_DOWN_COMMAND, COMMAND_PRIORITY_HIGH, KEY_ARROW_UP_COMMAND, KEY_ARROW_LEFT_COMMAND, KEY_ARROW_RIGHT_COMMAND, KEY_ESCAPE_COMMAND, DELETE_WORD_COMMAND, DELETE_LINE_COMMAND, DELETE_CHARACTER_COMMAND, COMMAND_PRIORITY_CRITICAL, KEY_BACKSPACE_COMMAND, KEY_DELETE_COMMAND, FORMAT_TEXT_COMMAND, FORMAT_ELEMENT_COMMAND, CONTROLLED_TEXT_INSERTION_COMMAND, KEY_TAB_COMMAND, FOCUS_COMMAND, SELECTION_INSERT_CLIPBOARD_NODES_COMMAND, $isTextNode, $getPreviousSelection } from 'lexical';
9
+
10
+ /**
11
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
12
+ *
13
+ * This source code is licensed under the MIT license found in the
14
+ * LICENSE file in the root directory of this source tree.
15
+ *
16
+ */
17
+
18
+ const PIXEL_VALUE_REG_EXP = /^(\d+(?:\.\d+)?)px$/;
19
+
20
+ /**
21
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
22
+ *
23
+ * This source code is licensed under the MIT license found in the
24
+ * LICENSE file in the root directory of this source tree.
25
+ *
26
+ */
27
+ const TableCellHeaderStates = {
28
+ BOTH: 3,
29
+ COLUMN: 2,
30
+ NO_STATUS: 0,
31
+ ROW: 1
32
+ };
33
+ /** @noInheritDoc */
34
+ class TableCellNode extends ElementNode {
35
+ /** @internal */
36
+
37
+ /** @internal */
38
+
39
+ /** @internal */
40
+
41
+ /** @internal */
42
+
43
+ /** @internal */
44
+
45
+ static getType() {
46
+ return 'tablecell';
47
+ }
48
+ static clone(node) {
49
+ const cellNode = new TableCellNode(node.__headerState, node.__colSpan, node.__width, node.__key);
50
+ cellNode.__rowSpan = node.__rowSpan;
51
+ cellNode.__backgroundColor = node.__backgroundColor;
52
+ return cellNode;
53
+ }
54
+ static importDOM() {
55
+ return {
56
+ td: node => ({
57
+ conversion: convertTableCellNodeElement,
58
+ priority: 0
59
+ }),
60
+ th: node => ({
61
+ conversion: convertTableCellNodeElement,
62
+ priority: 0
63
+ })
64
+ };
65
+ }
66
+ static importJSON(serializedNode) {
67
+ const colSpan = serializedNode.colSpan || 1;
68
+ const rowSpan = serializedNode.rowSpan || 1;
69
+ const cellNode = $createTableCellNode(serializedNode.headerState, colSpan, serializedNode.width || undefined);
70
+ cellNode.__rowSpan = rowSpan;
71
+ cellNode.__backgroundColor = serializedNode.backgroundColor || null;
72
+ return cellNode;
73
+ }
74
+ constructor(headerState = TableCellHeaderStates.NO_STATUS, colSpan = 1, width, key) {
75
+ super(key);
76
+ this.__colSpan = colSpan;
77
+ this.__rowSpan = 1;
78
+ this.__headerState = headerState;
79
+ this.__width = width;
80
+ this.__backgroundColor = null;
81
+ }
82
+ createDOM(config) {
83
+ const element = document.createElement(this.getTag());
84
+ if (this.__width) {
85
+ element.style.width = `${this.__width}px`;
86
+ }
87
+ if (this.__colSpan > 1) {
88
+ element.colSpan = this.__colSpan;
89
+ }
90
+ if (this.__rowSpan > 1) {
91
+ element.rowSpan = this.__rowSpan;
92
+ }
93
+ if (this.__backgroundColor !== null) {
94
+ element.style.backgroundColor = this.__backgroundColor;
95
+ }
96
+ addClassNamesToElement(element, config.theme.tableCell, this.hasHeader() && config.theme.tableCellHeader);
97
+ return element;
98
+ }
99
+ exportDOM(editor) {
100
+ const {
101
+ element
102
+ } = super.exportDOM(editor);
103
+ if (element) {
104
+ const element_ = element;
105
+ const maxWidth = 700;
106
+ const colCount = this.getParentOrThrow().getChildrenSize();
107
+ element_.style.border = '1px solid black';
108
+ if (this.__colSpan > 1) {
109
+ element_.colSpan = this.__colSpan;
110
+ }
111
+ if (this.__rowSpan > 1) {
112
+ element_.rowSpan = this.__rowSpan;
113
+ }
114
+ element_.style.width = `${this.getWidth() || Math.max(90, maxWidth / colCount)}px`;
115
+ element_.style.verticalAlign = 'top';
116
+ element_.style.textAlign = 'start';
117
+ const backgroundColor = this.getBackgroundColor();
118
+ if (backgroundColor !== null) {
119
+ element_.style.backgroundColor = backgroundColor;
120
+ } else if (this.hasHeader()) {
121
+ element_.style.backgroundColor = '#f2f3f5';
122
+ }
123
+ }
124
+ return {
125
+ element
126
+ };
127
+ }
128
+ exportJSON() {
129
+ return {
130
+ ...super.exportJSON(),
131
+ backgroundColor: this.getBackgroundColor(),
132
+ colSpan: this.__colSpan,
133
+ headerState: this.__headerState,
134
+ rowSpan: this.__rowSpan,
135
+ type: 'tablecell',
136
+ width: this.getWidth()
137
+ };
138
+ }
139
+ getColSpan() {
140
+ return this.__colSpan;
141
+ }
142
+ setColSpan(colSpan) {
143
+ this.getWritable().__colSpan = colSpan;
144
+ return this;
145
+ }
146
+ getRowSpan() {
147
+ return this.__rowSpan;
148
+ }
149
+ setRowSpan(rowSpan) {
150
+ this.getWritable().__rowSpan = rowSpan;
151
+ return this;
152
+ }
153
+ getTag() {
154
+ return this.hasHeader() ? 'th' : 'td';
155
+ }
156
+ setHeaderStyles(headerState) {
157
+ const self = this.getWritable();
158
+ self.__headerState = headerState;
159
+ return this.__headerState;
160
+ }
161
+ getHeaderStyles() {
162
+ return this.getLatest().__headerState;
163
+ }
164
+ setWidth(width) {
165
+ const self = this.getWritable();
166
+ self.__width = width;
167
+ return this.__width;
168
+ }
169
+ getWidth() {
170
+ return this.getLatest().__width;
171
+ }
172
+ getBackgroundColor() {
173
+ return this.getLatest().__backgroundColor;
174
+ }
175
+ setBackgroundColor(newBackgroundColor) {
176
+ this.getWritable().__backgroundColor = newBackgroundColor;
177
+ }
178
+ toggleHeaderStyle(headerStateToToggle) {
179
+ const self = this.getWritable();
180
+ if ((self.__headerState & headerStateToToggle) === headerStateToToggle) {
181
+ self.__headerState -= headerStateToToggle;
182
+ } else {
183
+ self.__headerState += headerStateToToggle;
184
+ }
185
+ return self;
186
+ }
187
+ hasHeaderState(headerState) {
188
+ return (this.getHeaderStyles() & headerState) === headerState;
189
+ }
190
+ hasHeader() {
191
+ return this.getLatest().__headerState !== TableCellHeaderStates.NO_STATUS;
192
+ }
193
+ updateDOM(prevNode) {
194
+ return prevNode.__headerState !== this.__headerState || prevNode.__width !== this.__width || prevNode.__colSpan !== this.__colSpan || prevNode.__rowSpan !== this.__rowSpan || prevNode.__backgroundColor !== this.__backgroundColor;
195
+ }
196
+ isShadowRoot() {
197
+ return true;
198
+ }
199
+ collapseAtStart() {
200
+ return true;
201
+ }
202
+ canBeEmpty() {
203
+ return false;
204
+ }
205
+ canIndent() {
206
+ return false;
207
+ }
208
+ }
209
+ function convertTableCellNodeElement(domNode) {
210
+ const domNode_ = domNode;
211
+ const nodeName = domNode.nodeName.toLowerCase();
212
+ let width = undefined;
213
+ if (PIXEL_VALUE_REG_EXP.test(domNode_.style.width)) {
214
+ width = parseFloat(domNode_.style.width);
215
+ }
216
+ const tableCellNode = $createTableCellNode(nodeName === 'th' ? TableCellHeaderStates.ROW : TableCellHeaderStates.NO_STATUS, domNode_.colSpan, width);
217
+ tableCellNode.__rowSpan = domNode_.rowSpan;
218
+ const backgroundColor = domNode_.style.backgroundColor;
219
+ if (backgroundColor !== '') {
220
+ tableCellNode.__backgroundColor = backgroundColor;
221
+ }
222
+ return {
223
+ after: childLexicalNodes => {
224
+ if (childLexicalNodes.length === 0) {
225
+ childLexicalNodes.push($createParagraphNode());
226
+ }
227
+ return childLexicalNodes;
228
+ },
229
+ forChild: (lexicalNode, parentLexicalNode) => {
230
+ if ($isTableCellNode(parentLexicalNode) && !$isElementNode(lexicalNode)) {
231
+ const paragraphNode = $createParagraphNode();
232
+ if ($isLineBreakNode(lexicalNode) && lexicalNode.getTextContent() === '\n') {
233
+ return null;
234
+ }
235
+ paragraphNode.append(lexicalNode);
236
+ return paragraphNode;
237
+ }
238
+ return lexicalNode;
239
+ },
240
+ node: tableCellNode
241
+ };
242
+ }
243
+ function $createTableCellNode(headerState, colSpan = 1, width) {
244
+ return $applyNodeReplacement(new TableCellNode(headerState, colSpan, width));
245
+ }
246
+ function $isTableCellNode(node) {
247
+ return node instanceof TableCellNode;
248
+ }
249
+
250
+ /**
251
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
252
+ *
253
+ * This source code is licensed under the MIT license found in the
254
+ * LICENSE file in the root directory of this source tree.
255
+ *
256
+ */
257
+ const INSERT_TABLE_COMMAND = createCommand('INSERT_TABLE_COMMAND');
258
+
259
+ /**
260
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
261
+ *
262
+ * This source code is licensed under the MIT license found in the
263
+ * LICENSE file in the root directory of this source tree.
264
+ *
265
+ */
266
+ /** @noInheritDoc */
267
+ class TableRowNode extends ElementNode {
268
+ /** @internal */
269
+
270
+ static getType() {
271
+ return 'tablerow';
272
+ }
273
+ static clone(node) {
274
+ return new TableRowNode(node.__height, node.__key);
275
+ }
276
+ static importDOM() {
277
+ return {
278
+ tr: node => ({
279
+ conversion: convertTableRowElement,
280
+ priority: 0
281
+ })
282
+ };
283
+ }
284
+ static importJSON(serializedNode) {
285
+ return $createTableRowNode(serializedNode.height);
286
+ }
287
+ constructor(height, key) {
288
+ super(key);
289
+ this.__height = height;
290
+ }
291
+ exportJSON() {
292
+ return {
293
+ ...super.exportJSON(),
294
+ ...(this.getHeight() && {
295
+ height: this.getHeight()
296
+ }),
297
+ type: 'tablerow',
298
+ version: 1
299
+ };
300
+ }
301
+ createDOM(config) {
302
+ const element = document.createElement('tr');
303
+ if (this.__height) {
304
+ element.style.height = `${this.__height}px`;
305
+ }
306
+ addClassNamesToElement(element, config.theme.tableRow);
307
+ return element;
308
+ }
309
+ isShadowRoot() {
310
+ return true;
311
+ }
312
+ setHeight(height) {
313
+ const self = this.getWritable();
314
+ self.__height = height;
315
+ return this.__height;
316
+ }
317
+ getHeight() {
318
+ return this.getLatest().__height;
319
+ }
320
+ updateDOM(prevNode) {
321
+ return prevNode.__height !== this.__height;
322
+ }
323
+ canBeEmpty() {
324
+ return false;
325
+ }
326
+ canIndent() {
327
+ return false;
328
+ }
329
+ }
330
+ function convertTableRowElement(domNode) {
331
+ const domNode_ = domNode;
332
+ let height = undefined;
333
+ if (PIXEL_VALUE_REG_EXP.test(domNode_.style.height)) {
334
+ height = parseFloat(domNode_.style.height);
335
+ }
336
+ return {
337
+ node: $createTableRowNode(height)
338
+ };
339
+ }
340
+ function $createTableRowNode(height) {
341
+ return $applyNodeReplacement(new TableRowNode(height));
342
+ }
343
+ function $isTableRowNode(node) {
344
+ return node instanceof TableRowNode;
345
+ }
346
+
347
+ /**
348
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
349
+ *
350
+ * This source code is licensed under the MIT license found in the
351
+ * LICENSE file in the root directory of this source tree.
352
+ *
353
+ */
354
+
355
+ const CAN_USE_DOM = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined';
356
+
357
+ /**
358
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
359
+ *
360
+ * This source code is licensed under the MIT license found in the
361
+ * LICENSE file in the root directory of this source tree.
362
+ *
363
+ */
364
+ function $createTableNodeWithDimensions(rowCount, columnCount, includeHeaders = true) {
365
+ const tableNode = $createTableNode();
366
+ for (let iRow = 0; iRow < rowCount; iRow++) {
367
+ const tableRowNode = $createTableRowNode();
368
+ for (let iColumn = 0; iColumn < columnCount; iColumn++) {
369
+ let headerState = TableCellHeaderStates.NO_STATUS;
370
+ if (typeof includeHeaders === 'object') {
371
+ if (iRow === 0 && includeHeaders.rows) {
372
+ headerState |= TableCellHeaderStates.ROW;
373
+ }
374
+ if (iColumn === 0 && includeHeaders.columns) {
375
+ headerState |= TableCellHeaderStates.COLUMN;
376
+ }
377
+ } else if (includeHeaders) {
378
+ if (iRow === 0) {
379
+ headerState |= TableCellHeaderStates.ROW;
380
+ }
381
+ if (iColumn === 0) {
382
+ headerState |= TableCellHeaderStates.COLUMN;
383
+ }
384
+ }
385
+ const tableCellNode = $createTableCellNode(headerState);
386
+ const paragraphNode = $createParagraphNode();
387
+ paragraphNode.append($createTextNode());
388
+ tableCellNode.append(paragraphNode);
389
+ tableRowNode.append(tableCellNode);
390
+ }
391
+ tableNode.append(tableRowNode);
392
+ }
393
+ return tableNode;
394
+ }
395
+ function $getTableCellNodeFromLexicalNode(startingNode) {
396
+ const node = $findMatchingParent(startingNode, n => $isTableCellNode(n));
397
+ if ($isTableCellNode(node)) {
398
+ return node;
399
+ }
400
+ return null;
401
+ }
402
+ function $getTableRowNodeFromTableCellNodeOrThrow(startingNode) {
403
+ const node = $findMatchingParent(startingNode, n => $isTableRowNode(n));
404
+ if ($isTableRowNode(node)) {
405
+ return node;
406
+ }
407
+ throw new Error('Expected table cell to be inside of table row.');
408
+ }
409
+ function $getTableNodeFromLexicalNodeOrThrow(startingNode) {
410
+ const node = $findMatchingParent(startingNode, n => $isTableNode(n));
411
+ if ($isTableNode(node)) {
412
+ return node;
413
+ }
414
+ throw new Error('Expected table cell to be inside of table.');
415
+ }
416
+ function $getTableRowIndexFromTableCellNode(tableCellNode) {
417
+ const tableRowNode = $getTableRowNodeFromTableCellNodeOrThrow(tableCellNode);
418
+ const tableNode = $getTableNodeFromLexicalNodeOrThrow(tableRowNode);
419
+ return tableNode.getChildren().findIndex(n => n.is(tableRowNode));
420
+ }
421
+ function $getTableColumnIndexFromTableCellNode(tableCellNode) {
422
+ const tableRowNode = $getTableRowNodeFromTableCellNodeOrThrow(tableCellNode);
423
+ return tableRowNode.getChildren().findIndex(n => n.is(tableCellNode));
424
+ }
425
+ function $getTableCellSiblingsFromTableCellNode(tableCellNode, table) {
426
+ const tableNode = $getTableNodeFromLexicalNodeOrThrow(tableCellNode);
427
+ const {
428
+ x,
429
+ y
430
+ } = tableNode.getCordsFromCellNode(tableCellNode, table);
431
+ return {
432
+ above: tableNode.getCellNodeFromCords(x, y - 1, table),
433
+ below: tableNode.getCellNodeFromCords(x, y + 1, table),
434
+ left: tableNode.getCellNodeFromCords(x - 1, y, table),
435
+ right: tableNode.getCellNodeFromCords(x + 1, y, table)
436
+ };
437
+ }
438
+ function $removeTableRowAtIndex(tableNode, indexToDelete) {
439
+ const tableRows = tableNode.getChildren();
440
+ if (indexToDelete >= tableRows.length || indexToDelete < 0) {
441
+ throw new Error('Expected table cell to be inside of table row.');
442
+ }
443
+ const targetRowNode = tableRows[indexToDelete];
444
+ targetRowNode.remove();
445
+ return tableNode;
446
+ }
447
+ function $insertTableRow(tableNode, targetIndex, shouldInsertAfter = true, rowCount, table) {
448
+ const tableRows = tableNode.getChildren();
449
+ if (targetIndex >= tableRows.length || targetIndex < 0) {
450
+ throw new Error('Table row target index out of range');
451
+ }
452
+ const targetRowNode = tableRows[targetIndex];
453
+ if ($isTableRowNode(targetRowNode)) {
454
+ for (let r = 0; r < rowCount; r++) {
455
+ const tableRowCells = targetRowNode.getChildren();
456
+ const tableColumnCount = tableRowCells.length;
457
+ const newTableRowNode = $createTableRowNode();
458
+ for (let c = 0; c < tableColumnCount; c++) {
459
+ const tableCellFromTargetRow = tableRowCells[c];
460
+ if (!$isTableCellNode(tableCellFromTargetRow)) {
461
+ throw Error(`Expected table cell`);
462
+ }
463
+ const {
464
+ above,
465
+ below
466
+ } = $getTableCellSiblingsFromTableCellNode(tableCellFromTargetRow, table);
467
+ let headerState = TableCellHeaderStates.NO_STATUS;
468
+ const width = above && above.getWidth() || below && below.getWidth() || undefined;
469
+ if (above && above.hasHeaderState(TableCellHeaderStates.COLUMN) || below && below.hasHeaderState(TableCellHeaderStates.COLUMN)) {
470
+ headerState |= TableCellHeaderStates.COLUMN;
471
+ }
472
+ const tableCellNode = $createTableCellNode(headerState, 1, width);
473
+ tableCellNode.append($createParagraphNode());
474
+ newTableRowNode.append(tableCellNode);
475
+ }
476
+ if (shouldInsertAfter) {
477
+ targetRowNode.insertAfter(newTableRowNode);
478
+ } else {
479
+ targetRowNode.insertBefore(newTableRowNode);
480
+ }
481
+ }
482
+ } else {
483
+ throw new Error('Row before insertion index does not exist.');
484
+ }
485
+ return tableNode;
486
+ }
487
+ const getHeaderState = (currentState, possibleState) => {
488
+ if (currentState === TableCellHeaderStates.BOTH || currentState === possibleState) {
489
+ return possibleState;
490
+ }
491
+ return TableCellHeaderStates.NO_STATUS;
492
+ };
493
+ function $insertTableRow__EXPERIMENTAL(insertAfter = true) {
494
+ const selection = $getSelection();
495
+ if (!($isRangeSelection(selection) || $isTableSelection(selection))) {
496
+ throw Error(`Expected a RangeSelection or GridSelection`);
497
+ }
498
+ const focus = selection.focus.getNode();
499
+ const [focusCell,, grid] = $getNodeTriplet(focus);
500
+ const [gridMap, focusCellMap] = $computeTableMap(grid, focusCell, focusCell);
501
+ const columnCount = gridMap[0].length;
502
+ const {
503
+ startRow: focusStartRow
504
+ } = focusCellMap;
505
+ if (insertAfter) {
506
+ const focusEndRow = focusStartRow + focusCell.__rowSpan - 1;
507
+ const focusEndRowMap = gridMap[focusEndRow];
508
+ const newRow = $createTableRowNode();
509
+ for (let i = 0; i < columnCount; i++) {
510
+ const {
511
+ cell,
512
+ startRow
513
+ } = focusEndRowMap[i];
514
+ if (startRow + cell.__rowSpan - 1 <= focusEndRow) {
515
+ const currentCell = focusEndRowMap[i].cell;
516
+ const currentCellHeaderState = currentCell.__headerState;
517
+ const headerState = getHeaderState(currentCellHeaderState, TableCellHeaderStates.COLUMN);
518
+ newRow.append($createTableCellNode(headerState).append($createParagraphNode()));
519
+ } else {
520
+ cell.setRowSpan(cell.__rowSpan + 1);
521
+ }
522
+ }
523
+ const focusEndRowNode = grid.getChildAtIndex(focusEndRow);
524
+ if (!$isTableRowNode(focusEndRowNode)) {
525
+ throw Error(`focusEndRow is not a TableRowNode`);
526
+ }
527
+ focusEndRowNode.insertAfter(newRow);
528
+ } else {
529
+ const focusStartRowMap = gridMap[focusStartRow];
530
+ const newRow = $createTableRowNode();
531
+ for (let i = 0; i < columnCount; i++) {
532
+ const {
533
+ cell,
534
+ startRow
535
+ } = focusStartRowMap[i];
536
+ if (startRow === focusStartRow) {
537
+ const currentCell = focusStartRowMap[i].cell;
538
+ const currentCellHeaderState = currentCell.__headerState;
539
+ const headerState = getHeaderState(currentCellHeaderState, TableCellHeaderStates.COLUMN);
540
+ newRow.append($createTableCellNode(headerState).append($createParagraphNode()));
541
+ } else {
542
+ cell.setRowSpan(cell.__rowSpan + 1);
543
+ }
544
+ }
545
+ const focusStartRowNode = grid.getChildAtIndex(focusStartRow);
546
+ if (!$isTableRowNode(focusStartRowNode)) {
547
+ throw Error(`focusEndRow is not a TableRowNode`);
548
+ }
549
+ focusStartRowNode.insertBefore(newRow);
550
+ }
551
+ }
552
+ function $insertTableColumn(tableNode, targetIndex, shouldInsertAfter = true, columnCount, table) {
553
+ const tableRows = tableNode.getChildren();
554
+ const tableCellsToBeInserted = [];
555
+ for (let r = 0; r < tableRows.length; r++) {
556
+ const currentTableRowNode = tableRows[r];
557
+ if ($isTableRowNode(currentTableRowNode)) {
558
+ for (let c = 0; c < columnCount; c++) {
559
+ const tableRowChildren = currentTableRowNode.getChildren();
560
+ if (targetIndex >= tableRowChildren.length || targetIndex < 0) {
561
+ throw new Error('Table column target index out of range');
562
+ }
563
+ const targetCell = tableRowChildren[targetIndex];
564
+ if (!$isTableCellNode(targetCell)) {
565
+ throw Error(`Expected table cell`);
566
+ }
567
+ const {
568
+ left,
569
+ right
570
+ } = $getTableCellSiblingsFromTableCellNode(targetCell, table);
571
+ let headerState = TableCellHeaderStates.NO_STATUS;
572
+ if (left && left.hasHeaderState(TableCellHeaderStates.ROW) || right && right.hasHeaderState(TableCellHeaderStates.ROW)) {
573
+ headerState |= TableCellHeaderStates.ROW;
574
+ }
575
+ const newTableCell = $createTableCellNode(headerState);
576
+ newTableCell.append($createParagraphNode());
577
+ tableCellsToBeInserted.push({
578
+ newTableCell,
579
+ targetCell
580
+ });
581
+ }
582
+ }
583
+ }
584
+ tableCellsToBeInserted.forEach(({
585
+ newTableCell,
586
+ targetCell
587
+ }) => {
588
+ if (shouldInsertAfter) {
589
+ targetCell.insertAfter(newTableCell);
590
+ } else {
591
+ targetCell.insertBefore(newTableCell);
592
+ }
593
+ });
594
+ return tableNode;
595
+ }
596
+ function $insertTableColumn__EXPERIMENTAL(insertAfter = true) {
597
+ const selection = $getSelection();
598
+ if (!($isRangeSelection(selection) || $isTableSelection(selection))) {
599
+ throw Error(`Expected a RangeSelection or GridSelection`);
600
+ }
601
+ const anchor = selection.anchor.getNode();
602
+ const focus = selection.focus.getNode();
603
+ const [anchorCell] = $getNodeTriplet(anchor);
604
+ const [focusCell,, grid] = $getNodeTriplet(focus);
605
+ const [gridMap, focusCellMap, anchorCellMap] = $computeTableMap(grid, focusCell, anchorCell);
606
+ const rowCount = gridMap.length;
607
+ const startColumn = insertAfter ? Math.max(focusCellMap.startColumn, anchorCellMap.startColumn) : Math.min(focusCellMap.startColumn, anchorCellMap.startColumn);
608
+ const insertAfterColumn = insertAfter ? startColumn + focusCell.__colSpan - 1 : startColumn - 1;
609
+ const gridFirstChild = grid.getFirstChild();
610
+ if (!$isTableRowNode(gridFirstChild)) {
611
+ throw Error(`Expected firstTable child to be a row`);
612
+ }
613
+ let firstInsertedCell = null;
614
+ function $createTableCellNodeForInsertTableColumn(headerState = TableCellHeaderStates.NO_STATUS) {
615
+ const cell = $createTableCellNode(headerState).append($createParagraphNode());
616
+ if (firstInsertedCell === null) {
617
+ firstInsertedCell = cell;
618
+ }
619
+ return cell;
620
+ }
621
+ let loopRow = gridFirstChild;
622
+ rowLoop: for (let i = 0; i < rowCount; i++) {
623
+ if (i !== 0) {
624
+ const currentRow = loopRow.getNextSibling();
625
+ if (!$isTableRowNode(currentRow)) {
626
+ throw Error(`Expected row nextSibling to be a row`);
627
+ }
628
+ loopRow = currentRow;
629
+ }
630
+ const rowMap = gridMap[i];
631
+ const currentCellHeaderState = rowMap[insertAfterColumn < 0 ? 0 : insertAfterColumn].cell.__headerState;
632
+ const headerState = getHeaderState(currentCellHeaderState, TableCellHeaderStates.ROW);
633
+ if (insertAfterColumn < 0) {
634
+ $insertFirst(loopRow, $createTableCellNodeForInsertTableColumn(headerState));
635
+ continue;
636
+ }
637
+ const {
638
+ cell: currentCell,
639
+ startColumn: currentStartColumn,
640
+ startRow: currentStartRow
641
+ } = rowMap[insertAfterColumn];
642
+ if (currentStartColumn + currentCell.__colSpan - 1 <= insertAfterColumn) {
643
+ let insertAfterCell = currentCell;
644
+ let insertAfterCellRowStart = currentStartRow;
645
+ let prevCellIndex = insertAfterColumn;
646
+ while (insertAfterCellRowStart !== i && insertAfterCell.__rowSpan > 1) {
647
+ prevCellIndex -= currentCell.__colSpan;
648
+ if (prevCellIndex >= 0) {
649
+ const {
650
+ cell: cell_,
651
+ startRow: startRow_
652
+ } = rowMap[prevCellIndex];
653
+ insertAfterCell = cell_;
654
+ insertAfterCellRowStart = startRow_;
655
+ } else {
656
+ loopRow.append($createTableCellNodeForInsertTableColumn(headerState));
657
+ continue rowLoop;
658
+ }
659
+ }
660
+ insertAfterCell.insertAfter($createTableCellNodeForInsertTableColumn(headerState));
661
+ } else {
662
+ currentCell.setColSpan(currentCell.__colSpan + 1);
663
+ }
664
+ }
665
+ if (firstInsertedCell !== null) {
666
+ $moveSelectionToCell(firstInsertedCell);
667
+ }
668
+ }
669
+ function $deleteTableColumn(tableNode, targetIndex) {
670
+ const tableRows = tableNode.getChildren();
671
+ for (let i = 0; i < tableRows.length; i++) {
672
+ const currentTableRowNode = tableRows[i];
673
+ if ($isTableRowNode(currentTableRowNode)) {
674
+ const tableRowChildren = currentTableRowNode.getChildren();
675
+ if (targetIndex >= tableRowChildren.length || targetIndex < 0) {
676
+ throw new Error('Table column target index out of range');
677
+ }
678
+ tableRowChildren[targetIndex].remove();
679
+ }
680
+ }
681
+ return tableNode;
682
+ }
683
+ function $deleteTableRow__EXPERIMENTAL() {
684
+ const selection = $getSelection();
685
+ if (!($isRangeSelection(selection) || $isTableSelection(selection))) {
686
+ throw Error(`Expected a RangeSelection or GridSelection`);
687
+ }
688
+ const anchor = selection.anchor.getNode();
689
+ const focus = selection.focus.getNode();
690
+ const [anchorCell,, grid] = $getNodeTriplet(anchor);
691
+ const [focusCell] = $getNodeTriplet(focus);
692
+ const [gridMap, anchorCellMap, focusCellMap] = $computeTableMap(grid, anchorCell, focusCell);
693
+ const {
694
+ startRow: anchorStartRow
695
+ } = anchorCellMap;
696
+ const {
697
+ startRow: focusStartRow
698
+ } = focusCellMap;
699
+ const focusEndRow = focusStartRow + focusCell.__rowSpan - 1;
700
+ if (gridMap.length === focusEndRow - anchorStartRow + 1) {
701
+ // Empty grid
702
+ grid.remove();
703
+ return;
704
+ }
705
+ const columnCount = gridMap[0].length;
706
+ const nextRow = gridMap[focusEndRow + 1];
707
+ const nextRowNode = grid.getChildAtIndex(focusEndRow + 1);
708
+ for (let row = focusEndRow; row >= anchorStartRow; row--) {
709
+ for (let column = columnCount - 1; column >= 0; column--) {
710
+ const {
711
+ cell,
712
+ startRow: cellStartRow,
713
+ startColumn: cellStartColumn
714
+ } = gridMap[row][column];
715
+ if (cellStartColumn !== column) {
716
+ // Don't repeat work for the same Cell
717
+ continue;
718
+ }
719
+ // Rows overflowing top have to be trimmed
720
+ if (row === anchorStartRow && cellStartRow < anchorStartRow) {
721
+ cell.setRowSpan(cell.__rowSpan - (cellStartRow - anchorStartRow));
722
+ }
723
+ // Rows overflowing bottom have to be trimmed and moved to the next row
724
+ if (cellStartRow >= anchorStartRow && cellStartRow + cell.__rowSpan - 1 > focusEndRow) {
725
+ cell.setRowSpan(cell.__rowSpan - (focusEndRow - cellStartRow + 1));
726
+ if (!(nextRowNode !== null)) {
727
+ throw Error(`Expected nextRowNode not to be null`);
728
+ }
729
+ if (column === 0) {
730
+ $insertFirst(nextRowNode, cell);
731
+ } else {
732
+ const {
733
+ cell: previousCell
734
+ } = nextRow[column - 1];
735
+ previousCell.insertAfter(cell);
736
+ }
737
+ }
738
+ }
739
+ const rowNode = grid.getChildAtIndex(row);
740
+ if (!$isTableRowNode(rowNode)) {
741
+ throw Error(`Expected GridNode childAtIndex(${String(row)}) to be RowNode`);
742
+ }
743
+ rowNode.remove();
744
+ }
745
+ if (nextRow !== undefined) {
746
+ const {
747
+ cell
748
+ } = nextRow[0];
749
+ $moveSelectionToCell(cell);
750
+ } else {
751
+ const previousRow = gridMap[anchorStartRow - 1];
752
+ const {
753
+ cell
754
+ } = previousRow[0];
755
+ $moveSelectionToCell(cell);
756
+ }
757
+ }
758
+ function $deleteTableColumn__EXPERIMENTAL() {
759
+ const selection = $getSelection();
760
+ if (!($isRangeSelection(selection) || $isTableSelection(selection))) {
761
+ throw Error(`Expected a RangeSelection or GridSelection`);
762
+ }
763
+ const anchor = selection.anchor.getNode();
764
+ const focus = selection.focus.getNode();
765
+ const [anchorCell,, grid] = $getNodeTriplet(anchor);
766
+ const [focusCell] = $getNodeTriplet(focus);
767
+ const [gridMap, anchorCellMap, focusCellMap] = $computeTableMap(grid, anchorCell, focusCell);
768
+ const {
769
+ startColumn: anchorStartColumn
770
+ } = anchorCellMap;
771
+ const {
772
+ startRow: focusStartRow,
773
+ startColumn: focusStartColumn
774
+ } = focusCellMap;
775
+ const startColumn = Math.min(anchorStartColumn, focusStartColumn);
776
+ const endColumn = Math.max(anchorStartColumn + anchorCell.__colSpan - 1, focusStartColumn + focusCell.__colSpan - 1);
777
+ const selectedColumnCount = endColumn - startColumn + 1;
778
+ const columnCount = gridMap[0].length;
779
+ if (columnCount === endColumn - startColumn + 1) {
780
+ // Empty grid
781
+ grid.selectPrevious();
782
+ grid.remove();
783
+ return;
784
+ }
785
+ const rowCount = gridMap.length;
786
+ for (let row = 0; row < rowCount; row++) {
787
+ for (let column = startColumn; column <= endColumn; column++) {
788
+ const {
789
+ cell,
790
+ startColumn: cellStartColumn
791
+ } = gridMap[row][column];
792
+ if (cellStartColumn < startColumn) {
793
+ if (column === startColumn) {
794
+ const overflowLeft = startColumn - cellStartColumn;
795
+ // Overflowing left
796
+ cell.setColSpan(cell.__colSpan -
797
+ // Possible overflow right too
798
+ Math.min(selectedColumnCount, cell.__colSpan - overflowLeft));
799
+ }
800
+ } else if (cellStartColumn + cell.__colSpan - 1 > endColumn) {
801
+ if (column === endColumn) {
802
+ // Overflowing right
803
+ const inSelectedArea = endColumn - cellStartColumn + 1;
804
+ cell.setColSpan(cell.__colSpan - inSelectedArea);
805
+ }
806
+ } else {
807
+ cell.remove();
808
+ }
809
+ }
810
+ }
811
+ const focusRowMap = gridMap[focusStartRow];
812
+ const nextColumn = focusRowMap[focusStartColumn + focusCell.__colSpan];
813
+ if (nextColumn !== undefined) {
814
+ const {
815
+ cell
816
+ } = nextColumn;
817
+ $moveSelectionToCell(cell);
818
+ } else {
819
+ const previousRow = focusRowMap[focusStartColumn - 1];
820
+ const {
821
+ cell
822
+ } = previousRow;
823
+ $moveSelectionToCell(cell);
824
+ }
825
+ }
826
+ function $moveSelectionToCell(cell) {
827
+ const firstDescendant = cell.getFirstDescendant();
828
+ if (firstDescendant == null) {
829
+ cell.selectStart();
830
+ } else {
831
+ firstDescendant.getParentOrThrow().selectStart();
832
+ }
833
+ }
834
+ function $insertFirst(parent, node) {
835
+ const firstChild = parent.getFirstChild();
836
+ if (firstChild !== null) {
837
+ firstChild.insertBefore(node);
838
+ } else {
839
+ parent.append(node);
840
+ }
841
+ }
842
+ function $unmergeCell() {
843
+ const selection = $getSelection();
844
+ if (!($isRangeSelection(selection) || $isTableSelection(selection))) {
845
+ throw Error(`Expected a RangeSelection or GridSelection`);
846
+ }
847
+ const anchor = selection.anchor.getNode();
848
+ const [cell, row, grid] = $getNodeTriplet(anchor);
849
+ const colSpan = cell.__colSpan;
850
+ const rowSpan = cell.__rowSpan;
851
+ if (colSpan > 1) {
852
+ for (let i = 1; i < colSpan; i++) {
853
+ cell.insertAfter($createTableCellNode(TableCellHeaderStates.NO_STATUS));
854
+ }
855
+ cell.setColSpan(1);
856
+ }
857
+ if (rowSpan > 1) {
858
+ const [map, cellMap] = $computeTableMap(grid, cell, cell);
859
+ const {
860
+ startColumn,
861
+ startRow
862
+ } = cellMap;
863
+ let currentRowNode;
864
+ for (let i = 1; i < rowSpan; i++) {
865
+ const currentRow = startRow + i;
866
+ const currentRowMap = map[currentRow];
867
+ currentRowNode = (currentRowNode || row).getNextSibling();
868
+ if (!$isTableRowNode(currentRowNode)) {
869
+ throw Error(`Expected row next sibling to be a row`);
870
+ }
871
+ let insertAfterCell = null;
872
+ for (let column = 0; column < startColumn; column++) {
873
+ const currentCellMap = currentRowMap[column];
874
+ const currentCell = currentCellMap.cell;
875
+ if (currentCellMap.startRow === currentRow) {
876
+ insertAfterCell = currentCell;
877
+ }
878
+ if (currentCell.__colSpan > 1) {
879
+ column += currentCell.__colSpan - 1;
880
+ }
881
+ }
882
+ if (insertAfterCell === null) {
883
+ for (let j = 0; j < colSpan; j++) {
884
+ $insertFirst(currentRowNode, $createTableCellNode(TableCellHeaderStates.NO_STATUS));
885
+ }
886
+ } else {
887
+ for (let j = 0; j < colSpan; j++) {
888
+ insertAfterCell.insertAfter($createTableCellNode(TableCellHeaderStates.NO_STATUS));
889
+ }
890
+ }
891
+ }
892
+ cell.setRowSpan(1);
893
+ }
894
+ }
895
+ function $computeTableMap(grid, cellA, cellB) {
896
+ const tableMap = [];
897
+ let cellAValue = null;
898
+ let cellBValue = null;
899
+ function write(startRow, startColumn, cell) {
900
+ const value = {
901
+ cell,
902
+ startColumn,
903
+ startRow
904
+ };
905
+ const rowSpan = cell.__rowSpan;
906
+ const colSpan = cell.__colSpan;
907
+ for (let i = 0; i < rowSpan; i++) {
908
+ if (tableMap[startRow + i] === undefined) {
909
+ tableMap[startRow + i] = [];
910
+ }
911
+ for (let j = 0; j < colSpan; j++) {
912
+ tableMap[startRow + i][startColumn + j] = value;
913
+ }
914
+ }
915
+ if (cellA.is(cell)) {
916
+ cellAValue = value;
917
+ }
918
+ if (cellB.is(cell)) {
919
+ cellBValue = value;
920
+ }
921
+ }
922
+ function isEmpty(row, column) {
923
+ return tableMap[row] === undefined || tableMap[row][column] === undefined;
924
+ }
925
+ const gridChildren = grid.getChildren();
926
+ for (let i = 0; i < gridChildren.length; i++) {
927
+ const row = gridChildren[i];
928
+ if (!$isTableRowNode(row)) {
929
+ throw Error(`Expected GridNode children to be TableRowNode`);
930
+ }
931
+ const rowChildren = row.getChildren();
932
+ let j = 0;
933
+ for (const cell of rowChildren) {
934
+ if (!$isTableCellNode(cell)) {
935
+ throw Error(`Expected TableRowNode children to be TableCellNode`);
936
+ }
937
+ while (!isEmpty(i, j)) {
938
+ j++;
939
+ }
940
+ write(i, j, cell);
941
+ j += cell.__colSpan;
942
+ }
943
+ }
944
+ if (!(cellAValue !== null)) {
945
+ throw Error(`Anchor not found in Grid`);
946
+ }
947
+ if (!(cellBValue !== null)) {
948
+ throw Error(`Focus not found in Grid`);
949
+ }
950
+ return [tableMap, cellAValue, cellBValue];
951
+ }
952
+ function $getNodeTriplet(source) {
953
+ let cell;
954
+ if (source instanceof TableCellNode) {
955
+ cell = source;
956
+ } else if ('__type' in source) {
957
+ const cell_ = $findMatchingParent(source, $isTableCellNode);
958
+ if (!$isTableCellNode(cell_)) {
959
+ throw Error(`Expected to find a parent TableCellNode`);
960
+ }
961
+ cell = cell_;
962
+ } else {
963
+ const cell_ = $findMatchingParent(source.getNode(), $isTableCellNode);
964
+ if (!$isTableCellNode(cell_)) {
965
+ throw Error(`Expected to find a parent TableCellNode`);
966
+ }
967
+ cell = cell_;
968
+ }
969
+ const row = cell.getParent();
970
+ if (!$isTableRowNode(row)) {
971
+ throw Error(`Expected TableCellNode to have a parent TableRowNode`);
972
+ }
973
+ const grid = row.getParent();
974
+ if (!$isTableNode(grid)) {
975
+ throw Error(`Expected TableRowNode to have a parent GridNode`);
976
+ }
977
+ return [cell, row, grid];
978
+ }
979
+ function $getTableCellNodeRect(tableCellNode) {
980
+ const [cellNode,, gridNode] = $getNodeTriplet(tableCellNode);
981
+ const rows = gridNode.getChildren();
982
+ const rowCount = rows.length;
983
+ const columnCount = rows[0].getChildren().length;
984
+
985
+ // Create a matrix of the same size as the table to track the position of each cell
986
+ const cellMatrix = new Array(rowCount);
987
+ for (let i = 0; i < rowCount; i++) {
988
+ cellMatrix[i] = new Array(columnCount);
989
+ }
990
+ for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
991
+ const row = rows[rowIndex];
992
+ const cells = row.getChildren();
993
+ let columnIndex = 0;
994
+ for (let cellIndex = 0; cellIndex < cells.length; cellIndex++) {
995
+ // Find the next available position in the matrix, skip the position of merged cells
996
+ while (cellMatrix[rowIndex][columnIndex]) {
997
+ columnIndex++;
998
+ }
999
+ const cell = cells[cellIndex];
1000
+ const rowSpan = cell.__rowSpan || 1;
1001
+ const colSpan = cell.__colSpan || 1;
1002
+
1003
+ // Put the cell into the corresponding position in the matrix
1004
+ for (let i = 0; i < rowSpan; i++) {
1005
+ for (let j = 0; j < colSpan; j++) {
1006
+ cellMatrix[rowIndex + i][columnIndex + j] = cell;
1007
+ }
1008
+ }
1009
+
1010
+ // Return to the original index, row span and column span of the cell.
1011
+ if (cellNode === cell) {
1012
+ return {
1013
+ colSpan,
1014
+ columnIndex,
1015
+ rowIndex,
1016
+ rowSpan
1017
+ };
1018
+ }
1019
+ columnIndex += colSpan;
1020
+ }
1021
+ }
1022
+ return null;
1023
+ }
1024
+
1025
+ /**
1026
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
1027
+ *
1028
+ * This source code is licensed under the MIT license found in the
1029
+ * LICENSE file in the root directory of this source tree.
1030
+ *
1031
+ */
1032
+ class TableSelection {
1033
+ constructor(tableKey, anchor, focus) {
1034
+ this.anchor = anchor;
1035
+ this.focus = focus;
1036
+ anchor._selection = this;
1037
+ focus._selection = this;
1038
+ this._cachedNodes = null;
1039
+ this.dirty = false;
1040
+ this.tableKey = tableKey;
1041
+ }
1042
+ getStartEndPoints() {
1043
+ return [this.anchor, this.focus];
1044
+ }
1045
+
1046
+ /**
1047
+ * Returns whether the Selection is "backwards", meaning the focus
1048
+ * logically precedes the anchor in the EditorState.
1049
+ * @returns true if the Selection is backwards, false otherwise.
1050
+ */
1051
+ isBackward() {
1052
+ return this.focus.isBefore(this.anchor);
1053
+ }
1054
+ getCachedNodes() {
1055
+ return this._cachedNodes;
1056
+ }
1057
+ setCachedNodes(nodes) {
1058
+ this._cachedNodes = nodes;
1059
+ }
1060
+ is(selection) {
1061
+ if (!$isTableSelection(selection)) {
1062
+ return false;
1063
+ }
1064
+ return this.tableKey === selection.tableKey && this.anchor.is(selection.anchor) && this.focus.is(selection.focus);
1065
+ }
1066
+ set(tableKey, anchorCellKey, focusCellKey) {
1067
+ this.dirty = true;
1068
+ this.tableKey = tableKey;
1069
+ this.anchor.key = anchorCellKey;
1070
+ this.focus.key = focusCellKey;
1071
+ this._cachedNodes = null;
1072
+ }
1073
+ clone() {
1074
+ return new TableSelection(this.tableKey, this.anchor, this.focus);
1075
+ }
1076
+ isCollapsed() {
1077
+ return false;
1078
+ }
1079
+ extract() {
1080
+ return this.getNodes();
1081
+ }
1082
+ insertRawText(text) {
1083
+ // Do nothing?
1084
+ }
1085
+ insertText() {
1086
+ // Do nothing?
1087
+ }
1088
+ insertNodes(nodes) {
1089
+ const focusNode = this.focus.getNode();
1090
+ if (!$isElementNode(focusNode)) {
1091
+ throw Error(`Expected TableSelection focus to be an ElementNode`);
1092
+ }
1093
+ const selection = $normalizeSelection__EXPERIMENTAL(focusNode.select(0, focusNode.getChildrenSize()));
1094
+ selection.insertNodes(nodes);
1095
+ }
1096
+
1097
+ // TODO Deprecate this method. It's confusing when used with colspan|rowspan
1098
+ getShape() {
1099
+ const anchorCellNode = $getNodeByKey(this.anchor.key);
1100
+ if (!$isTableCellNode(anchorCellNode)) {
1101
+ throw Error(`Expected TableSelection anchor to be (or a child of) TableCellNode`);
1102
+ }
1103
+ const anchorCellNodeRect = $getTableCellNodeRect(anchorCellNode);
1104
+ if (!(anchorCellNodeRect !== null)) {
1105
+ throw Error(`getCellRect: expected to find AnchorNode`);
1106
+ }
1107
+ const focusCellNode = $getNodeByKey(this.focus.key);
1108
+ if (!$isTableCellNode(focusCellNode)) {
1109
+ throw Error(`Expected TableSelection focus to be (or a child of) TableCellNode`);
1110
+ }
1111
+ const focusCellNodeRect = $getTableCellNodeRect(focusCellNode);
1112
+ if (!(focusCellNodeRect !== null)) {
1113
+ throw Error(`getCellRect: expected to find focusCellNode`);
1114
+ }
1115
+ const startX = Math.min(anchorCellNodeRect.columnIndex, focusCellNodeRect.columnIndex);
1116
+ const stopX = Math.max(anchorCellNodeRect.columnIndex, focusCellNodeRect.columnIndex);
1117
+ const startY = Math.min(anchorCellNodeRect.rowIndex, focusCellNodeRect.rowIndex);
1118
+ const stopY = Math.max(anchorCellNodeRect.rowIndex, focusCellNodeRect.rowIndex);
1119
+ return {
1120
+ fromX: Math.min(startX, stopX),
1121
+ fromY: Math.min(startY, stopY),
1122
+ toX: Math.max(startX, stopX),
1123
+ toY: Math.max(startY, stopY)
1124
+ };
1125
+ }
1126
+ getNodes() {
1127
+ const cachedNodes = this._cachedNodes;
1128
+ if (cachedNodes !== null) {
1129
+ return cachedNodes;
1130
+ }
1131
+ const anchorNode = this.anchor.getNode();
1132
+ const focusNode = this.focus.getNode();
1133
+ const anchorCell = $findMatchingParent(anchorNode, $isTableCellNode);
1134
+ // todo replace with triplet
1135
+ const focusCell = $findMatchingParent(focusNode, $isTableCellNode);
1136
+ if (!$isTableCellNode(anchorCell)) {
1137
+ throw Error(`Expected TableSelection anchor to be (or a child of) TableCellNode`);
1138
+ }
1139
+ if (!$isTableCellNode(focusCell)) {
1140
+ throw Error(`Expected TableSelection focus to be (or a child of) TableCellNode`);
1141
+ }
1142
+ const anchorRow = anchorCell.getParent();
1143
+ if (!$isTableRowNode(anchorRow)) {
1144
+ throw Error(`Expected anchorCell to have a parent TableRowNode`);
1145
+ }
1146
+ const tableNode = anchorRow.getParent();
1147
+ if (!$isTableNode(tableNode)) {
1148
+ throw Error(`Expected tableNode to have a parent TableNode`);
1149
+ }
1150
+ const focusCellGrid = focusCell.getParents()[1];
1151
+ if (focusCellGrid !== tableNode) {
1152
+ if (!tableNode.isParentOf(focusCell)) {
1153
+ // focus is on higher Grid level than anchor
1154
+ const gridParent = tableNode.getParent();
1155
+ if (!(gridParent != null)) {
1156
+ throw Error(`Expected gridParent to have a parent`);
1157
+ }
1158
+ this.set(this.tableKey, gridParent.getKey(), focusCell.getKey());
1159
+ } else {
1160
+ // anchor is on higher Grid level than focus
1161
+ const focusCellParent = focusCellGrid.getParent();
1162
+ if (!(focusCellParent != null)) {
1163
+ throw Error(`Expected focusCellParent to have a parent`);
1164
+ }
1165
+ this.set(this.tableKey, focusCell.getKey(), focusCellParent.getKey());
1166
+ }
1167
+ return this.getNodes();
1168
+ }
1169
+
1170
+ // TODO Mapping the whole Grid every time not efficient. We need to compute the entire state only
1171
+ // once (on load) and iterate on it as updates occur. However, to do this we need to have the
1172
+ // ability to store a state. Killing TableSelection and moving the logic to the plugin would make
1173
+ // this possible.
1174
+ const [map, cellAMap, cellBMap] = $computeTableMap(tableNode, anchorCell, focusCell);
1175
+ let minColumn = Math.min(cellAMap.startColumn, cellBMap.startColumn);
1176
+ let minRow = Math.min(cellAMap.startRow, cellBMap.startRow);
1177
+ let maxColumn = Math.max(cellAMap.startColumn + cellAMap.cell.__colSpan - 1, cellBMap.startColumn + cellBMap.cell.__colSpan - 1);
1178
+ let maxRow = Math.max(cellAMap.startRow + cellAMap.cell.__rowSpan - 1, cellBMap.startRow + cellBMap.cell.__rowSpan - 1);
1179
+ let exploredMinColumn = minColumn;
1180
+ let exploredMinRow = minRow;
1181
+ let exploredMaxColumn = minColumn;
1182
+ let exploredMaxRow = minRow;
1183
+ function expandBoundary(mapValue) {
1184
+ const {
1185
+ cell,
1186
+ startColumn: cellStartColumn,
1187
+ startRow: cellStartRow
1188
+ } = mapValue;
1189
+ minColumn = Math.min(minColumn, cellStartColumn);
1190
+ minRow = Math.min(minRow, cellStartRow);
1191
+ maxColumn = Math.max(maxColumn, cellStartColumn + cell.__colSpan - 1);
1192
+ maxRow = Math.max(maxRow, cellStartRow + cell.__rowSpan - 1);
1193
+ }
1194
+ while (minColumn < exploredMinColumn || minRow < exploredMinRow || maxColumn > exploredMaxColumn || maxRow > exploredMaxRow) {
1195
+ if (minColumn < exploredMinColumn) {
1196
+ // Expand on the left
1197
+ const rowDiff = exploredMaxRow - exploredMinRow;
1198
+ const previousColumn = exploredMinColumn - 1;
1199
+ for (let i = 0; i <= rowDiff; i++) {
1200
+ expandBoundary(map[exploredMinRow + i][previousColumn]);
1201
+ }
1202
+ exploredMinColumn = previousColumn;
1203
+ }
1204
+ if (minRow < exploredMinRow) {
1205
+ // Expand on top
1206
+ const columnDiff = exploredMaxColumn - exploredMinColumn;
1207
+ const previousRow = exploredMinRow - 1;
1208
+ for (let i = 0; i <= columnDiff; i++) {
1209
+ expandBoundary(map[previousRow][exploredMinColumn + i]);
1210
+ }
1211
+ exploredMinRow = previousRow;
1212
+ }
1213
+ if (maxColumn > exploredMaxColumn) {
1214
+ // Expand on the right
1215
+ const rowDiff = exploredMaxRow - exploredMinRow;
1216
+ const nextColumn = exploredMaxColumn + 1;
1217
+ for (let i = 0; i <= rowDiff; i++) {
1218
+ expandBoundary(map[exploredMinRow + i][nextColumn]);
1219
+ }
1220
+ exploredMaxColumn = nextColumn;
1221
+ }
1222
+ if (maxRow > exploredMaxRow) {
1223
+ // Expand on the bottom
1224
+ const columnDiff = exploredMaxColumn - exploredMinColumn;
1225
+ const nextRow = exploredMaxRow + 1;
1226
+ for (let i = 0; i <= columnDiff; i++) {
1227
+ expandBoundary(map[nextRow][exploredMinColumn + i]);
1228
+ }
1229
+ exploredMaxRow = nextRow;
1230
+ }
1231
+ }
1232
+ const nodes = [tableNode];
1233
+ let lastRow = null;
1234
+ for (let i = minRow; i <= maxRow; i++) {
1235
+ for (let j = minColumn; j <= maxColumn; j++) {
1236
+ const {
1237
+ cell
1238
+ } = map[i][j];
1239
+ const currentRow = cell.getParent();
1240
+ if (!$isTableRowNode(currentRow)) {
1241
+ throw Error(`Expected TableCellNode parent to be a TableRowNode`);
1242
+ }
1243
+ if (currentRow !== lastRow) {
1244
+ nodes.push(currentRow);
1245
+ }
1246
+ nodes.push(cell, ...$getChildrenRecursively(cell));
1247
+ lastRow = currentRow;
1248
+ }
1249
+ }
1250
+ if (!isCurrentlyReadOnlyMode()) {
1251
+ this._cachedNodes = nodes;
1252
+ }
1253
+ return nodes;
1254
+ }
1255
+ getTextContent() {
1256
+ const nodes = this.getNodes();
1257
+ let textContent = '';
1258
+ for (let i = 0; i < nodes.length; i++) {
1259
+ textContent += nodes[i].getTextContent();
1260
+ }
1261
+ return textContent;
1262
+ }
1263
+ }
1264
+ function $isTableSelection(x) {
1265
+ return x instanceof TableSelection;
1266
+ }
1267
+ function $createTableSelection() {
1268
+ const anchor = $createPoint('root', 0, 'element');
1269
+ const focus = $createPoint('root', 0, 'element');
1270
+ return new TableSelection('root', anchor, focus);
1271
+ }
1272
+ function $getChildrenRecursively(node) {
1273
+ const nodes = [];
1274
+ const stack = [node];
1275
+ while (stack.length > 0) {
1276
+ const currentNode = stack.pop();
1277
+ if (!(currentNode !== undefined)) {
1278
+ throw Error(`Stack.length > 0; can't be undefined`);
1279
+ }
1280
+ if ($isElementNode(currentNode)) {
1281
+ stack.unshift(...currentNode.getChildren());
1282
+ }
1283
+ if (currentNode !== node) {
1284
+ nodes.push(currentNode);
1285
+ }
1286
+ }
1287
+ return nodes;
1288
+ }
1289
+
1290
+ /**
1291
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
1292
+ *
1293
+ * This source code is licensed under the MIT license found in the
1294
+ * LICENSE file in the root directory of this source tree.
1295
+ *
1296
+ */
1297
+ const getDOMSelection = targetWindow => CAN_USE_DOM ? (targetWindow || window).getSelection() : null;
1298
+ class TableObserver {
1299
+ constructor(editor, tableNodeKey) {
1300
+ this.isHighlightingCells = false;
1301
+ this.anchorX = -1;
1302
+ this.anchorY = -1;
1303
+ this.focusX = -1;
1304
+ this.focusY = -1;
1305
+ this.listenersToRemove = new Set();
1306
+ this.tableNodeKey = tableNodeKey;
1307
+ this.editor = editor;
1308
+ this.table = {
1309
+ columns: 0,
1310
+ domRows: [],
1311
+ rows: 0
1312
+ };
1313
+ this.tableSelection = null;
1314
+ this.anchorCellNodeKey = null;
1315
+ this.focusCellNodeKey = null;
1316
+ this.anchorCell = null;
1317
+ this.focusCell = null;
1318
+ this.hasHijackedSelectionStyles = false;
1319
+ this.trackTable();
1320
+ }
1321
+ getTable() {
1322
+ return this.table;
1323
+ }
1324
+ removeListeners() {
1325
+ Array.from(this.listenersToRemove).forEach(removeListener => removeListener());
1326
+ }
1327
+ trackTable() {
1328
+ const observer = new MutationObserver(records => {
1329
+ this.editor.update(() => {
1330
+ let gridNeedsRedraw = false;
1331
+ for (let i = 0; i < records.length; i++) {
1332
+ const record = records[i];
1333
+ const target = record.target;
1334
+ const nodeName = target.nodeName;
1335
+ if (nodeName === 'TABLE' || nodeName === 'TR') {
1336
+ gridNeedsRedraw = true;
1337
+ break;
1338
+ }
1339
+ }
1340
+ if (!gridNeedsRedraw) {
1341
+ return;
1342
+ }
1343
+ const tableElement = this.editor.getElementByKey(this.tableNodeKey);
1344
+ if (!tableElement) {
1345
+ throw new Error('Expected to find TableElement in DOM');
1346
+ }
1347
+ this.table = getTable(tableElement);
1348
+ });
1349
+ });
1350
+ this.editor.update(() => {
1351
+ const tableElement = this.editor.getElementByKey(this.tableNodeKey);
1352
+ if (!tableElement) {
1353
+ throw new Error('Expected to find TableElement in DOM');
1354
+ }
1355
+ this.table = getTable(tableElement);
1356
+ observer.observe(tableElement, {
1357
+ childList: true,
1358
+ subtree: true
1359
+ });
1360
+ });
1361
+ }
1362
+ clearHighlight() {
1363
+ const editor = this.editor;
1364
+ this.isHighlightingCells = false;
1365
+ this.anchorX = -1;
1366
+ this.anchorY = -1;
1367
+ this.focusX = -1;
1368
+ this.focusY = -1;
1369
+ this.tableSelection = null;
1370
+ this.anchorCellNodeKey = null;
1371
+ this.focusCellNodeKey = null;
1372
+ this.anchorCell = null;
1373
+ this.focusCell = null;
1374
+ this.hasHijackedSelectionStyles = false;
1375
+ this.enableHighlightStyle();
1376
+ editor.update(() => {
1377
+ const tableNode = $getNodeByKey(this.tableNodeKey);
1378
+ if (!$isTableNode(tableNode)) {
1379
+ throw new Error('Expected TableNode.');
1380
+ }
1381
+ const tableElement = editor.getElementByKey(this.tableNodeKey);
1382
+ if (!tableElement) {
1383
+ throw new Error('Expected to find TableElement in DOM');
1384
+ }
1385
+ const grid = getTable(tableElement);
1386
+ $updateDOMForSelection(editor, grid, null);
1387
+ $setSelection(null);
1388
+ editor.dispatchCommand(SELECTION_CHANGE_COMMAND, undefined);
1389
+ });
1390
+ }
1391
+ enableHighlightStyle() {
1392
+ const editor = this.editor;
1393
+ editor.update(() => {
1394
+ const tableElement = editor.getElementByKey(this.tableNodeKey);
1395
+ if (!tableElement) {
1396
+ throw new Error('Expected to find TableElement in DOM');
1397
+ }
1398
+ removeClassNamesFromElement(tableElement, editor._config.theme.tableSelection);
1399
+ tableElement.classList.remove('disable-selection');
1400
+ this.hasHijackedSelectionStyles = false;
1401
+ });
1402
+ }
1403
+ disableHighlightStyle() {
1404
+ const editor = this.editor;
1405
+ editor.update(() => {
1406
+ const tableElement = editor.getElementByKey(this.tableNodeKey);
1407
+ if (!tableElement) {
1408
+ throw new Error('Expected to find TableElement in DOM');
1409
+ }
1410
+ addClassNamesToElement(tableElement, editor._config.theme.tableSelection);
1411
+ this.hasHijackedSelectionStyles = true;
1412
+ });
1413
+ }
1414
+ updateTableTableSelection(selection) {
1415
+ if (selection !== null && selection.tableKey === this.tableNodeKey) {
1416
+ const editor = this.editor;
1417
+ this.tableSelection = selection;
1418
+ this.isHighlightingCells = true;
1419
+ this.disableHighlightStyle();
1420
+ $updateDOMForSelection(editor, this.table, this.tableSelection);
1421
+ } else if (selection == null) {
1422
+ this.clearHighlight();
1423
+ } else {
1424
+ this.tableNodeKey = selection.tableKey;
1425
+ this.updateTableTableSelection(selection);
1426
+ }
1427
+ }
1428
+ setFocusCellForSelection(cell, ignoreStart = false) {
1429
+ const editor = this.editor;
1430
+ editor.update(() => {
1431
+ const tableNode = $getNodeByKey(this.tableNodeKey);
1432
+ if (!$isTableNode(tableNode)) {
1433
+ throw new Error('Expected TableNode.');
1434
+ }
1435
+ const tableElement = editor.getElementByKey(this.tableNodeKey);
1436
+ if (!tableElement) {
1437
+ throw new Error('Expected to find TableElement in DOM');
1438
+ }
1439
+ const cellX = cell.x;
1440
+ const cellY = cell.y;
1441
+ this.focusCell = cell;
1442
+ if (this.anchorCell !== null) {
1443
+ const domSelection = getDOMSelection(editor._window);
1444
+ // Collapse the selection
1445
+ if (domSelection) {
1446
+ domSelection.setBaseAndExtent(this.anchorCell.elem, 0, this.focusCell.elem, 0);
1447
+ }
1448
+ }
1449
+ if (!this.isHighlightingCells && (this.anchorX !== cellX || this.anchorY !== cellY || ignoreStart)) {
1450
+ this.isHighlightingCells = true;
1451
+ this.disableHighlightStyle();
1452
+ } else if (cellX === this.focusX && cellY === this.focusY) {
1453
+ return;
1454
+ }
1455
+ this.focusX = cellX;
1456
+ this.focusY = cellY;
1457
+ if (this.isHighlightingCells) {
1458
+ const focusTableCellNode = $getNearestNodeFromDOMNode(cell.elem);
1459
+ if (this.tableSelection != null && this.anchorCellNodeKey != null && $isTableCellNode(focusTableCellNode)) {
1460
+ const focusNodeKey = focusTableCellNode.getKey();
1461
+ this.tableSelection = this.tableSelection.clone() || $createTableSelection();
1462
+ this.focusCellNodeKey = focusNodeKey;
1463
+ this.tableSelection.set(this.tableNodeKey, this.anchorCellNodeKey, this.focusCellNodeKey);
1464
+ $setSelection(this.tableSelection);
1465
+ editor.dispatchCommand(SELECTION_CHANGE_COMMAND, undefined);
1466
+ $updateDOMForSelection(editor, this.table, this.tableSelection);
1467
+ }
1468
+ }
1469
+ });
1470
+ }
1471
+ setAnchorCellForSelection(cell) {
1472
+ this.isHighlightingCells = false;
1473
+ this.anchorCell = cell;
1474
+ this.anchorX = cell.x;
1475
+ this.anchorY = cell.y;
1476
+ this.editor.update(() => {
1477
+ const anchorTableCellNode = $getNearestNodeFromDOMNode(cell.elem);
1478
+ if ($isTableCellNode(anchorTableCellNode)) {
1479
+ const anchorNodeKey = anchorTableCellNode.getKey();
1480
+ this.tableSelection = this.tableSelection != null ? this.tableSelection.clone() : $createTableSelection();
1481
+ this.anchorCellNodeKey = anchorNodeKey;
1482
+ }
1483
+ });
1484
+ }
1485
+ formatCells(type) {
1486
+ this.editor.update(() => {
1487
+ const selection = $getSelection();
1488
+ if (!$isTableSelection(selection)) {
1489
+ {
1490
+ throw Error(`Expected grid selection`);
1491
+ }
1492
+ }
1493
+ const formatSelection = $createRangeSelection();
1494
+ const anchor = formatSelection.anchor;
1495
+ const focus = formatSelection.focus;
1496
+ selection.getNodes().forEach(cellNode => {
1497
+ if ($isTableCellNode(cellNode) && cellNode.getTextContentSize() !== 0) {
1498
+ anchor.set(cellNode.getKey(), 0, 'element');
1499
+ focus.set(cellNode.getKey(), cellNode.getChildrenSize(), 'element');
1500
+ formatSelection.formatText(type);
1501
+ }
1502
+ });
1503
+ $setSelection(selection);
1504
+ this.editor.dispatchCommand(SELECTION_CHANGE_COMMAND, undefined);
1505
+ });
1506
+ }
1507
+ clearText() {
1508
+ const editor = this.editor;
1509
+ editor.update(() => {
1510
+ const tableNode = $getNodeByKey(this.tableNodeKey);
1511
+ if (!$isTableNode(tableNode)) {
1512
+ throw new Error('Expected TableNode.');
1513
+ }
1514
+ const selection = $getSelection();
1515
+ if (!$isTableSelection(selection)) {
1516
+ {
1517
+ throw Error(`Expected grid selection`);
1518
+ }
1519
+ }
1520
+ const selectedNodes = selection.getNodes().filter($isTableCellNode);
1521
+ if (selectedNodes.length === this.table.columns * this.table.rows) {
1522
+ tableNode.selectPrevious();
1523
+ // Delete entire table
1524
+ tableNode.remove();
1525
+ const rootNode = $getRoot();
1526
+ rootNode.selectStart();
1527
+ return;
1528
+ }
1529
+ selectedNodes.forEach(cellNode => {
1530
+ if ($isElementNode(cellNode)) {
1531
+ const paragraphNode = $createParagraphNode();
1532
+ const textNode = $createTextNode();
1533
+ paragraphNode.append(textNode);
1534
+ cellNode.append(paragraphNode);
1535
+ cellNode.getChildren().forEach(child => {
1536
+ if (child !== paragraphNode) {
1537
+ child.remove();
1538
+ }
1539
+ });
1540
+ }
1541
+ });
1542
+ $updateDOMForSelection(editor, this.table, null);
1543
+ $setSelection(null);
1544
+ editor.dispatchCommand(SELECTION_CHANGE_COMMAND, undefined);
1545
+ });
1546
+ }
1547
+ }
1548
+
1549
+ /**
1550
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
1551
+ *
1552
+ * This source code is licensed under the MIT license found in the
1553
+ * LICENSE file in the root directory of this source tree.
1554
+ *
1555
+ */
1556
+ const LEXICAL_ELEMENT_KEY = '__lexicalTableSelection';
1557
+ function applyTableHandlers(tableNode, tableElement, editor, hasTabHandler) {
1558
+ const rootElement = editor.getRootElement();
1559
+ if (rootElement === null) {
1560
+ throw new Error('No root element.');
1561
+ }
1562
+ const tableObserver = new TableObserver(editor, tableNode.getKey());
1563
+ const editorWindow = editor._window || window;
1564
+ attachTableObserverToTableElement(tableElement, tableObserver);
1565
+ tableElement.addEventListener('mousedown', event => {
1566
+ setTimeout(() => {
1567
+ if (event.button !== 0) {
1568
+ return;
1569
+ }
1570
+ if (!editorWindow) {
1571
+ return;
1572
+ }
1573
+ const anchorCell = getDOMCellFromTarget(event.target);
1574
+ if (anchorCell !== null) {
1575
+ stopEvent(event);
1576
+ tableObserver.setAnchorCellForSelection(anchorCell);
1577
+ }
1578
+ const onMouseUp = () => {
1579
+ editorWindow.removeEventListener('mouseup', onMouseUp);
1580
+ editorWindow.removeEventListener('mousemove', onMouseMove);
1581
+ };
1582
+ const onMouseMove = moveEvent => {
1583
+ const focusCell = getDOMCellFromTarget(moveEvent.target);
1584
+ if (focusCell !== null && (tableObserver.anchorX !== focusCell.x || tableObserver.anchorY !== focusCell.y)) {
1585
+ moveEvent.preventDefault();
1586
+ tableObserver.setFocusCellForSelection(focusCell);
1587
+ }
1588
+ };
1589
+ editorWindow.addEventListener('mouseup', onMouseUp);
1590
+ editorWindow.addEventListener('mousemove', onMouseMove);
1591
+ }, 0);
1592
+ });
1593
+
1594
+ // Clear selection when clicking outside of dom.
1595
+ const mouseDownCallback = event => {
1596
+ if (event.button !== 0) {
1597
+ return;
1598
+ }
1599
+ editor.update(() => {
1600
+ const selection = $getSelection();
1601
+ const target = event.target;
1602
+ if ($isTableSelection(selection) && selection.tableKey === tableObserver.tableNodeKey && rootElement.contains(target)) {
1603
+ tableObserver.clearHighlight();
1604
+ }
1605
+ });
1606
+ };
1607
+ editorWindow.addEventListener('mousedown', mouseDownCallback);
1608
+ tableObserver.listenersToRemove.add(() => editorWindow.removeEventListener('mousedown', mouseDownCallback));
1609
+ tableObserver.listenersToRemove.add(editor.registerCommand(KEY_ARROW_DOWN_COMMAND, event => $handleArrowKey(editor, event, 'down', tableNode, tableObserver), COMMAND_PRIORITY_HIGH));
1610
+ tableObserver.listenersToRemove.add(editor.registerCommand(KEY_ARROW_UP_COMMAND, event => $handleArrowKey(editor, event, 'up', tableNode, tableObserver), COMMAND_PRIORITY_HIGH));
1611
+ tableObserver.listenersToRemove.add(editor.registerCommand(KEY_ARROW_LEFT_COMMAND, event => $handleArrowKey(editor, event, 'backward', tableNode, tableObserver), COMMAND_PRIORITY_HIGH));
1612
+ tableObserver.listenersToRemove.add(editor.registerCommand(KEY_ARROW_RIGHT_COMMAND, event => $handleArrowKey(editor, event, 'forward', tableNode, tableObserver), COMMAND_PRIORITY_HIGH));
1613
+ tableObserver.listenersToRemove.add(editor.registerCommand(KEY_ESCAPE_COMMAND, event => {
1614
+ const selection = $getSelection();
1615
+ if ($isTableSelection(selection)) {
1616
+ const focusCellNode = $findMatchingParent(selection.focus.getNode(), $isTableCellNode);
1617
+ if ($isTableCellNode(focusCellNode)) {
1618
+ stopEvent(event);
1619
+ focusCellNode.selectEnd();
1620
+ return true;
1621
+ }
1622
+ }
1623
+ return false;
1624
+ }, COMMAND_PRIORITY_HIGH));
1625
+ const deleteTextHandler = command => () => {
1626
+ const selection = $getSelection();
1627
+ if (!$isSelectionInTable(selection, tableNode)) {
1628
+ return false;
1629
+ }
1630
+ if ($isTableSelection(selection)) {
1631
+ tableObserver.clearText();
1632
+ return true;
1633
+ } else if ($isRangeSelection(selection)) {
1634
+ const tableCellNode = $findMatchingParent(selection.anchor.getNode(), n => $isTableCellNode(n));
1635
+ if (!$isTableCellNode(tableCellNode)) {
1636
+ return false;
1637
+ }
1638
+ const anchorNode = selection.anchor.getNode();
1639
+ const focusNode = selection.focus.getNode();
1640
+ const isAnchorInside = tableNode.isParentOf(anchorNode);
1641
+ const isFocusInside = tableNode.isParentOf(focusNode);
1642
+ const selectionContainsPartialTable = isAnchorInside && !isFocusInside || isFocusInside && !isAnchorInside;
1643
+ if (selectionContainsPartialTable) {
1644
+ tableObserver.clearText();
1645
+ return true;
1646
+ }
1647
+ const nearestElementNode = $findMatchingParent(selection.anchor.getNode(), n => $isElementNode(n));
1648
+ const topLevelCellElementNode = nearestElementNode && $findMatchingParent(nearestElementNode, n => $isElementNode(n) && $isTableCellNode(n.getParent()));
1649
+ if (!$isElementNode(topLevelCellElementNode) || !$isElementNode(nearestElementNode)) {
1650
+ return false;
1651
+ }
1652
+ if (command === DELETE_LINE_COMMAND && topLevelCellElementNode.getPreviousSibling() === null) {
1653
+ // TODO: Fix Delete Line in Table Cells.
1654
+ return true;
1655
+ }
1656
+ if (command === DELETE_CHARACTER_COMMAND || command === DELETE_WORD_COMMAND) {
1657
+ if (selection.isCollapsed() && selection.anchor.offset === 0) {
1658
+ if (nearestElementNode !== topLevelCellElementNode) {
1659
+ const children = nearestElementNode.getChildren();
1660
+ const newParagraphNode = $createParagraphNode();
1661
+ children.forEach(child => newParagraphNode.append(child));
1662
+ nearestElementNode.replace(newParagraphNode);
1663
+ nearestElementNode.getWritable().__parent = tableCellNode.getKey();
1664
+ return true;
1665
+ }
1666
+ }
1667
+ }
1668
+ }
1669
+ return false;
1670
+ };
1671
+ [DELETE_WORD_COMMAND, DELETE_LINE_COMMAND, DELETE_CHARACTER_COMMAND].forEach(command => {
1672
+ tableObserver.listenersToRemove.add(editor.registerCommand(command, deleteTextHandler(command), COMMAND_PRIORITY_CRITICAL));
1673
+ });
1674
+ const deleteCellHandler = event => {
1675
+ const selection = $getSelection();
1676
+ if (!$isSelectionInTable(selection, tableNode)) {
1677
+ return false;
1678
+ }
1679
+ if ($isTableSelection(selection)) {
1680
+ event.preventDefault();
1681
+ event.stopPropagation();
1682
+ tableObserver.clearText();
1683
+ return true;
1684
+ } else if ($isRangeSelection(selection)) {
1685
+ const tableCellNode = $findMatchingParent(selection.anchor.getNode(), n => $isTableCellNode(n));
1686
+ if (!$isTableCellNode(tableCellNode)) {
1687
+ return false;
1688
+ }
1689
+ }
1690
+ return false;
1691
+ };
1692
+ tableObserver.listenersToRemove.add(editor.registerCommand(KEY_BACKSPACE_COMMAND, deleteCellHandler, COMMAND_PRIORITY_CRITICAL));
1693
+ tableObserver.listenersToRemove.add(editor.registerCommand(KEY_DELETE_COMMAND, deleteCellHandler, COMMAND_PRIORITY_CRITICAL));
1694
+ tableObserver.listenersToRemove.add(editor.registerCommand(FORMAT_TEXT_COMMAND, payload => {
1695
+ const selection = $getSelection();
1696
+ if (!$isSelectionInTable(selection, tableNode)) {
1697
+ return false;
1698
+ }
1699
+ if ($isTableSelection(selection)) {
1700
+ tableObserver.formatCells(payload);
1701
+ return true;
1702
+ } else if ($isRangeSelection(selection)) {
1703
+ const tableCellNode = $findMatchingParent(selection.anchor.getNode(), n => $isTableCellNode(n));
1704
+ if (!$isTableCellNode(tableCellNode)) {
1705
+ return false;
1706
+ }
1707
+ }
1708
+ return false;
1709
+ }, COMMAND_PRIORITY_CRITICAL));
1710
+ tableObserver.listenersToRemove.add(editor.registerCommand(FORMAT_ELEMENT_COMMAND, formatType => {
1711
+ const selection = $getSelection();
1712
+ if (!$isTableSelection(selection) || !$isSelectionInTable(selection, tableNode)) {
1713
+ return false;
1714
+ }
1715
+ const anchorNode = selection.anchor.getNode();
1716
+ const focusNode = selection.focus.getNode();
1717
+ if (!$isTableCellNode(anchorNode) || !$isTableCellNode(focusNode)) {
1718
+ return false;
1719
+ }
1720
+ const [tableMap, anchorCell, focusCell] = $computeTableMap(tableNode, anchorNode, focusNode);
1721
+ const maxRow = Math.max(anchorCell.startRow, focusCell.startRow);
1722
+ const maxColumn = Math.max(anchorCell.startColumn, focusCell.startColumn);
1723
+ const minRow = Math.min(anchorCell.startRow, focusCell.startRow);
1724
+ const minColumn = Math.min(anchorCell.startColumn, focusCell.startColumn);
1725
+ for (let i = minRow; i <= maxRow; i++) {
1726
+ for (let j = minColumn; j <= maxColumn; j++) {
1727
+ const cell = tableMap[i][j].cell;
1728
+ cell.setFormat(formatType);
1729
+ const cellChildren = cell.getChildren();
1730
+ for (let k = 0; k < cellChildren.length; k++) {
1731
+ const child = cellChildren[k];
1732
+ if ($isElementNode(child) && !child.isInline()) {
1733
+ child.setFormat(formatType);
1734
+ }
1735
+ }
1736
+ }
1737
+ }
1738
+ return true;
1739
+ }, COMMAND_PRIORITY_CRITICAL));
1740
+ tableObserver.listenersToRemove.add(editor.registerCommand(CONTROLLED_TEXT_INSERTION_COMMAND, payload => {
1741
+ const selection = $getSelection();
1742
+ if (!$isSelectionInTable(selection, tableNode)) {
1743
+ return false;
1744
+ }
1745
+ if ($isTableSelection(selection)) {
1746
+ tableObserver.clearHighlight();
1747
+ return false;
1748
+ } else if ($isRangeSelection(selection)) {
1749
+ const tableCellNode = $findMatchingParent(selection.anchor.getNode(), n => $isTableCellNode(n));
1750
+ if (!$isTableCellNode(tableCellNode)) {
1751
+ return false;
1752
+ }
1753
+ }
1754
+ return false;
1755
+ }, COMMAND_PRIORITY_CRITICAL));
1756
+ if (hasTabHandler) {
1757
+ tableObserver.listenersToRemove.add(editor.registerCommand(KEY_TAB_COMMAND, event => {
1758
+ const selection = $getSelection();
1759
+ if (!$isRangeSelection(selection) || !selection.isCollapsed() || !$isSelectionInTable(selection, tableNode)) {
1760
+ return false;
1761
+ }
1762
+ const tableCellNode = $findCellNode(selection.anchor.getNode());
1763
+ if (tableCellNode === null) {
1764
+ return false;
1765
+ }
1766
+ stopEvent(event);
1767
+ const currentCords = tableNode.getCordsFromCellNode(tableCellNode, tableObserver.table);
1768
+ selectTableNodeInDirection(tableObserver, tableNode, currentCords.x, currentCords.y, !event.shiftKey ? 'forward' : 'backward');
1769
+ return true;
1770
+ }, COMMAND_PRIORITY_CRITICAL));
1771
+ }
1772
+ tableObserver.listenersToRemove.add(editor.registerCommand(FOCUS_COMMAND, payload => {
1773
+ return tableNode.isSelected();
1774
+ }, COMMAND_PRIORITY_HIGH));
1775
+ function getObserverCellFromCellNode(tableCellNode) {
1776
+ const currentCords = tableNode.getCordsFromCellNode(tableCellNode, tableObserver.table);
1777
+ return tableNode.getDOMCellFromCordsOrThrow(currentCords.x, currentCords.y, tableObserver.table);
1778
+ }
1779
+ tableObserver.listenersToRemove.add(editor.registerCommand(SELECTION_INSERT_CLIPBOARD_NODES_COMMAND, selectionPayload => {
1780
+ const {
1781
+ nodes,
1782
+ selection
1783
+ } = selectionPayload;
1784
+ const anchorAndFocus = selection.getStartEndPoints();
1785
+ const isTableSelection = $isTableSelection(selection);
1786
+ const isRangeSelection = $isRangeSelection(selection);
1787
+ const isSelectionInsideOfGrid = isRangeSelection && $findMatchingParent(selection.anchor.getNode(), n => $isTableCellNode(n)) !== null && $findMatchingParent(selection.focus.getNode(), n => $isTableCellNode(n)) !== null || isTableSelection;
1788
+ if (nodes.length !== 1 || !$isTableNode(nodes[0]) || !isSelectionInsideOfGrid || anchorAndFocus === null) {
1789
+ return false;
1790
+ }
1791
+ const [anchor] = anchorAndFocus;
1792
+ const newGrid = nodes[0];
1793
+ const newGridRows = newGrid.getChildren();
1794
+ const newColumnCount = newGrid.getFirstChildOrThrow().getChildrenSize();
1795
+ const newRowCount = newGrid.getChildrenSize();
1796
+ const gridCellNode = $findMatchingParent(anchor.getNode(), n => $isTableCellNode(n));
1797
+ const gridRowNode = gridCellNode && $findMatchingParent(gridCellNode, n => $isTableRowNode(n));
1798
+ const gridNode = gridRowNode && $findMatchingParent(gridRowNode, n => $isTableNode(n));
1799
+ if (!$isTableCellNode(gridCellNode) || !$isTableRowNode(gridRowNode) || !$isTableNode(gridNode)) {
1800
+ return false;
1801
+ }
1802
+ const startY = gridRowNode.getIndexWithinParent();
1803
+ const stopY = Math.min(gridNode.getChildrenSize() - 1, startY + newRowCount - 1);
1804
+ const startX = gridCellNode.getIndexWithinParent();
1805
+ const stopX = Math.min(gridRowNode.getChildrenSize() - 1, startX + newColumnCount - 1);
1806
+ const fromX = Math.min(startX, stopX);
1807
+ const fromY = Math.min(startY, stopY);
1808
+ const toX = Math.max(startX, stopX);
1809
+ const toY = Math.max(startY, stopY);
1810
+ const gridRowNodes = gridNode.getChildren();
1811
+ let newRowIdx = 0;
1812
+ let newAnchorCellKey;
1813
+ let newFocusCellKey;
1814
+ for (let r = fromY; r <= toY; r++) {
1815
+ const currentGridRowNode = gridRowNodes[r];
1816
+ if (!$isTableRowNode(currentGridRowNode)) {
1817
+ return false;
1818
+ }
1819
+ const newGridRowNode = newGridRows[newRowIdx];
1820
+ if (!$isTableRowNode(newGridRowNode)) {
1821
+ return false;
1822
+ }
1823
+ const gridCellNodes = currentGridRowNode.getChildren();
1824
+ const newGridCellNodes = newGridRowNode.getChildren();
1825
+ let newColumnIdx = 0;
1826
+ for (let c = fromX; c <= toX; c++) {
1827
+ const currentGridCellNode = gridCellNodes[c];
1828
+ if (!$isTableCellNode(currentGridCellNode)) {
1829
+ return false;
1830
+ }
1831
+ const newGridCellNode = newGridCellNodes[newColumnIdx];
1832
+ if (!$isTableCellNode(newGridCellNode)) {
1833
+ return false;
1834
+ }
1835
+ if (r === fromY && c === fromX) {
1836
+ newAnchorCellKey = currentGridCellNode.getKey();
1837
+ } else if (r === toY && c === toX) {
1838
+ newFocusCellKey = currentGridCellNode.getKey();
1839
+ }
1840
+ const originalChildren = currentGridCellNode.getChildren();
1841
+ newGridCellNode.getChildren().forEach(child => {
1842
+ if ($isTextNode(child)) {
1843
+ const paragraphNode = $createParagraphNode();
1844
+ paragraphNode.append(child);
1845
+ currentGridCellNode.append(child);
1846
+ } else {
1847
+ currentGridCellNode.append(child);
1848
+ }
1849
+ });
1850
+ originalChildren.forEach(n => n.remove());
1851
+ newColumnIdx++;
1852
+ }
1853
+ newRowIdx++;
1854
+ }
1855
+ if (newAnchorCellKey && newFocusCellKey) {
1856
+ const newTableSelection = $createTableSelection();
1857
+ newTableSelection.set(nodes[0].getKey(), newAnchorCellKey, newFocusCellKey);
1858
+ $setSelection(newTableSelection);
1859
+ }
1860
+ return true;
1861
+ }, COMMAND_PRIORITY_CRITICAL));
1862
+ tableObserver.listenersToRemove.add(editor.registerCommand(SELECTION_CHANGE_COMMAND, () => {
1863
+ const selection = $getSelection();
1864
+ const prevSelection = $getPreviousSelection();
1865
+ if ($isRangeSelection(selection)) {
1866
+ const {
1867
+ anchor,
1868
+ focus
1869
+ } = selection;
1870
+ const anchorNode = anchor.getNode();
1871
+ const focusNode = focus.getNode();
1872
+ // Using explicit comparison with table node to ensure it's not a nested table
1873
+ // as in that case we'll leave selection resolving to that table
1874
+ const anchorCellNode = $findCellNode(anchorNode);
1875
+ const focusCellNode = $findCellNode(focusNode);
1876
+ const isAnchorInside = !!(anchorCellNode && tableNode.is($findTableNode(anchorCellNode)));
1877
+ const isFocusInside = !!(focusCellNode && tableNode.is($findTableNode(focusCellNode)));
1878
+ const isPartialyWithinTable = isAnchorInside !== isFocusInside;
1879
+ const isWithinTable = isAnchorInside && isFocusInside;
1880
+ const isBackward = selection.isBackward();
1881
+ if (isPartialyWithinTable) {
1882
+ const newSelection = selection.clone();
1883
+ newSelection.focus.set(tableNode.getKey(), isBackward ? 0 : tableNode.getChildrenSize(), 'element');
1884
+ $setSelection(newSelection);
1885
+ $addHighlightStyleToTable(editor, tableObserver);
1886
+ } else if (isWithinTable) {
1887
+ // Handle case when selection spans across multiple cells but still
1888
+ // has range selection, then we convert it into grid selection
1889
+ if (!anchorCellNode.is(focusCellNode)) {
1890
+ tableObserver.setAnchorCellForSelection(getObserverCellFromCellNode(anchorCellNode));
1891
+ tableObserver.setFocusCellForSelection(getObserverCellFromCellNode(focusCellNode), true);
1892
+ }
1893
+ }
1894
+ }
1895
+ if (selection && !selection.is(prevSelection) && ($isTableSelection(selection) || $isTableSelection(prevSelection)) && tableObserver.tableSelection && !tableObserver.tableSelection.is(prevSelection)) {
1896
+ if ($isTableSelection(selection) && selection.tableKey === tableObserver.tableNodeKey) {
1897
+ tableObserver.updateTableTableSelection(selection);
1898
+ } else if (!$isTableSelection(selection) && $isTableSelection(prevSelection) && prevSelection.tableKey === tableObserver.tableNodeKey) {
1899
+ tableObserver.updateTableTableSelection(null);
1900
+ }
1901
+ return false;
1902
+ }
1903
+ if (tableObserver.hasHijackedSelectionStyles && !tableNode.isSelected()) {
1904
+ $removeHighlightStyleToTable(editor, tableObserver);
1905
+ } else if (!tableObserver.hasHijackedSelectionStyles && tableNode.isSelected()) {
1906
+ $addHighlightStyleToTable(editor, tableObserver);
1907
+ }
1908
+ return false;
1909
+ }, COMMAND_PRIORITY_CRITICAL));
1910
+ return tableObserver;
1911
+ }
1912
+ function attachTableObserverToTableElement(tableElement, tableObserver) {
1913
+ tableElement[LEXICAL_ELEMENT_KEY] = tableObserver;
1914
+ }
1915
+ function getTableObserverFromTableElement(tableElement) {
1916
+ return tableElement[LEXICAL_ELEMENT_KEY];
1917
+ }
1918
+ function getDOMCellFromTarget(node) {
1919
+ let currentNode = node;
1920
+ while (currentNode != null) {
1921
+ const nodeName = currentNode.nodeName;
1922
+ if (nodeName === 'TD' || nodeName === 'TH') {
1923
+ // @ts-expect-error: internal field
1924
+ const cell = currentNode._cell;
1925
+ if (cell === undefined) {
1926
+ return null;
1927
+ }
1928
+ return cell;
1929
+ }
1930
+ currentNode = currentNode.parentNode;
1931
+ }
1932
+ return null;
1933
+ }
1934
+ function getTable(tableElement) {
1935
+ const domRows = [];
1936
+ const grid = {
1937
+ columns: 0,
1938
+ domRows,
1939
+ rows: 0
1940
+ };
1941
+ let currentNode = tableElement.firstChild;
1942
+ let x = 0;
1943
+ let y = 0;
1944
+ domRows.length = 0;
1945
+ while (currentNode != null) {
1946
+ const nodeMame = currentNode.nodeName;
1947
+ if (nodeMame === 'TD' || nodeMame === 'TH') {
1948
+ const elem = currentNode;
1949
+ const cell = {
1950
+ elem,
1951
+ hasBackgroundColor: elem.style.backgroundColor !== '',
1952
+ highlighted: false,
1953
+ x,
1954
+ y
1955
+ };
1956
+
1957
+ // @ts-expect-error: internal field
1958
+ currentNode._cell = cell;
1959
+ let row = domRows[y];
1960
+ if (row === undefined) {
1961
+ row = domRows[y] = [];
1962
+ }
1963
+ row[x] = cell;
1964
+ } else {
1965
+ const child = currentNode.firstChild;
1966
+ if (child != null) {
1967
+ currentNode = child;
1968
+ continue;
1969
+ }
1970
+ }
1971
+ const sibling = currentNode.nextSibling;
1972
+ if (sibling != null) {
1973
+ x++;
1974
+ currentNode = sibling;
1975
+ continue;
1976
+ }
1977
+ const parent = currentNode.parentNode;
1978
+ if (parent != null) {
1979
+ const parentSibling = parent.nextSibling;
1980
+ if (parentSibling == null) {
1981
+ break;
1982
+ }
1983
+ y++;
1984
+ x = 0;
1985
+ currentNode = parentSibling;
1986
+ }
1987
+ }
1988
+ grid.columns = x + 1;
1989
+ grid.rows = y + 1;
1990
+ return grid;
1991
+ }
1992
+ function $updateDOMForSelection(editor, table, selection) {
1993
+ const selectedCellNodes = new Set(selection ? selection.getNodes() : []);
1994
+ $forEachTableCell(table, (cell, lexicalNode) => {
1995
+ const elem = cell.elem;
1996
+ if (selectedCellNodes.has(lexicalNode)) {
1997
+ cell.highlighted = true;
1998
+ $addHighlightToDOM(editor, cell);
1999
+ } else {
2000
+ cell.highlighted = false;
2001
+ $removeHighlightFromDOM(editor, cell);
2002
+ if (!elem.getAttribute('style')) {
2003
+ elem.removeAttribute('style');
2004
+ }
2005
+ }
2006
+ });
2007
+ }
2008
+ function $forEachTableCell(grid, cb) {
2009
+ const {
2010
+ domRows
2011
+ } = grid;
2012
+ for (let y = 0; y < domRows.length; y++) {
2013
+ const row = domRows[y];
2014
+ if (!row) {
2015
+ continue;
2016
+ }
2017
+ for (let x = 0; x < row.length; x++) {
2018
+ const cell = row[x];
2019
+ if (!cell) {
2020
+ continue;
2021
+ }
2022
+ const lexicalNode = $getNearestNodeFromDOMNode(cell.elem);
2023
+ if (lexicalNode !== null) {
2024
+ cb(cell, lexicalNode, {
2025
+ x,
2026
+ y
2027
+ });
2028
+ }
2029
+ }
2030
+ }
2031
+ }
2032
+ function $addHighlightStyleToTable(editor, tableSelection) {
2033
+ tableSelection.disableHighlightStyle();
2034
+ $forEachTableCell(tableSelection.table, cell => {
2035
+ cell.highlighted = true;
2036
+ $addHighlightToDOM(editor, cell);
2037
+ });
2038
+ }
2039
+ function $removeHighlightStyleToTable(editor, tableObserver) {
2040
+ tableObserver.enableHighlightStyle();
2041
+ $forEachTableCell(tableObserver.table, cell => {
2042
+ const elem = cell.elem;
2043
+ cell.highlighted = false;
2044
+ $removeHighlightFromDOM(editor, cell);
2045
+ if (!elem.getAttribute('style')) {
2046
+ elem.removeAttribute('style');
2047
+ }
2048
+ });
2049
+ }
2050
+ const selectTableNodeInDirection = (tableObserver, tableNode, x, y, direction) => {
2051
+ const isForward = direction === 'forward';
2052
+ switch (direction) {
2053
+ case 'backward':
2054
+ case 'forward':
2055
+ if (x !== (isForward ? tableObserver.table.columns - 1 : 0)) {
2056
+ selectTableCellNode(tableNode.getCellNodeFromCordsOrThrow(x + (isForward ? 1 : -1), y, tableObserver.table), isForward);
2057
+ } else {
2058
+ if (y !== (isForward ? tableObserver.table.rows - 1 : 0)) {
2059
+ selectTableCellNode(tableNode.getCellNodeFromCordsOrThrow(isForward ? 0 : tableObserver.table.columns - 1, y + (isForward ? 1 : -1), tableObserver.table), isForward);
2060
+ } else if (!isForward) {
2061
+ tableNode.selectPrevious();
2062
+ } else {
2063
+ tableNode.selectNext();
2064
+ }
2065
+ }
2066
+ return true;
2067
+ case 'up':
2068
+ if (y !== 0) {
2069
+ selectTableCellNode(tableNode.getCellNodeFromCordsOrThrow(x, y - 1, tableObserver.table), false);
2070
+ } else {
2071
+ tableNode.selectPrevious();
2072
+ }
2073
+ return true;
2074
+ case 'down':
2075
+ if (y !== tableObserver.table.rows - 1) {
2076
+ selectTableCellNode(tableNode.getCellNodeFromCordsOrThrow(x, y + 1, tableObserver.table), true);
2077
+ } else {
2078
+ tableNode.selectNext();
2079
+ }
2080
+ return true;
2081
+ default:
2082
+ return false;
2083
+ }
2084
+ };
2085
+ const adjustFocusNodeInDirection = (tableObserver, tableNode, x, y, direction) => {
2086
+ const isForward = direction === 'forward';
2087
+ switch (direction) {
2088
+ case 'backward':
2089
+ case 'forward':
2090
+ if (x !== (isForward ? tableObserver.table.columns - 1 : 0)) {
2091
+ tableObserver.setFocusCellForSelection(tableNode.getDOMCellFromCordsOrThrow(x + (isForward ? 1 : -1), y, tableObserver.table));
2092
+ }
2093
+ return true;
2094
+ case 'up':
2095
+ if (y !== 0) {
2096
+ tableObserver.setFocusCellForSelection(tableNode.getDOMCellFromCordsOrThrow(x, y - 1, tableObserver.table));
2097
+ return true;
2098
+ } else {
2099
+ return false;
2100
+ }
2101
+ case 'down':
2102
+ if (y !== tableObserver.table.rows - 1) {
2103
+ tableObserver.setFocusCellForSelection(tableNode.getDOMCellFromCordsOrThrow(x, y + 1, tableObserver.table));
2104
+ return true;
2105
+ } else {
2106
+ return false;
2107
+ }
2108
+ default:
2109
+ return false;
2110
+ }
2111
+ };
2112
+ function $isSelectionInTable(selection, tableNode) {
2113
+ if ($isRangeSelection(selection) || $isTableSelection(selection)) {
2114
+ const isAnchorInside = tableNode.isParentOf(selection.anchor.getNode());
2115
+ const isFocusInside = tableNode.isParentOf(selection.focus.getNode());
2116
+ return isAnchorInside && isFocusInside;
2117
+ }
2118
+ return false;
2119
+ }
2120
+ function selectTableCellNode(tableCell, fromStart) {
2121
+ if (fromStart) {
2122
+ tableCell.selectStart();
2123
+ } else {
2124
+ tableCell.selectEnd();
2125
+ }
2126
+ }
2127
+ const BROWSER_BLUE_RGB = '172,206,247';
2128
+ function $addHighlightToDOM(editor, cell) {
2129
+ const element = cell.elem;
2130
+ const node = $getNearestNodeFromDOMNode(element);
2131
+ if (!$isTableCellNode(node)) {
2132
+ throw Error(`Expected to find LexicalNode from Table Cell DOMNode`);
2133
+ }
2134
+ const backgroundColor = node.getBackgroundColor();
2135
+ if (backgroundColor === null) {
2136
+ element.style.setProperty('background-color', `rgb(${BROWSER_BLUE_RGB})`);
2137
+ } else {
2138
+ element.style.setProperty('background-image', `linear-gradient(to right, rgba(${BROWSER_BLUE_RGB},0.85), rgba(${BROWSER_BLUE_RGB},0.85))`);
2139
+ }
2140
+ element.style.setProperty('caret-color', 'transparent');
2141
+ }
2142
+ function $removeHighlightFromDOM(editor, cell) {
2143
+ const element = cell.elem;
2144
+ const node = $getNearestNodeFromDOMNode(element);
2145
+ if (!$isTableCellNode(node)) {
2146
+ throw Error(`Expected to find LexicalNode from Table Cell DOMNode`);
2147
+ }
2148
+ const backgroundColor = node.getBackgroundColor();
2149
+ if (backgroundColor === null) {
2150
+ element.style.removeProperty('background-color');
2151
+ }
2152
+ element.style.removeProperty('background-image');
2153
+ element.style.removeProperty('caret-color');
2154
+ }
2155
+ function $findCellNode(node) {
2156
+ const cellNode = $findMatchingParent(node, $isTableCellNode);
2157
+ return $isTableCellNode(cellNode) ? cellNode : null;
2158
+ }
2159
+ function $findTableNode(node) {
2160
+ const tableNode = $findMatchingParent(node, $isTableNode);
2161
+ return $isTableNode(tableNode) ? tableNode : null;
2162
+ }
2163
+ function $handleArrowKey(editor, event, direction, tableNode, tableObserver) {
2164
+ const selection = $getSelection();
2165
+ if (!$isSelectionInTable(selection, tableNode)) {
2166
+ return false;
2167
+ }
2168
+ if ($isRangeSelection(selection) && selection.isCollapsed()) {
2169
+ // Horizontal move between cels seem to work well without interruption
2170
+ // so just exit early, and handle vertical moves
2171
+ if (direction === 'backward' || direction === 'forward') {
2172
+ return false;
2173
+ }
2174
+ const {
2175
+ anchor,
2176
+ focus
2177
+ } = selection;
2178
+ const anchorCellNode = $findMatchingParent(anchor.getNode(), $isTableCellNode);
2179
+ const focusCellNode = $findMatchingParent(focus.getNode(), $isTableCellNode);
2180
+ if (!$isTableCellNode(anchorCellNode) || !anchorCellNode.is(focusCellNode)) {
2181
+ return false;
2182
+ }
2183
+ const anchorCellTable = $findTableNode(anchorCellNode);
2184
+ if (anchorCellTable !== tableNode && anchorCellTable != null) {
2185
+ const anchorCellTableElement = editor.getElementByKey(anchorCellTable.getKey());
2186
+ if (anchorCellTableElement != null) {
2187
+ tableObserver.table = getTable(anchorCellTableElement);
2188
+ return $handleArrowKey(editor, event, direction, anchorCellTable, tableObserver);
2189
+ }
2190
+ }
2191
+ const anchorCellDom = editor.getElementByKey(anchorCellNode.__key);
2192
+ const anchorDOM = editor.getElementByKey(anchor.key);
2193
+ if (anchorDOM == null || anchorCellDom == null) {
2194
+ return false;
2195
+ }
2196
+ let edgeSelectionRect;
2197
+ if (anchor.type === 'element') {
2198
+ edgeSelectionRect = anchorDOM.getBoundingClientRect();
2199
+ } else {
2200
+ const domSelection = window.getSelection();
2201
+ if (domSelection === null || domSelection.rangeCount === 0) {
2202
+ return false;
2203
+ }
2204
+ const range = domSelection.getRangeAt(0);
2205
+ edgeSelectionRect = range.getBoundingClientRect();
2206
+ }
2207
+ const edgeChild = direction === 'up' ? anchorCellNode.getFirstChild() : anchorCellNode.getLastChild();
2208
+ if (edgeChild == null) {
2209
+ return false;
2210
+ }
2211
+ const edgeChildDOM = editor.getElementByKey(edgeChild.__key);
2212
+ if (edgeChildDOM == null) {
2213
+ return false;
2214
+ }
2215
+ const edgeRect = edgeChildDOM.getBoundingClientRect();
2216
+ const isExiting = direction === 'up' ? edgeRect.top > edgeSelectionRect.top - edgeSelectionRect.height : edgeSelectionRect.bottom + edgeSelectionRect.height > edgeRect.bottom;
2217
+ if (isExiting) {
2218
+ stopEvent(event);
2219
+ const cords = tableNode.getCordsFromCellNode(anchorCellNode, tableObserver.table);
2220
+ if (event.shiftKey) {
2221
+ const cell = tableNode.getDOMCellFromCordsOrThrow(cords.x, cords.y, tableObserver.table);
2222
+ tableObserver.setAnchorCellForSelection(cell);
2223
+ tableObserver.setFocusCellForSelection(cell, true);
2224
+ } else {
2225
+ return selectTableNodeInDirection(tableObserver, tableNode, cords.x, cords.y, direction);
2226
+ }
2227
+ return true;
2228
+ }
2229
+ } else if ($isTableSelection(selection)) {
2230
+ const {
2231
+ anchor,
2232
+ focus
2233
+ } = selection;
2234
+ const anchorCellNode = $findMatchingParent(anchor.getNode(), $isTableCellNode);
2235
+ const focusCellNode = $findMatchingParent(focus.getNode(), $isTableCellNode);
2236
+ const [tableNodeFromSelection] = selection.getNodes();
2237
+ const tableElement = editor.getElementByKey(tableNodeFromSelection.getKey());
2238
+ if (!$isTableCellNode(anchorCellNode) || !$isTableCellNode(focusCellNode) || !$isTableNode(tableNodeFromSelection) || tableElement == null) {
2239
+ return false;
2240
+ }
2241
+ tableObserver.updateTableTableSelection(selection);
2242
+ const grid = getTable(tableElement);
2243
+ const cordsAnchor = tableNode.getCordsFromCellNode(anchorCellNode, grid);
2244
+ const anchorCell = tableNode.getDOMCellFromCordsOrThrow(cordsAnchor.x, cordsAnchor.y, grid);
2245
+ tableObserver.setAnchorCellForSelection(anchorCell);
2246
+ stopEvent(event);
2247
+ if (event.shiftKey) {
2248
+ const cords = tableNode.getCordsFromCellNode(focusCellNode, grid);
2249
+ return adjustFocusNodeInDirection(tableObserver, tableNodeFromSelection, cords.x, cords.y, direction);
2250
+ } else {
2251
+ focusCellNode.selectEnd();
2252
+ }
2253
+ return true;
2254
+ }
2255
+ return false;
2256
+ }
2257
+ function stopEvent(event) {
2258
+ event.preventDefault();
2259
+ event.stopImmediatePropagation();
2260
+ event.stopPropagation();
2261
+ }
2262
+
2263
+ /**
2264
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
2265
+ *
2266
+ * This source code is licensed under the MIT license found in the
2267
+ * LICENSE file in the root directory of this source tree.
2268
+ *
2269
+ */
2270
+ /** @noInheritDoc */
2271
+ class TableNode extends ElementNode {
2272
+ static getType() {
2273
+ return 'table';
2274
+ }
2275
+ static clone(node) {
2276
+ return new TableNode(node.__key);
2277
+ }
2278
+ static importDOM() {
2279
+ return {
2280
+ table: _node => ({
2281
+ conversion: convertTableElement,
2282
+ priority: 1
2283
+ })
2284
+ };
2285
+ }
2286
+ static importJSON(_serializedNode) {
2287
+ return $createTableNode();
2288
+ }
2289
+ constructor(key) {
2290
+ super(key);
2291
+ }
2292
+ exportJSON() {
2293
+ return {
2294
+ ...super.exportJSON(),
2295
+ type: 'table',
2296
+ version: 1
2297
+ };
2298
+ }
2299
+ createDOM(config, editor) {
2300
+ const tableElement = document.createElement('table');
2301
+ addClassNamesToElement(tableElement, config.theme.table);
2302
+ return tableElement;
2303
+ }
2304
+ updateDOM() {
2305
+ return false;
2306
+ }
2307
+ exportDOM(editor) {
2308
+ return {
2309
+ ...super.exportDOM(editor),
2310
+ after: tableElement => {
2311
+ if (tableElement) {
2312
+ const newElement = tableElement.cloneNode();
2313
+ const colGroup = document.createElement('colgroup');
2314
+ const tBody = document.createElement('tbody');
2315
+ if (isHTMLElement(tableElement)) {
2316
+ tBody.append(...tableElement.children);
2317
+ }
2318
+ const firstRow = this.getFirstChildOrThrow();
2319
+ if (!$isTableRowNode(firstRow)) {
2320
+ throw new Error('Expected to find row node.');
2321
+ }
2322
+ const colCount = firstRow.getChildrenSize();
2323
+ for (let i = 0; i < colCount; i++) {
2324
+ const col = document.createElement('col');
2325
+ colGroup.append(col);
2326
+ }
2327
+ newElement.replaceChildren(colGroup, tBody);
2328
+ return newElement;
2329
+ }
2330
+ }
2331
+ };
2332
+ }
2333
+ canBeEmpty() {
2334
+ return false;
2335
+ }
2336
+ isShadowRoot() {
2337
+ return true;
2338
+ }
2339
+ getCordsFromCellNode(tableCellNode, table) {
2340
+ const {
2341
+ rows,
2342
+ domRows
2343
+ } = table;
2344
+ for (let y = 0; y < rows; y++) {
2345
+ const row = domRows[y];
2346
+ if (row == null) {
2347
+ continue;
2348
+ }
2349
+ const x = row.findIndex(cell => {
2350
+ if (!cell) {
2351
+ return;
2352
+ }
2353
+ const {
2354
+ elem
2355
+ } = cell;
2356
+ const cellNode = $getNearestNodeFromDOMNode(elem);
2357
+ return cellNode === tableCellNode;
2358
+ });
2359
+ if (x !== -1) {
2360
+ return {
2361
+ x,
2362
+ y
2363
+ };
2364
+ }
2365
+ }
2366
+ throw new Error('Cell not found in table.');
2367
+ }
2368
+ getDOMCellFromCords(x, y, table) {
2369
+ const {
2370
+ domRows
2371
+ } = table;
2372
+ const row = domRows[y];
2373
+ if (row == null) {
2374
+ return null;
2375
+ }
2376
+ const cell = row[x];
2377
+ if (cell == null) {
2378
+ return null;
2379
+ }
2380
+ return cell;
2381
+ }
2382
+ getDOMCellFromCordsOrThrow(x, y, table) {
2383
+ const cell = this.getDOMCellFromCords(x, y, table);
2384
+ if (!cell) {
2385
+ throw new Error('Cell not found at cords.');
2386
+ }
2387
+ return cell;
2388
+ }
2389
+ getCellNodeFromCords(x, y, table) {
2390
+ const cell = this.getDOMCellFromCords(x, y, table);
2391
+ if (cell == null) {
2392
+ return null;
2393
+ }
2394
+ const node = $getNearestNodeFromDOMNode(cell.elem);
2395
+ if ($isTableCellNode(node)) {
2396
+ return node;
2397
+ }
2398
+ return null;
2399
+ }
2400
+ getCellNodeFromCordsOrThrow(x, y, table) {
2401
+ const node = this.getCellNodeFromCords(x, y, table);
2402
+ if (!node) {
2403
+ throw new Error('Node at cords not TableCellNode.');
2404
+ }
2405
+ return node;
2406
+ }
2407
+ canSelectBefore() {
2408
+ return true;
2409
+ }
2410
+ canIndent() {
2411
+ return false;
2412
+ }
2413
+ }
2414
+ function $getElementForTableNode(editor, tableNode) {
2415
+ const tableElement = editor.getElementByKey(tableNode.getKey());
2416
+ if (tableElement == null) {
2417
+ throw new Error('Table Element Not Found');
2418
+ }
2419
+ return getTable(tableElement);
2420
+ }
2421
+ function convertTableElement(_domNode) {
2422
+ return {
2423
+ node: $createTableNode()
2424
+ };
2425
+ }
2426
+ function $createTableNode() {
2427
+ return $applyNodeReplacement(new TableNode());
2428
+ }
2429
+ function $isTableNode(node) {
2430
+ return node instanceof TableNode;
2431
+ }
2432
+
2433
+ export { $computeTableMap, $createTableCellNode, $createTableNode, $createTableNodeWithDimensions, $createTableRowNode, $createTableSelection, $deleteTableColumn, $deleteTableColumn__EXPERIMENTAL, $deleteTableRow__EXPERIMENTAL, $getElementForTableNode, $getNodeTriplet, $getTableCellNodeFromLexicalNode, $getTableCellNodeRect, $getTableColumnIndexFromTableCellNode, $getTableNodeFromLexicalNodeOrThrow, $getTableRowIndexFromTableCellNode, $getTableRowNodeFromTableCellNodeOrThrow, $insertTableColumn, $insertTableColumn__EXPERIMENTAL, $insertTableRow, $insertTableRow__EXPERIMENTAL, $isTableCellNode, $isTableNode, $isTableRowNode, $isTableSelection, $removeTableRowAtIndex, $unmergeCell, INSERT_TABLE_COMMAND, TableCellHeaderStates, TableCellNode, TableNode, TableObserver, TableRowNode, applyTableHandlers, getDOMCellFromTarget, getTableObserverFromTableElement };