]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/lexical/core/nodes/common.ts
50d8843440c87f2b0a66eed69a4dfb66d3ff455c
[bookstack] / resources / js / wysiwyg / lexical / core / nodes / common.ts
1 import {sizeToPixels} from "../../../utils/dom";
2 import {SerializedCommonBlockNode} from "lexical/nodes/CommonBlockNode";
3 import {elem} from "../../../../services/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 interface NodeHasAlignment {
11     readonly __alignment: CommonBlockAlignment;
12     setAlignment(alignment: CommonBlockAlignment): void;
13     getAlignment(): CommonBlockAlignment;
14 }
15
16 export interface NodeHasId {
17     readonly __id: string;
18     setId(id: string): void;
19     getId(): string;
20 }
21
22 export interface NodeHasInset {
23     readonly __inset: number;
24     setInset(inset: number): void;
25     getInset(): number;
26 }
27
28 export interface NodeHasDirection {
29     readonly __dir: EditorNodeDirection;
30     setDirection(direction: EditorNodeDirection): void;
31     getDirection(): EditorNodeDirection;
32 }
33
34 export interface CommonBlockInterface extends NodeHasId, NodeHasAlignment, NodeHasInset, NodeHasDirection {}
35
36 export function extractAlignmentFromElement(element: HTMLElement): CommonBlockAlignment {
37     const textAlignStyle: string = element.style.textAlign || '';
38     if (validAlignments.includes(textAlignStyle as CommonBlockAlignment)) {
39         return textAlignStyle as CommonBlockAlignment;
40     }
41
42     if (element.classList.contains('align-left')) {
43         return 'left';
44     } else if (element.classList.contains('align-right')) {
45         return 'right'
46     } else if (element.classList.contains('align-center')) {
47         return 'center'
48     } else if (element.classList.contains('align-justify')) {
49         return 'justify'
50     }
51
52     return '';
53 }
54
55 export function extractInsetFromElement(element: HTMLElement): number {
56     const elemPadding: string = element.style.paddingLeft || '0';
57     return sizeToPixels(elemPadding);
58 }
59
60 export function extractDirectionFromElement(element: HTMLElement): EditorNodeDirection {
61     const elemDir = (element.dir || '').toLowerCase();
62     if (elemDir === 'rtl' || elemDir === 'ltr') {
63         return elemDir;
64     }
65
66     return null;
67 }
68
69 export function setCommonBlockPropsFromElement(element: HTMLElement, node: CommonBlockInterface): void {
70     if (element.id) {
71         node.setId(element.id);
72     }
73
74     node.setAlignment(extractAlignmentFromElement(element));
75     node.setInset(extractInsetFromElement(element));
76     node.setDirection(extractDirectionFromElement(element));
77 }
78
79 export function commonPropertiesDifferent(nodeA: CommonBlockInterface, nodeB: CommonBlockInterface): boolean {
80     return nodeA.__id !== nodeB.__id ||
81         nodeA.__alignment !== nodeB.__alignment ||
82         nodeA.__inset !== nodeB.__inset ||
83         nodeA.__dir !== nodeB.__dir;
84 }
85
86 export function applyCommonPropertyChanges(prevNode: CommonBlockInterface, currentNode: CommonBlockInterface, element: HTMLElement): void {
87     if (prevNode.__id !== currentNode.__id) {
88         element.setAttribute('id', currentNode.__id);
89     }
90
91     if (prevNode.__alignment !== currentNode.__alignment) {
92         for (const alignment of validAlignments) {
93             element.classList.remove('align-' + alignment);
94         }
95
96         if (currentNode.__alignment) {
97             element.classList.add('align-' + currentNode.__alignment);
98         }
99     }
100
101     if (prevNode.__inset !== currentNode.__inset) {
102         if (currentNode.__inset) {
103             element.style.paddingLeft = `${currentNode.__inset}px`;
104         } else {
105             element.style.removeProperty('paddingLeft');
106         }
107     }
108
109     if (prevNode.__dir !== currentNode.__dir) {
110         if (currentNode.__dir) {
111             element.dir = currentNode.__dir;
112         } else {
113             element.removeAttribute('dir');
114         }
115     }
116 }
117
118 export function updateElementWithCommonBlockProps(element: HTMLElement, node: CommonBlockInterface): void {
119     if (node.__id) {
120         element.setAttribute('id', node.__id);
121     }
122
123     if (node.__alignment) {
124         element.classList.add('align-' + node.__alignment);
125     }
126
127     if (node.__inset) {
128         element.style.paddingLeft = `${node.__inset}px`;
129     }
130
131     if (node.__dir) {
132         element.dir = node.__dir;
133     }
134 }
135
136 export function deserializeCommonBlockNode(serializedNode: SerializedCommonBlockNode, node: CommonBlockInterface): void {
137     node.setId(serializedNode.id);
138     node.setAlignment(serializedNode.alignment);
139     node.setInset(serializedNode.inset);
140     node.setDirection(serializedNode.direction);
141 }
142
143 export interface NodeHasSize {
144     setHeight(height: number): void;
145     setWidth(width: number): void;
146     getHeight(): number;
147     getWidth(): number;
148 }