Skip to content

Commit ab55dbb

Browse files
cnaples79etrepum
andauthored
[lexical][lexical-selection][lexical-utils] Refactor: Consolidate ancestor lookup via findMatchingParent (#7814)
Co-authored-by: Bob Ippolito <bob@redivi.com>
1 parent f783705 commit ab55dbb

File tree

5 files changed

+46
-78
lines changed

5 files changed

+46
-78
lines changed

packages/lexical-link/src/index.ts

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -645,7 +645,7 @@ export function $toggleLink(
645645
const firstNode = nodes[0];
646646
// if the first node is a LinkNode or if its
647647
// parent is a LinkNode, we update the URL, target and rel.
648-
const linkNode = $getAncestor(firstNode, $isLinkNode);
648+
const linkNode = $findMatchingParent(firstNode, $isLinkNode);
649649
if (linkNode !== null) {
650650
return updateLinkNode(linkNode);
651651
}
@@ -657,7 +657,7 @@ export function $toggleLink(
657657
if (!node.isAttached()) {
658658
continue;
659659
}
660-
const parentLinkNode = $getAncestor(node, $isLinkNode);
660+
const parentLinkNode = $findMatchingParent(node, $isLinkNode);
661661
if (parentLinkNode) {
662662
updateLinkNode(parentLinkNode);
663663
continue;
@@ -701,17 +701,6 @@ export function $toggleLink(
701701
/** @deprecated renamed to {@link $toggleLink} by @lexical/eslint-plugin rules-of-lexical */
702702
export const toggleLink = $toggleLink;
703703

704-
function $getAncestor<NodeType extends LexicalNode = LexicalNode>(
705-
node: LexicalNode,
706-
predicate: (ancestor: LexicalNode) => ancestor is NodeType,
707-
) {
708-
let parent = node;
709-
while (parent !== null && parent.getParent() !== null && !predicate(parent)) {
710-
parent = parent.getParentOrThrow();
711-
}
712-
return predicate(parent) ? parent : null;
713-
}
714-
715704
const PHONE_NUMBER_REGEX = /^\+?[0-9\s()-]{5,}$/;
716705

717706
/**

packages/lexical-selection/src/range-selection.ts

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
$caretFromPoint,
2222
$createRangeSelection,
2323
$extendCaretToRange,
24+
$findMatchingParent,
2425
$getPreviousSelection,
2526
$getSelection,
2627
$hasAncestor,
@@ -84,8 +85,11 @@ export function $setBlocksType<T extends ElementNode>(
8485
newSelection = $createRangeSelection();
8586
newSelection.anchor.set(anchor.key, anchor.offset, anchor.type);
8687
newSelection.focus.set(focus.key, focus.offset, focus.type);
87-
const anchorBlock = $getAncestor(anchor.getNode(), INTERNAL_$isBlock);
88-
const focusBlock = $getAncestor(focus.getNode(), INTERNAL_$isBlock);
88+
const anchorBlock = $findMatchingParent(
89+
anchor.getNode(),
90+
INTERNAL_$isBlock,
91+
);
92+
const focusBlock = $findMatchingParent(focus.getNode(), INTERNAL_$isBlock);
8993
if ($isElementNode(anchorBlock)) {
9094
blockMap.set(anchorBlock.getKey(), anchorBlock);
9195
}
@@ -97,7 +101,7 @@ export function $setBlocksType<T extends ElementNode>(
97101
if ($isElementNode(node) && INTERNAL_$isBlock(node)) {
98102
blockMap.set(node.getKey(), node);
99103
} else if (anchorAndFocus === null) {
100-
const ancestorBlock = $getAncestor(node, INTERNAL_$isBlock);
104+
const ancestorBlock = $findMatchingParent(node, INTERNAL_$isBlock);
101105
if ($isElementNode(ancestorBlock)) {
102106
blockMap.set(ancestorBlock.getKey(), ancestorBlock);
103107
}
@@ -641,14 +645,3 @@ export function $getSelectionStyleValueForProperty(
641645

642646
return styleValue === null ? defaultValue : styleValue;
643647
}
644-
645-
export function $getAncestor<NodeType extends LexicalNode = LexicalNode>(
646-
node: LexicalNode,
647-
predicate: (ancestor: LexicalNode) => ancestor is NodeType,
648-
) {
649-
let parent = node;
650-
while (parent !== null && parent.getParent() !== null && !predicate(parent)) {
651-
parent = parent.getParentOrThrow();
652-
}
653-
return predicate(parent) ? parent : null;
654-
}

packages/lexical-utils/src/index.ts

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
$caretFromPoint,
1111
$cloneWithProperties,
1212
$createParagraphNode,
13+
$findMatchingParent,
1314
$getAdjacentChildCaret,
1415
$getAdjacentSiblingOrParentSiblingCaret,
1516
$getCaretInDirection,
@@ -69,6 +70,7 @@ export {default as mergeRegister} from './mergeRegister';
6970
export {default as positionNodeOnRange} from './positionNodeOnRange';
7071
export {default as selectionAlwaysOnDisplay} from './selectionAlwaysOnDisplay';
7172
export {
73+
$findMatchingParent,
7274
$getAdjacentSiblingOrParentSiblingCaret,
7375
$splitNode,
7476
isBlockDomNode,
@@ -407,40 +409,6 @@ export type DOMNodeToLexicalConversionMap = Record<
407409
DOMNodeToLexicalConversion
408410
>;
409411

410-
/**
411-
* Starts with a node and moves up the tree (toward the root node) to find a matching node based on
412-
* the search parameters of the findFn. (Consider JavaScripts' .find() function where a testing function must be
413-
* passed as an argument. eg. if( (node) => node.__type === 'div') ) return true; otherwise return false
414-
* @param startingNode - The node where the search starts.
415-
* @param findFn - A testing function that returns true if the current node satisfies the testing parameters.
416-
* @returns A parent node that matches the findFn parameters, or null if one wasn't found.
417-
*/
418-
export const $findMatchingParent: {
419-
<T extends LexicalNode>(
420-
startingNode: LexicalNode,
421-
findFn: (node: LexicalNode) => node is T,
422-
): T | null;
423-
(
424-
startingNode: LexicalNode,
425-
findFn: (node: LexicalNode) => boolean,
426-
): LexicalNode | null;
427-
} = (
428-
startingNode: LexicalNode,
429-
findFn: (node: LexicalNode) => boolean,
430-
): LexicalNode | null => {
431-
let curr: ElementNode | LexicalNode | null = startingNode;
432-
433-
while (curr !== $getRoot() && curr != null) {
434-
if (findFn(curr)) {
435-
return curr;
436-
}
437-
438-
curr = curr.getParent();
439-
}
440-
441-
return null;
442-
};
443-
444412
/**
445413
* Attempts to resolve nested element nodes of the same type into a single node of that type.
446414
* It is generally used for marks/commenting

packages/lexical/src/LexicalUtils.ts

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1764,23 +1764,6 @@ export function $splitNode(
17641764
return [leftTree, rightTree];
17651765
}
17661766

1767-
export function $findMatchingParent(
1768-
startingNode: LexicalNode,
1769-
findFn: (node: LexicalNode) => boolean,
1770-
): LexicalNode | null {
1771-
let curr: ElementNode | LexicalNode | null = startingNode;
1772-
1773-
while (curr !== $getRoot() && curr != null) {
1774-
if (findFn(curr)) {
1775-
return curr;
1776-
}
1777-
1778-
curr = curr.getParent();
1779-
}
1780-
1781-
return null;
1782-
}
1783-
17841767
/**
17851768
* @param x - The element being tested
17861769
* @returns Returns true if x is an HTML anchor tag, false otherwise
@@ -2141,3 +2124,37 @@ export function $create<T extends LexicalNode>(klass: Klass<T>): T {
21412124
);
21422125
return new registeredNode.klass() as T;
21432126
}
2127+
2128+
/**
2129+
* Starts with a node and moves up the tree (toward the root node) to find a matching node based on
2130+
* the search parameters of the findFn. (Consider JavaScripts' .find() function where a testing function must be
2131+
* passed as an argument. eg. if( (node) => node.__type === 'div') ) return true; otherwise return false
2132+
* @param startingNode - The node where the search starts.
2133+
* @param findFn - A testing function that returns true if the current node satisfies the testing parameters.
2134+
* @returns `startingNode` or one of its ancestors that matches the `findFn` predicate and is not the `RootNode`, or `null` if no match was found.
2135+
*/
2136+
export const $findMatchingParent: {
2137+
<T extends LexicalNode>(
2138+
startingNode: LexicalNode,
2139+
findFn: (node: LexicalNode) => node is T,
2140+
): T | null;
2141+
(
2142+
startingNode: LexicalNode,
2143+
findFn: (node: LexicalNode) => boolean,
2144+
): LexicalNode | null;
2145+
} = (
2146+
startingNode: LexicalNode,
2147+
findFn: (node: LexicalNode) => boolean,
2148+
): LexicalNode | null => {
2149+
let curr: ElementNode | LexicalNode | null = startingNode;
2150+
2151+
while (curr != null && !$isRootNode(curr)) {
2152+
if (findFn(curr)) {
2153+
return curr;
2154+
}
2155+
2156+
curr = curr.getParent();
2157+
}
2158+
2159+
return null;
2160+
};

packages/lexical/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ export {
236236
$cloneWithProperties,
237237
$copyNode,
238238
$create,
239+
$findMatchingParent,
239240
$getAdjacentNode,
240241
$getEditor,
241242
$getNearestNodeFromDOMNode,

0 commit comments

Comments
 (0)