Skip to content

Commit

Permalink
moved merge nodes to the TableSelection
Browse files Browse the repository at this point in the history
  • Loading branch information
icrosil committed Dec 12, 2023
1 parent 208d71d commit 6f77827
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 170 deletions.
156 changes: 7 additions & 149 deletions packages/lexical-clipboard/src/clipboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,22 @@ import {
$cloneWithProperties,
$sliceSelectedTextNodeContent,
} from '@lexical/selection';
import {$findMatchingParent, objectKlassEquals} from '@lexical/utils';
import {objectKlassEquals} from '@lexical/utils';
import {
$createParagraphNode,
$createTabNode,
$getRoot,
$getSelection,
$INTERNAL_isPointSelection,
$isElementNode,
$isRangeSelection,
$isTextNode,
$parseSerializedNode,
BaseSelection,
COMMAND_PRIORITY_CRITICAL,
COPY_COMMAND,
DEPRECATED_$isGridCellNode,
DEPRECATED_$isGridNode,
DEPRECATED_$isGridRowNode,
DEPRECATED_GridNode,
INTERNAL_PointSelection,
isSelectionWithinEditor,
LexicalCommand,
LexicalEditor,
LexicalNode,
SELECTION_CHANGE_COMMAND,
SELECTION_INSERT_CLIPBOARD_NODES_COMMAND,
SerializedElementNode,
SerializedTextNode,
} from 'lexical';
Expand Down Expand Up @@ -204,151 +196,17 @@ export function $insertGeneratedNodes(
nodes: Array<LexicalNode>,
selection: BaseSelection,
): void {
const isPointSelection = $INTERNAL_isPointSelection(selection);
const isRangeSelection = $isRangeSelection(selection);
const isSelectionInsideOfGrid =
(isRangeSelection &&
$findMatchingParent(selection.anchor.getNode(), (n) =>
DEPRECATED_$isGridCellNode(n),
) !== null &&
$findMatchingParent(selection.focus.getNode(), (n) =>
DEPRECATED_$isGridCellNode(n),
) !== null) ||
(isPointSelection && !isRangeSelection);

if (
isSelectionInsideOfGrid &&
nodes.length === 1 &&
DEPRECATED_$isGridNode(nodes[0])
!editor.dispatchCommand(SELECTION_INSERT_CLIPBOARD_NODES_COMMAND, {
nodes,
selection,
})
) {
$mergeGridNodesStrategy(nodes, selection, false, editor);
return;
selection.insertNodes(nodes);
}

selection.insertNodes(nodes);
return;
}

function $mergeGridNodesStrategy(
nodes: LexicalNode[],
selection: INTERNAL_PointSelection,
isFromLexical: boolean,
editor: LexicalEditor,
) {
if (nodes.length !== 1 || !DEPRECATED_$isGridNode(nodes[0])) {
invariant(false, '$mergeGridNodesStrategy: Expected Grid insertion.');
}

const newGrid = nodes[0];
const newGridRows = newGrid.getChildren();
const newColumnCount = newGrid
.getFirstChildOrThrow<DEPRECATED_GridNode>()
.getChildrenSize();
const newRowCount = newGrid.getChildrenSize();
const gridCellNode = $findMatchingParent(selection.anchor.getNode(), (n) =>
DEPRECATED_$isGridCellNode(n),
);
const gridRowNode =
gridCellNode &&
$findMatchingParent(gridCellNode, (n) => DEPRECATED_$isGridRowNode(n));
const gridNode =
gridRowNode &&
$findMatchingParent(gridRowNode, (n) => DEPRECATED_$isGridNode(n));

if (
!DEPRECATED_$isGridCellNode(gridCellNode) ||
!DEPRECATED_$isGridRowNode(gridRowNode) ||
!DEPRECATED_$isGridNode(gridNode)
) {
invariant(
false,
'$mergeGridNodesStrategy: Expected selection to be inside of a Grid.',
);
}

const startY = gridRowNode.getIndexWithinParent();
const stopY = Math.min(
gridNode.getChildrenSize() - 1,
startY + newRowCount - 1,
);
const startX = gridCellNode.getIndexWithinParent();
const stopX = Math.min(
gridRowNode.getChildrenSize() - 1,
startX + newColumnCount - 1,
);
const fromX = Math.min(startX, stopX);
const fromY = Math.min(startY, stopY);
const toX = Math.max(startX, stopX);
const toY = Math.max(startY, stopY);
const gridRowNodes = gridNode.getChildren();
let newRowIdx = 0;
let newAnchorCellKey;
let newFocusCellKey;

for (let r = fromY; r <= toY; r++) {
const currentGridRowNode = gridRowNodes[r];

if (!DEPRECATED_$isGridRowNode(currentGridRowNode)) {
invariant(false, 'getNodes: expected to find GridRowNode');
}

const newGridRowNode = newGridRows[newRowIdx];

if (!DEPRECATED_$isGridRowNode(newGridRowNode)) {
invariant(false, 'getNodes: expected to find GridRowNode');
}

const gridCellNodes = currentGridRowNode.getChildren();
const newGridCellNodes = newGridRowNode.getChildren();
let newColumnIdx = 0;

for (let c = fromX; c <= toX; c++) {
const currentGridCellNode = gridCellNodes[c];

if (!DEPRECATED_$isGridCellNode(currentGridCellNode)) {
invariant(false, 'getNodes: expected to find GridCellNode');
}

const newGridCellNode = newGridCellNodes[newColumnIdx];

if (!DEPRECATED_$isGridCellNode(newGridCellNode)) {
invariant(false, 'getNodes: expected to find GridCellNode');
}

if (r === fromY && c === fromX) {
newAnchorCellKey = currentGridCellNode.getKey();
} else if (r === toY && c === toX) {
newFocusCellKey = currentGridCellNode.getKey();
}

const originalChildren = currentGridCellNode.getChildren();
newGridCellNode.getChildren().forEach((child) => {
if ($isTextNode(child)) {
const paragraphNode = $createParagraphNode();
paragraphNode.append(child);
currentGridCellNode.append(child);
} else {
currentGridCellNode.append(child);
}
});
originalChildren.forEach((n) => n.remove());
newColumnIdx++;
}

newRowIdx++;
}

if (newAnchorCellKey && newFocusCellKey) {
editor.dispatchCommand<
LexicalCommand<{node: LexicalNode; anchorKey: string; focusKey: string}>
>(SELECTION_CHANGE_COMMAND, {
anchorKey: newAnchorCellKey,
focusKey: newFocusCellKey,
node: gridNode,
});
}
}

export interface BaseSerializedNode {
children?: Array<BaseSerializedNode>;
type: string;
Expand Down
159 changes: 139 additions & 20 deletions packages/lexical-table/src/LexicalTableSelectionHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {TableNode} from './LexicalTableNode';
import type {Cell, Cells, Grid} from './LexicalTableSelection';
import type {
BaseSelection,
DEPRECATED_GridNode,
LexicalCommand,
LexicalEditor,
LexicalNode,
Expand All @@ -25,16 +26,20 @@ import {
$getNearestNodeFromDOMNode,
$getPreviousSelection,
$getSelection,
$INTERNAL_isPointSelection,
$isElementNode,
$isRangeSelection,
$isTextNode,
$setSelection,
COMMAND_PRIORITY_CRITICAL,
COMMAND_PRIORITY_HIGH,
CONTROLLED_TEXT_INSERTION_COMMAND,
DELETE_CHARACTER_COMMAND,
DELETE_LINE_COMMAND,
DELETE_WORD_COMMAND,
DEPRECATED_$isGridCellNode,
DEPRECATED_$isGridNode,
DEPRECATED_$isGridRowNode,
FOCUS_COMMAND,
FORMAT_TEXT_COMMAND,
KEY_ARROW_DOWN_COMMAND,
Expand All @@ -46,6 +51,7 @@ import {
KEY_ESCAPE_COMMAND,
KEY_TAB_COMMAND,
SELECTION_CHANGE_COMMAND,
SELECTION_INSERT_CLIPBOARD_NODES_COMMAND,
} from 'lexical';
import invariant from 'shared/invariant';

Expand Down Expand Up @@ -461,34 +467,147 @@ export function applyTableHandlers(
}

tableSelection.listenersToRemove.add(
editor.registerCommand<{
node: LexicalNode;
anchorKey: string;
focusKey: string;
}>(
SELECTION_CHANGE_COMMAND,
(
selectionPayload: {
node: LexicalNode;
anchorKey: string;
focusKey: string;
} | null,
) => {
editor.registerCommand(
SELECTION_INSERT_CLIPBOARD_NODES_COMMAND,
(selectionPayload) => {
const {nodes, selection} = selectionPayload;
const isPointSelection = $INTERNAL_isPointSelection(selection);
const isRangeSelection = $isRangeSelection(selection);
const isSelectionInsideOfGrid =
(isRangeSelection &&
$findMatchingParent(selection.anchor.getNode(), (n) =>
DEPRECATED_$isGridCellNode(n),
) !== null &&
$findMatchingParent(selection.focus.getNode(), (n) =>
DEPRECATED_$isGridCellNode(n),
) !== null) ||
(isPointSelection && !isRangeSelection);

if (
selectionPayload != null &&
DEPRECATED_$isGridNode(selectionPayload.node) &&
selectionPayload.anchorKey &&
selectionPayload.focusKey
nodes.length !== 1 ||
!DEPRECATED_$isGridNode(nodes[0]) ||
!isSelectionInsideOfGrid
) {
return false;
}
const {anchor} = selection;

const newGrid = nodes[0];
const newGridRows = newGrid.getChildren();
const newColumnCount = newGrid
.getFirstChildOrThrow<DEPRECATED_GridNode>()
.getChildrenSize();
const newRowCount = newGrid.getChildrenSize();
const gridCellNode = $findMatchingParent(anchor.getNode(), (n) =>
DEPRECATED_$isGridCellNode(n),
);
const gridRowNode =
gridCellNode &&
$findMatchingParent(gridCellNode, (n) =>
DEPRECATED_$isGridRowNode(n),
);
const gridNode =
gridRowNode &&
$findMatchingParent(gridRowNode, (n) => DEPRECATED_$isGridNode(n));

if (
!DEPRECATED_$isGridCellNode(gridCellNode) ||
!DEPRECATED_$isGridRowNode(gridRowNode) ||
!DEPRECATED_$isGridNode(gridNode)
) {
return false;
}

const startY = gridRowNode.getIndexWithinParent();
const stopY = Math.min(
gridNode.getChildrenSize() - 1,
startY + newRowCount - 1,
);
const startX = gridCellNode.getIndexWithinParent();
const stopX = Math.min(
gridRowNode.getChildrenSize() - 1,
startX + newColumnCount - 1,
);
const fromX = Math.min(startX, stopX);
const fromY = Math.min(startY, stopY);
const toX = Math.max(startX, stopX);
const toY = Math.max(startY, stopY);
const gridRowNodes = gridNode.getChildren();
let newRowIdx = 0;
let newAnchorCellKey;
let newFocusCellKey;

for (let r = fromY; r <= toY; r++) {
const currentGridRowNode = gridRowNodes[r];

if (!DEPRECATED_$isGridRowNode(currentGridRowNode)) {
return false;
}

const newGridRowNode = newGridRows[newRowIdx];

if (!DEPRECATED_$isGridRowNode(newGridRowNode)) {
return false;
}

const gridCellNodes = currentGridRowNode.getChildren();
const newGridCellNodes = newGridRowNode.getChildren();
let newColumnIdx = 0;

for (let c = fromX; c <= toX; c++) {
const currentGridCellNode = gridCellNodes[c];

if (!DEPRECATED_$isGridCellNode(currentGridCellNode)) {
return false;
}

const newGridCellNode = newGridCellNodes[newColumnIdx];

if (!DEPRECATED_$isGridCellNode(newGridCellNode)) {
return false;
}

if (r === fromY && c === fromX) {
newAnchorCellKey = currentGridCellNode.getKey();
} else if (r === toY && c === toX) {
newFocusCellKey = currentGridCellNode.getKey();
}

const originalChildren = currentGridCellNode.getChildren();
newGridCellNode.getChildren().forEach((child) => {
if ($isTextNode(child)) {
const paragraphNode = $createParagraphNode();
paragraphNode.append(child);
currentGridCellNode.append(child);
} else {
currentGridCellNode.append(child);
}
});
originalChildren.forEach((n) => n.remove());
newColumnIdx++;
}

newRowIdx++;
}
if (newAnchorCellKey && newFocusCellKey) {
const newGridSelection = DEPRECATED_$createGridSelection();
newGridSelection.set(
selectionPayload.node.getKey(),
selectionPayload.anchorKey,
selectionPayload.focusKey,
nodes[0].getKey(),
newAnchorCellKey,
newFocusCellKey,
);
$setSelection(newGridSelection);
}
return true;
},
COMMAND_PRIORITY_CRITICAL,
),
);

tableSelection.listenersToRemove.add(
editor.registerCommand(
SELECTION_CHANGE_COMMAND,
() => {
const selection = $getSelection();
const prevSelection = $getPreviousSelection();

Expand Down
Loading

0 comments on commit 6f77827

Please sign in to comment.