@baasix/lexical 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +281 -0
- package/dist/DateTimeComponent-4MX2IKXK.css +45 -0
- package/dist/DateTimeComponent-4MX2IKXK.css.map +1 -0
- package/dist/DateTimeComponent-7V5RW3AE.js +2928 -0
- package/dist/DateTimeComponent-7V5RW3AE.js.map +1 -0
- package/dist/EquationComponent-3IW3ONJD.js +159 -0
- package/dist/EquationComponent-3IW3ONJD.js.map +1 -0
- package/dist/EquationComponent-C2C6477P.css +28 -0
- package/dist/EquationComponent-C2C6477P.css.map +1 -0
- package/dist/ExcalidrawComponent-GZYLHNOY.js +267 -0
- package/dist/ExcalidrawComponent-GZYLHNOY.js.map +1 -0
- package/dist/ExcalidrawComponent-U6BD7DUB.css +152 -0
- package/dist/ExcalidrawComponent-U6BD7DUB.css.map +1 -0
- package/dist/ImageComponent-IYUGOT6O.css +73 -0
- package/dist/ImageComponent-IYUGOT6O.css.map +1 -0
- package/dist/ImageComponent-TTUYUG3M.js +429 -0
- package/dist/ImageComponent-TTUYUG3M.js.map +1 -0
- package/dist/PollComponent-CWI5UJF5.css +187 -0
- package/dist/PollComponent-CWI5UJF5.css.map +1 -0
- package/dist/PollComponent-KLJZSW5O.js +199 -0
- package/dist/PollComponent-KLJZSW5O.js.map +1 -0
- package/dist/StickyComponent-MZSTQF47.css +774 -0
- package/dist/StickyComponent-MZSTQF47.css.map +1 -0
- package/dist/StickyComponent-ZED6LNTX.js +233 -0
- package/dist/StickyComponent-ZED6LNTX.js.map +1 -0
- package/dist/chunk-35UCX4ZT.js +168 -0
- package/dist/chunk-35UCX4ZT.js.map +1 -0
- package/dist/chunk-44LUNGL3.js +16608 -0
- package/dist/chunk-44LUNGL3.js.map +1 -0
- package/dist/chunk-BHROZTZU.js +42 -0
- package/dist/chunk-BHROZTZU.js.map +1 -0
- package/dist/chunk-FJ6COZ4H.js +275 -0
- package/dist/chunk-FJ6COZ4H.js.map +1 -0
- package/dist/chunk-GSFNU3JE.js +211 -0
- package/dist/chunk-GSFNU3JE.js.map +1 -0
- package/dist/chunk-I43VQAHW.js +918 -0
- package/dist/chunk-I43VQAHW.js.map +1 -0
- package/dist/chunk-N22RKA5D.js +402 -0
- package/dist/chunk-N22RKA5D.js.map +1 -0
- package/dist/chunk-OIMMC5C3.js +134 -0
- package/dist/chunk-OIMMC5C3.js.map +1 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-PZ5AY32C.js.map +1 -0
- package/dist/chunk-TVF6V3BJ.js +41 -0
- package/dist/chunk-TVF6V3BJ.js.map +1 -0
- package/dist/chunk-XTKVR2JI.js +225 -0
- package/dist/chunk-XTKVR2JI.js.map +1 -0
- package/dist/emoji-list-MUV74SND.js +8 -0
- package/dist/emoji-list-MUV74SND.js.map +1 -0
- package/dist/index.css +4234 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +16050 -0
- package/dist/index.js.map +1 -0
- package/dist/landscape-RJWVK6ET.jpg +0 -0
- package/dist/yellow-flower-R72KOP74.jpg +0 -0
- package/package.json +76 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// src/context/SharedHistoryContext.tsx
|
|
2
|
+
import { createEmptyHistoryState } from "@lexical/react/LexicalHistoryPlugin";
|
|
3
|
+
import { createContext, useContext, useMemo } from "react";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
var Context = createContext({});
|
|
6
|
+
var SharedHistoryContext = ({
|
|
7
|
+
children
|
|
8
|
+
}) => {
|
|
9
|
+
const historyContext = useMemo(
|
|
10
|
+
() => ({ historyState: createEmptyHistoryState() }),
|
|
11
|
+
[]
|
|
12
|
+
);
|
|
13
|
+
return /* @__PURE__ */ jsx(Context.Provider, { value: historyContext, children });
|
|
14
|
+
};
|
|
15
|
+
var useSharedHistoryContext = () => {
|
|
16
|
+
return useContext(Context);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// src/ui/ContentEditable.tsx
|
|
20
|
+
import { ContentEditable } from "@lexical/react/LexicalContentEditable";
|
|
21
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
22
|
+
function LexicalContentEditable({
|
|
23
|
+
className,
|
|
24
|
+
placeholder,
|
|
25
|
+
placeholderClassName
|
|
26
|
+
}) {
|
|
27
|
+
return /* @__PURE__ */ jsx2(
|
|
28
|
+
ContentEditable,
|
|
29
|
+
{
|
|
30
|
+
className: className ?? "ContentEditable__root",
|
|
31
|
+
"aria-placeholder": placeholder,
|
|
32
|
+
placeholder: /* @__PURE__ */ jsx2("div", { className: placeholderClassName ?? "ContentEditable__placeholder", children: placeholder })
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export {
|
|
38
|
+
SharedHistoryContext,
|
|
39
|
+
useSharedHistoryContext,
|
|
40
|
+
LexicalContentEditable
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=chunk-BHROZTZU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/context/SharedHistoryContext.tsx","../src/ui/ContentEditable.tsx"],"sourcesContent":["/**\r\n * Copyright (c) Meta Platforms, Inc. and affiliates.\r\n *\r\n * This source code is licensed under the MIT license found in the\r\n * LICENSE file in the root directory of this source tree.\r\n *\r\n */\r\n\r\nimport type {HistoryState} from '@lexical/react/LexicalHistoryPlugin';\r\nimport type {JSX} from 'react';\r\n\r\nimport {createEmptyHistoryState} from '@lexical/react/LexicalHistoryPlugin';\r\nimport * as React from 'react';\r\nimport {createContext, ReactNode, useContext, useMemo} from 'react';\r\n\r\ntype ContextShape = {\r\n historyState?: HistoryState;\r\n};\r\n\r\nconst Context: React.Context<ContextShape> = createContext({});\r\n\r\nexport const SharedHistoryContext = ({\r\n children,\r\n}: {\r\n children: ReactNode;\r\n}): JSX.Element => {\r\n const historyContext = useMemo(\r\n () => ({historyState: createEmptyHistoryState()}),\r\n [],\r\n );\r\n return <Context.Provider value={historyContext}>{children}</Context.Provider>;\r\n};\r\n\r\nexport const useSharedHistoryContext = (): ContextShape => {\r\n return useContext(Context);\r\n};\r\n","/**\r\n * Copyright (c) Meta Platforms, Inc. and affiliates.\r\n *\r\n * This source code is licensed under the MIT license found in the\r\n * LICENSE file in the root directory of this source tree.\r\n *\r\n */\r\n\r\nimport type {JSX} from 'react';\r\n\r\nimport './ContentEditable.css';\r\n\r\nimport {ContentEditable} from '@lexical/react/LexicalContentEditable';\r\nimport * as React from 'react';\r\n\r\ntype Props = {\r\n className?: string;\r\n placeholderClassName?: string;\r\n placeholder: string;\r\n};\r\n\r\nexport default function LexicalContentEditable({\r\n className,\r\n placeholder,\r\n placeholderClassName,\r\n}: Props): JSX.Element {\r\n return (\r\n <ContentEditable\r\n className={className ?? 'ContentEditable__root'}\r\n aria-placeholder={placeholder}\r\n placeholder={\r\n <div className={placeholderClassName ?? 'ContentEditable__placeholder'}>\r\n {placeholder}\r\n </div>\r\n }\r\n />\r\n );\r\n}\r\n"],"mappings":";AAWA,SAAQ,+BAA8B;AAEtC,SAAQ,eAA0B,YAAY,eAAc;AAiBnD;AAXT,IAAM,UAAuC,cAAc,CAAC,CAAC;AAEtD,IAAM,uBAAuB,CAAC;AAAA,EACnC;AACF,MAEmB;AACjB,QAAM,iBAAiB;AAAA,IACrB,OAAO,EAAC,cAAc,wBAAwB,EAAC;AAAA,IAC/C,CAAC;AAAA,EACH;AACA,SAAO,oBAAC,QAAQ,UAAR,EAAiB,OAAO,gBAAiB,UAAS;AAC5D;AAEO,IAAM,0BAA0B,MAAoB;AACzD,SAAO,WAAW,OAAO;AAC3B;;;ACvBA,SAAQ,uBAAsB;AAmBtB,gBAAAA,YAAA;AAVO,SAAR,uBAAwC;AAAA,EAC7C;AAAA,EACA;AAAA,EACA;AACF,GAAuB;AACrB,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,aAAa;AAAA,MACxB,oBAAkB;AAAA,MAClB,aACE,gBAAAA,KAAC,SAAI,WAAW,wBAAwB,gCACrC,uBACH;AAAA;AAAA,EAEJ;AAEJ;","names":["jsx"]}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
// src/ui/ImageResizer.tsx
|
|
2
|
+
import { calculateZoomLevel } from "@lexical/utils";
|
|
3
|
+
import { useRef } from "react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
function clamp(value, min, max) {
|
|
6
|
+
return Math.min(Math.max(value, min), max);
|
|
7
|
+
}
|
|
8
|
+
var Direction = {
|
|
9
|
+
east: 1 << 0,
|
|
10
|
+
north: 1 << 3,
|
|
11
|
+
south: 1 << 1,
|
|
12
|
+
west: 1 << 2
|
|
13
|
+
};
|
|
14
|
+
function ImageResizer({
|
|
15
|
+
onResizeStart,
|
|
16
|
+
onResizeEnd,
|
|
17
|
+
buttonRef,
|
|
18
|
+
imageRef,
|
|
19
|
+
maxWidth,
|
|
20
|
+
editor,
|
|
21
|
+
showCaption,
|
|
22
|
+
setShowCaption,
|
|
23
|
+
captionsEnabled
|
|
24
|
+
}) {
|
|
25
|
+
const controlWrapperRef = useRef(null);
|
|
26
|
+
const userSelect = useRef({
|
|
27
|
+
priority: "",
|
|
28
|
+
value: "default"
|
|
29
|
+
});
|
|
30
|
+
const positioningRef = useRef({
|
|
31
|
+
currentHeight: 0,
|
|
32
|
+
currentWidth: 0,
|
|
33
|
+
direction: 0,
|
|
34
|
+
isResizing: false,
|
|
35
|
+
ratio: 0,
|
|
36
|
+
startHeight: 0,
|
|
37
|
+
startWidth: 0,
|
|
38
|
+
startX: 0,
|
|
39
|
+
startY: 0
|
|
40
|
+
});
|
|
41
|
+
const editorRootElement = editor.getRootElement();
|
|
42
|
+
const maxWidthContainer = maxWidth ? maxWidth : editorRootElement !== null ? editorRootElement.getBoundingClientRect().width - 20 : 100;
|
|
43
|
+
const maxHeightContainer = editorRootElement !== null ? editorRootElement.getBoundingClientRect().height - 20 : 100;
|
|
44
|
+
const minWidth = 100;
|
|
45
|
+
const minHeight = 100;
|
|
46
|
+
const setStartCursor = (direction) => {
|
|
47
|
+
const ew = direction === Direction.east || direction === Direction.west;
|
|
48
|
+
const ns = direction === Direction.north || direction === Direction.south;
|
|
49
|
+
const nwse = direction & Direction.north && direction & Direction.west || direction & Direction.south && direction & Direction.east;
|
|
50
|
+
const cursorDir = ew ? "ew" : ns ? "ns" : nwse ? "nwse" : "nesw";
|
|
51
|
+
if (editorRootElement !== null) {
|
|
52
|
+
editorRootElement.style.setProperty(
|
|
53
|
+
"cursor",
|
|
54
|
+
`${cursorDir}-resize`,
|
|
55
|
+
"important"
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
if (document.body !== null) {
|
|
59
|
+
document.body.style.setProperty(
|
|
60
|
+
"cursor",
|
|
61
|
+
`${cursorDir}-resize`,
|
|
62
|
+
"important"
|
|
63
|
+
);
|
|
64
|
+
userSelect.current.value = document.body.style.getPropertyValue(
|
|
65
|
+
"-webkit-user-select"
|
|
66
|
+
);
|
|
67
|
+
userSelect.current.priority = document.body.style.getPropertyPriority(
|
|
68
|
+
"-webkit-user-select"
|
|
69
|
+
);
|
|
70
|
+
document.body.style.setProperty(
|
|
71
|
+
"-webkit-user-select",
|
|
72
|
+
`none`,
|
|
73
|
+
"important"
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const setEndCursor = () => {
|
|
78
|
+
if (editorRootElement !== null) {
|
|
79
|
+
editorRootElement.style.setProperty("cursor", "text");
|
|
80
|
+
}
|
|
81
|
+
if (document.body !== null) {
|
|
82
|
+
document.body.style.setProperty("cursor", "default");
|
|
83
|
+
document.body.style.setProperty(
|
|
84
|
+
"-webkit-user-select",
|
|
85
|
+
userSelect.current.value,
|
|
86
|
+
userSelect.current.priority
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
const handlePointerDown = (event, direction) => {
|
|
91
|
+
if (!editor.isEditable()) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const image = imageRef.current;
|
|
95
|
+
const controlWrapper = controlWrapperRef.current;
|
|
96
|
+
if (image !== null && controlWrapper !== null) {
|
|
97
|
+
event.preventDefault();
|
|
98
|
+
const { width, height } = image.getBoundingClientRect();
|
|
99
|
+
const zoom = calculateZoomLevel(image);
|
|
100
|
+
const positioning = positioningRef.current;
|
|
101
|
+
positioning.startWidth = width;
|
|
102
|
+
positioning.startHeight = height;
|
|
103
|
+
positioning.ratio = width / height;
|
|
104
|
+
positioning.currentWidth = width;
|
|
105
|
+
positioning.currentHeight = height;
|
|
106
|
+
positioning.startX = event.clientX / zoom;
|
|
107
|
+
positioning.startY = event.clientY / zoom;
|
|
108
|
+
positioning.isResizing = true;
|
|
109
|
+
positioning.direction = direction;
|
|
110
|
+
setStartCursor(direction);
|
|
111
|
+
onResizeStart();
|
|
112
|
+
controlWrapper.classList.add("image-control-wrapper--resizing");
|
|
113
|
+
image.style.height = `${height}px`;
|
|
114
|
+
image.style.width = `${width}px`;
|
|
115
|
+
document.addEventListener("pointermove", handlePointerMove);
|
|
116
|
+
document.addEventListener("pointerup", handlePointerUp);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
const handlePointerMove = (event) => {
|
|
120
|
+
const image = imageRef.current;
|
|
121
|
+
const positioning = positioningRef.current;
|
|
122
|
+
const isHorizontal = positioning.direction & (Direction.east | Direction.west);
|
|
123
|
+
const isVertical = positioning.direction & (Direction.south | Direction.north);
|
|
124
|
+
if (image !== null && positioning.isResizing) {
|
|
125
|
+
const zoom = calculateZoomLevel(image);
|
|
126
|
+
if (isHorizontal && isVertical) {
|
|
127
|
+
let diff = Math.floor(positioning.startX - event.clientX / zoom);
|
|
128
|
+
diff = positioning.direction & Direction.east ? -diff : diff;
|
|
129
|
+
const width = clamp(
|
|
130
|
+
positioning.startWidth + diff,
|
|
131
|
+
minWidth,
|
|
132
|
+
maxWidthContainer
|
|
133
|
+
);
|
|
134
|
+
const height = width / positioning.ratio;
|
|
135
|
+
image.style.width = `${width}px`;
|
|
136
|
+
image.style.height = `${height}px`;
|
|
137
|
+
positioning.currentHeight = height;
|
|
138
|
+
positioning.currentWidth = width;
|
|
139
|
+
} else if (isVertical) {
|
|
140
|
+
let diff = Math.floor(positioning.startY - event.clientY / zoom);
|
|
141
|
+
diff = positioning.direction & Direction.south ? -diff : diff;
|
|
142
|
+
const height = clamp(
|
|
143
|
+
positioning.startHeight + diff,
|
|
144
|
+
minHeight,
|
|
145
|
+
maxHeightContainer
|
|
146
|
+
);
|
|
147
|
+
image.style.height = `${height}px`;
|
|
148
|
+
positioning.currentHeight = height;
|
|
149
|
+
} else {
|
|
150
|
+
let diff = Math.floor(positioning.startX - event.clientX / zoom);
|
|
151
|
+
diff = positioning.direction & Direction.east ? -diff : diff;
|
|
152
|
+
const width = clamp(
|
|
153
|
+
positioning.startWidth + diff,
|
|
154
|
+
minWidth,
|
|
155
|
+
maxWidthContainer
|
|
156
|
+
);
|
|
157
|
+
image.style.width = `${width}px`;
|
|
158
|
+
positioning.currentWidth = width;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
const handlePointerUp = () => {
|
|
163
|
+
const image = imageRef.current;
|
|
164
|
+
const positioning = positioningRef.current;
|
|
165
|
+
const controlWrapper = controlWrapperRef.current;
|
|
166
|
+
if (image !== null && controlWrapper !== null && positioning.isResizing) {
|
|
167
|
+
const width = positioning.currentWidth;
|
|
168
|
+
const height = positioning.currentHeight;
|
|
169
|
+
positioning.startWidth = 0;
|
|
170
|
+
positioning.startHeight = 0;
|
|
171
|
+
positioning.ratio = 0;
|
|
172
|
+
positioning.startX = 0;
|
|
173
|
+
positioning.startY = 0;
|
|
174
|
+
positioning.currentWidth = 0;
|
|
175
|
+
positioning.currentHeight = 0;
|
|
176
|
+
positioning.isResizing = false;
|
|
177
|
+
controlWrapper.classList.remove("image-control-wrapper--resizing");
|
|
178
|
+
setEndCursor();
|
|
179
|
+
onResizeEnd(width, height);
|
|
180
|
+
document.removeEventListener("pointermove", handlePointerMove);
|
|
181
|
+
document.removeEventListener("pointerup", handlePointerUp);
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
return /* @__PURE__ */ jsxs("div", { ref: controlWrapperRef, children: [
|
|
185
|
+
!showCaption && captionsEnabled && /* @__PURE__ */ jsx(
|
|
186
|
+
"button",
|
|
187
|
+
{
|
|
188
|
+
type: "button",
|
|
189
|
+
className: "image-caption-button",
|
|
190
|
+
ref: buttonRef,
|
|
191
|
+
onClick: () => {
|
|
192
|
+
setShowCaption(!showCaption);
|
|
193
|
+
},
|
|
194
|
+
children: "Add Caption"
|
|
195
|
+
}
|
|
196
|
+
),
|
|
197
|
+
/* @__PURE__ */ jsx(
|
|
198
|
+
"div",
|
|
199
|
+
{
|
|
200
|
+
className: "image-resizer image-resizer-n",
|
|
201
|
+
onPointerDown: (event) => {
|
|
202
|
+
handlePointerDown(event, Direction.north);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
),
|
|
206
|
+
/* @__PURE__ */ jsx(
|
|
207
|
+
"div",
|
|
208
|
+
{
|
|
209
|
+
className: "image-resizer image-resizer-ne",
|
|
210
|
+
onPointerDown: (event) => {
|
|
211
|
+
handlePointerDown(event, Direction.north | Direction.east);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
),
|
|
215
|
+
/* @__PURE__ */ jsx(
|
|
216
|
+
"div",
|
|
217
|
+
{
|
|
218
|
+
className: "image-resizer image-resizer-e",
|
|
219
|
+
onPointerDown: (event) => {
|
|
220
|
+
handlePointerDown(event, Direction.east);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
),
|
|
224
|
+
/* @__PURE__ */ jsx(
|
|
225
|
+
"div",
|
|
226
|
+
{
|
|
227
|
+
className: "image-resizer image-resizer-se",
|
|
228
|
+
onPointerDown: (event) => {
|
|
229
|
+
handlePointerDown(event, Direction.south | Direction.east);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
),
|
|
233
|
+
/* @__PURE__ */ jsx(
|
|
234
|
+
"div",
|
|
235
|
+
{
|
|
236
|
+
className: "image-resizer image-resizer-s",
|
|
237
|
+
onPointerDown: (event) => {
|
|
238
|
+
handlePointerDown(event, Direction.south);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
),
|
|
242
|
+
/* @__PURE__ */ jsx(
|
|
243
|
+
"div",
|
|
244
|
+
{
|
|
245
|
+
className: "image-resizer image-resizer-sw",
|
|
246
|
+
onPointerDown: (event) => {
|
|
247
|
+
handlePointerDown(event, Direction.south | Direction.west);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
),
|
|
251
|
+
/* @__PURE__ */ jsx(
|
|
252
|
+
"div",
|
|
253
|
+
{
|
|
254
|
+
className: "image-resizer image-resizer-w",
|
|
255
|
+
onPointerDown: (event) => {
|
|
256
|
+
handlePointerDown(event, Direction.west);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
),
|
|
260
|
+
/* @__PURE__ */ jsx(
|
|
261
|
+
"div",
|
|
262
|
+
{
|
|
263
|
+
className: "image-resizer image-resizer-nw",
|
|
264
|
+
onPointerDown: (event) => {
|
|
265
|
+
handlePointerDown(event, Direction.north | Direction.west);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
)
|
|
269
|
+
] });
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export {
|
|
273
|
+
ImageResizer
|
|
274
|
+
};
|
|
275
|
+
//# sourceMappingURL=chunk-FJ6COZ4H.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ui/ImageResizer.tsx"],"sourcesContent":["/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n *\n */\n\nimport type {LexicalEditor} from 'lexical';\nimport type {JSX} from 'react';\n\nimport {calculateZoomLevel} from '@lexical/utils';\nimport * as React from 'react';\nimport {useRef} from 'react';\n\nfunction clamp(value: number, min: number, max: number) {\n return Math.min(Math.max(value, min), max);\n}\n\nconst Direction = {\n east: 1 << 0,\n north: 1 << 3,\n south: 1 << 1,\n west: 1 << 2,\n};\n\nexport default function ImageResizer({\n onResizeStart,\n onResizeEnd,\n buttonRef,\n imageRef,\n maxWidth,\n editor,\n showCaption,\n setShowCaption,\n captionsEnabled,\n}: {\n editor: LexicalEditor;\n buttonRef: {current: null | HTMLButtonElement};\n imageRef: {current: null | HTMLElement};\n maxWidth?: number;\n onResizeEnd: (width: 'inherit' | number, height: 'inherit' | number) => void;\n onResizeStart: () => void;\n setShowCaption: (show: boolean) => void;\n showCaption: boolean;\n captionsEnabled: boolean;\n}): JSX.Element {\n const controlWrapperRef = useRef<HTMLDivElement>(null);\n const userSelect = useRef({\n priority: '',\n value: 'default',\n });\n const positioningRef = useRef<{\n currentHeight: 'inherit' | number;\n currentWidth: 'inherit' | number;\n direction: number;\n isResizing: boolean;\n ratio: number;\n startHeight: number;\n startWidth: number;\n startX: number;\n startY: number;\n }>({\n currentHeight: 0,\n currentWidth: 0,\n direction: 0,\n isResizing: false,\n ratio: 0,\n startHeight: 0,\n startWidth: 0,\n startX: 0,\n startY: 0,\n });\n const editorRootElement = editor.getRootElement();\n // Find max width, accounting for editor padding.\n const maxWidthContainer = maxWidth\n ? maxWidth\n : editorRootElement !== null\n ? editorRootElement.getBoundingClientRect().width - 20\n : 100;\n const maxHeightContainer =\n editorRootElement !== null\n ? editorRootElement.getBoundingClientRect().height - 20\n : 100;\n\n const minWidth = 100;\n const minHeight = 100;\n\n const setStartCursor = (direction: number) => {\n const ew = direction === Direction.east || direction === Direction.west;\n const ns = direction === Direction.north || direction === Direction.south;\n const nwse =\n (direction & Direction.north && direction & Direction.west) ||\n (direction & Direction.south && direction & Direction.east);\n\n const cursorDir = ew ? 'ew' : ns ? 'ns' : nwse ? 'nwse' : 'nesw';\n\n if (editorRootElement !== null) {\n editorRootElement.style.setProperty(\n 'cursor',\n `${cursorDir}-resize`,\n 'important',\n );\n }\n if (document.body !== null) {\n document.body.style.setProperty(\n 'cursor',\n `${cursorDir}-resize`,\n 'important',\n );\n userSelect.current.value = document.body.style.getPropertyValue(\n '-webkit-user-select',\n );\n userSelect.current.priority = document.body.style.getPropertyPriority(\n '-webkit-user-select',\n );\n document.body.style.setProperty(\n '-webkit-user-select',\n `none`,\n 'important',\n );\n }\n };\n\n const setEndCursor = () => {\n if (editorRootElement !== null) {\n editorRootElement.style.setProperty('cursor', 'text');\n }\n if (document.body !== null) {\n document.body.style.setProperty('cursor', 'default');\n document.body.style.setProperty(\n '-webkit-user-select',\n userSelect.current.value,\n userSelect.current.priority,\n );\n }\n };\n\n const handlePointerDown = (\n event: React.PointerEvent<HTMLDivElement>,\n direction: number,\n ) => {\n if (!editor.isEditable()) {\n return;\n }\n\n const image = imageRef.current;\n const controlWrapper = controlWrapperRef.current;\n\n if (image !== null && controlWrapper !== null) {\n event.preventDefault();\n const {width, height} = image.getBoundingClientRect();\n const zoom = calculateZoomLevel(image);\n const positioning = positioningRef.current;\n positioning.startWidth = width;\n positioning.startHeight = height;\n positioning.ratio = width / height;\n positioning.currentWidth = width;\n positioning.currentHeight = height;\n positioning.startX = event.clientX / zoom;\n positioning.startY = event.clientY / zoom;\n positioning.isResizing = true;\n positioning.direction = direction;\n\n setStartCursor(direction);\n onResizeStart();\n\n controlWrapper.classList.add('image-control-wrapper--resizing');\n image.style.height = `${height}px`;\n image.style.width = `${width}px`;\n\n document.addEventListener('pointermove', handlePointerMove);\n document.addEventListener('pointerup', handlePointerUp);\n }\n };\n const handlePointerMove = (event: PointerEvent) => {\n const image = imageRef.current;\n const positioning = positioningRef.current;\n\n const isHorizontal =\n positioning.direction & (Direction.east | Direction.west);\n const isVertical =\n positioning.direction & (Direction.south | Direction.north);\n\n if (image !== null && positioning.isResizing) {\n const zoom = calculateZoomLevel(image);\n // Corner cursor\n if (isHorizontal && isVertical) {\n let diff = Math.floor(positioning.startX - event.clientX / zoom);\n diff = positioning.direction & Direction.east ? -diff : diff;\n\n const width = clamp(\n positioning.startWidth + diff,\n minWidth,\n maxWidthContainer,\n );\n\n const height = width / positioning.ratio;\n image.style.width = `${width}px`;\n image.style.height = `${height}px`;\n positioning.currentHeight = height;\n positioning.currentWidth = width;\n } else if (isVertical) {\n let diff = Math.floor(positioning.startY - event.clientY / zoom);\n diff = positioning.direction & Direction.south ? -diff : diff;\n\n const height = clamp(\n positioning.startHeight + diff,\n minHeight,\n maxHeightContainer,\n );\n\n image.style.height = `${height}px`;\n positioning.currentHeight = height;\n } else {\n let diff = Math.floor(positioning.startX - event.clientX / zoom);\n diff = positioning.direction & Direction.east ? -diff : diff;\n\n const width = clamp(\n positioning.startWidth + diff,\n minWidth,\n maxWidthContainer,\n );\n\n image.style.width = `${width}px`;\n positioning.currentWidth = width;\n }\n }\n };\n const handlePointerUp = () => {\n const image = imageRef.current;\n const positioning = positioningRef.current;\n const controlWrapper = controlWrapperRef.current;\n if (image !== null && controlWrapper !== null && positioning.isResizing) {\n const width = positioning.currentWidth;\n const height = positioning.currentHeight;\n positioning.startWidth = 0;\n positioning.startHeight = 0;\n positioning.ratio = 0;\n positioning.startX = 0;\n positioning.startY = 0;\n positioning.currentWidth = 0;\n positioning.currentHeight = 0;\n positioning.isResizing = false;\n\n controlWrapper.classList.remove('image-control-wrapper--resizing');\n\n setEndCursor();\n onResizeEnd(width, height);\n\n document.removeEventListener('pointermove', handlePointerMove);\n document.removeEventListener('pointerup', handlePointerUp);\n }\n };\n return (\n <div ref={controlWrapperRef}>\n {!showCaption && captionsEnabled && (\n <button type=\"button\"\n className=\"image-caption-button\"\n ref={buttonRef}\n onClick={() => {\n setShowCaption(!showCaption);\n }}>\n Add Caption\n </button>\n )}\n <div\n className=\"image-resizer image-resizer-n\"\n onPointerDown={(event) => {\n handlePointerDown(event, Direction.north);\n }}\n />\n <div\n className=\"image-resizer image-resizer-ne\"\n onPointerDown={(event) => {\n handlePointerDown(event, Direction.north | Direction.east);\n }}\n />\n <div\n className=\"image-resizer image-resizer-e\"\n onPointerDown={(event) => {\n handlePointerDown(event, Direction.east);\n }}\n />\n <div\n className=\"image-resizer image-resizer-se\"\n onPointerDown={(event) => {\n handlePointerDown(event, Direction.south | Direction.east);\n }}\n />\n <div\n className=\"image-resizer image-resizer-s\"\n onPointerDown={(event) => {\n handlePointerDown(event, Direction.south);\n }}\n />\n <div\n className=\"image-resizer image-resizer-sw\"\n onPointerDown={(event) => {\n handlePointerDown(event, Direction.south | Direction.west);\n }}\n />\n <div\n className=\"image-resizer image-resizer-w\"\n onPointerDown={(event) => {\n handlePointerDown(event, Direction.west);\n }}\n />\n <div\n className=\"image-resizer image-resizer-nw\"\n onPointerDown={(event) => {\n handlePointerDown(event, Direction.north | Direction.west);\n }}\n />\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
// src/ui/KatexRenderer.tsx
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
var katexPromise = null;
|
|
5
|
+
var katexLoaded = null;
|
|
6
|
+
function getKatex() {
|
|
7
|
+
if (!katexPromise) {
|
|
8
|
+
katexPromise = Promise.all([
|
|
9
|
+
import("katex"),
|
|
10
|
+
// @ts-expect-error -- CSS module import handled by bundler
|
|
11
|
+
import("katex/dist/katex.css")
|
|
12
|
+
]).then(([mod]) => {
|
|
13
|
+
katexLoaded = mod.default;
|
|
14
|
+
return mod.default;
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return katexPromise;
|
|
18
|
+
}
|
|
19
|
+
function getKatexSync() {
|
|
20
|
+
return katexLoaded;
|
|
21
|
+
}
|
|
22
|
+
function KatexRenderer({
|
|
23
|
+
equation,
|
|
24
|
+
inline,
|
|
25
|
+
onDoubleClick
|
|
26
|
+
}) {
|
|
27
|
+
const katexElementRef = useRef(null);
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
let cancelled = false;
|
|
30
|
+
getKatex().then((katex) => {
|
|
31
|
+
if (cancelled) return;
|
|
32
|
+
const katexElement = katexElementRef.current;
|
|
33
|
+
if (katexElement !== null) {
|
|
34
|
+
katex.render(equation, katexElement, {
|
|
35
|
+
displayMode: !inline,
|
|
36
|
+
errorColor: "#cc0000",
|
|
37
|
+
output: "html",
|
|
38
|
+
strict: "warn",
|
|
39
|
+
throwOnError: false,
|
|
40
|
+
trust: false
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
return () => {
|
|
45
|
+
cancelled = true;
|
|
46
|
+
};
|
|
47
|
+
}, [equation, inline]);
|
|
48
|
+
return (
|
|
49
|
+
// We use an empty image tag either side to ensure Android doesn't try and compose from the
|
|
50
|
+
// inner text from Katex. There didn't seem to be any other way of making this work,
|
|
51
|
+
// without having a physical space.
|
|
52
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
53
|
+
/* @__PURE__ */ jsx(
|
|
54
|
+
"img",
|
|
55
|
+
{
|
|
56
|
+
src: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
|
|
57
|
+
width: "0",
|
|
58
|
+
height: "0",
|
|
59
|
+
alt: ""
|
|
60
|
+
}
|
|
61
|
+
),
|
|
62
|
+
/* @__PURE__ */ jsx(
|
|
63
|
+
"span",
|
|
64
|
+
{
|
|
65
|
+
role: "button",
|
|
66
|
+
tabIndex: -1,
|
|
67
|
+
onDoubleClick,
|
|
68
|
+
ref: katexElementRef
|
|
69
|
+
}
|
|
70
|
+
),
|
|
71
|
+
/* @__PURE__ */ jsx(
|
|
72
|
+
"img",
|
|
73
|
+
{
|
|
74
|
+
src: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
|
|
75
|
+
width: "0",
|
|
76
|
+
height: "0",
|
|
77
|
+
alt: ""
|
|
78
|
+
}
|
|
79
|
+
)
|
|
80
|
+
] })
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// src/nodes/EquationNode.tsx
|
|
85
|
+
import { $applyNodeReplacement, DecoratorNode } from "lexical";
|
|
86
|
+
import * as React from "react";
|
|
87
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
88
|
+
var EquationComponent = React.lazy(() => import("./EquationComponent-3IW3ONJD.js"));
|
|
89
|
+
function $convertEquationElement(domNode) {
|
|
90
|
+
let equation = domNode.getAttribute("data-lexical-equation");
|
|
91
|
+
const inline = domNode.getAttribute("data-lexical-inline") === "true";
|
|
92
|
+
equation = atob(equation || "");
|
|
93
|
+
if (equation) {
|
|
94
|
+
const node = $createEquationNode(equation, inline);
|
|
95
|
+
return { node };
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
var EquationNode = class _EquationNode extends DecoratorNode {
|
|
100
|
+
__equation;
|
|
101
|
+
__inline;
|
|
102
|
+
static getType() {
|
|
103
|
+
return "equation";
|
|
104
|
+
}
|
|
105
|
+
static clone(node) {
|
|
106
|
+
return new _EquationNode(node.__equation, node.__inline, node.__key);
|
|
107
|
+
}
|
|
108
|
+
constructor(equation, inline, key) {
|
|
109
|
+
super(key);
|
|
110
|
+
this.__equation = equation;
|
|
111
|
+
this.__inline = inline ?? false;
|
|
112
|
+
}
|
|
113
|
+
static importJSON(serializedNode) {
|
|
114
|
+
return $createEquationNode(
|
|
115
|
+
serializedNode.equation,
|
|
116
|
+
serializedNode.inline
|
|
117
|
+
).updateFromJSON(serializedNode);
|
|
118
|
+
}
|
|
119
|
+
exportJSON() {
|
|
120
|
+
return {
|
|
121
|
+
...super.exportJSON(),
|
|
122
|
+
equation: this.getEquation(),
|
|
123
|
+
inline: this.__inline
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
createDOM(_config) {
|
|
127
|
+
const element = document.createElement(this.__inline ? "span" : "div");
|
|
128
|
+
element.className = "editor-equation";
|
|
129
|
+
return element;
|
|
130
|
+
}
|
|
131
|
+
exportDOM() {
|
|
132
|
+
const element = document.createElement(this.__inline ? "span" : "div");
|
|
133
|
+
const equation = btoa(this.__equation);
|
|
134
|
+
element.setAttribute("data-lexical-equation", equation);
|
|
135
|
+
element.setAttribute("data-lexical-inline", `${this.__inline}`);
|
|
136
|
+
const katex = getKatexSync();
|
|
137
|
+
if (katex) {
|
|
138
|
+
katex.render(this.__equation, element, {
|
|
139
|
+
displayMode: !this.__inline,
|
|
140
|
+
errorColor: "#cc0000",
|
|
141
|
+
output: "html",
|
|
142
|
+
strict: "warn",
|
|
143
|
+
throwOnError: false,
|
|
144
|
+
trust: false
|
|
145
|
+
});
|
|
146
|
+
} else {
|
|
147
|
+
element.textContent = this.__equation;
|
|
148
|
+
}
|
|
149
|
+
return { element };
|
|
150
|
+
}
|
|
151
|
+
static importDOM() {
|
|
152
|
+
return {
|
|
153
|
+
div: (domNode) => {
|
|
154
|
+
if (!domNode.hasAttribute("data-lexical-equation")) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
conversion: $convertEquationElement,
|
|
159
|
+
priority: 2
|
|
160
|
+
};
|
|
161
|
+
},
|
|
162
|
+
span: (domNode) => {
|
|
163
|
+
if (!domNode.hasAttribute("data-lexical-equation")) {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
conversion: $convertEquationElement,
|
|
168
|
+
priority: 1
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
updateDOM(prevNode) {
|
|
174
|
+
return this.__inline !== prevNode.__inline;
|
|
175
|
+
}
|
|
176
|
+
getTextContent() {
|
|
177
|
+
return this.__equation;
|
|
178
|
+
}
|
|
179
|
+
getEquation() {
|
|
180
|
+
return this.__equation;
|
|
181
|
+
}
|
|
182
|
+
setEquation(equation) {
|
|
183
|
+
const writable = this.getWritable();
|
|
184
|
+
writable.__equation = equation;
|
|
185
|
+
}
|
|
186
|
+
decorate() {
|
|
187
|
+
return /* @__PURE__ */ jsx2(
|
|
188
|
+
EquationComponent,
|
|
189
|
+
{
|
|
190
|
+
equation: this.__equation,
|
|
191
|
+
inline: this.__inline,
|
|
192
|
+
nodeKey: this.__key
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
function $createEquationNode(equation = "", inline = false) {
|
|
198
|
+
const equationNode = new EquationNode(equation, inline);
|
|
199
|
+
return $applyNodeReplacement(equationNode);
|
|
200
|
+
}
|
|
201
|
+
function $isEquationNode(node) {
|
|
202
|
+
return node instanceof EquationNode;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export {
|
|
206
|
+
KatexRenderer,
|
|
207
|
+
EquationNode,
|
|
208
|
+
$createEquationNode,
|
|
209
|
+
$isEquationNode
|
|
210
|
+
};
|
|
211
|
+
//# sourceMappingURL=chunk-GSFNU3JE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ui/KatexRenderer.tsx","../src/nodes/EquationNode.tsx"],"sourcesContent":["/**\r\n * Copyright (c) Meta Platforms, Inc. and affiliates.\r\n *\r\n * This source code is licensed under the MIT license found in the\r\n * LICENSE file in the root directory of this source tree.\r\n *\r\n */\r\n\r\nimport type {JSX} from 'react';\r\n\r\nimport * as React from 'react';\r\nimport {useEffect, useRef} from 'react';\r\n\r\nlet katexPromise: Promise<typeof import('katex')['default']> | null = null;\r\nlet katexLoaded: typeof import('katex')['default'] | null = null;\r\n\r\nfunction getKatex(): Promise<typeof import('katex')['default']> {\r\n if (!katexPromise) {\r\n katexPromise = Promise.all([\r\n import('katex'),\r\n // @ts-expect-error -- CSS module import handled by bundler\r\n import('katex/dist/katex.css'),\r\n ]).then(([mod]) => {\r\n katexLoaded = mod.default;\r\n return mod.default;\r\n });\r\n }\r\n return katexPromise;\r\n}\r\n\r\n/**\r\n * Returns the katex module synchronously if it has already been loaded,\r\n * or null if it hasn't been loaded yet. Used by EquationNode.exportDOM()\r\n * which must be synchronous.\r\n */\r\nfunction getKatexSync(): typeof import('katex')['default'] | null {\r\n return katexLoaded;\r\n}\r\n\r\nexport {getKatex, getKatexSync};\r\n\r\nexport default function KatexRenderer({\r\n equation,\r\n inline,\r\n onDoubleClick,\r\n}: Readonly<{\r\n equation: string;\r\n inline: boolean;\r\n onDoubleClick: () => void;\r\n}>): JSX.Element {\r\n const katexElementRef = useRef(null);\r\n\r\n useEffect(() => {\r\n let cancelled = false;\r\n getKatex().then((katex) => {\r\n if (cancelled) return;\r\n const katexElement = katexElementRef.current;\r\n if (katexElement !== null) {\r\n katex.render(equation, katexElement, {\r\n displayMode: !inline,\r\n errorColor: '#cc0000',\r\n output: 'html',\r\n strict: 'warn',\r\n throwOnError: false,\r\n trust: false,\r\n });\r\n }\r\n });\r\n return () => { cancelled = true; };\r\n }, [equation, inline]);\r\n\r\n return (\r\n // We use an empty image tag either side to ensure Android doesn't try and compose from the\r\n // inner text from Katex. There didn't seem to be any other way of making this work,\r\n // without having a physical space.\r\n <>\r\n <img\r\n src=\"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\"\r\n width=\"0\"\r\n height=\"0\"\r\n alt=\"\"\r\n />\r\n <span\r\n role=\"button\"\r\n tabIndex={-1}\r\n onDoubleClick={onDoubleClick}\r\n ref={katexElementRef}\r\n />\r\n <img\r\n src=\"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\"\r\n width=\"0\"\r\n height=\"0\"\r\n alt=\"\"\r\n />\r\n </>\r\n );\r\n}\r\n","/**\r\n * Copyright (c) Meta Platforms, Inc. and affiliates.\r\n *\r\n * This source code is licensed under the MIT license found in the\r\n * LICENSE file in the root directory of this source tree.\r\n *\r\n */\r\n\r\nimport type {\r\n DOMConversionMap,\r\n DOMConversionOutput,\r\n EditorConfig,\r\n LexicalNode,\r\n NodeKey,\r\n SerializedLexicalNode,\r\n Spread,\r\n} from 'lexical';\r\nimport type {JSX} from 'react';\r\n\r\nimport {$applyNodeReplacement, DecoratorNode, DOMExportOutput} from 'lexical';\r\nimport * as React from 'react';\r\n\r\nimport {getKatexSync} from '../ui/KatexRenderer';\r\n\r\nconst EquationComponent = React.lazy(() => import('./EquationComponent'));\r\n\r\nexport type SerializedEquationNode = Spread<\r\n {\r\n equation: string;\r\n inline: boolean;\r\n },\r\n SerializedLexicalNode\r\n>;\r\n\r\nfunction $convertEquationElement(\r\n domNode: HTMLElement,\r\n): null | DOMConversionOutput {\r\n let equation = domNode.getAttribute('data-lexical-equation');\r\n const inline = domNode.getAttribute('data-lexical-inline') === 'true';\r\n // Decode the equation from base64\r\n equation = atob(equation || '');\r\n if (equation) {\r\n const node = $createEquationNode(equation, inline);\r\n return {node};\r\n }\r\n\r\n return null;\r\n}\r\n\r\nexport class EquationNode extends DecoratorNode<JSX.Element> {\r\n __equation: string;\r\n __inline: boolean;\r\n\r\n static getType(): string {\r\n return 'equation';\r\n }\r\n\r\n static clone(node: EquationNode): EquationNode {\r\n return new EquationNode(node.__equation, node.__inline, node.__key);\r\n }\r\n\r\n constructor(equation: string, inline?: boolean, key?: NodeKey) {\r\n super(key);\r\n this.__equation = equation;\r\n this.__inline = inline ?? false;\r\n }\r\n\r\n static importJSON(serializedNode: SerializedEquationNode): EquationNode {\r\n return $createEquationNode(\r\n serializedNode.equation,\r\n serializedNode.inline,\r\n ).updateFromJSON(serializedNode);\r\n }\r\n\r\n exportJSON(): SerializedEquationNode {\r\n return {\r\n ...super.exportJSON(),\r\n equation: this.getEquation(),\r\n inline: this.__inline,\r\n };\r\n }\r\n\r\n createDOM(_config: EditorConfig): HTMLElement {\r\n const element = document.createElement(this.__inline ? 'span' : 'div');\r\n // EquationNodes should implement `user-action:none` in their CSS to avoid issues with deletion on Android.\r\n element.className = 'editor-equation';\r\n return element;\r\n }\r\n\r\n exportDOM(): DOMExportOutput {\r\n const element = document.createElement(this.__inline ? 'span' : 'div');\r\n // Encode the equation as base64 to avoid issues with special characters\r\n const equation = btoa(this.__equation);\r\n element.setAttribute('data-lexical-equation', equation);\r\n element.setAttribute('data-lexical-inline', `${this.__inline}`);\r\n // Use katex synchronously if already loaded (it will be, since\r\n // EquationComponent renders via KatexRenderer which triggers the load).\r\n // Falls back to plain text if katex hasn't loaded yet.\r\n const katex = getKatexSync();\r\n if (katex) {\r\n katex.render(this.__equation, element, {\r\n displayMode: !this.__inline,\r\n errorColor: '#cc0000',\r\n output: 'html',\r\n strict: 'warn',\r\n throwOnError: false,\r\n trust: false,\r\n });\r\n } else {\r\n element.textContent = this.__equation;\r\n }\r\n return {element};\r\n }\r\n\r\n static importDOM(): DOMConversionMap | null {\r\n return {\r\n div: (domNode: HTMLElement) => {\r\n if (!domNode.hasAttribute('data-lexical-equation')) {\r\n return null;\r\n }\r\n return {\r\n conversion: $convertEquationElement,\r\n priority: 2,\r\n };\r\n },\r\n span: (domNode: HTMLElement) => {\r\n if (!domNode.hasAttribute('data-lexical-equation')) {\r\n return null;\r\n }\r\n return {\r\n conversion: $convertEquationElement,\r\n priority: 1,\r\n };\r\n },\r\n };\r\n }\r\n\r\n updateDOM(prevNode: this): boolean {\r\n // If the inline property changes, replace the element\r\n return this.__inline !== prevNode.__inline;\r\n }\r\n\r\n getTextContent(): string {\r\n return this.__equation;\r\n }\r\n\r\n getEquation(): string {\r\n return this.__equation;\r\n }\r\n\r\n setEquation(equation: string): void {\r\n const writable = this.getWritable();\r\n writable.__equation = equation;\r\n }\r\n\r\n decorate(): JSX.Element {\r\n return (\r\n <EquationComponent\r\n equation={this.__equation}\r\n inline={this.__inline}\r\n nodeKey={this.__key}\r\n />\r\n );\r\n }\r\n}\r\n\r\nexport function $createEquationNode(\r\n equation = '',\r\n inline = false,\r\n): EquationNode {\r\n const equationNode = new EquationNode(equation, inline);\r\n return $applyNodeReplacement(equationNode);\r\n}\r\n\r\nexport function $isEquationNode(\r\n node: LexicalNode | null | undefined,\r\n): node is EquationNode {\r\n return node instanceof EquationNode;\r\n}\r\n"],"mappings":"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","names":["jsx"]}
|