@lexical/table 0.39.1-nightly.20260120.0 → 0.39.1-nightly.20260121.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.
@@ -2008,23 +2008,42 @@ class TableObserver {
2008
2008
  const cellX = cell.x;
2009
2009
  const cellY = cell.y;
2010
2010
  this.focusCell = cell;
2011
- if (!this.isHighlightingCells && (this.anchorX !== cellX || this.anchorY !== cellY || ignoreStart)) {
2012
- this.isHighlightingCells = true;
2013
- this.$disableHighlightStyle();
2014
- } else if (cellX === this.focusX && cellY === this.focusY) {
2011
+
2012
+ // Enable highlighting if: ignoreStart is true, or anchor differs from focus,
2013
+ // or we have valid tableSelection with anchor (for first drag after column switch)
2014
+ if (!this.isHighlightingCells) {
2015
+ const shouldEnable = ignoreStart || this.anchorX !== cellX || this.anchorY !== cellY || this.tableSelection != null && this.anchorCellNodeKey != null;
2016
+ if (shouldEnable) {
2017
+ this.isHighlightingCells = true;
2018
+ this.$disableHighlightStyle();
2019
+ }
2020
+ }
2021
+
2022
+ // Skip if we're trying to select the same cell we already have selected
2023
+ // But only if focusX/focusY are valid (not -1, which means not reset)
2024
+ if (this.focusX !== -1 && this.focusY !== -1 && cellX === this.focusX && cellY === this.focusY) {
2015
2025
  return false;
2016
2026
  }
2017
2027
  this.focusX = cellX;
2018
2028
  this.focusY = cellY;
2019
2029
  if (this.isHighlightingCells) {
2020
2030
  const focusTableCellNode = $getNearestTableCellInTableFromDOMNode(tableNode, cell.elem);
2021
- if (this.tableSelection != null && this.anchorCellNodeKey != null && focusTableCellNode !== null) {
2022
- this.focusCellNodeKey = focusTableCellNode.getKey();
2023
- this.tableSelection = $createTableSelectionFrom(tableNode, this.$getAnchorTableCellOrThrow(), focusTableCellNode);
2024
- lexical.$setSelection(this.tableSelection);
2025
- editor.dispatchCommand(lexical.SELECTION_CHANGE_COMMAND, undefined);
2026
- $updateDOMForSelection(editor, this.table, this.tableSelection);
2027
- return true;
2031
+ if (this.tableSelection != null && this.anchorCellNodeKey != null) {
2032
+ let targetCellNode = focusTableCellNode;
2033
+
2034
+ // Fallback: use coordinates if DOM lookup failed (handles timing issues on first drag)
2035
+ if (targetCellNode === null && ignoreStart) {
2036
+ targetCellNode = tableNode.getCellNodeFromCords(cellX, cellY, this.table);
2037
+ }
2038
+ if (targetCellNode !== null) {
2039
+ const anchorTableCell = this.$getAnchorTableCellOrThrow();
2040
+ this.focusCellNodeKey = targetCellNode.getKey();
2041
+ this.tableSelection = $createTableSelectionFrom(tableNode, anchorTableCell, targetCellNode);
2042
+ lexical.$setSelection(this.tableSelection);
2043
+ editor.dispatchCommand(lexical.SELECTION_CHANGE_COMMAND, undefined);
2044
+ $updateDOMForSelection(editor, this.table, this.tableSelection);
2045
+ return true;
2046
+ }
2028
2047
  }
2029
2048
  }
2030
2049
  return false;
@@ -2054,13 +2073,23 @@ class TableObserver {
2054
2073
  this.anchorCell = cell;
2055
2074
  this.anchorX = cell.x;
2056
2075
  this.anchorY = cell.y;
2076
+ // Reset focus state to prevent stale values from previous selections
2077
+ this.focusX = -1;
2078
+ this.focusY = -1;
2079
+ this.focusCell = null;
2080
+ this.focusCellNodeKey = null;
2057
2081
  const {
2058
2082
  tableNode
2059
2083
  } = this.$lookup();
2060
2084
  const anchorTableCellNode = $getNearestTableCellInTableFromDOMNode(tableNode, cell.elem);
2061
2085
  if (anchorTableCellNode !== null) {
2062
2086
  const anchorNodeKey = anchorTableCellNode.getKey();
2063
- this.tableSelection = this.tableSelection != null ? this.tableSelection.clone() : $createTableSelection();
2087
+ if (this.tableSelection != null) {
2088
+ this.tableSelection = this.tableSelection.clone();
2089
+ this.tableSelection.set(tableNode.getKey(), anchorNodeKey, anchorNodeKey);
2090
+ } else {
2091
+ this.tableSelection = $createTableSelectionFrom(tableNode, anchorTableCellNode, anchorTableCellNode);
2092
+ }
2064
2093
  this.anchorCellNodeKey = anchorNodeKey;
2065
2094
  }
2066
2095
  }
@@ -2177,10 +2206,18 @@ function applyTableHandlers(tableNode, element, editor, hasTabHandler) {
2177
2206
  const tableElement = getTableElement(tableNode, element);
2178
2207
  attachTableObserverToTableElement(tableElement, tableObserver);
2179
2208
  tableObserver.listenersToRemove.add(() => detachTableObserverFromTableElement(tableElement, tableObserver));
2180
- const createPointerHandlers = () => {
2209
+ const createPointerHandlers = startingCell => {
2181
2210
  if (tableObserver.isSelecting) {
2182
2211
  return;
2183
2212
  }
2213
+ tableObserver.isSelecting = true;
2214
+
2215
+ // Set anchor immediately if starting cell provided (handles direct drag without click)
2216
+ if (startingCell !== null && tableObserver.anchorCell === null) {
2217
+ editor.update(() => {
2218
+ tableObserver.$setAnchorCellForSelection(startingCell);
2219
+ });
2220
+ }
2184
2221
  const onPointerUp = () => {
2185
2222
  tableObserver.isSelecting = false;
2186
2223
  editorWindow.removeEventListener('pointerup', onPointerUp);
@@ -2210,15 +2247,23 @@ function applyTableHandlers(tableNode, element, editor, hasTabHandler) {
2210
2247
  }
2211
2248
  }
2212
2249
  }
2213
- if (focusCell && (tableObserver.focusCell === null || focusCell.elem !== tableObserver.focusCell.elem)) {
2214
- tableObserver.setNextFocus({
2215
- focusCell,
2216
- override
2217
- });
2218
- editor.dispatchCommand(lexical.SELECTION_CHANGE_COMMAND, undefined);
2250
+ if (focusCell) {
2251
+ const anchorCell = focusCell;
2252
+ // Fallback: set anchor if still missing (handles race conditions)
2253
+ if (tableObserver.anchorCell === null) {
2254
+ editor.update(() => {
2255
+ tableObserver.$setAnchorCellForSelection(anchorCell);
2256
+ });
2257
+ }
2258
+ if (tableObserver.focusCell === null || focusCell.elem !== tableObserver.focusCell.elem) {
2259
+ tableObserver.setNextFocus({
2260
+ focusCell,
2261
+ override
2262
+ });
2263
+ editor.dispatchCommand(lexical.SELECTION_CHANGE_COMMAND, undefined);
2264
+ }
2219
2265
  }
2220
2266
  };
2221
- tableObserver.isSelecting = true;
2222
2267
  editorWindow.addEventListener('pointerup', onPointerUp, tableObserver.listenerOptions);
2223
2268
  editorWindow.addEventListener('pointermove', onPointerMove, tableObserver.listenerOptions);
2224
2269
  };
@@ -2254,7 +2299,10 @@ function applyTableHandlers(tableNode, element, editor, hasTabHandler) {
2254
2299
  }
2255
2300
  });
2256
2301
  }
2257
- createPointerHandlers();
2302
+
2303
+ // Pass the target cell to createPointerHandlers so it can be used as anchor
2304
+ // if user drags directly without clicking first
2305
+ createPointerHandlers(targetCell);
2258
2306
  };
2259
2307
  tableElement.addEventListener('pointerdown', onPointerDown, tableObserver.listenerOptions);
2260
2308
  tableObserver.listenersToRemove.add(() => {
@@ -2501,133 +2549,6 @@ function applyTableHandlers(tableNode, element, editor, hasTabHandler) {
2501
2549
  tableObserver.listenersToRemove.add(editor.registerCommand(lexical.FOCUS_COMMAND, payload => {
2502
2550
  return tableNode.isSelected();
2503
2551
  }, lexical.COMMAND_PRIORITY_HIGH));
2504
- tableObserver.listenersToRemove.add(editor.registerCommand(lexical.SELECTION_INSERT_CLIPBOARD_NODES_COMMAND, (selectionPayload, dispatchEditor) => {
2505
- if (editor !== dispatchEditor) {
2506
- return false;
2507
- }
2508
- const {
2509
- nodes,
2510
- selection
2511
- } = selectionPayload;
2512
- const anchorAndFocus = selection.getStartEndPoints();
2513
- const isTableSelection = $isTableSelection(selection);
2514
- const isRangeSelection = lexical.$isRangeSelection(selection);
2515
- const isSelectionInsideOfGrid = isRangeSelection && utils.$findMatchingParent(selection.anchor.getNode(), n => $isTableCellNode(n)) !== null && utils.$findMatchingParent(selection.focus.getNode(), n => $isTableCellNode(n)) !== null || isTableSelection;
2516
- if (nodes.length !== 1 || !$isTableNode(nodes[0]) || !isSelectionInsideOfGrid || anchorAndFocus === null) {
2517
- return false;
2518
- }
2519
- const [anchor, focus] = anchorAndFocus;
2520
- const [anchorCellNode, anchorRowNode, gridNode] = $getNodeTriplet(anchor);
2521
- const focusCellNode = utils.$findMatchingParent(focus.getNode(), n => $isTableCellNode(n));
2522
- if (!$isTableCellNode(anchorCellNode) || !$isTableCellNode(focusCellNode) || !$isTableRowNode(anchorRowNode) || !$isTableNode(gridNode)) {
2523
- return false;
2524
- }
2525
- const templateGrid = nodes[0];
2526
- const [initialGridMap, anchorCellMap, focusCellMap] = $computeTableMap(gridNode, anchorCellNode, focusCellNode);
2527
- const [templateGridMap] = $computeTableMapSkipCellCheck(templateGrid, null, null);
2528
- const initialRowCount = initialGridMap.length;
2529
- const initialColCount = initialRowCount > 0 ? initialGridMap[0].length : 0;
2530
-
2531
- // If we have a range selection, we'll fit the template grid into the
2532
- // table, growing the table if necessary.
2533
- let startRow = anchorCellMap.startRow;
2534
- let startCol = anchorCellMap.startColumn;
2535
- let affectedRowCount = templateGridMap.length;
2536
- let affectedColCount = affectedRowCount > 0 ? templateGridMap[0].length : 0;
2537
- if (isTableSelection) {
2538
- // If we have a table selection, we'll only modify the cells within
2539
- // the selection boundary.
2540
- const selectionBoundary = $computeTableCellRectBoundary(initialGridMap, anchorCellMap, focusCellMap);
2541
- const selectionRowCount = selectionBoundary.maxRow - selectionBoundary.minRow + 1;
2542
- const selectionColCount = selectionBoundary.maxColumn - selectionBoundary.minColumn + 1;
2543
- startRow = selectionBoundary.minRow;
2544
- startCol = selectionBoundary.minColumn;
2545
- affectedRowCount = Math.min(affectedRowCount, selectionRowCount);
2546
- affectedColCount = Math.min(affectedColCount, selectionColCount);
2547
- }
2548
-
2549
- // Step 1: Unmerge all merged cells within the affected area
2550
- let didPerformMergeOperations = false;
2551
- const lastRowForUnmerge = Math.min(initialRowCount, startRow + affectedRowCount) - 1;
2552
- const lastColForUnmerge = Math.min(initialColCount, startCol + affectedColCount) - 1;
2553
- const unmergedKeys = new Set();
2554
- for (let row = startRow; row <= lastRowForUnmerge; row++) {
2555
- for (let col = startCol; col <= lastColForUnmerge; col++) {
2556
- const cellMap = initialGridMap[row][col];
2557
- if (unmergedKeys.has(cellMap.cell.getKey())) {
2558
- continue; // cell was a merged cell that was already handled
2559
- }
2560
- if (cellMap.cell.__rowSpan === 1 && cellMap.cell.__colSpan === 1) {
2561
- continue; // cell is not a merged cell
2562
- }
2563
- $unmergeCellNode(cellMap.cell);
2564
- unmergedKeys.add(cellMap.cell.getKey());
2565
- didPerformMergeOperations = true;
2566
- }
2567
- }
2568
- let [interimGridMap] = $computeTableMapSkipCellCheck(gridNode.getWritable(), null, null);
2569
-
2570
- // Step 2: Expand current table (if needed)
2571
- const rowsToInsert = affectedRowCount - initialRowCount + startRow;
2572
- for (let i = 0; i < rowsToInsert; i++) {
2573
- const cellMap = interimGridMap[initialRowCount - 1][0];
2574
- $insertTableRowAtNode(cellMap.cell);
2575
- }
2576
- const colsToInsert = affectedColCount - initialColCount + startCol;
2577
- for (let i = 0; i < colsToInsert; i++) {
2578
- const cellMap = interimGridMap[0][initialColCount - 1];
2579
- $insertTableColumnAtNode(cellMap.cell, true, false);
2580
- }
2581
- [interimGridMap] = $computeTableMapSkipCellCheck(gridNode.getWritable(), null, null);
2582
-
2583
- // Step 3: Merge cells and set cell content, to match template grid
2584
- for (let row = startRow; row < startRow + affectedRowCount; row++) {
2585
- for (let col = startCol; col < startCol + affectedColCount; col++) {
2586
- const templateRow = row - startRow;
2587
- const templateCol = col - startCol;
2588
- const templateCellMap = templateGridMap[templateRow][templateCol];
2589
- if (templateCellMap.startRow !== templateRow || templateCellMap.startColumn !== templateCol) {
2590
- continue; // cell is a merged cell that was already handled
2591
- }
2592
- const templateCell = templateCellMap.cell;
2593
- if (templateCell.__rowSpan !== 1 || templateCell.__colSpan !== 1) {
2594
- const cellsToMerge = [];
2595
- const lastRowForMerge = Math.min(row + templateCell.__rowSpan, startRow + affectedRowCount) - 1;
2596
- const lastColForMerge = Math.min(col + templateCell.__colSpan, startCol + affectedColCount) - 1;
2597
- for (let r = row; r <= lastRowForMerge; r++) {
2598
- for (let c = col; c <= lastColForMerge; c++) {
2599
- const cellMap = interimGridMap[r][c];
2600
- cellsToMerge.push(cellMap.cell);
2601
- }
2602
- }
2603
- $mergeCells(cellsToMerge);
2604
- didPerformMergeOperations = true;
2605
- }
2606
- const {
2607
- cell
2608
- } = interimGridMap[row][col];
2609
- const originalChildren = cell.getChildren();
2610
- templateCell.getChildren().forEach(child => {
2611
- if (lexical.$isTextNode(child)) {
2612
- const paragraphNode = lexical.$createParagraphNode();
2613
- paragraphNode.append(child);
2614
- cell.append(child);
2615
- } else {
2616
- cell.append(child);
2617
- }
2618
- });
2619
- originalChildren.forEach(n => n.remove());
2620
- }
2621
- }
2622
- if (isTableSelection && didPerformMergeOperations) {
2623
- // reset the table selection in case the anchor or focus cell was
2624
- // removed via merge operations
2625
- const [finalGridMap] = $computeTableMapSkipCellCheck(gridNode.getWritable(), null, null);
2626
- const newAnchorCellMap = finalGridMap[anchorCellMap.startRow][anchorCellMap.startColumn];
2627
- newAnchorCellMap.cell.selectEnd();
2628
- }
2629
- return true;
2630
- }, lexical.COMMAND_PRIORITY_HIGH));
2631
2552
  tableObserver.listenersToRemove.add(editor.registerCommand(lexical.SELECTION_CHANGE_COMMAND, () => {
2632
2553
  const selection = lexical.$getSelection();
2633
2554
  const prevSelection = lexical.$getPreviousSelection();
@@ -2644,9 +2565,14 @@ function applyTableHandlers(tableNode, element, editor, hasTabHandler) {
2644
2565
  tableObserver.$setFocusCellForSelection(focusCell);
2645
2566
  return true;
2646
2567
  }
2647
- } else if (focusCell !== tableObserver.anchorCell && $isSelectionInTable(selection, tableNode)) {
2568
+ } else if (focusCell !== tableObserver.anchorCell && tableObserver.anchorCell !== null && tableObserver.anchorCellNodeKey !== null && tableObserver.tableSelection !== null) {
2648
2569
  // The selection has crossed cells
2649
- tableObserver.$setFocusCellForSelection(focusCell);
2570
+ // If we have an anchor cell set and tableSelection initialized,
2571
+ // we have all the necessary state to create the selection.
2572
+ // The presence of nextFocus means we're dragging, so process it.
2573
+ // Use ignoreStart=true to ensure isHighlightingCells is set correctly
2574
+ // on the first drag attempt, especially when switching columns.
2575
+ tableObserver.$setFocusCellForSelection(focusCell, true);
2650
2576
  return true;
2651
2577
  }
2652
2578
  }
@@ -4229,7 +4155,7 @@ function registerTableSelectionObserver(editor, hasTabHandler = true) {
4229
4155
  }
4230
4156
 
4231
4157
  /**
4232
- * Register the INSERT_TABLE_COMMAND listener and the table integrity transforms. The
4158
+ * Register table command listeners and the table integrity transforms. The
4233
4159
  * table selection observer should be registered separately after this with
4234
4160
  * {@link registerTableSelectionObserver}.
4235
4161
  *
@@ -4247,10 +4173,17 @@ function registerTablePlugin(editor, options) {
4247
4173
  } = options ?? {};
4248
4174
  return utils.mergeRegister(editor.registerCommand(INSERT_TABLE_COMMAND, payload => {
4249
4175
  return $insertTable(payload, hasNestedTables.peek());
4250
- }, lexical.COMMAND_PRIORITY_EDITOR), editor.registerCommand(lexical.SELECTION_INSERT_CLIPBOARD_NODES_COMMAND, ({
4251
- nodes,
4252
- selection
4253
- }, dispatchEditor) => {
4176
+ }, lexical.COMMAND_PRIORITY_EDITOR), editor.registerCommand(lexical.SELECTION_INSERT_CLIPBOARD_NODES_COMMAND, (selectionPayload, dispatchEditor) => {
4177
+ if (editor !== dispatchEditor) {
4178
+ return false;
4179
+ }
4180
+ if ($tableSelectionInsertClipboardNodesCommand(selectionPayload)) {
4181
+ return true;
4182
+ }
4183
+ const {
4184
+ selection,
4185
+ nodes
4186
+ } = selectionPayload;
4254
4187
  if (hasNestedTables.peek() || editor !== dispatchEditor || !lexical.$isRangeSelection(selection)) {
4255
4188
  return false;
4256
4189
  }
@@ -4258,6 +4191,130 @@ function registerTablePlugin(editor, options) {
4258
4191
  return isInsideTableCell && nodes.some($isTableNode);
4259
4192
  }, lexical.COMMAND_PRIORITY_EDITOR), editor.registerCommand(lexical.SELECT_ALL_COMMAND, $tableSelectAllCommand, lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.CLICK_COMMAND, $tableClickCommand, lexical.COMMAND_PRIORITY_EDITOR), editor.registerNodeTransform(TableNode, $tableTransform), editor.registerNodeTransform(TableRowNode, $tableRowTransform), editor.registerNodeTransform(TableCellNode, $tableCellTransform));
4260
4193
  }
4194
+ function $tableSelectionInsertClipboardNodesCommand(selectionPayload) {
4195
+ const {
4196
+ nodes,
4197
+ selection
4198
+ } = selectionPayload;
4199
+ const anchorAndFocus = selection.getStartEndPoints();
4200
+ const isTableSelection = $isTableSelection(selection);
4201
+ const isRangeSelection = lexical.$isRangeSelection(selection);
4202
+ const isSelectionInsideOfGrid = isRangeSelection && utils.$findMatchingParent(selection.anchor.getNode(), n => $isTableCellNode(n)) !== null && utils.$findMatchingParent(selection.focus.getNode(), n => $isTableCellNode(n)) !== null || isTableSelection;
4203
+ if (nodes.length !== 1 || !$isTableNode(nodes[0]) || !isSelectionInsideOfGrid || anchorAndFocus === null) {
4204
+ return false;
4205
+ }
4206
+ const [anchor, focus] = anchorAndFocus;
4207
+ const [anchorCellNode, anchorRowNode, gridNode] = $getNodeTriplet(anchor);
4208
+ const focusCellNode = utils.$findMatchingParent(focus.getNode(), n => $isTableCellNode(n));
4209
+ if (!$isTableCellNode(anchorCellNode) || !$isTableCellNode(focusCellNode) || !$isTableRowNode(anchorRowNode) || !$isTableNode(gridNode)) {
4210
+ return false;
4211
+ }
4212
+ const templateGrid = nodes[0];
4213
+ const [initialGridMap, anchorCellMap, focusCellMap] = $computeTableMap(gridNode, anchorCellNode, focusCellNode);
4214
+ const [templateGridMap] = $computeTableMapSkipCellCheck(templateGrid, null, null);
4215
+ const initialRowCount = initialGridMap.length;
4216
+ const initialColCount = initialRowCount > 0 ? initialGridMap[0].length : 0;
4217
+
4218
+ // If we have a range selection, we'll fit the template grid into the
4219
+ // table, growing the table if necessary.
4220
+ let startRow = anchorCellMap.startRow;
4221
+ let startCol = anchorCellMap.startColumn;
4222
+ let affectedRowCount = templateGridMap.length;
4223
+ let affectedColCount = affectedRowCount > 0 ? templateGridMap[0].length : 0;
4224
+ if (isTableSelection) {
4225
+ // If we have a table selection, we'll only modify the cells within
4226
+ // the selection boundary.
4227
+ const selectionBoundary = $computeTableCellRectBoundary(initialGridMap, anchorCellMap, focusCellMap);
4228
+ const selectionRowCount = selectionBoundary.maxRow - selectionBoundary.minRow + 1;
4229
+ const selectionColCount = selectionBoundary.maxColumn - selectionBoundary.minColumn + 1;
4230
+ startRow = selectionBoundary.minRow;
4231
+ startCol = selectionBoundary.minColumn;
4232
+ affectedRowCount = Math.min(affectedRowCount, selectionRowCount);
4233
+ affectedColCount = Math.min(affectedColCount, selectionColCount);
4234
+ }
4235
+
4236
+ // Step 1: Unmerge all merged cells within the affected area
4237
+ let didPerformMergeOperations = false;
4238
+ const lastRowForUnmerge = Math.min(initialRowCount, startRow + affectedRowCount) - 1;
4239
+ const lastColForUnmerge = Math.min(initialColCount, startCol + affectedColCount) - 1;
4240
+ const unmergedKeys = new Set();
4241
+ for (let row = startRow; row <= lastRowForUnmerge; row++) {
4242
+ for (let col = startCol; col <= lastColForUnmerge; col++) {
4243
+ const cellMap = initialGridMap[row][col];
4244
+ if (unmergedKeys.has(cellMap.cell.getKey())) {
4245
+ continue; // cell was a merged cell that was already handled
4246
+ }
4247
+ if (cellMap.cell.__rowSpan === 1 && cellMap.cell.__colSpan === 1) {
4248
+ continue; // cell is not a merged cell
4249
+ }
4250
+ $unmergeCellNode(cellMap.cell);
4251
+ unmergedKeys.add(cellMap.cell.getKey());
4252
+ didPerformMergeOperations = true;
4253
+ }
4254
+ }
4255
+ let [interimGridMap] = $computeTableMapSkipCellCheck(gridNode.getWritable(), null, null);
4256
+
4257
+ // Step 2: Expand current table (if needed)
4258
+ const rowsToInsert = affectedRowCount - initialRowCount + startRow;
4259
+ for (let i = 0; i < rowsToInsert; i++) {
4260
+ const cellMap = interimGridMap[initialRowCount - 1][0];
4261
+ $insertTableRowAtNode(cellMap.cell);
4262
+ }
4263
+ const colsToInsert = affectedColCount - initialColCount + startCol;
4264
+ for (let i = 0; i < colsToInsert; i++) {
4265
+ const cellMap = interimGridMap[0][initialColCount - 1];
4266
+ $insertTableColumnAtNode(cellMap.cell, true, false);
4267
+ }
4268
+ [interimGridMap] = $computeTableMapSkipCellCheck(gridNode.getWritable(), null, null);
4269
+
4270
+ // Step 3: Merge cells and set cell content, to match template grid
4271
+ for (let row = startRow; row < startRow + affectedRowCount; row++) {
4272
+ for (let col = startCol; col < startCol + affectedColCount; col++) {
4273
+ const templateRow = row - startRow;
4274
+ const templateCol = col - startCol;
4275
+ const templateCellMap = templateGridMap[templateRow][templateCol];
4276
+ if (templateCellMap.startRow !== templateRow || templateCellMap.startColumn !== templateCol) {
4277
+ continue; // cell is a merged cell that was already handled
4278
+ }
4279
+ const templateCell = templateCellMap.cell;
4280
+ if (templateCell.__rowSpan !== 1 || templateCell.__colSpan !== 1) {
4281
+ const cellsToMerge = [];
4282
+ const lastRowForMerge = Math.min(row + templateCell.__rowSpan, startRow + affectedRowCount) - 1;
4283
+ const lastColForMerge = Math.min(col + templateCell.__colSpan, startCol + affectedColCount) - 1;
4284
+ for (let r = row; r <= lastRowForMerge; r++) {
4285
+ for (let c = col; c <= lastColForMerge; c++) {
4286
+ const cellMap = interimGridMap[r][c];
4287
+ cellsToMerge.push(cellMap.cell);
4288
+ }
4289
+ }
4290
+ $mergeCells(cellsToMerge);
4291
+ didPerformMergeOperations = true;
4292
+ }
4293
+ const {
4294
+ cell
4295
+ } = interimGridMap[row][col];
4296
+ const originalChildren = cell.getChildren();
4297
+ templateCell.getChildren().forEach(child => {
4298
+ if (lexical.$isTextNode(child)) {
4299
+ const paragraphNode = lexical.$createParagraphNode();
4300
+ paragraphNode.append(child);
4301
+ cell.append(child);
4302
+ } else {
4303
+ cell.append(child);
4304
+ }
4305
+ });
4306
+ originalChildren.forEach(n => n.remove());
4307
+ }
4308
+ }
4309
+ if (isTableSelection && didPerformMergeOperations) {
4310
+ // reset the table selection in case the anchor or focus cell was
4311
+ // removed via merge operations
4312
+ const [finalGridMap] = $computeTableMapSkipCellCheck(gridNode.getWritable(), null, null);
4313
+ const newAnchorCellMap = finalGridMap[anchorCellMap.startRow][anchorCellMap.startColumn];
4314
+ newAnchorCellMap.cell.selectEnd();
4315
+ }
4316
+ return true;
4317
+ }
4261
4318
 
4262
4319
  /**
4263
4320
  * Copyright (c) Meta Platforms, Inc. and affiliates.