@lexical/table 0.29.1-nightly.20250401.0 → 0.29.1-nightly.20250403.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LexicalTable.dev.js +296 -56
- package/LexicalTable.dev.mjs +293 -58
- package/LexicalTable.js.flow +30 -0
- package/LexicalTable.mjs +5 -0
- package/LexicalTable.node.mjs +5 -0
- package/LexicalTable.prod.js +1 -1
- package/LexicalTable.prod.mjs +1 -1
- package/LexicalTableUtils.d.ts +43 -4
- package/index.d.ts +1 -1
- package/package.json +4 -4
package/LexicalTable.dev.mjs
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
*/
|
8
8
|
|
9
9
|
import { addClassNamesToElement, $descendantsMatching, $findMatchingParent, removeClassNamesFromElement, objectKlassEquals, isHTMLElement as isHTMLElement$1, $insertFirst as $insertFirst$1, mergeRegister, $insertNodeToNearestRoot, $unwrapAndFilterDescendants } from '@lexical/utils';
|
10
|
-
import { ElementNode, isHTMLElement, $isInlineElementOrDecoratorNode, $isTextNode, $isLineBreakNode, $createParagraphNode, $applyNodeReplacement, createCommand, $createTextNode, $getSelection, $isRangeSelection, $
|
10
|
+
import { ElementNode, isHTMLElement, $isInlineElementOrDecoratorNode, $isTextNode, $isLineBreakNode, $createParagraphNode, $applyNodeReplacement, createCommand, $createTextNode, $getSelection, $isRangeSelection, $isParagraphNode, $createPoint, $isElementNode, $normalizeSelection__EXPERIMENTAL, isCurrentlyReadOnlyMode, TEXT_TYPE_TO_FORMAT, $getNodeByKey, $getEditor, $setSelection, SELECTION_CHANGE_COMMAND, getDOMSelection, $createRangeSelection, $isRootNode, INSERT_PARAGRAPH_COMMAND, COMMAND_PRIORITY_HIGH, KEY_ESCAPE_COMMAND, COMMAND_PRIORITY_CRITICAL, CUT_COMMAND, FORMAT_TEXT_COMMAND, FORMAT_ELEMENT_COMMAND, CONTROLLED_TEXT_INSERTION_COMMAND, KEY_TAB_COMMAND, FOCUS_COMMAND, SELECTION_INSERT_CLIPBOARD_NODES_COMMAND, $getPreviousSelection, $getNearestNodeFromDOMNode, $createRangeSelectionFromDom, $isRootOrShadowRoot, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_UP_COMMAND, KEY_ARROW_LEFT_COMMAND, KEY_ARROW_RIGHT_COMMAND, DELETE_WORD_COMMAND, DELETE_LINE_COMMAND, DELETE_CHARACTER_COMMAND, KEY_BACKSPACE_COMMAND, KEY_DELETE_COMMAND, isDOMNode, $caretFromPoint, $isExtendableTextPointCaret, $extendCaretToRange, $isSiblingCaret, $getSiblingCaret, $setPointFromCaret, $normalizeCaret, $getAdjacentChildCaret, $isChildCaret, $getChildCaret, setDOMUnmanaged, COMMAND_PRIORITY_EDITOR, CLICK_COMMAND } from 'lexical';
|
11
11
|
import { copyToClipboard, $getClipboardDataFromSelection } from '@lexical/clipboard';
|
12
12
|
|
13
13
|
/**
|
@@ -533,6 +533,10 @@ function $removeTableRowAtIndex(tableNode, indexToDelete) {
|
|
533
533
|
targetRowNode.remove();
|
534
534
|
return tableNode;
|
535
535
|
}
|
536
|
+
|
537
|
+
/**
|
538
|
+
* @deprecated This function does not support merged cells. Use {@link $insertTableRowAtSelection} or {@link $insertTableRowAtNode} instead.
|
539
|
+
*/
|
536
540
|
function $insertTableRow(tableNode, targetIndex, shouldInsertAfter = true, rowCount, table) {
|
537
541
|
const tableRows = tableNode.getChildren();
|
538
542
|
if (targetIndex >= tableRows.length || targetIndex < 0) {
|
@@ -585,7 +589,7 @@ const getHeaderState = (currentState, possibleState) => {
|
|
585
589
|
* taking into account any spans. If successful, returns the
|
586
590
|
* inserted table row node.
|
587
591
|
*/
|
588
|
-
function $
|
592
|
+
function $insertTableRowAtSelection(insertAfter = true) {
|
589
593
|
const selection = $getSelection();
|
590
594
|
if (!($isRangeSelection(selection) || $isTableSelection(selection))) {
|
591
595
|
formatDevErrorMessage(`Expected a RangeSelection or TableSelection`);
|
@@ -594,17 +598,40 @@ function $insertTableRow__EXPERIMENTAL(insertAfter = true) {
|
|
594
598
|
const focus = selection.focus.getNode();
|
595
599
|
const [anchorCell] = $getNodeTriplet(anchor);
|
596
600
|
const [focusCell,, grid] = $getNodeTriplet(focus);
|
597
|
-
const [
|
598
|
-
const columnCount = gridMap[0].length;
|
601
|
+
const [, focusCellMap, anchorCellMap] = $computeTableMap(grid, focusCell, anchorCell);
|
599
602
|
const {
|
600
603
|
startRow: anchorStartRow
|
601
604
|
} = anchorCellMap;
|
602
605
|
const {
|
603
606
|
startRow: focusStartRow
|
604
607
|
} = focusCellMap;
|
608
|
+
if (insertAfter) {
|
609
|
+
return $insertTableRowAtNode(anchorStartRow + anchorCell.__rowSpan > focusStartRow + focusCell.__rowSpan ? anchorCell : focusCell, true);
|
610
|
+
} else {
|
611
|
+
return $insertTableRowAtNode(focusStartRow < anchorStartRow ? focusCell : anchorCell, false);
|
612
|
+
}
|
613
|
+
}
|
614
|
+
|
615
|
+
/**
|
616
|
+
* @deprecated renamed to {@link $insertTableRowAtSelection}
|
617
|
+
*/
|
618
|
+
const $insertTableRow__EXPERIMENTAL = $insertTableRowAtSelection;
|
619
|
+
|
620
|
+
/**
|
621
|
+
* Inserts a table row before or after the given cell node,
|
622
|
+
* taking into account any spans. If successful, returns the
|
623
|
+
* inserted table row node.
|
624
|
+
*/
|
625
|
+
function $insertTableRowAtNode(cellNode, insertAfter = true) {
|
626
|
+
const [,, grid] = $getNodeTriplet(cellNode);
|
627
|
+
const [gridMap, cellMap] = $computeTableMap(grid, cellNode, cellNode);
|
628
|
+
const columnCount = gridMap[0].length;
|
629
|
+
const {
|
630
|
+
startRow: cellStartRow
|
631
|
+
} = cellMap;
|
605
632
|
let insertedRow = null;
|
606
633
|
if (insertAfter) {
|
607
|
-
const insertAfterEndRow =
|
634
|
+
const insertAfterEndRow = cellStartRow + cellNode.__rowSpan - 1;
|
608
635
|
const insertAfterEndRowMap = gridMap[insertAfterEndRow];
|
609
636
|
const newRow = $createTableRowNode();
|
610
637
|
for (let i = 0; i < columnCount; i++) {
|
@@ -628,7 +655,7 @@ function $insertTableRow__EXPERIMENTAL(insertAfter = true) {
|
|
628
655
|
insertAfterEndRowNode.insertAfter(newRow);
|
629
656
|
insertedRow = newRow;
|
630
657
|
} else {
|
631
|
-
const insertBeforeStartRow =
|
658
|
+
const insertBeforeStartRow = cellStartRow;
|
632
659
|
const insertBeforeStartRowMap = gridMap[insertBeforeStartRow];
|
633
660
|
const newRow = $createTableRowNode();
|
634
661
|
for (let i = 0; i < columnCount; i++) {
|
@@ -654,6 +681,10 @@ function $insertTableRow__EXPERIMENTAL(insertAfter = true) {
|
|
654
681
|
}
|
655
682
|
return insertedRow;
|
656
683
|
}
|
684
|
+
|
685
|
+
/**
|
686
|
+
* @deprecated This function does not support merged cells. Use {@link $insertTableColumnAtSelection} or {@link $insertTableColumnAtNode} instead.
|
687
|
+
*/
|
657
688
|
function $insertTableColumn(tableNode, targetIndex, shouldInsertAfter = true, columnCount, table) {
|
658
689
|
const tableRows = tableNode.getChildren();
|
659
690
|
const tableCellsToBeInserted = [];
|
@@ -704,7 +735,7 @@ function $insertTableColumn(tableNode, targetIndex, shouldInsertAfter = true, co
|
|
704
735
|
* taking into account any spans. If successful, returns the
|
705
736
|
* first inserted cell node.
|
706
737
|
*/
|
707
|
-
function $
|
738
|
+
function $insertTableColumnAtSelection(insertAfter = true) {
|
708
739
|
const selection = $getSelection();
|
709
740
|
if (!($isRangeSelection(selection) || $isTableSelection(selection))) {
|
710
741
|
formatDevErrorMessage(`Expected a RangeSelection or TableSelection`);
|
@@ -713,10 +744,38 @@ function $insertTableColumn__EXPERIMENTAL(insertAfter = true) {
|
|
713
744
|
const focus = selection.focus.getNode();
|
714
745
|
const [anchorCell] = $getNodeTriplet(anchor);
|
715
746
|
const [focusCell,, grid] = $getNodeTriplet(focus);
|
716
|
-
const [
|
747
|
+
const [, focusCellMap, anchorCellMap] = $computeTableMap(grid, focusCell, anchorCell);
|
748
|
+
const {
|
749
|
+
startColumn: anchorStartColumn
|
750
|
+
} = anchorCellMap;
|
751
|
+
const {
|
752
|
+
startColumn: focusStartColumn
|
753
|
+
} = focusCellMap;
|
754
|
+
if (insertAfter) {
|
755
|
+
return $insertTableColumnAtNode(anchorStartColumn + anchorCell.__colSpan > focusStartColumn + focusCell.__colSpan ? anchorCell : focusCell, true);
|
756
|
+
} else {
|
757
|
+
return $insertTableColumnAtNode(focusStartColumn < anchorStartColumn ? focusCell : anchorCell, false);
|
758
|
+
}
|
759
|
+
}
|
760
|
+
|
761
|
+
/**
|
762
|
+
* @deprecated renamed to {@link $insertTableColumnAtSelection}
|
763
|
+
*/
|
764
|
+
const $insertTableColumn__EXPERIMENTAL = $insertTableColumnAtSelection;
|
765
|
+
|
766
|
+
/**
|
767
|
+
* Inserts a column before or after the given cell node,
|
768
|
+
* taking into account any spans. If successful, returns the
|
769
|
+
* first inserted cell node.
|
770
|
+
*/
|
771
|
+
function $insertTableColumnAtNode(cellNode, insertAfter = true, shouldSetSelection = true) {
|
772
|
+
const [,, grid] = $getNodeTriplet(cellNode);
|
773
|
+
const [gridMap, cellMap] = $computeTableMap(grid, cellNode, cellNode);
|
717
774
|
const rowCount = gridMap.length;
|
718
|
-
const
|
719
|
-
|
775
|
+
const {
|
776
|
+
startColumn
|
777
|
+
} = cellMap;
|
778
|
+
const insertAfterColumn = insertAfter ? startColumn + cellNode.__colSpan - 1 : startColumn - 1;
|
720
779
|
const gridFirstChild = grid.getFirstChild();
|
721
780
|
if (!$isTableRowNode(gridFirstChild)) {
|
722
781
|
formatDevErrorMessage(`Expected firstTable child to be a row`);
|
@@ -773,7 +832,7 @@ function $insertTableColumn__EXPERIMENTAL(insertAfter = true) {
|
|
773
832
|
currentCell.setColSpan(currentCell.__colSpan + 1);
|
774
833
|
}
|
775
834
|
}
|
776
|
-
if (firstInsertedCell !== null) {
|
835
|
+
if (firstInsertedCell !== null && shouldSetSelection) {
|
777
836
|
$moveSelectionToCell(firstInsertedCell);
|
778
837
|
}
|
779
838
|
const colWidths = grid.getColWidths();
|
@@ -786,6 +845,10 @@ function $insertTableColumn__EXPERIMENTAL(insertAfter = true) {
|
|
786
845
|
}
|
787
846
|
return firstInsertedCell;
|
788
847
|
}
|
848
|
+
|
849
|
+
/**
|
850
|
+
* @deprecated This function does not support merged cells. Use {@link $deleteTableColumnAtSelection} instead.
|
851
|
+
*/
|
789
852
|
function $deleteTableColumn(tableNode, targetIndex) {
|
790
853
|
const tableRows = tableNode.getChildren();
|
791
854
|
for (let i = 0; i < tableRows.length; i++) {
|
@@ -800,7 +863,7 @@ function $deleteTableColumn(tableNode, targetIndex) {
|
|
800
863
|
}
|
801
864
|
return tableNode;
|
802
865
|
}
|
803
|
-
function $
|
866
|
+
function $deleteTableRowAtSelection() {
|
804
867
|
const selection = $getSelection();
|
805
868
|
if (!($isRangeSelection(selection) || $isTableSelection(selection))) {
|
806
869
|
formatDevErrorMessage(`Expected a RangeSelection or TableSelection`);
|
@@ -885,7 +948,12 @@ function $deleteTableRow__EXPERIMENTAL() {
|
|
885
948
|
$moveSelectionToCell(cell);
|
886
949
|
}
|
887
950
|
}
|
888
|
-
|
951
|
+
|
952
|
+
/**
|
953
|
+
* @deprecated renamed to {@link $deleteTableRowAtSelection}
|
954
|
+
*/
|
955
|
+
const $deleteTableRow__EXPERIMENTAL = $deleteTableRowAtSelection;
|
956
|
+
function $deleteTableColumnAtSelection() {
|
889
957
|
const selection = $getSelection();
|
890
958
|
if (!($isRangeSelection(selection) || $isTableSelection(selection))) {
|
891
959
|
formatDevErrorMessage(`Expected a RangeSelection or TableSelection`);
|
@@ -959,6 +1027,11 @@ function $deleteTableColumn__EXPERIMENTAL() {
|
|
959
1027
|
grid.setColWidths(newColWidths);
|
960
1028
|
}
|
961
1029
|
}
|
1030
|
+
|
1031
|
+
/**
|
1032
|
+
* @deprecated renamed to {@link $deleteTableColumnAtSelection}
|
1033
|
+
*/
|
1034
|
+
const $deleteTableColumn__EXPERIMENTAL = $deleteTableColumnAtSelection;
|
962
1035
|
function $moveSelectionToCell(cell) {
|
963
1036
|
const firstDescendant = cell.getFirstDescendant();
|
964
1037
|
if (firstDescendant == null) {
|
@@ -975,13 +1048,122 @@ function $insertFirst(parent, node) {
|
|
975
1048
|
parent.append(node);
|
976
1049
|
}
|
977
1050
|
}
|
1051
|
+
function $mergeCells(cellNodes) {
|
1052
|
+
if (cellNodes.length === 0) {
|
1053
|
+
return null;
|
1054
|
+
}
|
1055
|
+
|
1056
|
+
// Find the table node
|
1057
|
+
const tableNode = $getTableNodeFromLexicalNodeOrThrow(cellNodes[0]);
|
1058
|
+
const [gridMap] = $computeTableMapSkipCellCheck(tableNode, null, null);
|
1059
|
+
|
1060
|
+
// Find the boundaries of the selection including merged cells
|
1061
|
+
let minRow = Infinity;
|
1062
|
+
let maxRow = -Infinity;
|
1063
|
+
let minCol = Infinity;
|
1064
|
+
let maxCol = -Infinity;
|
1065
|
+
|
1066
|
+
// First pass: find the actual boundaries considering merged cells
|
1067
|
+
const processedCells = new Set();
|
1068
|
+
for (const row of gridMap) {
|
1069
|
+
for (const mapCell of row) {
|
1070
|
+
if (!mapCell || !mapCell.cell) {
|
1071
|
+
continue;
|
1072
|
+
}
|
1073
|
+
const cellKey = mapCell.cell.getKey();
|
1074
|
+
if (processedCells.has(cellKey)) {
|
1075
|
+
continue;
|
1076
|
+
}
|
1077
|
+
if (cellNodes.some(cell => cell.is(mapCell.cell))) {
|
1078
|
+
processedCells.add(cellKey);
|
1079
|
+
// Get the actual position of this cell in the grid
|
1080
|
+
const cellStartRow = mapCell.startRow;
|
1081
|
+
const cellStartCol = mapCell.startColumn;
|
1082
|
+
const cellRowSpan = mapCell.cell.__rowSpan || 1;
|
1083
|
+
const cellColSpan = mapCell.cell.__colSpan || 1;
|
1084
|
+
|
1085
|
+
// Update boundaries considering the cell's actual position and span
|
1086
|
+
minRow = Math.min(minRow, cellStartRow);
|
1087
|
+
maxRow = Math.max(maxRow, cellStartRow + cellRowSpan - 1);
|
1088
|
+
minCol = Math.min(minCol, cellStartCol);
|
1089
|
+
maxCol = Math.max(maxCol, cellStartCol + cellColSpan - 1);
|
1090
|
+
}
|
1091
|
+
}
|
1092
|
+
}
|
1093
|
+
|
1094
|
+
// Validate boundaries
|
1095
|
+
if (minRow === Infinity || minCol === Infinity) {
|
1096
|
+
return null;
|
1097
|
+
}
|
1098
|
+
|
1099
|
+
// The total span of the merged cell
|
1100
|
+
const totalRowSpan = maxRow - minRow + 1;
|
1101
|
+
const totalColSpan = maxCol - minCol + 1;
|
1102
|
+
|
1103
|
+
// Use the top-left cell as the target cell
|
1104
|
+
const targetCellMap = gridMap[minRow][minCol];
|
1105
|
+
if (!targetCellMap.cell) {
|
1106
|
+
return null;
|
1107
|
+
}
|
1108
|
+
const targetCell = targetCellMap.cell;
|
1109
|
+
|
1110
|
+
// Set the spans for the target cell
|
1111
|
+
targetCell.setColSpan(totalColSpan);
|
1112
|
+
targetCell.setRowSpan(totalRowSpan);
|
1113
|
+
|
1114
|
+
// Move content from other cells to the target cell
|
1115
|
+
const seenCells = new Set([targetCell.getKey()]);
|
1116
|
+
|
1117
|
+
// Second pass: merge content and remove other cells
|
1118
|
+
for (let row = minRow; row <= maxRow; row++) {
|
1119
|
+
for (let col = minCol; col <= maxCol; col++) {
|
1120
|
+
const mapCell = gridMap[row][col];
|
1121
|
+
if (!mapCell.cell) {
|
1122
|
+
continue;
|
1123
|
+
}
|
1124
|
+
const currentCell = mapCell.cell;
|
1125
|
+
const key = currentCell.getKey();
|
1126
|
+
if (!seenCells.has(key)) {
|
1127
|
+
seenCells.add(key);
|
1128
|
+
const isEmpty = $cellContainsEmptyParagraph(currentCell);
|
1129
|
+
if (!isEmpty) {
|
1130
|
+
targetCell.append(...currentCell.getChildren());
|
1131
|
+
}
|
1132
|
+
currentCell.remove();
|
1133
|
+
}
|
1134
|
+
}
|
1135
|
+
}
|
1136
|
+
|
1137
|
+
// Ensure target cell has content
|
1138
|
+
if (targetCell.getChildrenSize() === 0) {
|
1139
|
+
targetCell.append($createParagraphNode());
|
1140
|
+
}
|
1141
|
+
return targetCell;
|
1142
|
+
}
|
1143
|
+
function $cellContainsEmptyParagraph(cell) {
|
1144
|
+
if (cell.getChildrenSize() !== 1) {
|
1145
|
+
return false;
|
1146
|
+
}
|
1147
|
+
const firstChild = cell.getFirstChildOrThrow();
|
1148
|
+
if (!$isParagraphNode(firstChild) || !firstChild.isEmpty()) {
|
1149
|
+
return false;
|
1150
|
+
}
|
1151
|
+
return true;
|
1152
|
+
}
|
978
1153
|
function $unmergeCell() {
|
979
1154
|
const selection = $getSelection();
|
980
1155
|
if (!($isRangeSelection(selection) || $isTableSelection(selection))) {
|
981
1156
|
formatDevErrorMessage(`Expected a RangeSelection or TableSelection`);
|
982
1157
|
}
|
983
1158
|
const anchor = selection.anchor.getNode();
|
984
|
-
const
|
1159
|
+
const cellNode = $findMatchingParent(anchor, $isTableCellNode);
|
1160
|
+
if (!$isTableCellNode(cellNode)) {
|
1161
|
+
formatDevErrorMessage(`Expected to find a parent TableCellNode`);
|
1162
|
+
}
|
1163
|
+
return $unmergeCellNode(cellNode);
|
1164
|
+
}
|
1165
|
+
function $unmergeCellNode(cellNode) {
|
1166
|
+
const [cell, row, grid] = $getNodeTriplet(cellNode);
|
985
1167
|
const colSpan = cell.__colSpan;
|
986
1168
|
const rowSpan = cell.__rowSpan;
|
987
1169
|
if (colSpan === 1 && rowSpan === 1) {
|
@@ -2283,62 +2465,115 @@ function applyTableHandlers(tableNode, element, editor, hasTabHandler) {
|
|
2283
2465
|
if (nodes.length !== 1 || !$isTableNode(nodes[0]) || !isSelectionInsideOfGrid || anchorAndFocus === null) {
|
2284
2466
|
return false;
|
2285
2467
|
}
|
2286
|
-
const [anchor] = anchorAndFocus;
|
2287
|
-
const
|
2288
|
-
const
|
2289
|
-
|
2290
|
-
const newRowCount = newGrid.getChildrenSize();
|
2291
|
-
const gridCellNode = $findMatchingParent(anchor.getNode(), n => $isTableCellNode(n));
|
2292
|
-
const gridRowNode = gridCellNode && $findMatchingParent(gridCellNode, n => $isTableRowNode(n));
|
2293
|
-
const gridNode = gridRowNode && $findMatchingParent(gridRowNode, n => $isTableNode(n));
|
2294
|
-
if (!$isTableCellNode(gridCellNode) || !$isTableRowNode(gridRowNode) || !$isTableNode(gridNode)) {
|
2468
|
+
const [anchor, focus] = anchorAndFocus;
|
2469
|
+
const [anchorCellNode, anchorRowNode, gridNode] = $getNodeTriplet(anchor);
|
2470
|
+
const focusCellNode = $findMatchingParent(focus.getNode(), n => $isTableCellNode(n));
|
2471
|
+
if (!$isTableCellNode(anchorCellNode) || !$isTableCellNode(focusCellNode) || !$isTableRowNode(anchorRowNode) || !$isTableNode(gridNode)) {
|
2295
2472
|
return false;
|
2296
2473
|
}
|
2297
|
-
const
|
2298
|
-
const
|
2299
|
-
const
|
2300
|
-
const
|
2301
|
-
const
|
2302
|
-
|
2303
|
-
|
2304
|
-
|
2305
|
-
|
2306
|
-
let
|
2307
|
-
|
2308
|
-
|
2309
|
-
|
2310
|
-
|
2311
|
-
|
2312
|
-
const
|
2313
|
-
|
2314
|
-
|
2474
|
+
const templateGrid = nodes[0];
|
2475
|
+
const [initialGridMap, anchorCellMap, focusCellMap] = $computeTableMap(gridNode, anchorCellNode, focusCellNode);
|
2476
|
+
const [templateGridMap] = $computeTableMapSkipCellCheck(templateGrid, null, null);
|
2477
|
+
const initialRowCount = initialGridMap.length;
|
2478
|
+
const initialColCount = initialRowCount > 0 ? initialGridMap[0].length : 0;
|
2479
|
+
|
2480
|
+
// If we have a range selection, we'll fit the template grid into the
|
2481
|
+
// table, growing the table if necessary.
|
2482
|
+
let startRow = anchorCellMap.startRow;
|
2483
|
+
let startCol = anchorCellMap.startColumn;
|
2484
|
+
let affectedRowCount = templateGridMap.length;
|
2485
|
+
let affectedColCount = affectedRowCount > 0 ? templateGridMap[0].length : 0;
|
2486
|
+
if (isTableSelection) {
|
2487
|
+
// If we have a table selection, we'll only modify the cells within
|
2488
|
+
// the selection boundary.
|
2489
|
+
const selectionBoundary = $computeTableCellRectBoundary(initialGridMap, anchorCellMap, focusCellMap);
|
2490
|
+
const selectionRowCount = selectionBoundary.maxRow - selectionBoundary.minRow + 1;
|
2491
|
+
const selectionColCount = selectionBoundary.maxColumn - selectionBoundary.minColumn + 1;
|
2492
|
+
startRow = selectionBoundary.minRow;
|
2493
|
+
startCol = selectionBoundary.minColumn;
|
2494
|
+
affectedRowCount = Math.min(affectedRowCount, selectionRowCount);
|
2495
|
+
affectedColCount = Math.min(affectedColCount, selectionColCount);
|
2496
|
+
}
|
2497
|
+
|
2498
|
+
// Step 1: Unmerge all merged cells within the affected area
|
2499
|
+
let didPerformMergeOperations = false;
|
2500
|
+
const lastRowForUnmerge = Math.min(initialRowCount, startRow + affectedRowCount) - 1;
|
2501
|
+
const lastColForUnmerge = Math.min(initialColCount, startCol + affectedColCount) - 1;
|
2502
|
+
const unmergedKeys = new Set();
|
2503
|
+
for (let row = startRow; row <= lastRowForUnmerge; row++) {
|
2504
|
+
for (let col = startCol; col <= lastColForUnmerge; col++) {
|
2505
|
+
const cellMap = initialGridMap[row][col];
|
2506
|
+
if (unmergedKeys.has(cellMap.cell.getKey())) {
|
2507
|
+
continue; // cell was a merged cell that was already handled
|
2508
|
+
}
|
2509
|
+
if (cellMap.cell.__rowSpan === 1 && cellMap.cell.__colSpan === 1) {
|
2510
|
+
continue; // cell is not a merged cell
|
2511
|
+
}
|
2512
|
+
$unmergeCellNode(cellMap.cell);
|
2513
|
+
unmergedKeys.add(cellMap.cell.getKey());
|
2514
|
+
didPerformMergeOperations = true;
|
2315
2515
|
}
|
2316
|
-
|
2317
|
-
|
2318
|
-
|
2319
|
-
|
2320
|
-
|
2321
|
-
|
2322
|
-
|
2516
|
+
}
|
2517
|
+
let [interimGridMap] = $computeTableMapSkipCellCheck(gridNode.getWritable(), null, null);
|
2518
|
+
|
2519
|
+
// Step 2: Expand current table (if needed)
|
2520
|
+
const rowsToInsert = affectedRowCount - initialRowCount + startRow;
|
2521
|
+
for (let i = 0; i < rowsToInsert; i++) {
|
2522
|
+
const cellMap = interimGridMap[initialRowCount - 1][0];
|
2523
|
+
$insertTableRowAtNode(cellMap.cell);
|
2524
|
+
}
|
2525
|
+
const colsToInsert = affectedColCount - initialColCount + startCol;
|
2526
|
+
for (let i = 0; i < colsToInsert; i++) {
|
2527
|
+
const cellMap = interimGridMap[0][initialColCount - 1];
|
2528
|
+
$insertTableColumnAtNode(cellMap.cell, true, false);
|
2529
|
+
}
|
2530
|
+
[interimGridMap] = $computeTableMapSkipCellCheck(gridNode.getWritable(), null, null);
|
2531
|
+
|
2532
|
+
// Step 3: Merge cells and set cell content, to match template grid
|
2533
|
+
for (let row = startRow; row < startRow + affectedRowCount; row++) {
|
2534
|
+
for (let col = startCol; col < startCol + affectedColCount; col++) {
|
2535
|
+
const templateRow = row - startRow;
|
2536
|
+
const templateCol = col - startCol;
|
2537
|
+
const templateCellMap = templateGridMap[templateRow][templateCol];
|
2538
|
+
if (templateCellMap.startRow !== templateRow || templateCellMap.startColumn !== templateCol) {
|
2539
|
+
continue; // cell is a merged cell that was already handled
|
2323
2540
|
}
|
2324
|
-
const
|
2325
|
-
if (
|
2326
|
-
|
2541
|
+
const templateCell = templateCellMap.cell;
|
2542
|
+
if (templateCell.__rowSpan !== 1 || templateCell.__colSpan !== 1) {
|
2543
|
+
const cellsToMerge = [];
|
2544
|
+
const lastRowForMerge = Math.min(row + templateCell.__rowSpan, startRow + affectedRowCount) - 1;
|
2545
|
+
const lastColForMerge = Math.min(col + templateCell.__colSpan, startCol + affectedColCount) - 1;
|
2546
|
+
for (let r = row; r <= lastRowForMerge; r++) {
|
2547
|
+
for (let c = col; c <= lastColForMerge; c++) {
|
2548
|
+
const cellMap = interimGridMap[r][c];
|
2549
|
+
cellsToMerge.push(cellMap.cell);
|
2550
|
+
}
|
2551
|
+
}
|
2552
|
+
$mergeCells(cellsToMerge);
|
2553
|
+
didPerformMergeOperations = true;
|
2327
2554
|
}
|
2328
|
-
const
|
2329
|
-
|
2555
|
+
const {
|
2556
|
+
cell
|
2557
|
+
} = interimGridMap[row][col];
|
2558
|
+
const originalChildren = cell.getChildren();
|
2559
|
+
templateCell.getChildren().forEach(child => {
|
2330
2560
|
if ($isTextNode(child)) {
|
2331
2561
|
const paragraphNode = $createParagraphNode();
|
2332
2562
|
paragraphNode.append(child);
|
2333
|
-
|
2563
|
+
cell.append(child);
|
2334
2564
|
} else {
|
2335
|
-
|
2565
|
+
cell.append(child);
|
2336
2566
|
}
|
2337
2567
|
});
|
2338
2568
|
originalChildren.forEach(n => n.remove());
|
2339
|
-
newColumnIdx++;
|
2340
2569
|
}
|
2341
|
-
|
2570
|
+
}
|
2571
|
+
if (isTableSelection && didPerformMergeOperations) {
|
2572
|
+
// reset the table selection in case the anchor or focus cell was
|
2573
|
+
// removed via merge operations
|
2574
|
+
const [finalGridMap] = $computeTableMapSkipCellCheck(gridNode.getWritable(), null, null);
|
2575
|
+
const newAnchorCellMap = finalGridMap[anchorCellMap.startRow][anchorCellMap.startColumn];
|
2576
|
+
newAnchorCellMap.cell.selectEnd();
|
2342
2577
|
}
|
2343
2578
|
return true;
|
2344
2579
|
}, COMMAND_PRIORITY_CRITICAL));
|
@@ -3889,4 +4124,4 @@ function registerTablePlugin(editor) {
|
|
3889
4124
|
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(CLICK_COMMAND, $tableClickCommand, COMMAND_PRIORITY_EDITOR), editor.registerNodeTransform(TableNode, $tableTransform), editor.registerNodeTransform(TableRowNode, $tableRowTransform), editor.registerNodeTransform(TableCellNode, $tableCellTransform));
|
3890
4125
|
}
|
3891
4126
|
|
3892
|
-
export { $computeTableMap, $computeTableMapSkipCellCheck, $createTableCellNode, $createTableNode, $createTableNodeWithDimensions, $createTableRowNode, $createTableSelection, $createTableSelectionFrom, $deleteTableColumn, $deleteTableColumn__EXPERIMENTAL, $deleteTableRow__EXPERIMENTAL, $findCellNode, $findTableNode, $getElementForTableNode, $getNodeTriplet, $getTableAndElementByKey, $getTableCellNodeFromLexicalNode, $getTableCellNodeRect, $getTableColumnIndexFromTableCellNode, $getTableNodeFromLexicalNodeOrThrow, $getTableRowIndexFromTableCellNode, $getTableRowNodeFromTableCellNodeOrThrow, $insertTableColumn, $insertTableColumn__EXPERIMENTAL, $insertTableRow, $insertTableRow__EXPERIMENTAL, $isScrollableTablesActive, $isTableCellNode, $isTableNode, $isTableRowNode, $isTableSelection, $removeTableRowAtIndex, $unmergeCell, INSERT_TABLE_COMMAND, TableCellHeaderStates, TableCellNode, TableNode, TableObserver, TableRowNode, applyTableHandlers, getDOMCellFromTarget, getTableElement, getTableObserverFromTableElement, registerTableCellUnmergeTransform, registerTablePlugin, registerTableSelectionObserver, setScrollableTablesActive };
|
4127
|
+
export { $computeTableMap, $computeTableMapSkipCellCheck, $createTableCellNode, $createTableNode, $createTableNodeWithDimensions, $createTableRowNode, $createTableSelection, $createTableSelectionFrom, $deleteTableColumn, $deleteTableColumnAtSelection, $deleteTableColumn__EXPERIMENTAL, $deleteTableRowAtSelection, $deleteTableRow__EXPERIMENTAL, $findCellNode, $findTableNode, $getElementForTableNode, $getNodeTriplet, $getTableAndElementByKey, $getTableCellNodeFromLexicalNode, $getTableCellNodeRect, $getTableColumnIndexFromTableCellNode, $getTableNodeFromLexicalNodeOrThrow, $getTableRowIndexFromTableCellNode, $getTableRowNodeFromTableCellNodeOrThrow, $insertTableColumn, $insertTableColumnAtSelection, $insertTableColumn__EXPERIMENTAL, $insertTableRow, $insertTableRowAtSelection, $insertTableRow__EXPERIMENTAL, $isScrollableTablesActive, $isTableCellNode, $isTableNode, $isTableRowNode, $isTableSelection, $mergeCells, $removeTableRowAtIndex, $unmergeCell, INSERT_TABLE_COMMAND, TableCellHeaderStates, TableCellNode, TableNode, TableObserver, TableRowNode, applyTableHandlers, getDOMCellFromTarget, getTableElement, getTableObserverFromTableElement, registerTableCellUnmergeTransform, registerTablePlugin, registerTableSelectionObserver, setScrollableTablesActive };
|
package/LexicalTable.js.flow
CHANGED
@@ -239,6 +239,7 @@ declare export function $removeTableRowAtIndex(
|
|
239
239
|
indexToDelete: number,
|
240
240
|
): TableNode;
|
241
241
|
|
242
|
+
/** @deprecated This function does not support merged cells. Use {@link $insertTableRowAtSelection} or {@link $insertTableRowAtNode} instead. */
|
242
243
|
declare export function $insertTableRow(
|
243
244
|
tableNode: TableNode,
|
244
245
|
targetIndex: number,
|
@@ -247,6 +248,7 @@ declare export function $insertTableRow(
|
|
247
248
|
table: TableDOMTable,
|
248
249
|
): TableNode;
|
249
250
|
|
251
|
+
/** @deprecated This function does not support merged cells. Use {@link $insertTableColumnAtSelection} or {@link $insertTableColumnAtNode} instead. */
|
250
252
|
declare export function $insertTableColumn(
|
251
253
|
tableNode: TableNode,
|
252
254
|
targetIndex: number,
|
@@ -255,21 +257,49 @@ declare export function $insertTableColumn(
|
|
255
257
|
table: TableDOMTable,
|
256
258
|
): TableNode;
|
257
259
|
|
260
|
+
/** @deprecated This function does not support merged cells. Use {@link $deleteTableColumnAtSelection} instead. */
|
258
261
|
declare export function $deleteTableColumn(
|
259
262
|
tableNode: TableNode,
|
260
263
|
targetIndex: number,
|
261
264
|
): TableNode;
|
262
265
|
|
266
|
+
declare export function $insertTableRowAtSelection(
|
267
|
+
insertAfter?: boolean,
|
268
|
+
): TableRowNode | null;
|
269
|
+
|
270
|
+
declare export function $insertTableRowAtNode(
|
271
|
+
cellNode: TableCellNode,
|
272
|
+
insertAfter?: boolean,
|
273
|
+
): TableRowNode | null;
|
274
|
+
|
275
|
+
declare export function $insertTableColumnAtSelection(
|
276
|
+
insertAfter?: boolean,
|
277
|
+
): TableCellNode | null;
|
278
|
+
|
279
|
+
declare export function $insertTableColumnAtNode(
|
280
|
+
cellNode: TableCellNode,
|
281
|
+
insertAfter?: boolean,
|
282
|
+
shouldSetSelection?: boolean,
|
283
|
+
): TableCellNode | null
|
284
|
+
|
285
|
+
declare export function $deleteTableRowAtSelection(): void;
|
286
|
+
|
287
|
+
declare export function $deleteTableColumnAtSelection(): void;
|
288
|
+
|
289
|
+
/** @deprecated renamed to {@link $insertTableRowAtSelection} */
|
263
290
|
declare export function $insertTableRow__EXPERIMENTAL(
|
264
291
|
insertAfter: boolean,
|
265
292
|
): TableRowNode | null;
|
266
293
|
|
294
|
+
/** @deprecated renamed to {@link $insertTableColumnAtSelection} */
|
267
295
|
declare export function $insertTableColumn__EXPERIMENTAL(
|
268
296
|
insertAfter: boolean,
|
269
297
|
): TableCellNode | null;
|
270
298
|
|
299
|
+
/** @deprecated renamed to {@link $deleteTableRowAtSelection} */
|
271
300
|
declare export function $deleteTableRow__EXPERIMENTAL(): void;
|
272
301
|
|
302
|
+
/** @deprecated renamed to {@link $deleteTableColumnAtSelection} */
|
273
303
|
declare export function $deleteTableColumn__EXPERIMENTAL(): void;
|
274
304
|
|
275
305
|
declare export function $unmergeCell(): void;
|
package/LexicalTable.mjs
CHANGED
@@ -18,7 +18,9 @@ export const $createTableRowNode = mod.$createTableRowNode;
|
|
18
18
|
export const $createTableSelection = mod.$createTableSelection;
|
19
19
|
export const $createTableSelectionFrom = mod.$createTableSelectionFrom;
|
20
20
|
export const $deleteTableColumn = mod.$deleteTableColumn;
|
21
|
+
export const $deleteTableColumnAtSelection = mod.$deleteTableColumnAtSelection;
|
21
22
|
export const $deleteTableColumn__EXPERIMENTAL = mod.$deleteTableColumn__EXPERIMENTAL;
|
23
|
+
export const $deleteTableRowAtSelection = mod.$deleteTableRowAtSelection;
|
22
24
|
export const $deleteTableRow__EXPERIMENTAL = mod.$deleteTableRow__EXPERIMENTAL;
|
23
25
|
export const $findCellNode = mod.$findCellNode;
|
24
26
|
export const $findTableNode = mod.$findTableNode;
|
@@ -32,14 +34,17 @@ export const $getTableNodeFromLexicalNodeOrThrow = mod.$getTableNodeFromLexicalN
|
|
32
34
|
export const $getTableRowIndexFromTableCellNode = mod.$getTableRowIndexFromTableCellNode;
|
33
35
|
export const $getTableRowNodeFromTableCellNodeOrThrow = mod.$getTableRowNodeFromTableCellNodeOrThrow;
|
34
36
|
export const $insertTableColumn = mod.$insertTableColumn;
|
37
|
+
export const $insertTableColumnAtSelection = mod.$insertTableColumnAtSelection;
|
35
38
|
export const $insertTableColumn__EXPERIMENTAL = mod.$insertTableColumn__EXPERIMENTAL;
|
36
39
|
export const $insertTableRow = mod.$insertTableRow;
|
40
|
+
export const $insertTableRowAtSelection = mod.$insertTableRowAtSelection;
|
37
41
|
export const $insertTableRow__EXPERIMENTAL = mod.$insertTableRow__EXPERIMENTAL;
|
38
42
|
export const $isScrollableTablesActive = mod.$isScrollableTablesActive;
|
39
43
|
export const $isTableCellNode = mod.$isTableCellNode;
|
40
44
|
export const $isTableNode = mod.$isTableNode;
|
41
45
|
export const $isTableRowNode = mod.$isTableRowNode;
|
42
46
|
export const $isTableSelection = mod.$isTableSelection;
|
47
|
+
export const $mergeCells = mod.$mergeCells;
|
43
48
|
export const $removeTableRowAtIndex = mod.$removeTableRowAtIndex;
|
44
49
|
export const $unmergeCell = mod.$unmergeCell;
|
45
50
|
export const INSERT_TABLE_COMMAND = mod.INSERT_TABLE_COMMAND;
|
package/LexicalTable.node.mjs
CHANGED
@@ -16,7 +16,9 @@ export const $createTableRowNode = mod.$createTableRowNode;
|
|
16
16
|
export const $createTableSelection = mod.$createTableSelection;
|
17
17
|
export const $createTableSelectionFrom = mod.$createTableSelectionFrom;
|
18
18
|
export const $deleteTableColumn = mod.$deleteTableColumn;
|
19
|
+
export const $deleteTableColumnAtSelection = mod.$deleteTableColumnAtSelection;
|
19
20
|
export const $deleteTableColumn__EXPERIMENTAL = mod.$deleteTableColumn__EXPERIMENTAL;
|
21
|
+
export const $deleteTableRowAtSelection = mod.$deleteTableRowAtSelection;
|
20
22
|
export const $deleteTableRow__EXPERIMENTAL = mod.$deleteTableRow__EXPERIMENTAL;
|
21
23
|
export const $findCellNode = mod.$findCellNode;
|
22
24
|
export const $findTableNode = mod.$findTableNode;
|
@@ -30,14 +32,17 @@ export const $getTableNodeFromLexicalNodeOrThrow = mod.$getTableNodeFromLexicalN
|
|
30
32
|
export const $getTableRowIndexFromTableCellNode = mod.$getTableRowIndexFromTableCellNode;
|
31
33
|
export const $getTableRowNodeFromTableCellNodeOrThrow = mod.$getTableRowNodeFromTableCellNodeOrThrow;
|
32
34
|
export const $insertTableColumn = mod.$insertTableColumn;
|
35
|
+
export const $insertTableColumnAtSelection = mod.$insertTableColumnAtSelection;
|
33
36
|
export const $insertTableColumn__EXPERIMENTAL = mod.$insertTableColumn__EXPERIMENTAL;
|
34
37
|
export const $insertTableRow = mod.$insertTableRow;
|
38
|
+
export const $insertTableRowAtSelection = mod.$insertTableRowAtSelection;
|
35
39
|
export const $insertTableRow__EXPERIMENTAL = mod.$insertTableRow__EXPERIMENTAL;
|
36
40
|
export const $isScrollableTablesActive = mod.$isScrollableTablesActive;
|
37
41
|
export const $isTableCellNode = mod.$isTableCellNode;
|
38
42
|
export const $isTableNode = mod.$isTableNode;
|
39
43
|
export const $isTableRowNode = mod.$isTableRowNode;
|
40
44
|
export const $isTableSelection = mod.$isTableSelection;
|
45
|
+
export const $mergeCells = mod.$mergeCells;
|
41
46
|
export const $removeTableRowAtIndex = mod.$removeTableRowAtIndex;
|
42
47
|
export const $unmergeCell = mod.$unmergeCell;
|
43
48
|
export const INSERT_TABLE_COMMAND = mod.INSERT_TABLE_COMMAND;
|