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