From 2add15bd728b0f02af619e425d97b441d5832145 Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
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