Skip to content

Commit

Permalink
feature: expose forEachSelectedTextNode (#6981)
Browse files Browse the repository at this point in the history
Co-authored-by: Bob Ippolito <[email protected]>
  • Loading branch information
GermanJablo and etrepum authored Dec 21, 2024
1 parent 23715f5 commit e0dafb8
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 15 deletions.
2 changes: 2 additions & 0 deletions packages/lexical-selection/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import {
$addNodeStyle,
$forEachSelectedTextNode,
$isAtNodeEnd,
$patchStyleText,
$sliceSelectedTextNodeContent,
Expand Down Expand Up @@ -36,6 +37,7 @@ export {

export {
$addNodeStyle,
$forEachSelectedTextNode,
$isAtNodeEnd,
$patchStyleText,
$sliceSelectedTextNodeContent,
Expand Down
38 changes: 23 additions & 15 deletions packages/lexical-selection/src/lexical-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
$getCharacterOffsets,
$getNodeByKey,
$getPreviousSelection,
$getSelection,
$isElementNode,
$isRangeSelection,
$isRootNode,
Expand Down Expand Up @@ -288,23 +289,30 @@ export function $patchStyleText(
) => string)
>,
): void {
const selectedNodes = selection.getNodes();
const selectedNodesLength = selectedNodes.length;
const anchorAndFocus = selection.getStartEndPoints();
if (anchorAndFocus === null) {
if (selection.isCollapsed() && $isRangeSelection(selection)) {
$patchStyle(selection, patch);
} else {
$forEachSelectedTextNode((textNode) => {
$patchStyle(textNode, patch);
});
}
}

export function $forEachSelectedTextNode(
fn: (textNode: TextNode) => void,
): void {
const selection = $getSelection();
if (!$isRangeSelection(selection)) {
return;
}
const [anchor, focus] = anchorAndFocus;
const selectedNodes = selection.getNodes();
const selectedNodesLength = selectedNodes.length;
const {anchor, focus} = selection;

const lastIndex = selectedNodesLength - 1;
let firstNode = selectedNodes[0];
let lastNode = selectedNodes[lastIndex];

if (selection.isCollapsed() && $isRangeSelection(selection)) {
$patchStyle(selection, patch);
return;
}

const firstNodeText = firstNode.getTextContent();
const firstNodeTextLength = firstNodeText.length;
const focusOffset = focus.offset;
Expand Down Expand Up @@ -355,14 +363,14 @@ export function $patchStyleText(
$isTokenOrSegmented(firstNode) ||
(startOffset === 0 && endOffset === firstNodeTextLength)
) {
$patchStyle(firstNode, patch);
fn(firstNode);
firstNode.select(startOffset, endOffset);
} else {
// The node is partially selected, so split it into two nodes
// and style the selected one.
const splitNodes = firstNode.splitText(startOffset, endOffset);
const replacement = startOffset === 0 ? splitNodes[0] : splitNodes[1];
$patchStyle(replacement, patch);
fn(replacement);
replacement.select(0, endOffset - startOffset);
}
} // multiple nodes selected.
Expand All @@ -383,7 +391,7 @@ export function $patchStyleText(
}
}

$patchStyle(firstNode as TextNode, patch);
fn(firstNode as TextNode);
}

if ($isTextNode(lastNode) && lastNode.canHaveFormat()) {
Expand All @@ -404,7 +412,7 @@ export function $patchStyleText(
}

if (endOffset !== 0 || endType === 'element') {
$patchStyle(lastNode as TextNode, patch);
fn(lastNode as TextNode);
}
}

Expand All @@ -420,7 +428,7 @@ export function $patchStyleText(
selectedNodeKey !== lastNode.getKey() &&
!selectedNode.isToken()
) {
$patchStyle(selectedNode, patch);
fn(selectedNode as TextNode);
}
}
}
Expand Down
1 change: 1 addition & 0 deletions packages/lexical/src/LexicalSelection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1180,6 +1180,7 @@ export class RangeSelection implements BaseSelection {
}
}

// TO-DO: Migrate this method to the new utility function $forEachSelectedTextNode (share similar logic)
/**
* Applies the provided format to the TextNodes in the Selection, splitting or
* merging nodes as necessary.
Expand Down

0 comments on commit e0dafb8

Please sign in to comment.