diff --git a/resources/js/wysiwyg/helpers.ts b/resources/js/wysiwyg/helpers.ts index 62e945721..64bcb6490 100644 --- a/resources/js/wysiwyg/helpers.ts +++ b/resources/js/wysiwyg/helpers.ts @@ -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 index 000000000..fbd019e72 --- /dev/null +++ b/resources/js/wysiwyg/nodes/horizontal-rule.ts @@ -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 diff --git a/resources/js/wysiwyg/nodes/index.ts b/resources/js/wysiwyg/nodes/index.ts index 6b1b66e66..befc2ab2e 100644 --- a/resources/js/wysiwyg/nodes/index.ts +++ b/resources/js/wysiwyg/nodes/index.ts @@ -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 | TableRowNode, TableCellNode, ImageNode, + HorizontalRuleNode, DetailsNode, SummaryNode, CustomParagraphNode, LinkNode, diff --git a/resources/js/wysiwyg/ui/defaults/button-definitions.ts b/resources/js/wysiwyg/ui/defaults/button-definitions.ts index 5e5f0d409..aa8b27ec5 100644 --- a/resources/js/wysiwyg/ui/defaults/button-definitions.ts +++ b/resources/js/wysiwyg/ui/defaults/button-definitions.ts @@ -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, diff --git a/resources/js/wysiwyg/ui/toolbars.ts b/resources/js/wysiwyg/ui/toolbars.ts index 7f7e99a78..a8ba52c5f 100644 --- a/resources/js/wysiwyg/ui/toolbars.ts +++ b/resources/js/wysiwyg/ui/toolbars.ts @@ -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() {