@haklex/rich-editor 0.1.0 → 0.2.0
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/dist/AlertQuoteEditNode-C55sxsR3.js +267 -0
- package/dist/KaTeXRenderer-CQQT3BMw.js +215 -0
- package/dist/LinkCardRenderer-CigqFwCv.js +45 -0
- package/dist/MermaidPlugin-BrOr-wQi.js +67 -0
- package/dist/PresentDialogContext-DKNicgia.js +74 -0
- package/dist/RubyRenderer-jOkydJHg.js +15 -0
- package/dist/SubmitShortcutPlugin-D-7XrQfm.js +2186 -0
- package/dist/commands-entry.mjs +54 -74
- package/dist/config-CNiK9v2M.js +1246 -0
- package/dist/grid.css-CJCkLTZc.js +44 -0
- package/dist/index.mjs +121 -180
- package/dist/katex.css-CIOEOXyd.js +145 -0
- package/dist/node-registry-DOYK_WIp.js +669 -0
- package/dist/nodes-entry.mjs +5 -50
- package/dist/normalizeSerializedEditorState-B-1wmGzd.js +78 -0
- package/dist/plugins-entry.mjs +3 -28
- package/dist/renderers-entry.mjs +41 -61
- package/dist/rich-editor.css +2 -1
- package/dist/static-entry.mjs +16 -66
- package/dist/styles-entry.mjs +3 -21
- package/dist/theme-B5B2EOWM.js +1099 -0
- package/package.json +30 -30
- package/dist/AlertQuoteEditNode-sPNf3_7P.js +0 -293
- package/dist/KaTeXRenderer-CQyQzNTJ.js +0 -218
- package/dist/LinkCardRenderer-QmkOlyXb.js +0 -36
- package/dist/MermaidPlugin-DKuGUcCG.js +0 -101
- package/dist/PresentDialogContext-DRroMIoK.js +0 -71
- package/dist/RubyRenderer-CJQmODir.js +0 -14
- package/dist/SubmitShortcutPlugin-D9uKYHda.js +0 -2427
- package/dist/config-Dl3ZkytB.js +0 -1362
- package/dist/grid.css-Md5-Cfx_.js +0 -11
- package/dist/katex.css-Csc-7N7u.js +0 -28
- package/dist/node-registry-CovhHUB6.js +0 -824
- package/dist/normalizeSerializedEditorState-k5G4xSi9.js +0 -85
- package/dist/theme-lEwScxEX.js +0 -1113
|
@@ -0,0 +1,669 @@
|
|
|
1
|
+
import { C as ImageNode, E as FootnoteNode, _ as KaTeXInlineNode, b as KaTeXBlockNode, c as SpoilerNode, d as MermaidNode, i as TagNode, j as _defineProperty, m as MentionNode, t as editorTheme } from "./theme-B5B2EOWM.js";
|
|
2
|
+
import { l as RendererWrapper, s as useFootnoteDefinitions } from "./KaTeXRenderer-CQQT3BMw.js";
|
|
3
|
+
import { l as semanticClassNames, r as clsx, u as sharedStyles } from "./katex.css-CIOEOXyd.js";
|
|
4
|
+
import { C as $isCodeBlockNode, M as normalizeBannerType, N as BannerRenderer, O as $isBannerNode, S as CommentNode, T as CodeBlockRenderer, f as $isGridContainerNode, g as FootnoteSectionNode, i as VideoNode, j as BannerNode, n as builtinNodes, p as GridContainerNode, s as RubyNode, u as LinkCardNode, v as DetailsNode, w as CodeBlockNode } from "./config-CNiK9v2M.js";
|
|
5
|
+
import { n as gridStyles, t as gridClassNames } from "./grid.css-CJCkLTZc.js";
|
|
6
|
+
import { n as AlertQuoteEditNode, r as NESTED_EDITOR_NODES } from "./AlertQuoteEditNode-C55sxsR3.js";
|
|
7
|
+
import { LinkPlugin } from "@lexical/react/LexicalLinkPlugin";
|
|
8
|
+
import { ListPlugin } from "@lexical/react/LexicalListPlugin";
|
|
9
|
+
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
10
|
+
import { createElement, useCallback, useEffect, useState } from "react";
|
|
11
|
+
import { $createNodeSelection, $createParagraphNode, $getNodeByKey, $getRoot, $getSelection, $insertNodes, $isDecoratorNode, $isElementNode, $isNodeSelection, $nodesOfType, $setSelection, createEditor } from "lexical";
|
|
12
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
13
|
+
import { Flag, LayoutGrid, Minus, Plus } from "lucide-react";
|
|
14
|
+
import { ContentEditable } from "@lexical/react/LexicalContentEditable";
|
|
15
|
+
import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary";
|
|
16
|
+
import { LexicalNestedComposer } from "@lexical/react/LexicalNestedComposer";
|
|
17
|
+
import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
|
|
18
|
+
import { useLexicalNodeSelection } from "@lexical/react/useLexicalNodeSelection";
|
|
19
|
+
//#region src/utils/codeBlockSelectionIntent.ts
|
|
20
|
+
var codeBlockCursorIntentMap = /* @__PURE__ */ new Map();
|
|
21
|
+
function setCodeBlockCursorIntent(nodeKey, placement) {
|
|
22
|
+
codeBlockCursorIntentMap.set(nodeKey, placement);
|
|
23
|
+
}
|
|
24
|
+
function consumeCodeBlockCursorIntent(nodeKey) {
|
|
25
|
+
const placement = codeBlockCursorIntentMap.get(nodeKey);
|
|
26
|
+
if (!placement) return null;
|
|
27
|
+
codeBlockCursorIntentMap.delete(nodeKey);
|
|
28
|
+
return placement;
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/components/decorators/BannerEditDecorator.tsx
|
|
32
|
+
function BannerEditDecorator({ nodeKey, bannerType, contentEditor }) {
|
|
33
|
+
const [editor] = useLexicalComposerContext();
|
|
34
|
+
const editable = editor.isEditable();
|
|
35
|
+
const handleTypeChange = useCallback((newType) => {
|
|
36
|
+
editor.update(() => {
|
|
37
|
+
const node = $getNodeByKey(nodeKey);
|
|
38
|
+
if ($isBannerNode(node)) node.setBannerType(newType);
|
|
39
|
+
});
|
|
40
|
+
}, [editor, nodeKey]);
|
|
41
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
42
|
+
className: "rich-banner-inner",
|
|
43
|
+
children: [/* @__PURE__ */ jsx(RendererWrapper, {
|
|
44
|
+
defaultRenderer: BannerRenderer,
|
|
45
|
+
rendererKey: "Banner",
|
|
46
|
+
props: {
|
|
47
|
+
type: bannerType,
|
|
48
|
+
editable,
|
|
49
|
+
onTypeChange: editable ? handleTypeChange : void 0
|
|
50
|
+
}
|
|
51
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
52
|
+
className: "rich-banner-content",
|
|
53
|
+
children: /* @__PURE__ */ jsxs(LexicalNestedComposer, {
|
|
54
|
+
initialEditor: contentEditor,
|
|
55
|
+
children: [
|
|
56
|
+
/* @__PURE__ */ jsx(RichTextPlugin, {
|
|
57
|
+
ErrorBoundary: LexicalErrorBoundary,
|
|
58
|
+
contentEditable: /* @__PURE__ */ jsx(ContentEditable, {
|
|
59
|
+
"aria-placeholder": "",
|
|
60
|
+
className: "rich-banner-content-editable",
|
|
61
|
+
placeholder: /* @__PURE__ */ jsx("span", { style: { display: "none" } }),
|
|
62
|
+
style: { outline: "none" }
|
|
63
|
+
})
|
|
64
|
+
}),
|
|
65
|
+
/* @__PURE__ */ jsx(ListPlugin, {}),
|
|
66
|
+
/* @__PURE__ */ jsx(LinkPlugin, {})
|
|
67
|
+
]
|
|
68
|
+
})
|
|
69
|
+
})]
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
//#endregion
|
|
73
|
+
//#region src/nodes/BannerEditNode.ts
|
|
74
|
+
function createContentEditor() {
|
|
75
|
+
return createEditor({
|
|
76
|
+
namespace: "BannerContent",
|
|
77
|
+
nodes: NESTED_EDITOR_NODES,
|
|
78
|
+
theme: editorTheme,
|
|
79
|
+
onError: (error) => {
|
|
80
|
+
console.error("[BannerContent]", error);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
var BannerEditNode = class BannerEditNode extends BannerNode {
|
|
85
|
+
static clone(node) {
|
|
86
|
+
const cloned = new BannerEditNode(node.__bannerType, node.__contentState, node.__key);
|
|
87
|
+
cloned.__contentEditor = node.__contentEditor;
|
|
88
|
+
return cloned;
|
|
89
|
+
}
|
|
90
|
+
constructor(bannerType, contentState, key) {
|
|
91
|
+
super(bannerType, contentState, key);
|
|
92
|
+
_defineProperty(this, "__contentEditor", void 0);
|
|
93
|
+
this.__contentEditor = createContentEditor();
|
|
94
|
+
if (contentState) {
|
|
95
|
+
const editorState = this.__contentEditor.parseEditorState(contentState);
|
|
96
|
+
this.__contentEditor.setEditorState(editorState);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
getContentEditor() {
|
|
100
|
+
return this.__contentEditor;
|
|
101
|
+
}
|
|
102
|
+
static importJSON(serializedNode) {
|
|
103
|
+
const legacy = serializedNode;
|
|
104
|
+
const bannerType = normalizeBannerType(serializedNode.bannerType);
|
|
105
|
+
if (serializedNode.content) return new BannerEditNode(bannerType, serializedNode.content);
|
|
106
|
+
if (legacy.children) return new BannerEditNode(bannerType, { root: {
|
|
107
|
+
children: legacy.children,
|
|
108
|
+
direction: null,
|
|
109
|
+
format: "",
|
|
110
|
+
indent: 0,
|
|
111
|
+
type: "root",
|
|
112
|
+
version: 1
|
|
113
|
+
} });
|
|
114
|
+
return new BannerEditNode(bannerType);
|
|
115
|
+
}
|
|
116
|
+
exportJSON() {
|
|
117
|
+
return {
|
|
118
|
+
...super.exportJSON(),
|
|
119
|
+
type: "banner",
|
|
120
|
+
bannerType: this.__bannerType,
|
|
121
|
+
content: this.__contentEditor.getEditorState().toJSON(),
|
|
122
|
+
version: 1
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
decorate(_editor, _config) {
|
|
126
|
+
return createElement(BannerEditDecorator, {
|
|
127
|
+
nodeKey: this.__key,
|
|
128
|
+
bannerType: this.__bannerType,
|
|
129
|
+
contentEditor: this.__contentEditor
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
_defineProperty(BannerEditNode, "commandItems", [{
|
|
134
|
+
title: "Banner",
|
|
135
|
+
icon: createElement(Flag, { size: 20 }),
|
|
136
|
+
description: "Highlighted banner block",
|
|
137
|
+
keywords: [
|
|
138
|
+
"banner",
|
|
139
|
+
"notice",
|
|
140
|
+
"announcement"
|
|
141
|
+
],
|
|
142
|
+
section: "ADVANCED",
|
|
143
|
+
placement: ["slash", "toolbar"],
|
|
144
|
+
group: "insert",
|
|
145
|
+
onSelect: (editor) => {
|
|
146
|
+
editor.update(() => {
|
|
147
|
+
$insertNodes([$createBannerEditNode("note")]);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}]);
|
|
151
|
+
function $createBannerEditNode(bannerType, contentState) {
|
|
152
|
+
return new BannerEditNode(bannerType, contentState);
|
|
153
|
+
}
|
|
154
|
+
//#endregion
|
|
155
|
+
//#region src/components/decorators/CodeBlockEditDecorator.tsx
|
|
156
|
+
function CodeBlockEditDecorator({ nodeKey, code, language }) {
|
|
157
|
+
const [editor] = useLexicalComposerContext();
|
|
158
|
+
const [isSelected] = useLexicalNodeSelection(nodeKey);
|
|
159
|
+
const [shouldFocusEditor, setShouldFocusEditor] = useState(false);
|
|
160
|
+
const [cursorPlacement, setCursorPlacement] = useState("start");
|
|
161
|
+
const editable = editor.isEditable();
|
|
162
|
+
useEffect(() => {
|
|
163
|
+
if (!editable || !isSelected) {
|
|
164
|
+
setShouldFocusEditor(false);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
let nextShouldFocus = false;
|
|
168
|
+
let nextCursorPlacement = "start";
|
|
169
|
+
editor.getEditorState().read(() => {
|
|
170
|
+
const selection = $getSelection();
|
|
171
|
+
const selectedNodes = $isNodeSelection(selection) ? selection.getNodes() : null;
|
|
172
|
+
nextShouldFocus = selectedNodes !== null && selectedNodes.length === 1 && selectedNodes[0]?.getKey() === nodeKey;
|
|
173
|
+
if (nextShouldFocus) nextCursorPlacement = consumeCodeBlockCursorIntent(nodeKey) ?? "start";
|
|
174
|
+
});
|
|
175
|
+
setShouldFocusEditor(nextShouldFocus);
|
|
176
|
+
if (nextShouldFocus) setCursorPlacement(nextCursorPlacement);
|
|
177
|
+
}, [
|
|
178
|
+
editable,
|
|
179
|
+
editor,
|
|
180
|
+
isSelected,
|
|
181
|
+
nodeKey
|
|
182
|
+
]);
|
|
183
|
+
const handleCodeChange = useCallback((newCode) => {
|
|
184
|
+
editor.update(() => {
|
|
185
|
+
const node = $getNodeByKey(nodeKey);
|
|
186
|
+
if ($isCodeBlockNode(node)) node.setCode(newCode);
|
|
187
|
+
});
|
|
188
|
+
}, [editor, nodeKey]);
|
|
189
|
+
const handleLanguageChange = useCallback((newLanguage) => {
|
|
190
|
+
editor.update(() => {
|
|
191
|
+
const node = $getNodeByKey(nodeKey);
|
|
192
|
+
if ($isCodeBlockNode(node)) node.setLanguage(newLanguage);
|
|
193
|
+
});
|
|
194
|
+
}, [editor, nodeKey]);
|
|
195
|
+
const handleDelete = useCallback(() => {
|
|
196
|
+
editor.getRootElement()?.focus({ preventScroll: true });
|
|
197
|
+
editor.update(() => {
|
|
198
|
+
const node = $getNodeByKey(nodeKey);
|
|
199
|
+
if (!node) return;
|
|
200
|
+
const prev = node.getPreviousSibling();
|
|
201
|
+
const next = node.getNextSibling();
|
|
202
|
+
const parent = node.getParent();
|
|
203
|
+
node.remove();
|
|
204
|
+
if (prev) {
|
|
205
|
+
if ($isElementNode(prev)) prev.selectEnd();
|
|
206
|
+
else {
|
|
207
|
+
if ($isDecoratorNode(prev) && prev.getType() === "code-block") setCodeBlockCursorIntent(prev.getKey(), "end");
|
|
208
|
+
const selection = $createNodeSelection();
|
|
209
|
+
selection.add(prev.getKey());
|
|
210
|
+
$setSelection(selection);
|
|
211
|
+
}
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
if (next) {
|
|
215
|
+
if ($isElementNode(next)) next.selectStart();
|
|
216
|
+
else {
|
|
217
|
+
if ($isDecoratorNode(next) && next.getType() === "code-block") setCodeBlockCursorIntent(next.getKey(), "start");
|
|
218
|
+
const selection = $createNodeSelection();
|
|
219
|
+
selection.add(next.getKey());
|
|
220
|
+
$setSelection(selection);
|
|
221
|
+
}
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
if (parent) {
|
|
225
|
+
const p = $createParagraphNode();
|
|
226
|
+
parent.append(p);
|
|
227
|
+
p.selectStart();
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
}, [editor, nodeKey]);
|
|
231
|
+
const handleExitBlock = useCallback((direction) => {
|
|
232
|
+
editor.getRootElement()?.focus({ preventScroll: true });
|
|
233
|
+
editor.update(() => {
|
|
234
|
+
const node = $getNodeByKey(nodeKey);
|
|
235
|
+
if (!node) return;
|
|
236
|
+
if (direction === "before") {
|
|
237
|
+
const prev = node.getPreviousSibling();
|
|
238
|
+
if (!prev) return;
|
|
239
|
+
if ($isElementNode(prev)) prev.selectEnd();
|
|
240
|
+
else {
|
|
241
|
+
if ($isDecoratorNode(prev) && prev.getType() === "code-block") setCodeBlockCursorIntent(prev.getKey(), "end");
|
|
242
|
+
const selection = $createNodeSelection();
|
|
243
|
+
selection.add(prev.getKey());
|
|
244
|
+
$setSelection(selection);
|
|
245
|
+
}
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
let next = node.getNextSibling();
|
|
249
|
+
if (!next) {
|
|
250
|
+
const p = $createParagraphNode();
|
|
251
|
+
node.insertAfter(p);
|
|
252
|
+
next = p;
|
|
253
|
+
}
|
|
254
|
+
if ($isElementNode(next)) next.selectStart();
|
|
255
|
+
else {
|
|
256
|
+
if ($isDecoratorNode(next) && next.getType() === "code-block") setCodeBlockCursorIntent(next.getKey(), "start");
|
|
257
|
+
const selection = $createNodeSelection();
|
|
258
|
+
selection.add(next.getKey());
|
|
259
|
+
$setSelection(selection);
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
}, [editor, nodeKey]);
|
|
263
|
+
return /* @__PURE__ */ jsx(RendererWrapper, {
|
|
264
|
+
defaultRenderer: CodeBlockRenderer,
|
|
265
|
+
rendererKey: "CodeBlock",
|
|
266
|
+
props: {
|
|
267
|
+
code,
|
|
268
|
+
language,
|
|
269
|
+
editable,
|
|
270
|
+
onCodeChange: editable ? handleCodeChange : void 0,
|
|
271
|
+
onLanguageChange: editable ? handleLanguageChange : void 0,
|
|
272
|
+
onDelete: editable ? handleDelete : void 0,
|
|
273
|
+
onExitBlock: editable ? handleExitBlock : void 0,
|
|
274
|
+
selected: editable ? shouldFocusEditor : false,
|
|
275
|
+
cursorPlacement: editable ? cursorPlacement : "start"
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
//#endregion
|
|
280
|
+
//#region src/nodes/CodeBlockEditNode.ts
|
|
281
|
+
var _CodeBlockEditNode;
|
|
282
|
+
var CodeBlockEditNode = class CodeBlockEditNode extends CodeBlockNode {
|
|
283
|
+
static clone(node) {
|
|
284
|
+
return new CodeBlockEditNode(node.__code, node.__language, node.__key);
|
|
285
|
+
}
|
|
286
|
+
static importJSON(serializedNode) {
|
|
287
|
+
return new CodeBlockEditNode(serializedNode.code, serializedNode.language);
|
|
288
|
+
}
|
|
289
|
+
decorate(_editor, _config) {
|
|
290
|
+
return createElement(CodeBlockEditDecorator, {
|
|
291
|
+
nodeKey: this.__key,
|
|
292
|
+
code: this.__code,
|
|
293
|
+
language: this.__language
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
_CodeBlockEditNode = CodeBlockEditNode;
|
|
298
|
+
_defineProperty(CodeBlockEditNode, "commandItems", CodeBlockNode.commandItems.map((item) => ({
|
|
299
|
+
...item,
|
|
300
|
+
onSelect: (editor) => {
|
|
301
|
+
editor.update(() => {
|
|
302
|
+
$insertNodes([new _CodeBlockEditNode("", "text")]);
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
})));
|
|
306
|
+
function $createCodeBlockEditNode(code, language) {
|
|
307
|
+
return new CodeBlockEditNode(code, language);
|
|
308
|
+
}
|
|
309
|
+
//#endregion
|
|
310
|
+
//#region src/components/renderers/FootnoteSectionEditRenderer.tsx
|
|
311
|
+
function FootnoteSectionEditRenderer({ definitions }) {
|
|
312
|
+
const [editor] = useLexicalComposerContext();
|
|
313
|
+
const { displayNumberMap } = useFootnoteDefinitions();
|
|
314
|
+
const sortedEntries = Object.entries(definitions).sort(([a], [b]) => (displayNumberMap[a] ?? 0) - (displayNumberMap[b] ?? 0));
|
|
315
|
+
const handleContentChange = useCallback((identifier, newContent) => {
|
|
316
|
+
editor.update(() => {
|
|
317
|
+
const sectionNodes = $nodesOfType(FootnoteSectionNode);
|
|
318
|
+
if (sectionNodes.length > 0) sectionNodes[0].setDefinition(identifier, newContent);
|
|
319
|
+
});
|
|
320
|
+
}, [editor]);
|
|
321
|
+
const handleRemove = useCallback((identifier) => {
|
|
322
|
+
editor.update(() => {
|
|
323
|
+
const sectionNodes = $nodesOfType(FootnoteSectionNode);
|
|
324
|
+
if (sectionNodes.length > 0) sectionNodes[0].removeDefinition(identifier);
|
|
325
|
+
});
|
|
326
|
+
}, [editor]);
|
|
327
|
+
if (sortedEntries.length === 0) return null;
|
|
328
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
329
|
+
role: "doc-endnotes",
|
|
330
|
+
className: clsx("rich-footnote-section-content", "rich-footnote-section-edit", semanticClassNames.footnoteSection, sharedStyles.footnoteSection),
|
|
331
|
+
children: [/* @__PURE__ */ jsx("hr", { className: clsx(semanticClassNames.footnoteSectionDivider, sharedStyles.footnoteSectionDivider) }), /* @__PURE__ */ jsx("ol", {
|
|
332
|
+
className: clsx(semanticClassNames.footnoteSectionList, sharedStyles.footnoteSectionList),
|
|
333
|
+
children: sortedEntries.map(([identifier, content]) => {
|
|
334
|
+
const displayNum = displayNumberMap[identifier] ?? identifier;
|
|
335
|
+
return /* @__PURE__ */ jsxs("li", {
|
|
336
|
+
id: `footnote-${identifier}`,
|
|
337
|
+
className: clsx(semanticClassNames.footnoteSectionItem, sharedStyles.footnoteSectionItem, semanticClassNames.footnoteSectionItemEdit, sharedStyles.footnoteSectionItemEdit),
|
|
338
|
+
children: [
|
|
339
|
+
/* @__PURE__ */ jsxs("span", {
|
|
340
|
+
className: clsx(semanticClassNames.footnoteSectionItemNum, sharedStyles.footnoteSectionItemNum),
|
|
341
|
+
children: [displayNum, "."]
|
|
342
|
+
}),
|
|
343
|
+
/* @__PURE__ */ jsx("input", {
|
|
344
|
+
placeholder: `Footnote content for [^${identifier}]`,
|
|
345
|
+
type: "text",
|
|
346
|
+
value: content,
|
|
347
|
+
className: clsx(semanticClassNames.footnoteSectionItemInput, sharedStyles.footnoteSectionItemInput),
|
|
348
|
+
onChange: (e) => handleContentChange(identifier, e.target.value)
|
|
349
|
+
}),
|
|
350
|
+
/* @__PURE__ */ jsx("button", {
|
|
351
|
+
"aria-label": `Remove footnote ${identifier}`,
|
|
352
|
+
type: "button",
|
|
353
|
+
className: clsx(semanticClassNames.footnoteSectionItemRemove, sharedStyles.footnoteSectionItemRemove),
|
|
354
|
+
onClick: () => handleRemove(identifier),
|
|
355
|
+
children: "×"
|
|
356
|
+
})
|
|
357
|
+
]
|
|
358
|
+
}, identifier);
|
|
359
|
+
})
|
|
360
|
+
})]
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
//#endregion
|
|
364
|
+
//#region src/nodes/FootnoteSectionEditNode.ts
|
|
365
|
+
var FootnoteSectionEditNode = class FootnoteSectionEditNode extends FootnoteSectionNode {
|
|
366
|
+
static getType() {
|
|
367
|
+
return "footnote-section";
|
|
368
|
+
}
|
|
369
|
+
static clone(node) {
|
|
370
|
+
return new FootnoteSectionEditNode({ ...node.__definitions }, node.__key);
|
|
371
|
+
}
|
|
372
|
+
static importJSON(serializedNode) {
|
|
373
|
+
return new FootnoteSectionEditNode(serializedNode.definitions);
|
|
374
|
+
}
|
|
375
|
+
decorate(_editor, _config) {
|
|
376
|
+
return createElement(FootnoteSectionEditRenderer, {
|
|
377
|
+
definitions: this.__definitions,
|
|
378
|
+
nodeKey: this.__key
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
//#endregion
|
|
383
|
+
//#region src/components/decorators/GridEditDecorator.tsx
|
|
384
|
+
var COL_OPTIONS = [
|
|
385
|
+
1,
|
|
386
|
+
2,
|
|
387
|
+
3,
|
|
388
|
+
4
|
|
389
|
+
];
|
|
390
|
+
function GridEditDecorator({ nodeKey, cols: initialCols, gap, cellEditors }) {
|
|
391
|
+
const [editor] = useLexicalComposerContext();
|
|
392
|
+
const [currentCols, setCurrentCols] = useState(initialCols);
|
|
393
|
+
useEffect(() => {
|
|
394
|
+
return editor.registerUpdateListener(({ editorState }) => {
|
|
395
|
+
editorState.read(() => {
|
|
396
|
+
const node = $getNodeByKey(nodeKey);
|
|
397
|
+
if ($isGridContainerNode(node)) setCurrentCols(node.getCols());
|
|
398
|
+
});
|
|
399
|
+
});
|
|
400
|
+
}, [editor, nodeKey]);
|
|
401
|
+
const handleSetCols = useCallback((cols) => {
|
|
402
|
+
editor.update(() => {
|
|
403
|
+
const node = $getNodeByKey(nodeKey);
|
|
404
|
+
if ($isGridContainerNode(node)) node.setCols(cols);
|
|
405
|
+
});
|
|
406
|
+
}, [editor, nodeKey]);
|
|
407
|
+
const handleAddRow = useCallback(() => {
|
|
408
|
+
editor.update(() => {
|
|
409
|
+
const node = $getNodeByKey(nodeKey);
|
|
410
|
+
if ($isGridContainerNode(node)) node.addCells(node.getCols());
|
|
411
|
+
});
|
|
412
|
+
}, [editor, nodeKey]);
|
|
413
|
+
const handleRemoveRow = useCallback(() => {
|
|
414
|
+
editor.update(() => {
|
|
415
|
+
const node = $getNodeByKey(nodeKey);
|
|
416
|
+
if (!$isGridEditNode(node)) return;
|
|
417
|
+
const cols = node.getCols();
|
|
418
|
+
const editors = node.getCellEditors();
|
|
419
|
+
if (editors.length <= cols) return;
|
|
420
|
+
if (!editors.slice(-cols).every((cellEditor) => cellEditor.getEditorState().read(() => $getRoot().getTextContentSize() === 0))) return;
|
|
421
|
+
node.removeCells(cols);
|
|
422
|
+
});
|
|
423
|
+
}, [editor, nodeKey]);
|
|
424
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
|
|
425
|
+
className: clsx(gridClassNames.toolbar, gridStyles.toolbar),
|
|
426
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
427
|
+
children: [
|
|
428
|
+
/* @__PURE__ */ jsx("span", {
|
|
429
|
+
className: clsx(gridClassNames.toolbarIcon, gridStyles.toolbarIcon),
|
|
430
|
+
children: /* @__PURE__ */ jsx(LayoutGrid, { size: 14 })
|
|
431
|
+
}),
|
|
432
|
+
COL_OPTIONS.map((n) => /* @__PURE__ */ jsx("button", {
|
|
433
|
+
"aria-label": `${n} columns`,
|
|
434
|
+
type: "button",
|
|
435
|
+
className: clsx(gridClassNames.colButton, gridStyles.colButton, n === currentCols && gridClassNames.colButtonActive, n === currentCols && gridStyles.colButtonActive),
|
|
436
|
+
onClick: () => handleSetCols(n),
|
|
437
|
+
children: n
|
|
438
|
+
}, n)),
|
|
439
|
+
/* @__PURE__ */ jsx("div", { className: clsx(gridClassNames.toolbarDivider, gridStyles.toolbarDivider) }),
|
|
440
|
+
/* @__PURE__ */ jsx("button", {
|
|
441
|
+
"aria-label": "Add row",
|
|
442
|
+
className: clsx(gridClassNames.actionButton, gridStyles.actionButton),
|
|
443
|
+
type: "button",
|
|
444
|
+
onClick: handleAddRow,
|
|
445
|
+
children: /* @__PURE__ */ jsx(Plus, { size: 14 })
|
|
446
|
+
}),
|
|
447
|
+
/* @__PURE__ */ jsx("button", {
|
|
448
|
+
"aria-label": "Remove row",
|
|
449
|
+
className: clsx(gridClassNames.actionButton, gridStyles.actionButton),
|
|
450
|
+
type: "button",
|
|
451
|
+
onClick: handleRemoveRow,
|
|
452
|
+
children: /* @__PURE__ */ jsx(Minus, { size: 14 })
|
|
453
|
+
})
|
|
454
|
+
]
|
|
455
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
456
|
+
className: clsx(gridClassNames.inner, gridStyles.inner),
|
|
457
|
+
style: {
|
|
458
|
+
gridTemplateColumns: `repeat(${currentCols}, 1fr)`,
|
|
459
|
+
gap
|
|
460
|
+
},
|
|
461
|
+
children: cellEditors.map((cellEditor, i) => /* @__PURE__ */ jsx("div", {
|
|
462
|
+
className: clsx(gridClassNames.cell, gridStyles.cell),
|
|
463
|
+
children: /* @__PURE__ */ jsxs(LexicalNestedComposer, {
|
|
464
|
+
initialEditor: cellEditor,
|
|
465
|
+
children: [
|
|
466
|
+
/* @__PURE__ */ jsx(RichTextPlugin, {
|
|
467
|
+
ErrorBoundary: LexicalErrorBoundary,
|
|
468
|
+
contentEditable: /* @__PURE__ */ jsx(ContentEditable, {
|
|
469
|
+
"aria-placeholder": "",
|
|
470
|
+
className: clsx(gridClassNames.cellEditable, gridStyles.cellEditable),
|
|
471
|
+
placeholder: /* @__PURE__ */ jsx("span", { style: { display: "none" } })
|
|
472
|
+
})
|
|
473
|
+
}),
|
|
474
|
+
/* @__PURE__ */ jsx(ListPlugin, {}),
|
|
475
|
+
/* @__PURE__ */ jsx(LinkPlugin, {})
|
|
476
|
+
]
|
|
477
|
+
})
|
|
478
|
+
}, i))
|
|
479
|
+
})] });
|
|
480
|
+
}
|
|
481
|
+
//#endregion
|
|
482
|
+
//#region src/nodes/GridEditNode.ts
|
|
483
|
+
function createCellEditor() {
|
|
484
|
+
return createEditor({
|
|
485
|
+
namespace: "GridCell",
|
|
486
|
+
nodes: NESTED_EDITOR_NODES,
|
|
487
|
+
theme: editorTheme,
|
|
488
|
+
onError: (error) => {
|
|
489
|
+
console.error("[GridCell]", error);
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
var GridEditNode = class GridEditNode extends GridContainerNode {
|
|
494
|
+
static clone(node) {
|
|
495
|
+
const cloned = new GridEditNode(node.__cols, node.__gap, node.__cellStates, node.__key);
|
|
496
|
+
cloned.__cellEditors = [...node.__cellEditors];
|
|
497
|
+
return cloned;
|
|
498
|
+
}
|
|
499
|
+
constructor(cols = 2, gap, cellStates, key) {
|
|
500
|
+
super(cols, gap, cellStates, key);
|
|
501
|
+
_defineProperty(this, "__cellEditors", void 0);
|
|
502
|
+
this.__cellEditors = this.__cellStates.map((state) => {
|
|
503
|
+
const editor = createCellEditor();
|
|
504
|
+
const editorState = editor.parseEditorState(state);
|
|
505
|
+
editor.setEditorState(editorState);
|
|
506
|
+
return editor;
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
getCellEditors() {
|
|
510
|
+
return this.getLatest().__cellEditors;
|
|
511
|
+
}
|
|
512
|
+
setCols(cols) {
|
|
513
|
+
const writable = this.getWritable();
|
|
514
|
+
const prev = writable.__cellEditors.length;
|
|
515
|
+
writable.__cols = cols;
|
|
516
|
+
if (cols > prev) for (let i = prev; i < cols; i++) {
|
|
517
|
+
const editor = createCellEditor();
|
|
518
|
+
writable.__cellEditors.push(editor);
|
|
519
|
+
writable.__cellStates.push({ root: {
|
|
520
|
+
children: [{
|
|
521
|
+
type: "paragraph",
|
|
522
|
+
children: [],
|
|
523
|
+
direction: null,
|
|
524
|
+
format: "",
|
|
525
|
+
indent: 0,
|
|
526
|
+
textFormat: 0,
|
|
527
|
+
textStyle: "",
|
|
528
|
+
version: 1
|
|
529
|
+
}],
|
|
530
|
+
direction: null,
|
|
531
|
+
format: "",
|
|
532
|
+
indent: 0,
|
|
533
|
+
type: "root",
|
|
534
|
+
version: 1
|
|
535
|
+
} });
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
addCells(count) {
|
|
539
|
+
const writable = this.getWritable();
|
|
540
|
+
for (let i = 0; i < count; i++) {
|
|
541
|
+
const editor = createCellEditor();
|
|
542
|
+
writable.__cellEditors.push(editor);
|
|
543
|
+
writable.__cellStates.push({ root: {
|
|
544
|
+
children: [{
|
|
545
|
+
type: "paragraph",
|
|
546
|
+
children: [],
|
|
547
|
+
direction: null,
|
|
548
|
+
format: "",
|
|
549
|
+
indent: 0,
|
|
550
|
+
textFormat: 0,
|
|
551
|
+
textStyle: "",
|
|
552
|
+
version: 1
|
|
553
|
+
}],
|
|
554
|
+
direction: null,
|
|
555
|
+
format: "",
|
|
556
|
+
indent: 0,
|
|
557
|
+
type: "root",
|
|
558
|
+
version: 1
|
|
559
|
+
} });
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
removeCells(count) {
|
|
563
|
+
const writable = this.getWritable();
|
|
564
|
+
const editors = writable.__cellEditors;
|
|
565
|
+
const states = writable.__cellStates;
|
|
566
|
+
const toRemove = Math.min(count, editors.length);
|
|
567
|
+
for (let i = 0; i < toRemove; i++) {
|
|
568
|
+
const editor = editors.at(-1);
|
|
569
|
+
if (!editor) break;
|
|
570
|
+
if (!editor.getEditorState().read(() => {
|
|
571
|
+
return $getRoot().getTextContentSize() === 0;
|
|
572
|
+
})) break;
|
|
573
|
+
editors.pop();
|
|
574
|
+
states.pop();
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
static importJSON(serializedNode) {
|
|
578
|
+
const legacy = serializedNode;
|
|
579
|
+
const cols = legacy.cols || 2;
|
|
580
|
+
const rawGap = legacy.gap;
|
|
581
|
+
const gap = typeof rawGap === "number" ? `${rawGap}px` : rawGap;
|
|
582
|
+
if (legacy.cells && legacy.cells.length > 0) return new GridEditNode(cols, gap, legacy.cells);
|
|
583
|
+
if (legacy.children) return new GridEditNode(cols, gap, legacy.children.map((child) => {
|
|
584
|
+
return { root: {
|
|
585
|
+
children: [child],
|
|
586
|
+
direction: null,
|
|
587
|
+
format: "",
|
|
588
|
+
indent: 0,
|
|
589
|
+
type: "root",
|
|
590
|
+
version: 1
|
|
591
|
+
} };
|
|
592
|
+
}));
|
|
593
|
+
return new GridEditNode(cols, gap);
|
|
594
|
+
}
|
|
595
|
+
exportJSON() {
|
|
596
|
+
return {
|
|
597
|
+
...super.exportJSON(),
|
|
598
|
+
type: "grid-container",
|
|
599
|
+
cols: this.__cols,
|
|
600
|
+
gap: this.__gap,
|
|
601
|
+
cells: this.__cellEditors.map((editor) => editor.getEditorState().toJSON()),
|
|
602
|
+
version: 1
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
decorate(_editor, _config) {
|
|
606
|
+
return createElement(GridEditDecorator, {
|
|
607
|
+
nodeKey: this.__key,
|
|
608
|
+
cols: this.__cols,
|
|
609
|
+
gap: this.__gap,
|
|
610
|
+
cellEditors: this.__cellEditors
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
};
|
|
614
|
+
_defineProperty(GridEditNode, "slashMenuItems", [{
|
|
615
|
+
title: "Grid",
|
|
616
|
+
icon: createElement(LayoutGrid, { size: 20 }),
|
|
617
|
+
description: "Grid layout container",
|
|
618
|
+
keywords: [
|
|
619
|
+
"grid",
|
|
620
|
+
"columns",
|
|
621
|
+
"layout"
|
|
622
|
+
],
|
|
623
|
+
section: "LAYOUT",
|
|
624
|
+
onSelect: (editor) => {
|
|
625
|
+
editor.update(() => {
|
|
626
|
+
$insertNodes([$createGridEditNode(2)]);
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
}]);
|
|
630
|
+
function $createGridEditNode(cols = 2, gap) {
|
|
631
|
+
return new GridEditNode(cols, gap);
|
|
632
|
+
}
|
|
633
|
+
function $isGridEditNode(node) {
|
|
634
|
+
return node instanceof GridEditNode;
|
|
635
|
+
}
|
|
636
|
+
//#endregion
|
|
637
|
+
//#region src/config-edit.ts
|
|
638
|
+
var customEditNodes = [
|
|
639
|
+
SpoilerNode,
|
|
640
|
+
MentionNode,
|
|
641
|
+
KaTeXInlineNode,
|
|
642
|
+
KaTeXBlockNode,
|
|
643
|
+
ImageNode,
|
|
644
|
+
AlertQuoteEditNode,
|
|
645
|
+
CodeBlockEditNode,
|
|
646
|
+
FootnoteNode,
|
|
647
|
+
FootnoteSectionEditNode,
|
|
648
|
+
VideoNode,
|
|
649
|
+
LinkCardNode,
|
|
650
|
+
CommentNode,
|
|
651
|
+
DetailsNode,
|
|
652
|
+
GridEditNode,
|
|
653
|
+
BannerEditNode,
|
|
654
|
+
MermaidNode,
|
|
655
|
+
RubyNode,
|
|
656
|
+
TagNode
|
|
657
|
+
];
|
|
658
|
+
var allEditNodes = [...builtinNodes, ...customEditNodes];
|
|
659
|
+
//#endregion
|
|
660
|
+
//#region src/node-registry.ts
|
|
661
|
+
var _resolvedEditNodes = null;
|
|
662
|
+
function setResolvedEditNodes(nodes) {
|
|
663
|
+
_resolvedEditNodes = nodes;
|
|
664
|
+
}
|
|
665
|
+
function getResolvedEditNodes() {
|
|
666
|
+
return _resolvedEditNodes ?? allEditNodes;
|
|
667
|
+
}
|
|
668
|
+
//#endregion
|
|
669
|
+
export { FootnoteSectionEditNode as a, setCodeBlockCursorIntent as c, customEditNodes as i, setResolvedEditNodes as n, $createCodeBlockEditNode as o, allEditNodes as r, $createBannerEditNode as s, getResolvedEditNodes as t };
|