]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/nodes/_common.ts
Lexical: Added direction support to extra blocks
[bookstack] / resources / js / wysiwyg / nodes / _common.ts
1 import {LexicalNode, Spread} from "lexical";
2 import type {SerializedElementNode} from "lexical/nodes/LexicalElementNode";
3 import {el, sizeToPixels} from "../utils/dom";
4
5 export type CommonBlockAlignment = 'left' | 'right' | 'center' | 'justify' | '';
6 const validAlignments: CommonBlockAlignment[] = ['left', 'right', 'center', 'justify'];
7
8 type EditorNodeDirection = 'ltr' | 'rtl' | null;
9
10 export type SerializedCommonBlockNode = Spread<{
11     id: string;
12     alignment: CommonBlockAlignment;
13     inset: number;
14 }, SerializedElementNode>
15
16 export interface NodeHasAlignment {
17     readonly __alignment: CommonBlockAlignment;
18     setAlignment(alignment: CommonBlockAlignment): void;
19     getAlignment(): CommonBlockAlignment;
20 }
21
22 export interface NodeHasId {
23     readonly __id: string;
24     setId(id: string): void;
25     getId(): string;
26 }
27
28 export interface NodeHasInset {
29     readonly __inset: number;
30     setInset(inset: number): void;
31     getInset(): number;
32 }
33
34 export interface NodeHasDirection {
35     readonly __dir: EditorNodeDirection;
36     setDirection(direction: EditorNodeDirection): void;
37     getDirection(): EditorNodeDirection;
38 }
39
40 interface CommonBlockInterface extends NodeHasId, NodeHasAlignment, NodeHasInset, NodeHasDirection {}
41
42 export function extractAlignmentFromElement(element: HTMLElement): CommonBlockAlignment {
43     const textAlignStyle: string = element.style.textAlign || '';
44     if (validAlignments.includes(textAlignStyle as CommonBlockAlignment)) {
45         return textAlignStyle as CommonBlockAlignment;
46     }
47
48     if (element.classList.contains('align-left')) {
49         return 'left';
50     } else if (element.classList.contains('align-right')) {
51         return 'right'
52     } else if (element.classList.contains('align-center')) {
53         return 'center'
54     } else if (element.classList.contains('align-justify')) {
55         return 'justify'
56     }
57
58     return '';
59 }
60
61 export function extractInsetFromElement(element: HTMLElement): number {
62     const elemPadding: string = element.style.paddingLeft || '0';
63     return sizeToPixels(elemPadding);
64 }
65
66 export function extractDirectionFromElement(element: HTMLElement): EditorNodeDirection {
67     const elemDir = (element.dir || '').toLowerCase();
68     if (elemDir === 'rtl' || elemDir === 'ltr') {
69         return elemDir;
70     }
71
72     return null;
73 }
74
75 export function setCommonBlockPropsFromElement(element: HTMLElement, node: CommonBlockInterface): void {
76     if (element.id) {
77         node.setId(element.id);
78     }
79
80     node.setAlignment(extractAlignmentFromElement(element));
81     node.setInset(extractInsetFromElement(element));
82     node.setDirection(extractDirectionFromElement(element));
83 }
84
85 export function commonPropertiesDifferent(nodeA: CommonBlockInterface, nodeB: CommonBlockInterface): boolean {
86     return nodeA.__id !== nodeB.__id ||
87         nodeA.__alignment !== nodeB.__alignment ||
88         nodeA.__inset !== nodeB.__inset ||
89         nodeA.__dir !== nodeB.__dir;
90 }
91
92 export function updateElementWithCommonBlockProps(element: HTMLElement, node: CommonBlockInterface): void {
93     if (node.__id) {
94         element.setAttribute('id', node.__id);
95     }
96
97     if (node.__alignment) {
98         element.classList.add('align-' + node.__alignment);
99     }
100
101     if (node.__inset) {
102         element.style.paddingLeft = `${node.__inset}px`;
103     }
104
105     if (node.__dir) {
106         element.dir = node.__dir;
107     }
108 }
109
110 export function deserializeCommonBlockNode(serializedNode: SerializedCommonBlockNode, node: CommonBlockInterface): void {
111     node.setId(serializedNode.id);
112     node.setAlignment(serializedNode.alignment);
113     node.setInset(serializedNode.inset);
114     node.setDirection(serializedNode.direction);
115 }
116
117 export interface NodeHasSize {
118     setHeight(height: number): void;
119     setWidth(width: number): void;
120     getHeight(): number;
121     getWidth(): number;
122 }