mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-10-01 01:36:00 -04:00
Lexical: Added direction support to extra blocks
Also removed duplicated dir functionality that remained in core.
This commit is contained in:
parent
e6edd9340e
commit
2add15bd72
@ -135,10 +135,6 @@ export class ParagraphNode extends ElementNode {
|
|||||||
const formatType = this.getFormatType();
|
const formatType = this.getFormatType();
|
||||||
element.style.textAlign = formatType;
|
element.style.textAlign = formatType;
|
||||||
|
|
||||||
const direction = this.getDirection();
|
|
||||||
if (direction) {
|
|
||||||
element.dir = direction;
|
|
||||||
}
|
|
||||||
const indent = this.getIndent();
|
const indent = this.getIndent();
|
||||||
if (indent > 0) {
|
if (indent > 0) {
|
||||||
// padding-inline-start is not widely supported in email HTML, but
|
// padding-inline-start is not widely supported in email HTML, but
|
||||||
@ -156,7 +152,6 @@ export class ParagraphNode extends ElementNode {
|
|||||||
const node = $createParagraphNode();
|
const node = $createParagraphNode();
|
||||||
node.setFormat(serializedNode.format);
|
node.setFormat(serializedNode.format);
|
||||||
node.setIndent(serializedNode.indent);
|
node.setIndent(serializedNode.indent);
|
||||||
node.setDirection(serializedNode.direction);
|
|
||||||
node.setTextFormat(serializedNode.textFormat);
|
node.setTextFormat(serializedNode.textFormat);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
@ -158,11 +158,6 @@ export class QuoteNode extends ElementNode {
|
|||||||
|
|
||||||
const formatType = this.getFormatType();
|
const formatType = this.getFormatType();
|
||||||
element.style.textAlign = formatType;
|
element.style.textAlign = formatType;
|
||||||
|
|
||||||
const direction = this.getDirection();
|
|
||||||
if (direction) {
|
|
||||||
element.dir = direction;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -174,7 +169,6 @@ export class QuoteNode extends ElementNode {
|
|||||||
const node = $createQuoteNode();
|
const node = $createQuoteNode();
|
||||||
node.setFormat(serializedNode.format);
|
node.setFormat(serializedNode.format);
|
||||||
node.setIndent(serializedNode.indent);
|
node.setIndent(serializedNode.indent);
|
||||||
node.setDirection(serializedNode.direction);
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -324,11 +318,6 @@ export class HeadingNode extends ElementNode {
|
|||||||
|
|
||||||
const formatType = this.getFormatType();
|
const formatType = this.getFormatType();
|
||||||
element.style.textAlign = formatType;
|
element.style.textAlign = formatType;
|
||||||
|
|
||||||
const direction = this.getDirection();
|
|
||||||
if (direction) {
|
|
||||||
element.dir = direction;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -340,7 +329,6 @@ export class HeadingNode extends ElementNode {
|
|||||||
const node = $createHeadingNode(serializedNode.tag);
|
const node = $createHeadingNode(serializedNode.tag);
|
||||||
node.setFormat(serializedNode.format);
|
node.setFormat(serializedNode.format);
|
||||||
node.setIndent(serializedNode.indent);
|
node.setIndent(serializedNode.indent);
|
||||||
node.setDirection(serializedNode.direction);
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ export function extractInsetFromElement(element: HTMLElement): number {
|
|||||||
return sizeToPixels(elemPadding);
|
return sizeToPixels(elemPadding);
|
||||||
}
|
}
|
||||||
|
|
||||||
function extractDirectionFromElement(element: HTMLElement): EditorNodeDirection {
|
export function extractDirectionFromElement(element: HTMLElement): EditorNodeDirection {
|
||||||
const elemDir = (element.dir || '').toLowerCase();
|
const elemDir = (element.dir || '').toLowerCase();
|
||||||
if (elemDir === 'rtl' || elemDir === 'ltr') {
|
if (elemDir === 'rtl' || elemDir === 'ltr') {
|
||||||
return elemDir;
|
return elemDir;
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import {
|
import {
|
||||||
DOMConversionFn,
|
DOMConversionFn,
|
||||||
DOMConversionMap,
|
DOMConversionMap, EditorConfig,
|
||||||
LexicalNode,
|
LexicalNode,
|
||||||
Spread
|
Spread
|
||||||
} from "lexical";
|
} from "lexical";
|
||||||
import {EditorConfig} from "lexical/LexicalEditor";
|
|
||||||
import {$isListItemNode, ListItemNode, ListNode, ListType, SerializedListNode} from "@lexical/list";
|
import {$isListItemNode, ListItemNode, ListNode, ListType, SerializedListNode} from "@lexical/list";
|
||||||
import {$createCustomListItemNode} from "./custom-list-item";
|
import {$createCustomListItemNode} from "./custom-list-item";
|
||||||
|
import {extractDirectionFromElement} from "./_common";
|
||||||
|
|
||||||
|
|
||||||
export type SerializedCustomListNode = Spread<{
|
export type SerializedCustomListNode = Spread<{
|
||||||
@ -33,6 +33,7 @@ export class CustomListNode extends ListNode {
|
|||||||
static clone(node: CustomListNode) {
|
static clone(node: CustomListNode) {
|
||||||
const newNode = new CustomListNode(node.__listType, node.__start, node.__key);
|
const newNode = new CustomListNode(node.__listType, node.__start, node.__key);
|
||||||
newNode.__id = node.__id;
|
newNode.__id = node.__id;
|
||||||
|
newNode.__dir = node.__dir;
|
||||||
return newNode;
|
return newNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,9 +43,18 @@ export class CustomListNode extends ListNode {
|
|||||||
dom.setAttribute('id', this.__id);
|
dom.setAttribute('id', this.__id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.__dir) {
|
||||||
|
dom.setAttribute('dir', this.__dir);
|
||||||
|
}
|
||||||
|
|
||||||
return dom;
|
return dom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateDOM(prevNode: ListNode, dom: HTMLElement, config: EditorConfig): boolean {
|
||||||
|
return super.updateDOM(prevNode, dom, config) ||
|
||||||
|
prevNode.__dir !== this.__dir;
|
||||||
|
}
|
||||||
|
|
||||||
exportJSON(): SerializedCustomListNode {
|
exportJSON(): SerializedCustomListNode {
|
||||||
return {
|
return {
|
||||||
...super.exportJSON(),
|
...super.exportJSON(),
|
||||||
@ -57,6 +67,7 @@ export class CustomListNode extends ListNode {
|
|||||||
static importJSON(serializedNode: SerializedCustomListNode): CustomListNode {
|
static importJSON(serializedNode: SerializedCustomListNode): CustomListNode {
|
||||||
const node = $createCustomListNode(serializedNode.listType);
|
const node = $createCustomListNode(serializedNode.listType);
|
||||||
node.setId(serializedNode.id);
|
node.setId(serializedNode.id);
|
||||||
|
node.setDirection(serializedNode.direction);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,6 +80,10 @@ export class CustomListNode extends ListNode {
|
|||||||
(baseResult.node as CustomListNode).setId(element.id);
|
(baseResult.node as CustomListNode).setId(element.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (element.dir && baseResult?.node) {
|
||||||
|
(baseResult.node as CustomListNode).setDirection(extractDirectionFromElement(element));
|
||||||
|
}
|
||||||
|
|
||||||
if (baseResult) {
|
if (baseResult) {
|
||||||
baseResult.after = $normalizeChildren;
|
baseResult.after = $normalizeChildren;
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,11 @@ import {
|
|||||||
LexicalEditor,
|
LexicalEditor,
|
||||||
LexicalNode,
|
LexicalNode,
|
||||||
SerializedElementNode, Spread,
|
SerializedElementNode, Spread,
|
||||||
|
EditorConfig,
|
||||||
} from 'lexical';
|
} from 'lexical';
|
||||||
import type {EditorConfig} from "lexical/LexicalEditor";
|
|
||||||
|
|
||||||
import {el} from "../utils/dom";
|
import {el} from "../utils/dom";
|
||||||
|
import {extractDirectionFromElement} from "./_common";
|
||||||
|
|
||||||
export type SerializedDetailsNode = Spread<{
|
export type SerializedDetailsNode = Spread<{
|
||||||
id: string;
|
id: string;
|
||||||
@ -34,6 +35,7 @@ export class DetailsNode extends ElementNode {
|
|||||||
static clone(node: DetailsNode): DetailsNode {
|
static clone(node: DetailsNode): DetailsNode {
|
||||||
const newNode = new DetailsNode(node.__key);
|
const newNode = new DetailsNode(node.__key);
|
||||||
newNode.__id = node.__id;
|
newNode.__id = node.__id;
|
||||||
|
newNode.__dir = node.__dir;
|
||||||
return newNode;
|
return newNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,11 +45,16 @@ export class DetailsNode extends ElementNode {
|
|||||||
el.setAttribute('id', this.__id);
|
el.setAttribute('id', this.__id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.__dir) {
|
||||||
|
el.setAttribute('dir', this.__dir);
|
||||||
|
}
|
||||||
|
|
||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateDOM(prevNode: DetailsNode, dom: HTMLElement) {
|
updateDOM(prevNode: DetailsNode, dom: HTMLElement) {
|
||||||
return prevNode.__id !== this.__id;
|
return prevNode.__id !== this.__id
|
||||||
|
|| prevNode.__dir !== this.__dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
static importDOM(): DOMConversionMap|null {
|
static importDOM(): DOMConversionMap|null {
|
||||||
@ -60,6 +67,10 @@ export class DetailsNode extends ElementNode {
|
|||||||
node.setId(element.id);
|
node.setId(element.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (element.dir) {
|
||||||
|
node.setDirection(extractDirectionFromElement(element));
|
||||||
|
}
|
||||||
|
|
||||||
return {node};
|
return {node};
|
||||||
},
|
},
|
||||||
priority: 3,
|
priority: 3,
|
||||||
@ -80,6 +91,7 @@ export class DetailsNode extends ElementNode {
|
|||||||
static importJSON(serializedNode: SerializedDetailsNode): DetailsNode {
|
static importJSON(serializedNode: SerializedDetailsNode): DetailsNode {
|
||||||
const node = $createDetailsNode();
|
const node = $createDetailsNode();
|
||||||
node.setId(serializedNode.id);
|
node.setId(serializedNode.id);
|
||||||
|
node.setDirection(serializedNode.direction);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,16 +2,14 @@
|
|||||||
|
|
||||||
## In progress
|
## 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
|
## Main Todo
|
||||||
|
|
||||||
- Mac: Shortcut support via command.
|
- Mac: Shortcut support via command.
|
||||||
- Translations
|
- Translations
|
||||||
|
- Form closing on submit
|
||||||
|
- Update toolbar overflows to match existing editor, incl. direction dynamic controls
|
||||||
|
|
||||||
## Secondary Todo
|
## Secondary Todo
|
||||||
|
|
||||||
@ -19,7 +17,11 @@
|
|||||||
- Color picker for color controls
|
- Color picker for color controls
|
||||||
- Table caption text support
|
- 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)
|
- 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
|
## Bugs
|
||||||
|
|
||||||
- List selection can get lost on nesting/unnesting
|
- 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)
|
Loading…
Reference in New Issue
Block a user