From 2add15bd728b0f02af619e425d97b441d5832145 Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Sun, 22 Sep 2024 12:07:24 +0100 Subject: [PATCH] Lexical: Added direction support to extra blocks Also removed duplicated dir functionality that remained in core. --- .../core/nodes/LexicalParagraphNode.ts | 5 ----- .../js/wysiwyg/lexical/rich-text/index.ts | 12 ------------ resources/js/wysiwyg/nodes/_common.ts | 2 +- resources/js/wysiwyg/nodes/custom-list.ts | 19 +++++++++++++++++-- resources/js/wysiwyg/nodes/details.ts | 16 ++++++++++++++-- resources/js/wysiwyg/todo.md | 14 ++++++++------ 6 files changed, 40 insertions(+), 28 deletions(-) diff --git a/resources/js/wysiwyg/lexical/core/nodes/LexicalParagraphNode.ts b/resources/js/wysiwyg/lexical/core/nodes/LexicalParagraphNode.ts index deab3a2cc..4e69dc21c 100644 --- a/resources/js/wysiwyg/lexical/core/nodes/LexicalParagraphNode.ts +++ b/resources/js/wysiwyg/lexical/core/nodes/LexicalParagraphNode.ts @@ -135,10 +135,6 @@ export class ParagraphNode extends ElementNode { const formatType = this.getFormatType(); element.style.textAlign = formatType; - const direction = this.getDirection(); - if (direction) { - element.dir = direction; - } const indent = this.getIndent(); if (indent > 0) { // padding-inline-start is not widely supported in email HTML, but @@ -156,7 +152,6 @@ export class ParagraphNode extends ElementNode { const node = $createParagraphNode(); node.setFormat(serializedNode.format); node.setIndent(serializedNode.indent); - node.setDirection(serializedNode.direction); node.setTextFormat(serializedNode.textFormat); return node; } diff --git a/resources/js/wysiwyg/lexical/rich-text/index.ts b/resources/js/wysiwyg/lexical/rich-text/index.ts index fd9162566..d937060c6 100644 --- a/resources/js/wysiwyg/lexical/rich-text/index.ts +++ b/resources/js/wysiwyg/lexical/rich-text/index.ts @@ -158,11 +158,6 @@ export class QuoteNode extends ElementNode { const formatType = this.getFormatType(); element.style.textAlign = formatType; - - const direction = this.getDirection(); - if (direction) { - element.dir = direction; - } } return { @@ -174,7 +169,6 @@ export class QuoteNode extends ElementNode { const node = $createQuoteNode(); node.setFormat(serializedNode.format); node.setIndent(serializedNode.indent); - node.setDirection(serializedNode.direction); return node; } @@ -324,11 +318,6 @@ export class HeadingNode extends ElementNode { const formatType = this.getFormatType(); element.style.textAlign = formatType; - - const direction = this.getDirection(); - if (direction) { - element.dir = direction; - } } return { @@ -340,7 +329,6 @@ export class HeadingNode extends ElementNode { const node = $createHeadingNode(serializedNode.tag); node.setFormat(serializedNode.format); node.setIndent(serializedNode.indent); - node.setDirection(serializedNode.direction); return node; } diff --git a/resources/js/wysiwyg/nodes/_common.ts b/resources/js/wysiwyg/nodes/_common.ts index 36e692f25..71849bb45 100644 --- a/resources/js/wysiwyg/nodes/_common.ts +++ b/resources/js/wysiwyg/nodes/_common.ts @@ -63,7 +63,7 @@ export function extractInsetFromElement(element: HTMLElement): number { return sizeToPixels(elemPadding); } -function extractDirectionFromElement(element: HTMLElement): EditorNodeDirection { +export function extractDirectionFromElement(element: HTMLElement): EditorNodeDirection { const elemDir = (element.dir || '').toLowerCase(); if (elemDir === 'rtl' || elemDir === 'ltr') { return elemDir; diff --git a/resources/js/wysiwyg/nodes/custom-list.ts b/resources/js/wysiwyg/nodes/custom-list.ts index a6c473999..4b05fa62e 100644 --- a/resources/js/wysiwyg/nodes/custom-list.ts +++ b/resources/js/wysiwyg/nodes/custom-list.ts @@ -1,12 +1,12 @@ import { DOMConversionFn, - DOMConversionMap, + DOMConversionMap, EditorConfig, LexicalNode, Spread } from "lexical"; -import {EditorConfig} from "lexical/LexicalEditor"; import {$isListItemNode, ListItemNode, ListNode, ListType, SerializedListNode} from "@lexical/list"; import {$createCustomListItemNode} from "./custom-list-item"; +import {extractDirectionFromElement} from "./_common"; export type SerializedCustomListNode = Spread<{ @@ -33,6 +33,7 @@ export class CustomListNode extends ListNode { static clone(node: CustomListNode) { const newNode = new CustomListNode(node.__listType, node.__start, node.__key); newNode.__id = node.__id; + newNode.__dir = node.__dir; return newNode; } @@ -42,9 +43,18 @@ export class CustomListNode extends ListNode { dom.setAttribute('id', this.__id); } + if (this.__dir) { + dom.setAttribute('dir', this.__dir); + } + return dom; } + updateDOM(prevNode: ListNode, dom: HTMLElement, config: EditorConfig): boolean { + return super.updateDOM(prevNode, dom, config) || + prevNode.__dir !== this.__dir; + } + exportJSON(): SerializedCustomListNode { return { ...super.exportJSON(), @@ -57,6 +67,7 @@ export class CustomListNode extends ListNode { static importJSON(serializedNode: SerializedCustomListNode): CustomListNode { const node = $createCustomListNode(serializedNode.listType); node.setId(serializedNode.id); + node.setDirection(serializedNode.direction); return node; } @@ -69,6 +80,10 @@ export class CustomListNode extends ListNode { (baseResult.node as CustomListNode).setId(element.id); } + if (element.dir && baseResult?.node) { + (baseResult.node as CustomListNode).setDirection(extractDirectionFromElement(element)); + } + if (baseResult) { baseResult.after = $normalizeChildren; } diff --git a/resources/js/wysiwyg/nodes/details.ts b/resources/js/wysiwyg/nodes/details.ts index 119619da6..de87696f3 100644 --- a/resources/js/wysiwyg/nodes/details.ts +++ b/resources/js/wysiwyg/nodes/details.ts @@ -5,10 +5,11 @@ import { LexicalEditor, LexicalNode, SerializedElementNode, Spread, + EditorConfig, } from 'lexical'; -import type {EditorConfig} from "lexical/LexicalEditor"; import {el} from "../utils/dom"; +import {extractDirectionFromElement} from "./_common"; export type SerializedDetailsNode = Spread<{ id: string; @@ -34,6 +35,7 @@ export class DetailsNode extends ElementNode { static clone(node: DetailsNode): DetailsNode { const newNode = new DetailsNode(node.__key); newNode.__id = node.__id; + newNode.__dir = node.__dir; return newNode; } @@ -43,11 +45,16 @@ export class DetailsNode extends ElementNode { el.setAttribute('id', this.__id); } + if (this.__dir) { + el.setAttribute('dir', this.__dir); + } + return el; } updateDOM(prevNode: DetailsNode, dom: HTMLElement) { - return prevNode.__id !== this.__id; + return prevNode.__id !== this.__id + || prevNode.__dir !== this.__dir; } static importDOM(): DOMConversionMap|null { @@ -60,6 +67,10 @@ export class DetailsNode extends ElementNode { node.setId(element.id); } + if (element.dir) { + node.setDirection(extractDirectionFromElement(element)); + } + return {node}; }, priority: 3, @@ -80,6 +91,7 @@ export class DetailsNode extends ElementNode { static importJSON(serializedNode: SerializedDetailsNode): DetailsNode { const node = $createDetailsNode(); node.setId(serializedNode.id); + node.setDirection(serializedNode.direction); return node; } diff --git a/resources/js/wysiwyg/todo.md b/resources/js/wysiwyg/todo.md index 7fcf25444..66878f37d 100644 --- a/resources/js/wysiwyg/todo.md +++ b/resources/js/wysiwyg/todo.md @@ -2,16 +2,14 @@ ## In progress -- RTL/LTR support - - Basic implementation added - - Test across main range of content blocks - - Test that HTML is being set as expected - - Test editor defaults when between RTL/LTR modes +// ## Main Todo - Mac: Shortcut support via command. - Translations +- Form closing on submit +- Update toolbar overflows to match existing editor, incl. direction dynamic controls ## Secondary Todo @@ -19,7 +17,11 @@ - Color picker for color controls - Table caption text support - Support media src conversions (https://github.com/tinymce/tinymce/blob/release/6.6/modules/tinymce/src/plugins/media/main/ts/core/UrlPatterns.ts) +- Check translation coverage ## Bugs -- List selection can get lost on nesting/unnesting \ No newline at end of file +- List selection can get lost on nesting/unnesting +- Can't escape lists when bottom element +- Content not properly saving on new pages +- BookStack UI (non-editor) shortcuts can trigger in editor (`/` for example) \ No newline at end of file