X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/aa1fac62d56a25044698afcdd48298bb74299d16..refs/pull/5280/head:/resources/js/wysiwyg/utils/selection.ts diff --git a/resources/js/wysiwyg/utils/selection.ts b/resources/js/wysiwyg/utils/selection.ts index 74dd94527..67c2d91b2 100644 --- a/resources/js/wysiwyg/utils/selection.ts +++ b/resources/js/wysiwyg/utils/selection.ts @@ -1,23 +1,24 @@ 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; @@ -81,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) { @@ -105,6 +106,69 @@ 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); + + if (lastSelection) { + window.requestAnimationFrame(() => { + editor.update(() => { + $setSelection(lastSelection.clone()); + }) + }); + } +} + export function $selectionContainsNode(selection: BaseSelection | null, node: LexicalNode): boolean { if (!selection) { return false; @@ -120,10 +184,30 @@ export function $selectionContainsNode(selection: BaseSelection | null, node: Le 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; } } @@ -138,14 +222,19 @@ export function $getBlockElementNodesInSelection(selection: BaseSelection | null const blockNodes: Map = 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[] { + if (!selection) { + return []; + } + + return selection.getNodes().filter(node => $isDecoratorNode(node)); } \ No newline at end of file