]> BookStack Code Mirror - bookstack/commitdiff
Lexical: Standardised helper function format
authorDan Brown <redacted>
Wed, 17 Jul 2024 15:45:57 +0000 (16:45 +0100)
committerDan Brown <redacted>
Wed, 17 Jul 2024 15:45:57 +0000 (16:45 +0100)
resources/js/wysiwyg/helpers.ts
resources/js/wysiwyg/ui/decorators/code-block.ts
resources/js/wysiwyg/ui/decorators/image.ts
resources/js/wysiwyg/ui/defaults/button-definitions.ts
resources/js/wysiwyg/ui/framework/blocks/table-creator.ts

index 600e71dd1ccc589d996f65276d34e4a704bff358..a7c3e4453bc28a9acbe60920d8df9ddab9db6adc 100644 (file)
@@ -4,7 +4,7 @@ import {
     $getSelection, $isElementNode,
     $isTextNode, $setSelection,
     BaseSelection, ElementFormatType, ElementNode,
-    LexicalEditor, LexicalNode, TextFormatType
+    LexicalNode, TextFormatType
 } from "lexical";
 import {LexicalElementNodeCreator, LexicalNodeMatcher} from "./nodes";
 import {$findMatchingParent, $getNearestBlockElementAncestorOrThrow} from "@lexical/utils";
@@ -30,11 +30,11 @@ export function el(tag: string, attrs: Record<string, string|null> = {}, childre
     return el;
 }
 
-export function selectionContainsNodeType(selection: BaseSelection|null, matcher: LexicalNodeMatcher): boolean {
-    return getNodeFromSelection(selection, matcher) !== null;
+export function $selectionContainsNodeType(selection: BaseSelection|null, matcher: LexicalNodeMatcher): boolean {
+    return $getNodeFromSelection(selection, matcher) !== null;
 }
 
-export function getNodeFromSelection(selection: BaseSelection|null, matcher: LexicalNodeMatcher): LexicalNode|null {
+export function $getNodeFromSelection(selection: BaseSelection|null, matcher: LexicalNodeMatcher): LexicalNode|null {
     if (!selection) {
         return null;
     }
@@ -54,7 +54,7 @@ export function getNodeFromSelection(selection: BaseSelection|null, matcher: Lex
     return null;
 }
 
-export function selectionContainsTextFormat(selection: BaseSelection|null, format: TextFormatType): boolean {
+export function $selectionContainsTextFormat(selection: BaseSelection|null, format: TextFormatType): boolean {
     if (!selection) {
         return false;
     }
@@ -68,19 +68,17 @@ export function selectionContainsTextFormat(selection: BaseSelection|null, forma
     return false;
 }
 
-export function toggleSelectionBlockNodeType(editor: LexicalEditor, matcher: LexicalNodeMatcher, creator: LexicalElementNodeCreator) {
-    editor.update(() => {
-        const selection = $getSelection();
-        const blockElement = selection ? $getNearestBlockElementAncestorOrThrow(selection.getNodes()[0]) : null;
-        if (selection && matcher(blockElement)) {
-            $setBlocksType(selection, $createParagraphNode);
-        } else {
-            $setBlocksType(selection, creator);
-        }
-    });
+export function $toggleSelectionBlockNodeType(matcher: LexicalNodeMatcher, creator: LexicalElementNodeCreator) {
+    const selection = $getSelection();
+    const blockElement = selection ? $getNearestBlockElementAncestorOrThrow(selection.getNodes()[0]) : null;
+    if (selection && matcher(blockElement)) {
+        $setBlocksType(selection, $createParagraphNode);
+    } else {
+        $setBlocksType(selection, creator);
+    }
 }
 
-export function insertNewBlockNodeAtSelection(node: LexicalNode, insertAfter: boolean = true) {
+export function $insertNewBlockNodeAtSelection(node: LexicalNode, insertAfter: boolean = true) {
     const selection = $getSelection();
     const blockElement = selection ? $getNearestBlockElementAncestorOrThrow(selection.getNodes()[0]) : null;
 
@@ -95,13 +93,13 @@ export function insertNewBlockNodeAtSelection(node: LexicalNode, insertAfter: bo
     }
 }
 
-export function selectSingleNode(node: LexicalNode) {
+export function $selectSingleNode(node: LexicalNode) {
     const nodeSelection = $createNodeSelection();
     nodeSelection.add(node.getKey());
     $setSelection(nodeSelection);
 }
 
-export function selectionContainsNode(selection: BaseSelection|null, node: LexicalNode): boolean {
+export function $selectionContainsNode(selection: BaseSelection|null, node: LexicalNode): boolean {
     if (!selection) {
         return false;
     }
@@ -116,8 +114,8 @@ export function selectionContainsNode(selection: BaseSelection|null, node: Lexic
     return false;
 }
 
-export function selectionContainsElementFormat(selection: BaseSelection|null, format: ElementFormatType): boolean {
-    const nodes = getBlockElementNodesInSelection(selection);
+export function $selectionContainsElementFormat(selection: BaseSelection|null, format: ElementFormatType): boolean {
+    const nodes = $getBlockElementNodesInSelection(selection);
     for (const node of nodes) {
         if (node.getFormatType() === format) {
             return true;
@@ -127,7 +125,7 @@ export function selectionContainsElementFormat(selection: BaseSelection|null, fo
     return false;
 }
 
-export function getBlockElementNodesInSelection(selection: BaseSelection|null): ElementNode[] {
+export function $getBlockElementNodesInSelection(selection: BaseSelection|null): ElementNode[] {
     if (!selection) {
         return [];
     }
index 11cc02e8f68395866010357cd95f1b9d4f821147..cfb2c6aefb232989b7836c20ac2f87a6e7645c6b 100644 (file)
@@ -1,7 +1,7 @@
 import {EditorDecorator} from "../framework/decorator";
 import {EditorUiContext} from "../framework/core";
 import {$openCodeEditorForNode, CodeBlockNode} from "../../nodes/code-block";
-import {selectionContainsNode, selectSingleNode} from "../../helpers";
+import {$selectionContainsNode, $selectSingleNode} from "../../helpers";
 import {context} from "esbuild";
 import {BaseSelection} from "lexical";
 
@@ -36,7 +36,7 @@ export class CodeBlockDecorator extends EditorDecorator {
 
         element.addEventListener('click', event => {
             context.editor.update(() => {
-                selectSingleNode(this.getNode());
+                $selectSingleNode(this.getNode());
             })
         });
 
@@ -47,7 +47,7 @@ export class CodeBlockDecorator extends EditorDecorator {
         });
 
         const selectionChange = (selection: BaseSelection|null): void => {
-            element.classList.toggle('selected', selectionContainsNode(selection, codeNode));
+            element.classList.toggle('selected', $selectionContainsNode(selection, codeNode));
         };
         context.manager.onSelectionChange(selectionChange);
         this.onDestroy(() => {
index 1bc1ea5437a2b0cb5bff12d9370ed27f6afe29ea..2046260a02352f41c214c5d150bf20a30dd62363 100644 (file)
@@ -1,5 +1,5 @@
 import {EditorDecorator} from "../framework/decorator";
-import {el, selectSingleNode} from "../../helpers";
+import {el, $selectSingleNode} from "../../helpers";
 import {$createNodeSelection, $setSelection} from "lexical";
 import {EditorUiContext} from "../framework/core";
 import {ImageNode} from "../../nodes/image";
@@ -41,7 +41,7 @@ export class ImageDecorator extends EditorDecorator {
             tracker = this.setupTracker(decorateEl, context);
 
             context.editor.update(() => {
-                selectSingleNode(this.getNode());
+                $selectSingleNode(this.getNode());
             });
         };
 
index d1d22dae1eaff400b554e559dbb42e17141a5fe7..bf725f8c8c86f58afb8d482d75f5ae43584306a6 100644 (file)
@@ -21,11 +21,11 @@ import {
     UNDO_COMMAND
 } from "lexical";
 import {
-    getBlockElementNodesInSelection,
-    getNodeFromSelection, insertNewBlockNodeAtSelection, selectionContainsElementFormat,
-    selectionContainsNodeType,
-    selectionContainsTextFormat,
-    toggleSelectionBlockNodeType
+    $getBlockElementNodesInSelection,
+    $getNodeFromSelection, $insertNewBlockNodeAtSelection, $selectionContainsElementFormat,
+    $selectionContainsNodeType,
+    $selectionContainsTextFormat,
+    $toggleSelectionBlockNodeType
 } from "../../helpers";
 import {$createCalloutNode, $isCalloutNodeOfCategory, CalloutCategory} from "../../nodes/callout";
 import {
@@ -116,14 +116,15 @@ function buildCalloutButton(category: CalloutCategory, name: string): EditorButt
     return {
         label: `${name} Callout`,
         action(context: EditorUiContext) {
-            toggleSelectionBlockNodeType(
-                context.editor,
-                (node) => $isCalloutNodeOfCategory(node, category),
-                () => $createCalloutNode(category),
-            )
+            context.editor.update(() => {
+                $toggleSelectionBlockNodeType(
+                    (node) => $isCalloutNodeOfCategory(node, category),
+                    () => $createCalloutNode(category),
+                )
+            });
         },
         isActive(selection: BaseSelection|null): boolean {
-            return selectionContainsNodeType(selection, (node) => $isCalloutNodeOfCategory(node, category));
+            return $selectionContainsNodeType(selection, (node) => $isCalloutNodeOfCategory(node, category));
         }
     };
 }
@@ -141,14 +142,15 @@ function buildHeaderButton(tag: HeadingTagType, name: string): EditorButtonDefin
     return {
         label: name,
         action(context: EditorUiContext) {
-            toggleSelectionBlockNodeType(
-                context.editor,
+            context.editor.update(() => {
+                $toggleSelectionBlockNodeType(
                 (node) => isHeaderNodeOfTag(node, tag),
                 () => $createHeadingNode(tag),
-            )
+                )
+            });
         },
         isActive(selection: BaseSelection|null): boolean {
-            return selectionContainsNodeType(selection, (node) => isHeaderNodeOfTag(node, tag));
+            return $selectionContainsNodeType(selection, (node) => isHeaderNodeOfTag(node, tag));
         }
     };
 }
@@ -161,20 +163,24 @@ export const h5: EditorButtonDefinition = buildHeaderButton('h5', 'Tiny Header')
 export const blockquote: EditorButtonDefinition = {
     label: 'Blockquote',
     action(context: EditorUiContext) {
-        toggleSelectionBlockNodeType(context.editor, $isQuoteNode, $createQuoteNode);
+        context.editor.update(() => {
+            $toggleSelectionBlockNodeType($isQuoteNode, $createQuoteNode);
+        });
     },
     isActive(selection: BaseSelection|null): boolean {
-        return selectionContainsNodeType(selection, $isQuoteNode);
+        return $selectionContainsNodeType(selection, $isQuoteNode);
     }
 };
 
 export const paragraph: EditorButtonDefinition = {
     label: 'Paragraph',
     action(context: EditorUiContext) {
-        toggleSelectionBlockNodeType(context.editor, $isParagraphNode, $createParagraphNode);
+        context.editor.update(() => {
+            $toggleSelectionBlockNodeType($isParagraphNode, $createParagraphNode);
+        });
     },
     isActive(selection: BaseSelection|null): boolean {
-        return selectionContainsNodeType(selection, $isParagraphNode);
+        return $selectionContainsNodeType(selection, $isParagraphNode);
     }
 }
 
@@ -186,7 +192,7 @@ function buildFormatButton(label: string, format: TextFormatType, icon: string):
             context.editor.dispatchCommand(FORMAT_TEXT_COMMAND, format);
         },
         isActive(selection: BaseSelection|null): boolean {
-            return selectionContainsTextFormat(selection, format);
+            return $selectionContainsTextFormat(selection, format);
         }
     };
 }
@@ -222,7 +228,7 @@ export const clearFormating: EditorButtonDefinition = {
 
 function setAlignmentForSection(alignment: ElementFormatType): void {
     const selection = $getSelection();
-    const elements = getBlockElementNodesInSelection(selection);
+    const elements = $getBlockElementNodesInSelection(selection);
     for (const node of elements) {
         node.setFormat(alignment);
     }
@@ -235,7 +241,7 @@ export const alignLeft: EditorButtonDefinition = {
         context.editor.update(() => setAlignmentForSection('left'));
     },
     isActive(selection: BaseSelection|null) {
-        return selectionContainsElementFormat(selection, 'left');
+        return $selectionContainsElementFormat(selection, 'left');
     }
 };
 
@@ -246,7 +252,7 @@ export const alignCenter: EditorButtonDefinition = {
         context.editor.update(() => setAlignmentForSection('center'));
     },
     isActive(selection: BaseSelection|null) {
-        return selectionContainsElementFormat(selection, 'center');
+        return $selectionContainsElementFormat(selection, 'center');
     }
 };
 
@@ -257,7 +263,7 @@ export const alignRight: EditorButtonDefinition = {
         context.editor.update(() => setAlignmentForSection('right'));
     },
     isActive(selection: BaseSelection|null) {
-        return selectionContainsElementFormat(selection, 'right');
+        return $selectionContainsElementFormat(selection, 'right');
     }
 };
 
@@ -268,7 +274,7 @@ export const alignJustify: EditorButtonDefinition = {
         context.editor.update(() => setAlignmentForSection('justify'));
     },
     isActive(selection: BaseSelection|null) {
-        return selectionContainsElementFormat(selection, 'justify');
+        return $selectionContainsElementFormat(selection, 'justify');
     }
 };
 
@@ -288,7 +294,7 @@ function buildListButton(label: string, type: ListType, icon: string): EditorBut
             });
         },
         isActive(selection: BaseSelection|null): boolean {
-            return selectionContainsNodeType(selection, (node: LexicalNode | null | undefined): boolean => {
+            return $selectionContainsNodeType(selection, (node: LexicalNode | null | undefined): boolean => {
                 return $isListNode(node) && (node as ListNode).getListType() === type;
             });
         }
@@ -307,7 +313,7 @@ export const link: EditorButtonDefinition = {
         const linkModal = context.manager.createModal('link');
         context.editor.getEditorState().read(() => {
             const selection = $getSelection();
-            const selectedLink = getNodeFromSelection(selection, $isLinkNode) as LinkNode|null;
+            const selectedLink = $getNodeFromSelection(selection, $isLinkNode) as LinkNode|null;
 
             let formDefaults = {};
             if (selectedLink) {
@@ -329,7 +335,7 @@ export const link: EditorButtonDefinition = {
         });
     },
     isActive(selection: BaseSelection|null): boolean {
-        return selectionContainsNodeType(selection, $isLinkNode);
+        return $selectionContainsNodeType(selection, $isLinkNode);
     }
 };
 
@@ -339,7 +345,7 @@ export const unlink: EditorButtonDefinition = {
     action(context: EditorUiContext) {
         context.editor.update(() => {
             const selection = context.lastSelection;
-            const selectedLink = getNodeFromSelection(selection, $isLinkNode) as LinkNode|null;
+            const selectedLink = $getNodeFromSelection(selection, $isLinkNode) as LinkNode|null;
             const selectionPoints = selection?.getStartEndPoints();
 
             if (selectedLink) {
@@ -369,7 +375,7 @@ export const image: EditorButtonDefinition = {
     action(context: EditorUiContext) {
         const imageModal = context.manager.createModal('image');
         const selection = context.lastSelection;
-        const selectedImage = getNodeFromSelection(selection, $isImageNode) as ImageNode|null;
+        const selectedImage = $getNodeFromSelection(selection, $isImageNode) as ImageNode|null;
 
         context.editor.getEditorState().read(() => {
             let formDefaults = {};
@@ -392,7 +398,7 @@ export const image: EditorButtonDefinition = {
         });
     },
     isActive(selection: BaseSelection|null): boolean {
-        return selectionContainsNodeType(selection, $isImageNode);
+        return $selectionContainsNodeType(selection, $isImageNode);
     }
 };
 
@@ -401,11 +407,11 @@ export const horizontalRule: EditorButtonDefinition = {
     icon: horizontalRuleIcon,
     action(context: EditorUiContext) {
         context.editor.update(() => {
-            insertNewBlockNodeAtSelection($createHorizontalRuleNode(), false);
+            $insertNewBlockNodeAtSelection($createHorizontalRuleNode(), false);
         });
     },
     isActive(selection: BaseSelection|null): boolean {
-        return selectionContainsNodeType(selection, $isHorizontalRuleNode);
+        return $selectionContainsNodeType(selection, $isHorizontalRuleNode);
     }
 };
 
@@ -415,12 +421,12 @@ export const codeBlock: EditorButtonDefinition = {
     action(context: EditorUiContext) {
         context.editor.getEditorState().read(() => {
             const selection = $getSelection();
-            const codeBlock = getNodeFromSelection(context.lastSelection, $isCodeBlockNode) as (CodeBlockNode|null);
+            const codeBlock = $getNodeFromSelection(context.lastSelection, $isCodeBlockNode) as (CodeBlockNode|null);
             if (codeBlock === null) {
                 context.editor.update(() => {
                     const codeBlock = $createCodeBlockNode();
                     codeBlock.setCode(selection?.getTextContent() || '');
-                    insertNewBlockNodeAtSelection(codeBlock, true);
+                    $insertNewBlockNodeAtSelection(codeBlock, true);
                     $openCodeEditorForNode(context.editor, codeBlock);
                     codeBlock.selectStart();
                 });
@@ -430,7 +436,7 @@ export const codeBlock: EditorButtonDefinition = {
         });
     },
     isActive(selection: BaseSelection|null): boolean {
-        return selectionContainsNodeType(selection, $isCodeBlockNode);
+        return $selectionContainsNodeType(selection, $isCodeBlockNode);
     }
 };
 
@@ -463,7 +469,7 @@ export const details: EditorButtonDefinition = {
         });
     },
     isActive(selection: BaseSelection|null): boolean {
-        return selectionContainsNodeType(selection, $isDetailsNode);
+        return $selectionContainsNodeType(selection, $isDetailsNode);
     }
 }
 
index 8c28953d566fe7f9e6a81ab24ae3a242820385e4..1981fcb8624029085e86f14b26f67db322af02bb 100644 (file)
@@ -1,4 +1,4 @@
-import {el, insertNewBlockNodeAtSelection} from "../../../helpers";
+import {el, $insertNewBlockNodeAtSelection} from "../../../helpers";
 import {EditorUiElement} from "../core";
 import {$createTableNodeWithDimensions} from "@lexical/table";
 import {CustomTableNode} from "../../../nodes/custom-table";
@@ -75,7 +75,7 @@ export class EditorTableCreator extends EditorUiElement {
 
         this.getContext().editor.update(() => {
             const table = $createTableNodeWithDimensions(rows, columns, false) as CustomTableNode;
-            insertNewBlockNodeAtSelection(table);
+            $insertNewBlockNodeAtSelection(table);
         });
     }
 }
\ No newline at end of file