]> BookStack Code Mirror - bookstack/blobdiff - resources/js/wysiwyg/utils/selection.ts
respective book and chapter structure added.
[bookstack] / resources / js / wysiwyg / utils / selection.ts
index 4aa21045f3013641ac115c8ba336cf431de66729..67c2d91b26c0bc086a6c2bec747631c6c587a729 100644 (file)
@@ -2,7 +2,7 @@ import {
     $createNodeSelection,
     $createParagraphNode, $createRangeSelection,
     $getRoot,
-    $getSelection, $isDecoratorNode,
+    $getSelection, $isBlockElementNode, $isDecoratorNode,
     $isElementNode,
     $isTextNode,
     $setSelection,
@@ -10,7 +10,7 @@ import {
     ElementFormatType,
     ElementNode, LexicalEditor,
     LexicalNode,
-    TextFormatType
+    TextFormatType, TextNode
 } from "lexical";
 import {$findMatchingParent, $getNearestBlockElementAncestorOrThrow} from "@lexical/utils";
 import {LexicalElementNodeCreator, LexicalNodeMatcher} from "../nodes";
@@ -82,8 +82,8 @@ export function $insertNewBlockNodeAtSelection(node: LexicalNode, insertAfter: b
 }
 
 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) {
@@ -106,6 +106,57 @@ export function $selectSingleNode(node: LexicalNode) {
     $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);
 
@@ -148,6 +199,22 @@ export function $selectionContainsAlignment(selection: BaseSelection | null, ali
     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;
+        }
+    }
+
+    return false;
+}
+
 export function $getBlockElementNodesInSelection(selection: BaseSelection | null): ElementNode[] {
     if (!selection) {
         return [];