]> BookStack Code Mirror - bookstack/commitdiff
Lexical: Added horizontal rule node
authorDan Brown <redacted>
Thu, 27 Jun 2024 14:48:06 +0000 (15:48 +0100)
committerDan Brown <redacted>
Thu, 27 Jun 2024 14:48:06 +0000 (15:48 +0100)
resources/js/wysiwyg/helpers.ts
resources/js/wysiwyg/nodes/horizontal-rule.ts [new file with mode: 0644]
resources/js/wysiwyg/nodes/index.ts
resources/js/wysiwyg/ui/defaults/button-definitions.ts
resources/js/wysiwyg/ui/toolbars.ts

index 62e9457213eba623c029d297fc86610d8aa3ed64..64bcb64902e943939e907da1cba7fe7cb7fbd427 100644 (file)
@@ -80,12 +80,16 @@ export function toggleSelectionBlockNodeType(editor: LexicalEditor, matcher: Lex
     });
 }
 
-export function insertNewBlockNodeAtSelection(node: LexicalNode) {
+export function insertNewBlockNodeAtSelection(node: LexicalNode, insertAfter: boolean = true) {
     const selection = $getSelection();
     const blockElement = selection ? $getNearestBlockElementAncestorOrThrow(selection.getNodes()[0]) : null;
 
     if (blockElement) {
-        blockElement.insertAfter(node);
+        if (insertAfter) {
+            blockElement.insertAfter(node);
+        } else {
+            blockElement.insertBefore(node);
+        }
     } else {
         $getRoot().append(node);
     }
diff --git a/resources/js/wysiwyg/nodes/horizontal-rule.ts b/resources/js/wysiwyg/nodes/horizontal-rule.ts
new file mode 100644 (file)
index 0000000..fbd019e
--- /dev/null
@@ -0,0 +1,64 @@
+import {
+    DOMConversion,
+    DOMConversionMap, DOMConversionOutput,
+    ElementNode,
+    LexicalEditor,
+    LexicalNode,
+    SerializedElementNode,
+} from 'lexical';
+import type {EditorConfig} from "lexical/LexicalEditor";
+
+export class HorizontalRuleNode extends ElementNode {
+
+    static getType() {
+        return 'horizontal-rule';
+    }
+
+    static clone(node: HorizontalRuleNode): HorizontalRuleNode {
+        return new HorizontalRuleNode(node.__key);
+    }
+
+    createDOM(_config: EditorConfig, _editor: LexicalEditor) {
+        return document.createElement('hr');
+    }
+
+    updateDOM(prevNode: unknown, dom: HTMLElement) {
+        return false;
+    }
+
+    static importDOM(): DOMConversionMap|null {
+        return {
+            hr(node: HTMLElement): DOMConversion|null {
+                return {
+                    conversion: (element: HTMLElement): DOMConversionOutput|null => {
+                        return {
+                            node: new HorizontalRuleNode(),
+                        };
+                    },
+                    priority: 3,
+                };
+            },
+        };
+    }
+
+    exportJSON(): SerializedElementNode {
+        return {
+            ...super.exportJSON(),
+            type: 'horizontal-rule',
+            version: 1,
+        };
+    }
+
+    static importJSON(serializedNode: SerializedElementNode): HorizontalRuleNode {
+        return $createHorizontalRuleNode();
+    }
+
+}
+
+export function $createHorizontalRuleNode() {
+    return new HorizontalRuleNode();
+}
+
+export function $isHorizontalRuleNode(node: LexicalNode | null | undefined) {
+    return node instanceof HorizontalRuleNode;
+}
\ No newline at end of file
index 6b1b66e66785de362fdd307dd879381a301f5448..befc2ab2e05442f5f1ef49d2edc8030252443428 100644 (file)
@@ -8,6 +8,7 @@ import {DetailsNode, SummaryNode} from "./details";
 import {ListItemNode, ListNode} from "@lexical/list";
 import {TableCellNode, TableNode, TableRowNode} from "@lexical/table";
 import {CustomTableNode} from "./custom-table";
+import {HorizontalRuleNode} from "./horizontal-rule";
 
 /**
  * Load the nodes for lexical.
@@ -23,6 +24,7 @@ export function getNodesForPageEditor(): (KlassConstructor<typeof LexicalNode> |
         TableRowNode,
         TableCellNode,
         ImageNode,
+        HorizontalRuleNode,
         DetailsNode, SummaryNode,
         CustomParagraphNode,
         LinkNode,
index 5e5f0d40904e105a5ec4cc8c8f697c8865db2701..aa8b27ec56c7c625d47dd3df4754eca7de11ced1 100644 (file)
@@ -9,7 +9,7 @@ import {
     UNDO_COMMAND
 } from "lexical";
 import {
-    getNodeFromSelection,
+    getNodeFromSelection, insertNewBlockNodeAtSelection,
     selectionContainsNodeType,
     selectionContainsTextFormat,
     toggleSelectionBlockNodeType
@@ -47,8 +47,10 @@ import listCheckIcon from "@icons/editor/list-check.svg"
 import linkIcon from "@icons/editor/link.svg"
 import tableIcon from "@icons/editor/table.svg"
 import imageIcon from "@icons/editor/image.svg"
+import horizontalRuleIcon from "@icons/editor/horizontal-rule.svg"
 import detailsIcon from "@icons/editor/details.svg"
 import sourceIcon from "@icons/editor/source-view.svg"
+import {$createHorizontalRuleNode, $isHorizontalRuleNode, HorizontalRuleNode} from "../../nodes/horizontal-rule";
 
 export const undo: EditorButtonDefinition = {
     label: 'Undo',
@@ -294,6 +296,19 @@ export const image: EditorButtonDefinition = {
     }
 };
 
+export const horizontalRule: EditorButtonDefinition = {
+    label: 'Insert horizontal line',
+    icon: horizontalRuleIcon,
+    action(context: EditorUiContext) {
+        context.editor.update(() => {
+            insertNewBlockNodeAtSelection($createHorizontalRuleNode(), false);
+        });
+    },
+    isActive(selection: BaseSelection|null): boolean {
+        return selectionContainsNodeType(selection, $isHorizontalRuleNode);
+    }
+};
+
 export const details: EditorButtonDefinition = {
     label: 'Insert collapsible block',
     icon: detailsIcon,
index 7f7e99a785a331044563292b35d5453d2e96ebec..a8ba52c5f42c8706412ba123c8fe69d4a69a6b05 100644 (file)
@@ -2,7 +2,7 @@ import {EditorButton} from "./framework/buttons";
 import {
     blockquote, bold, bulletList, clearFormating, code,
     dangerCallout, details,
-    h2, h3, h4, h5, highlightColor, image,
+    h2, h3, h4, h5, highlightColor, horizontalRule, image,
     infoCallout, italic, link, numberList, paragraph,
     redo, source, strikethrough, subscript,
     successCallout, superscript, table, taskList, textColor, underline,
@@ -67,6 +67,7 @@ export function getMainEditorFullToolbar(): EditorContainerUiElement {
             new EditorTableCreator(),
         ]),
         new EditorButton(image),
+        new EditorButton(horizontalRule),
         new EditorButton(details),
 
         // Meta elements
@@ -74,21 +75,10 @@ export function getMainEditorFullToolbar(): EditorContainerUiElement {
 
         // Test
         new EditorButton({
-            label: 'Expand table col 2',
+            label: 'Test button',
             action(context: EditorUiContext) {
                 context.editor.update(() => {
-                    const root = $getRoot();
-                    let table: CustomTableNode|null = null;
-                    for (const child of root.getChildren()) {
-                        if ($isCustomTableNode(child)) {
-                            table = child as CustomTableNode;
-                            break;
-                        }
-                    }
-
-                    if (table) {
-                        $setTableColumnWidth(table, 1, 500);
-                    }
+                    // Do stuff
                 });
             },
             isActive() {