import {
$createNodeSelection,
- $createParagraphNode,
+ $createParagraphNode, $createRangeSelection,
$getRoot,
- $getSelection,
+ $getSelection, $isBlockElementNode, $isDecoratorNode,
$isElementNode,
$isTextNode,
$setSelection,
- BaseSelection,
+ BaseSelection, DecoratorNode,
ElementFormatType,
ElementNode, LexicalEditor,
LexicalNode,
- TextFormatType
+ TextFormatType, TextNode
} from "lexical";
import {$findMatchingParent, $getNearestBlockElementAncestorOrThrow} from "@lexical/utils";
import {LexicalElementNodeCreator, LexicalNodeMatcher} from "../nodes";
import {$setBlocksType} from "@lexical/selection";
-import {$getParentOfType} from "./nodes";
+import {$getNearestNodeBlockParent, $getParentOfType, nodeHasAlignment} from "./nodes";
import {$createCustomParagraphNode} from "../nodes/custom-paragraph";
+import {CommonBlockAlignment} from "../nodes/_common";
const lastSelectionByEditor = new WeakMap<LexicalEditor, BaseSelection|null>;
}
export function $insertNewBlockNodesAtSelection(nodes: LexicalNode[], insertAfter: boolean = true) {
- const selection = $getSelection();
- const blockElement = selection ? $getNearestBlockElementAncestorOrThrow(selection.getNodes()[0]) : null;
+ const selectionNodes = $getSelection()?.getNodes() || [];
+ const blockElement = selectionNodes.length > 0 ? $getNearestNodeBlockParent(selectionNodes[0]) : null;
if (blockElement) {
if (insertAfter) {
$setSelection(nodeSelection);
}
+function getFirstTextNodeInNodes(nodes: LexicalNode[]): TextNode|null {
+ for (const node of nodes) {
+ if ($isTextNode(node)) {
+ return node;
+ }
+
+ if ($isElementNode(node)) {
+ const children = node.getChildren();
+ const textNode = getFirstTextNodeInNodes(children);
+ if (textNode !== null) {
+ return textNode;
+ }
+ }
+ }
+
+ return null;
+}
+
+function getLastTextNodeInNodes(nodes: LexicalNode[]): TextNode|null {
+ const revNodes = [...nodes].reverse();
+ for (const node of revNodes) {
+ if ($isTextNode(node)) {
+ return node;
+ }
+
+ if ($isElementNode(node)) {
+ const children = [...node.getChildren()].reverse();
+ const textNode = getLastTextNodeInNodes(children);
+ if (textNode !== null) {
+ return textNode;
+ }
+ }
+ }
+
+ return null;
+}
+
+export function $selectNodes(nodes: LexicalNode[]) {
+ if (nodes.length === 0) {
+ return;
+ }
+
+ const selection = $createRangeSelection();
+ const firstText = getFirstTextNodeInNodes(nodes);
+ const lastText = getLastTextNodeInNodes(nodes);
+ if (firstText && lastText) {
+ selection.setTextNodeRange(firstText, 0, lastText, lastText.getTextContentSize() || 0)
+ $setSelection(selection);
+ }
+}
+
+export function $toggleSelection(editor: LexicalEditor) {
+ const lastSelection = getLastSelection(editor);
+
+ if (lastSelection) {
+ window.requestAnimationFrame(() => {
+ editor.update(() => {
+ $setSelection(lastSelection.clone());
+ })
+ });
+ }
+}
+
export function $selectionContainsNode(selection: BaseSelection | null, node: LexicalNode): boolean {
if (!selection) {
return false;
return false;
}
-export function $selectionContainsElementFormat(selection: BaseSelection | null, format: ElementFormatType): boolean {
- const nodes = $getBlockElementNodesInSelection(selection);
+export function $selectionContainsAlignment(selection: BaseSelection | null, alignment: CommonBlockAlignment): boolean {
+
+ const nodes = [
+ ...(selection?.getNodes() || []),
+ ...$getBlockElementNodesInSelection(selection)
+ ];
for (const node of nodes) {
- if (node.getFormatType() === format) {
+ if (nodeHasAlignment(node) && node.getAlignment() === alignment) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+export function $selectionContainsDirection(selection: BaseSelection | null, direction: 'rtl'|'ltr'): boolean {
+
+ const nodes = [
+ ...(selection?.getNodes() || []),
+ ...$getBlockElementNodesInSelection(selection)
+ ];
+
+ for (const node of nodes) {
+ if ($isBlockElementNode(node) && node.getDirection() === direction) {
return true;
}
}
const blockNodes: Map<string, ElementNode> = new Map();
for (const node of selection.getNodes()) {
- const blockElement = $findMatchingParent(node, (node) => {
- return $isElementNode(node) && !node.isInline();
- }) as ElementNode | null;
-
- if (blockElement) {
+ const blockElement = $getNearestNodeBlockParent(node);
+ if ($isElementNode(blockElement)) {
blockNodes.set(blockElement.getKey(), blockElement);
}
}
return Array.from(blockNodes.values());
+}
+
+export function $getDecoratorNodesInSelection(selection: BaseSelection | null): DecoratorNode<any>[] {
+ if (!selection) {
+ return [];
+ }
+
+ return selection.getNodes().filter(node => $isDecoratorNode(node));
}
\ No newline at end of file