@haklex/rich-editor 0.1.1 → 0.3.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/RubyRenderer-jOkydJHg.js +15 -0
- package/dist/SubmitShortcutPlugin-DhyVFzoj.js +2186 -0
- package/dist/commands-entry.mjs +54 -74
- package/dist/components/decorators/PollEditDecorator.d.ts +13 -0
- package/dist/components/decorators/PollEditDecorator.d.ts.map +1 -0
- package/dist/components/renderers/PollRenderer.d.ts +3 -0
- package/dist/components/renderers/PollRenderer.d.ts.map +1 -0
- package/dist/config-B5BuLljq.js +1633 -0
- package/dist/config-edit.d.ts.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/context/PollDataContext.d.ts +11 -0
- package/dist/context/PollDataContext.d.ts.map +1 -0
- package/dist/extractPolls-DO31LNrp.js +116 -0
- package/dist/grid.css-CJCkLTZc.js +44 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +121 -180
- package/dist/katex.css-CIOEOXyd.js +145 -0
- package/dist/node-registry-Dz5OTkh4.js +946 -0
- package/dist/nodes/PollEditNode.d.ts +14 -0
- package/dist/nodes/PollEditNode.d.ts.map +1 -0
- package/dist/nodes/PollNode.d.ts +52 -0
- package/dist/nodes/PollNode.d.ts.map +1 -0
- package/dist/nodes-entry.d.ts +3 -0
- package/dist/nodes-entry.d.ts.map +1 -1
- 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.d.ts +5 -0
- package/dist/static-entry.d.ts.map +1 -1
- package/dist/static-entry.mjs +16 -66
- package/dist/styles/index.d.ts +2 -0
- package/dist/styles/index.d.ts.map +1 -1
- package/dist/styles/poll-edit.css.d.ts +35 -0
- package/dist/styles/poll-edit.css.d.ts.map +1 -0
- package/dist/styles/poll.css.d.ts +43 -0
- package/dist/styles/poll.css.d.ts.map +1 -0
- package/dist/styles-entry.mjs +3 -21
- package/dist/theme-B5B2EOWM.js +1099 -0
- package/dist/types/poll.d.ts +36 -0
- package/dist/types/poll.d.ts.map +1 -0
- package/dist/types/renderer-config.d.ts +3 -0
- package/dist/types/renderer-config.d.ts.map +1 -1
- package/dist/utils/extractPolls.d.ts +4 -0
- package/dist/utils/extractPolls.d.ts.map +1 -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,1633 @@
|
|
|
1
|
+
import { A as AlertQuoteNode, C as ImageNode, E as FootnoteNode, F as useNestedContentRenderer, M as extractTextContent, _ as KaTeXInlineNode, b as KaTeXBlockNode, c as SpoilerNode, d as MermaidNode, i as TagNode, j as _defineProperty, m as MentionNode } from "./theme-B5B2EOWM.js";
|
|
2
|
+
import { l as RendererWrapper, m as useVariant, s as useFootnoteDefinitions, u as createRendererDecoration } from "./KaTeXRenderer-CQQT3BMw.js";
|
|
3
|
+
import { l as semanticClassNames, r as clsx, u as sharedStyles } from "./katex.css-CIOEOXyd.js";
|
|
4
|
+
import { i as detailsStyles, n as gridStyles, r as detailsClassNames, t as gridClassNames } from "./grid.css-CJCkLTZc.js";
|
|
5
|
+
import { t as LinkCardRenderer } from "./LinkCardRenderer-CigqFwCv.js";
|
|
6
|
+
import { AutoLinkNode, LinkNode } from "@lexical/link";
|
|
7
|
+
import { createContext, createElement, use, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
8
|
+
import { HeadingNode, QuoteNode } from "@lexical/rich-text";
|
|
9
|
+
import { $createTextNode, $insertNodes, DecoratorNode, ElementNode, TextNode } from "lexical";
|
|
10
|
+
import { HorizontalRuleNode } from "@lexical/extension";
|
|
11
|
+
import { CodeNode } from "@lexical/code-core";
|
|
12
|
+
import { ListItemNode, ListNode } from "@lexical/list";
|
|
13
|
+
import { TableCellNode, TableNode, TableRowNode } from "@lexical/table";
|
|
14
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
15
|
+
import { ChevronRight, Code, Link, MessageSquareQuote, Video } from "lucide-react";
|
|
16
|
+
import { customAlphabet } from "nanoid";
|
|
17
|
+
//#region src/components/renderers/BannerRenderer.tsx
|
|
18
|
+
var BannerRenderer = ({ type }) => {
|
|
19
|
+
return /* @__PURE__ */ jsx("span", { className: `rich-banner-icon rich-banner-icon-${type}` });
|
|
20
|
+
};
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/components/renderers/BannerStaticDecorator.tsx
|
|
23
|
+
function BannerStaticDecorator({ bannerType, contentState }) {
|
|
24
|
+
const renderContent = useNestedContentRenderer();
|
|
25
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
26
|
+
className: "rich-banner-inner",
|
|
27
|
+
children: [/* @__PURE__ */ jsx(RendererWrapper, {
|
|
28
|
+
defaultRenderer: BannerRenderer,
|
|
29
|
+
props: { type: bannerType },
|
|
30
|
+
rendererKey: "Banner"
|
|
31
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
32
|
+
className: "rich-banner-content",
|
|
33
|
+
children: renderContent(contentState)
|
|
34
|
+
})]
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region src/nodes/BannerNode.ts
|
|
39
|
+
var LEGACY_TYPE_MAP = {
|
|
40
|
+
info: "note",
|
|
41
|
+
success: "tip",
|
|
42
|
+
error: "caution"
|
|
43
|
+
};
|
|
44
|
+
function normalizeBannerType(type) {
|
|
45
|
+
if (type in LEGACY_TYPE_MAP) return LEGACY_TYPE_MAP[type];
|
|
46
|
+
return type || "note";
|
|
47
|
+
}
|
|
48
|
+
var BANNER_TYPES = [
|
|
49
|
+
"note",
|
|
50
|
+
"tip",
|
|
51
|
+
"important",
|
|
52
|
+
"warning",
|
|
53
|
+
"caution"
|
|
54
|
+
];
|
|
55
|
+
var BANNER_LABELS = {
|
|
56
|
+
note: "Note",
|
|
57
|
+
tip: "Tip",
|
|
58
|
+
important: "Important",
|
|
59
|
+
warning: "Warning",
|
|
60
|
+
caution: "Caution"
|
|
61
|
+
};
|
|
62
|
+
var BannerNode = class BannerNode extends DecoratorNode {
|
|
63
|
+
static getType() {
|
|
64
|
+
return "banner";
|
|
65
|
+
}
|
|
66
|
+
static clone(node) {
|
|
67
|
+
return new BannerNode(node.__bannerType, node.__contentState, node.__key);
|
|
68
|
+
}
|
|
69
|
+
constructor(bannerType, contentState, key) {
|
|
70
|
+
super(key);
|
|
71
|
+
_defineProperty(this, "__bannerType", void 0);
|
|
72
|
+
_defineProperty(this, "__contentState", void 0);
|
|
73
|
+
this.__bannerType = bannerType;
|
|
74
|
+
this.__contentState = contentState || { root: {
|
|
75
|
+
children: [{
|
|
76
|
+
type: "paragraph",
|
|
77
|
+
children: [],
|
|
78
|
+
direction: null,
|
|
79
|
+
format: "",
|
|
80
|
+
indent: 0,
|
|
81
|
+
textFormat: 0,
|
|
82
|
+
textStyle: "",
|
|
83
|
+
version: 1
|
|
84
|
+
}],
|
|
85
|
+
direction: null,
|
|
86
|
+
format: "",
|
|
87
|
+
indent: 0,
|
|
88
|
+
type: "root",
|
|
89
|
+
version: 1
|
|
90
|
+
} };
|
|
91
|
+
}
|
|
92
|
+
createDOM(_config) {
|
|
93
|
+
const div = document.createElement("div");
|
|
94
|
+
div.className = `rich-banner rich-banner-${this.__bannerType}`;
|
|
95
|
+
return div;
|
|
96
|
+
}
|
|
97
|
+
updateDOM(prevNode, dom) {
|
|
98
|
+
if (prevNode.__bannerType !== this.__bannerType) dom.className = `rich-banner rich-banner-${this.__bannerType}`;
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
isInline() {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
getBannerType() {
|
|
105
|
+
return this.__bannerType;
|
|
106
|
+
}
|
|
107
|
+
setBannerType(bannerType) {
|
|
108
|
+
const writable = this.getWritable();
|
|
109
|
+
writable.__bannerType = bannerType;
|
|
110
|
+
}
|
|
111
|
+
getContentState() {
|
|
112
|
+
return this.getLatest().__contentState;
|
|
113
|
+
}
|
|
114
|
+
setContentState(state) {
|
|
115
|
+
const writable = this.getWritable();
|
|
116
|
+
writable.__contentState = state;
|
|
117
|
+
}
|
|
118
|
+
getTextContent() {
|
|
119
|
+
return extractTextContent(this.__contentState);
|
|
120
|
+
}
|
|
121
|
+
static importJSON(serializedNode) {
|
|
122
|
+
const legacy = serializedNode;
|
|
123
|
+
const bannerType = normalizeBannerType(serializedNode.bannerType);
|
|
124
|
+
if (serializedNode.content) return new BannerNode(bannerType, serializedNode.content);
|
|
125
|
+
if (legacy.children) return new BannerNode(bannerType, { root: {
|
|
126
|
+
children: legacy.children,
|
|
127
|
+
direction: null,
|
|
128
|
+
format: "",
|
|
129
|
+
indent: 0,
|
|
130
|
+
type: "root",
|
|
131
|
+
version: 1
|
|
132
|
+
} });
|
|
133
|
+
return new BannerNode(bannerType);
|
|
134
|
+
}
|
|
135
|
+
exportJSON() {
|
|
136
|
+
return {
|
|
137
|
+
...super.exportJSON(),
|
|
138
|
+
type: "banner",
|
|
139
|
+
bannerType: this.__bannerType,
|
|
140
|
+
content: this.__contentState,
|
|
141
|
+
version: 1
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
decorate(_editor, _config) {
|
|
145
|
+
return createElement(BannerStaticDecorator, {
|
|
146
|
+
bannerType: this.__bannerType,
|
|
147
|
+
contentState: this.__contentState
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
function $isBannerNode(node) {
|
|
152
|
+
return node instanceof BannerNode;
|
|
153
|
+
}
|
|
154
|
+
//#endregion
|
|
155
|
+
//#region src/context/ColorSchemeContext.tsx
|
|
156
|
+
var ColorSchemeContext = createContext("light");
|
|
157
|
+
function ColorSchemeProvider({ colorScheme, children }) {
|
|
158
|
+
return /* @__PURE__ */ jsx(ColorSchemeContext.Provider, {
|
|
159
|
+
value: colorScheme,
|
|
160
|
+
children
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
function useColorScheme() {
|
|
164
|
+
return use(ColorSchemeContext);
|
|
165
|
+
}
|
|
166
|
+
//#endregion
|
|
167
|
+
//#region src/utils/shiki.ts
|
|
168
|
+
var codeToHtmlFn = null;
|
|
169
|
+
var shikiLoadPromise = null;
|
|
170
|
+
function loadCodeToHtml() {
|
|
171
|
+
if (codeToHtmlFn) return Promise.resolve(codeToHtmlFn);
|
|
172
|
+
if (!shikiLoadPromise) shikiLoadPromise = import("shiki/bundle/web").then((mod) => {
|
|
173
|
+
codeToHtmlFn = mod.codeToHtml;
|
|
174
|
+
return mod.codeToHtml;
|
|
175
|
+
}).catch((err) => {
|
|
176
|
+
shikiLoadPromise = null;
|
|
177
|
+
throw err;
|
|
178
|
+
});
|
|
179
|
+
return shikiLoadPromise;
|
|
180
|
+
}
|
|
181
|
+
//#endregion
|
|
182
|
+
//#region src/components/renderers/CodeBlockRenderer.tsx
|
|
183
|
+
function CodeBlockRenderer({ code, language, showLineNumbers: showLineNumbersProp }) {
|
|
184
|
+
const variant = useVariant();
|
|
185
|
+
const showLineNumbers = showLineNumbersProp ?? variant !== "comment";
|
|
186
|
+
const shikiTheme = useColorScheme() === "dark" ? "github-dark" : "github-light";
|
|
187
|
+
const [highlightedHtml, setHighlightedHtml] = useState(null);
|
|
188
|
+
const [copied, setCopied] = useState(false);
|
|
189
|
+
const copyTimerRef = useRef(void 0);
|
|
190
|
+
useEffect(() => {
|
|
191
|
+
let cancelled = false;
|
|
192
|
+
setHighlightedHtml(null);
|
|
193
|
+
loadCodeToHtml().then((toHtml) => toHtml(code, {
|
|
194
|
+
lang: language,
|
|
195
|
+
theme: shikiTheme
|
|
196
|
+
})).then((html) => {
|
|
197
|
+
if (!cancelled) setHighlightedHtml(html);
|
|
198
|
+
}).catch(() => {
|
|
199
|
+
if (!cancelled) setHighlightedHtml(null);
|
|
200
|
+
});
|
|
201
|
+
return () => {
|
|
202
|
+
cancelled = true;
|
|
203
|
+
};
|
|
204
|
+
}, [
|
|
205
|
+
code,
|
|
206
|
+
language,
|
|
207
|
+
shikiTheme
|
|
208
|
+
]);
|
|
209
|
+
useEffect(() => {
|
|
210
|
+
return () => clearTimeout(copyTimerRef.current);
|
|
211
|
+
}, []);
|
|
212
|
+
const handleCopy = useCallback(() => {
|
|
213
|
+
navigator.clipboard.writeText(code).then(() => {
|
|
214
|
+
setCopied(true);
|
|
215
|
+
clearTimeout(copyTimerRef.current);
|
|
216
|
+
copyTimerRef.current = setTimeout(() => setCopied(false), 2e3);
|
|
217
|
+
}).catch(() => {});
|
|
218
|
+
}, [code]);
|
|
219
|
+
const header = language ? /* @__PURE__ */ jsxs("div", {
|
|
220
|
+
className: "rich-code-block-header",
|
|
221
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
222
|
+
className: "rich-code-block-lang",
|
|
223
|
+
children: language
|
|
224
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
225
|
+
"aria-label": copied ? "Copied to clipboard" : "Copy code",
|
|
226
|
+
className: "rich-code-block-copy",
|
|
227
|
+
type: "button",
|
|
228
|
+
onClick: handleCopy,
|
|
229
|
+
children: copied ? "Copied" : "Copy"
|
|
230
|
+
})]
|
|
231
|
+
}) : null;
|
|
232
|
+
const wrapperClass = showLineNumbers ? "rich-code-block rich-code-block-numbered" : "rich-code-block";
|
|
233
|
+
if (highlightedHtml) return /* @__PURE__ */ jsxs("div", {
|
|
234
|
+
className: wrapperClass,
|
|
235
|
+
children: [header, /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: highlightedHtml } })]
|
|
236
|
+
});
|
|
237
|
+
const lines = code.split("\n");
|
|
238
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
239
|
+
className: wrapperClass,
|
|
240
|
+
children: [header, /* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { children: lines.map((line, i) => /* @__PURE__ */ jsxs("span", {
|
|
241
|
+
className: "line",
|
|
242
|
+
children: [line, i < lines.length - 1 ? "\n" : ""]
|
|
243
|
+
}, i)) }) })]
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
//#endregion
|
|
247
|
+
//#region src/nodes/CodeBlockNode.ts
|
|
248
|
+
var CodeBlockNode = class CodeBlockNode extends DecoratorNode {
|
|
249
|
+
static getType() {
|
|
250
|
+
return "code-block";
|
|
251
|
+
}
|
|
252
|
+
static clone(node) {
|
|
253
|
+
return new CodeBlockNode(node.__code, node.__language, node.__key);
|
|
254
|
+
}
|
|
255
|
+
constructor(code, language, key) {
|
|
256
|
+
super(key);
|
|
257
|
+
_defineProperty(this, "__code", void 0);
|
|
258
|
+
_defineProperty(this, "__language", void 0);
|
|
259
|
+
this.__code = code;
|
|
260
|
+
this.__language = language;
|
|
261
|
+
}
|
|
262
|
+
createDOM(_config) {
|
|
263
|
+
const div = document.createElement("div");
|
|
264
|
+
div.className = "rich-code-block-wrapper";
|
|
265
|
+
return div;
|
|
266
|
+
}
|
|
267
|
+
updateDOM() {
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
isInline() {
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
isKeyboardSelectable() {
|
|
274
|
+
return true;
|
|
275
|
+
}
|
|
276
|
+
static importJSON(serializedNode) {
|
|
277
|
+
return $createCodeBlockNode(serializedNode.code, serializedNode.language);
|
|
278
|
+
}
|
|
279
|
+
exportJSON() {
|
|
280
|
+
return {
|
|
281
|
+
...super.exportJSON(),
|
|
282
|
+
type: "code-block",
|
|
283
|
+
code: this.__code,
|
|
284
|
+
language: this.__language,
|
|
285
|
+
version: 1
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
getCode() {
|
|
289
|
+
return this.__code;
|
|
290
|
+
}
|
|
291
|
+
setCode(code) {
|
|
292
|
+
const writable = this.getWritable();
|
|
293
|
+
writable.__code = code;
|
|
294
|
+
}
|
|
295
|
+
getLanguage() {
|
|
296
|
+
return this.__language;
|
|
297
|
+
}
|
|
298
|
+
setLanguage(language) {
|
|
299
|
+
const writable = this.getWritable();
|
|
300
|
+
writable.__language = language;
|
|
301
|
+
}
|
|
302
|
+
decorate(_editor, _config) {
|
|
303
|
+
return createRendererDecoration("CodeBlock", CodeBlockRenderer, {
|
|
304
|
+
code: this.__code,
|
|
305
|
+
language: this.__language
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
_defineProperty(CodeBlockNode, "commandItems", [{
|
|
310
|
+
title: "Code Block",
|
|
311
|
+
icon: createElement(Code, { size: 20 }),
|
|
312
|
+
description: "Syntax-highlighted code",
|
|
313
|
+
keywords: [
|
|
314
|
+
"code",
|
|
315
|
+
"snippet",
|
|
316
|
+
"codeblock"
|
|
317
|
+
],
|
|
318
|
+
section: "MEDIA",
|
|
319
|
+
placement: ["slash", "toolbar"],
|
|
320
|
+
group: "insert",
|
|
321
|
+
onSelect: (editor) => {
|
|
322
|
+
editor.update(() => {
|
|
323
|
+
$insertNodes([$createCodeBlockNode("", "text")]);
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
}]);
|
|
327
|
+
function $createCodeBlockNode(code, language) {
|
|
328
|
+
return new CodeBlockNode(code, language);
|
|
329
|
+
}
|
|
330
|
+
function $isCodeBlockNode(node) {
|
|
331
|
+
return node instanceof CodeBlockNode;
|
|
332
|
+
}
|
|
333
|
+
//#endregion
|
|
334
|
+
//#region src/nodes/CommentNode.ts
|
|
335
|
+
var DEFAULT_COMMENT_TEXT = "comment";
|
|
336
|
+
var CommentNode = class CommentNode extends TextNode {
|
|
337
|
+
static getType() {
|
|
338
|
+
return "comment";
|
|
339
|
+
}
|
|
340
|
+
static clone(node) {
|
|
341
|
+
return new CommentNode(node.__text, node.__key);
|
|
342
|
+
}
|
|
343
|
+
static importDOM() {
|
|
344
|
+
return {
|
|
345
|
+
"#comment": () => ({
|
|
346
|
+
conversion: (domNode) => {
|
|
347
|
+
if (!(domNode instanceof Comment)) return null;
|
|
348
|
+
return { node: $createCommentNode(domNode.data) };
|
|
349
|
+
},
|
|
350
|
+
priority: 4
|
|
351
|
+
}),
|
|
352
|
+
"span": () => ({
|
|
353
|
+
conversion: (domNode) => {
|
|
354
|
+
if (!(domNode instanceof HTMLElement)) return null;
|
|
355
|
+
if (!domNode.classList.contains(semanticClassNames.comment)) return null;
|
|
356
|
+
return { node: $createCommentNode(domNode.dataset.comment ?? domNode.textContent ?? "") };
|
|
357
|
+
},
|
|
358
|
+
priority: 2
|
|
359
|
+
})
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
constructor(text, key) {
|
|
363
|
+
super(text, key);
|
|
364
|
+
}
|
|
365
|
+
createDOM(config) {
|
|
366
|
+
const element = super.createDOM(config);
|
|
367
|
+
element.classList.add(semanticClassNames.comment, sharedStyles.comment);
|
|
368
|
+
element.dataset.comment = this.__text;
|
|
369
|
+
return element;
|
|
370
|
+
}
|
|
371
|
+
updateDOM(prevNode, dom, config) {
|
|
372
|
+
const updated = super.updateDOM(prevNode, dom, config);
|
|
373
|
+
dom.classList.add(semanticClassNames.comment, sharedStyles.comment);
|
|
374
|
+
if (prevNode.__text !== this.__text) dom.dataset.comment = this.__text;
|
|
375
|
+
return updated;
|
|
376
|
+
}
|
|
377
|
+
exportDOM(_editor) {
|
|
378
|
+
return { element: document.createComment(this.getTextContent()) };
|
|
379
|
+
}
|
|
380
|
+
static importJSON(serializedNode) {
|
|
381
|
+
const node = $createCommentNode(serializedNode.text ?? "");
|
|
382
|
+
node.setFormat(serializedNode.format ?? 0);
|
|
383
|
+
node.setDetail(serializedNode.detail ?? 0);
|
|
384
|
+
node.setMode(serializedNode.mode ?? "normal");
|
|
385
|
+
node.setStyle(serializedNode.style ?? "");
|
|
386
|
+
return node;
|
|
387
|
+
}
|
|
388
|
+
exportJSON() {
|
|
389
|
+
return {
|
|
390
|
+
...super.exportJSON(),
|
|
391
|
+
type: "comment",
|
|
392
|
+
version: 1
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
_defineProperty(CommentNode, "commandItems", [{
|
|
397
|
+
title: "HTML Comment",
|
|
398
|
+
icon: createElement(MessageSquareQuote, { size: 20 }),
|
|
399
|
+
description: "Insert an HTML comment node",
|
|
400
|
+
keywords: [
|
|
401
|
+
"comment",
|
|
402
|
+
"html",
|
|
403
|
+
"annotation",
|
|
404
|
+
"hidden",
|
|
405
|
+
"<!-- -->"
|
|
406
|
+
],
|
|
407
|
+
section: "INLINE",
|
|
408
|
+
placement: ["slash", "toolbar"],
|
|
409
|
+
group: "insert",
|
|
410
|
+
onSelect: (editor, _queryString) => {
|
|
411
|
+
editor.update(() => {
|
|
412
|
+
const node = $createCommentNode(DEFAULT_COMMENT_TEXT);
|
|
413
|
+
$insertNodes([node]);
|
|
414
|
+
node.select(0, node.getTextContentSize());
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
}]);
|
|
418
|
+
function $createCommentNode(text) {
|
|
419
|
+
return new CommentNode(text);
|
|
420
|
+
}
|
|
421
|
+
function $createCommentPlaceholderNode() {
|
|
422
|
+
return $createCommentNode(DEFAULT_COMMENT_TEXT);
|
|
423
|
+
}
|
|
424
|
+
function $isCommentNode(node) {
|
|
425
|
+
return node instanceof CommentNode;
|
|
426
|
+
}
|
|
427
|
+
//#endregion
|
|
428
|
+
//#region src/utils/lucide-dom.ts
|
|
429
|
+
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
430
|
+
var DEFAULT_ATTRS = {
|
|
431
|
+
xmlns: SVG_NS,
|
|
432
|
+
width: "24",
|
|
433
|
+
height: "24",
|
|
434
|
+
viewBox: "0 0 24 24",
|
|
435
|
+
fill: "none",
|
|
436
|
+
stroke: "currentColor",
|
|
437
|
+
"stroke-width": "2",
|
|
438
|
+
"stroke-linecap": "round",
|
|
439
|
+
"stroke-linejoin": "round"
|
|
440
|
+
};
|
|
441
|
+
function createLucideSvg(iconNode, attrs = {}) {
|
|
442
|
+
const svg = document.createElementNS(SVG_NS, "svg");
|
|
443
|
+
const merged = {
|
|
444
|
+
...DEFAULT_ATTRS,
|
|
445
|
+
...attrs
|
|
446
|
+
};
|
|
447
|
+
for (const [k, v] of Object.entries(merged)) svg.setAttribute(k, v);
|
|
448
|
+
for (const [tag, elAttrs] of iconNode) {
|
|
449
|
+
const el = document.createElementNS(SVG_NS, tag);
|
|
450
|
+
for (const [k, v] of Object.entries(elAttrs)) {
|
|
451
|
+
if (k === "key") continue;
|
|
452
|
+
el.setAttribute(k, v);
|
|
453
|
+
}
|
|
454
|
+
svg.append(el);
|
|
455
|
+
}
|
|
456
|
+
return svg;
|
|
457
|
+
}
|
|
458
|
+
//#endregion
|
|
459
|
+
//#region src/nodes/DetailsNode.ts
|
|
460
|
+
var ChevronRightIconNode = [["path", { d: "M8 6L12 10L8 14" }]];
|
|
461
|
+
var DetailsNode = class DetailsNode extends ElementNode {
|
|
462
|
+
static getType() {
|
|
463
|
+
return "details";
|
|
464
|
+
}
|
|
465
|
+
static clone(node) {
|
|
466
|
+
return new DetailsNode(node.__summary, node.__open, node.__key);
|
|
467
|
+
}
|
|
468
|
+
constructor(summary, open = false, key) {
|
|
469
|
+
super(key);
|
|
470
|
+
_defineProperty(this, "__summary", void 0);
|
|
471
|
+
_defineProperty(this, "__open", void 0);
|
|
472
|
+
this.__summary = summary;
|
|
473
|
+
this.__open = open;
|
|
474
|
+
}
|
|
475
|
+
createDOM(_config) {
|
|
476
|
+
const details = document.createElement("details");
|
|
477
|
+
details.className = `${detailsClassNames.details} ${detailsStyles.details}`;
|
|
478
|
+
if (this.__open) details.open = true;
|
|
479
|
+
const summary = document.createElement("summary");
|
|
480
|
+
summary.className = `${detailsClassNames.summary} ${detailsStyles.summary}`;
|
|
481
|
+
const chevron = document.createElement("span");
|
|
482
|
+
chevron.className = `${detailsClassNames.chevron} ${detailsStyles.chevron}`;
|
|
483
|
+
chevron.setAttribute("aria-hidden", "true");
|
|
484
|
+
chevron.append(createLucideSvg(ChevronRightIconNode, {
|
|
485
|
+
"width": "20",
|
|
486
|
+
"height": "20",
|
|
487
|
+
"viewBox": "0 0 20 20",
|
|
488
|
+
"stroke-width": "1.5"
|
|
489
|
+
}));
|
|
490
|
+
summary.append(chevron);
|
|
491
|
+
const label = document.createElement("span");
|
|
492
|
+
label.className = `${detailsClassNames.summaryText} ${detailsStyles.summaryText}`;
|
|
493
|
+
label.textContent = this.__summary;
|
|
494
|
+
summary.append(label);
|
|
495
|
+
const content = document.createElement("div");
|
|
496
|
+
content.className = `${detailsClassNames.content} ${detailsStyles.content}`;
|
|
497
|
+
details.append(summary, content);
|
|
498
|
+
return details;
|
|
499
|
+
}
|
|
500
|
+
updateDOM(prevNode, dom) {
|
|
501
|
+
const details = dom;
|
|
502
|
+
if (prevNode.__open !== this.__open) details.open = this.__open;
|
|
503
|
+
if (prevNode.__summary !== this.__summary) {
|
|
504
|
+
const label = dom.querySelector(`.${detailsClassNames.summaryText}`);
|
|
505
|
+
if (label) label.textContent = this.__summary;
|
|
506
|
+
}
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
509
|
+
static importJSON(serializedNode) {
|
|
510
|
+
return $createDetailsNode(serializedNode.summary, serializedNode.open);
|
|
511
|
+
}
|
|
512
|
+
exportJSON() {
|
|
513
|
+
return {
|
|
514
|
+
...super.exportJSON(),
|
|
515
|
+
type: "details",
|
|
516
|
+
summary: this.__summary,
|
|
517
|
+
open: this.__open,
|
|
518
|
+
version: 1
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
getSummary() {
|
|
522
|
+
return this.getLatest().__summary;
|
|
523
|
+
}
|
|
524
|
+
setSummary(summary) {
|
|
525
|
+
const writable = this.getWritable();
|
|
526
|
+
writable.__summary = summary;
|
|
527
|
+
}
|
|
528
|
+
getOpen() {
|
|
529
|
+
return this.getLatest().__open;
|
|
530
|
+
}
|
|
531
|
+
setOpen(open) {
|
|
532
|
+
const writable = this.getWritable();
|
|
533
|
+
writable.__open = open;
|
|
534
|
+
}
|
|
535
|
+
toggleOpen() {
|
|
536
|
+
this.setOpen(!this.getOpen());
|
|
537
|
+
}
|
|
538
|
+
getDOMSlot(element) {
|
|
539
|
+
const content = element.querySelector(`.${detailsClassNames.content}`);
|
|
540
|
+
return super.getDOMSlot(element).withElement(content);
|
|
541
|
+
}
|
|
542
|
+
isInline() {
|
|
543
|
+
return false;
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
_defineProperty(DetailsNode, "slashMenuItems", [{
|
|
547
|
+
title: "Details",
|
|
548
|
+
icon: createElement(ChevronRight, { size: 20 }),
|
|
549
|
+
description: "Collapsible content block",
|
|
550
|
+
keywords: [
|
|
551
|
+
"details",
|
|
552
|
+
"toggle",
|
|
553
|
+
"collapse",
|
|
554
|
+
"accordion"
|
|
555
|
+
],
|
|
556
|
+
section: "ADVANCED",
|
|
557
|
+
onSelect: (editor) => {
|
|
558
|
+
editor.update(() => {
|
|
559
|
+
$insertNodes([$createDetailsNode("Details")]);
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
}]);
|
|
563
|
+
function $createDetailsNode(summary, open = false) {
|
|
564
|
+
return new DetailsNode(summary, open);
|
|
565
|
+
}
|
|
566
|
+
//#endregion
|
|
567
|
+
//#region src/components/renderers/FootnoteSectionRenderer.tsx
|
|
568
|
+
function FootnoteSectionRenderer({ definitions }) {
|
|
569
|
+
const { displayNumberMap } = useFootnoteDefinitions();
|
|
570
|
+
const sortedEntries = Object.entries(definitions).sort(([a], [b]) => (displayNumberMap[a] ?? 0) - (displayNumberMap[b] ?? 0));
|
|
571
|
+
if (sortedEntries.length === 0) return null;
|
|
572
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
573
|
+
role: "doc-endnotes",
|
|
574
|
+
className: clsx("rich-footnote-section-content", semanticClassNames.footnoteSection, sharedStyles.footnoteSection),
|
|
575
|
+
children: [/* @__PURE__ */ jsx("hr", { className: clsx(semanticClassNames.footnoteSectionDivider, sharedStyles.footnoteSectionDivider) }), /* @__PURE__ */ jsx("ol", {
|
|
576
|
+
className: clsx(semanticClassNames.footnoteSectionList, sharedStyles.footnoteSectionList),
|
|
577
|
+
children: sortedEntries.map(([identifier, content]) => {
|
|
578
|
+
return /* @__PURE__ */ jsx(FootnoteSectionItem, {
|
|
579
|
+
content,
|
|
580
|
+
displayNum: displayNumberMap[identifier] ?? identifier,
|
|
581
|
+
identifier
|
|
582
|
+
}, identifier);
|
|
583
|
+
})
|
|
584
|
+
})]
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
function FootnoteSectionItem({ identifier, content, displayNum }) {
|
|
588
|
+
const targetId = `footnote-${identifier}`;
|
|
589
|
+
const refId = `footnote-ref-${identifier}`;
|
|
590
|
+
const handleBackClick = useCallback((e) => {
|
|
591
|
+
e.preventDefault();
|
|
592
|
+
const refElement = document.getElementById(refId);
|
|
593
|
+
if (!refElement) return;
|
|
594
|
+
refElement.scrollIntoView({
|
|
595
|
+
behavior: "smooth",
|
|
596
|
+
block: "center"
|
|
597
|
+
});
|
|
598
|
+
refElement.classList.add(semanticClassNames.footnoteHighlight, sharedStyles.footnoteHighlight);
|
|
599
|
+
window.setTimeout(() => {
|
|
600
|
+
refElement.classList.remove(semanticClassNames.footnoteHighlight, sharedStyles.footnoteHighlight);
|
|
601
|
+
}, 1200);
|
|
602
|
+
}, [refId]);
|
|
603
|
+
return /* @__PURE__ */ jsxs("li", {
|
|
604
|
+
className: clsx(semanticClassNames.footnoteSectionItem, sharedStyles.footnoteSectionItem),
|
|
605
|
+
id: targetId,
|
|
606
|
+
value: typeof displayNum === "number" ? displayNum : void 0,
|
|
607
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
608
|
+
className: "rich-footnote-section-item-content",
|
|
609
|
+
children: content
|
|
610
|
+
}), /* @__PURE__ */ jsx("a", {
|
|
611
|
+
"aria-label": `Back to reference ${displayNum}`,
|
|
612
|
+
className: clsx(semanticClassNames.footnoteBackRef, sharedStyles.footnoteBackRef),
|
|
613
|
+
href: `#${refId}`,
|
|
614
|
+
role: "doc-backlink",
|
|
615
|
+
onClick: handleBackClick,
|
|
616
|
+
children: "↩"
|
|
617
|
+
})]
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
//#endregion
|
|
621
|
+
//#region src/nodes/FootnoteSectionNode.ts
|
|
622
|
+
var FootnoteSectionNode = class FootnoteSectionNode extends DecoratorNode {
|
|
623
|
+
static getType() {
|
|
624
|
+
return "footnote-section";
|
|
625
|
+
}
|
|
626
|
+
static clone(node) {
|
|
627
|
+
return new FootnoteSectionNode({ ...node.__definitions }, node.__key);
|
|
628
|
+
}
|
|
629
|
+
constructor(definitions, key) {
|
|
630
|
+
super(key);
|
|
631
|
+
_defineProperty(this, "__definitions", void 0);
|
|
632
|
+
this.__definitions = definitions;
|
|
633
|
+
}
|
|
634
|
+
createDOM(_config) {
|
|
635
|
+
const div = document.createElement("div");
|
|
636
|
+
div.className = `${semanticClassNames.footnoteSection} ${sharedStyles.footnoteSection}`;
|
|
637
|
+
return div;
|
|
638
|
+
}
|
|
639
|
+
updateDOM() {
|
|
640
|
+
return false;
|
|
641
|
+
}
|
|
642
|
+
isInline() {
|
|
643
|
+
return false;
|
|
644
|
+
}
|
|
645
|
+
static importJSON(serializedNode) {
|
|
646
|
+
return $createFootnoteSectionNode(serializedNode.definitions);
|
|
647
|
+
}
|
|
648
|
+
exportJSON() {
|
|
649
|
+
return {
|
|
650
|
+
...super.exportJSON(),
|
|
651
|
+
type: "footnote-section",
|
|
652
|
+
definitions: this.__definitions,
|
|
653
|
+
version: 1
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
getDefinitions() {
|
|
657
|
+
return this.getLatest().__definitions;
|
|
658
|
+
}
|
|
659
|
+
setDefinitions(definitions) {
|
|
660
|
+
const writable = this.getWritable();
|
|
661
|
+
writable.__definitions = definitions;
|
|
662
|
+
}
|
|
663
|
+
getDefinition(identifier) {
|
|
664
|
+
return this.getLatest().__definitions[identifier];
|
|
665
|
+
}
|
|
666
|
+
setDefinition(identifier, content) {
|
|
667
|
+
const writable = this.getWritable();
|
|
668
|
+
writable.__definitions = {
|
|
669
|
+
...writable.__definitions,
|
|
670
|
+
[identifier]: content
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
removeDefinition(identifier) {
|
|
674
|
+
const writable = this.getWritable();
|
|
675
|
+
const { [identifier]: _, ...rest } = writable.__definitions;
|
|
676
|
+
writable.__definitions = rest;
|
|
677
|
+
}
|
|
678
|
+
decorate(_editor, _config) {
|
|
679
|
+
return createRendererDecoration("FootnoteSection", FootnoteSectionRenderer, {
|
|
680
|
+
definitions: this.__definitions,
|
|
681
|
+
nodeKey: this.__key
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
};
|
|
685
|
+
function $createFootnoteSectionNode(definitions = {}) {
|
|
686
|
+
return new FootnoteSectionNode(definitions);
|
|
687
|
+
}
|
|
688
|
+
function $isFootnoteSectionNode(node) {
|
|
689
|
+
return node instanceof FootnoteSectionNode;
|
|
690
|
+
}
|
|
691
|
+
//#endregion
|
|
692
|
+
//#region src/components/renderers/GridStaticDecorator.tsx
|
|
693
|
+
function GridStaticDecorator({ cols, gap, cellStates }) {
|
|
694
|
+
const renderContent = useNestedContentRenderer();
|
|
695
|
+
return /* @__PURE__ */ jsx("div", {
|
|
696
|
+
className: clsx(gridClassNames.inner, gridStyles.inner),
|
|
697
|
+
style: {
|
|
698
|
+
display: "grid",
|
|
699
|
+
gridTemplateColumns: `repeat(${cols}, 1fr)`,
|
|
700
|
+
gap
|
|
701
|
+
},
|
|
702
|
+
children: cellStates.map((state, i) => /* @__PURE__ */ jsx("div", {
|
|
703
|
+
className: clsx(gridClassNames.cell, gridStyles.cell),
|
|
704
|
+
children: renderContent(state)
|
|
705
|
+
}, i))
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
//#endregion
|
|
709
|
+
//#region src/nodes/GridContainerNode.ts
|
|
710
|
+
var GridContainerNode = class GridContainerNode extends DecoratorNode {
|
|
711
|
+
static getType() {
|
|
712
|
+
return "grid-container";
|
|
713
|
+
}
|
|
714
|
+
static clone(node) {
|
|
715
|
+
return new GridContainerNode(node.__cols, node.__gap, [...node.__cellStates], node.__key);
|
|
716
|
+
}
|
|
717
|
+
constructor(cols = 2, gap, cellStates, key) {
|
|
718
|
+
super(key);
|
|
719
|
+
_defineProperty(this, "__cols", void 0);
|
|
720
|
+
_defineProperty(this, "__gap", void 0);
|
|
721
|
+
_defineProperty(this, "__cellStates", void 0);
|
|
722
|
+
this.__cols = cols;
|
|
723
|
+
this.__gap = gap || "16px";
|
|
724
|
+
if (cellStates) this.__cellStates = cellStates;
|
|
725
|
+
else {
|
|
726
|
+
const emptyState = { root: {
|
|
727
|
+
children: [{
|
|
728
|
+
type: "paragraph",
|
|
729
|
+
children: [],
|
|
730
|
+
direction: null,
|
|
731
|
+
format: "",
|
|
732
|
+
indent: 0,
|
|
733
|
+
textFormat: 0,
|
|
734
|
+
textStyle: "",
|
|
735
|
+
version: 1
|
|
736
|
+
}],
|
|
737
|
+
direction: null,
|
|
738
|
+
format: "",
|
|
739
|
+
indent: 0,
|
|
740
|
+
type: "root",
|
|
741
|
+
version: 1
|
|
742
|
+
} };
|
|
743
|
+
this.__cellStates = Array.from({ length: cols }, () => emptyState);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
createDOM(_config) {
|
|
747
|
+
const div = document.createElement("div");
|
|
748
|
+
div.className = `${gridClassNames.container} ${gridStyles.container}`;
|
|
749
|
+
return div;
|
|
750
|
+
}
|
|
751
|
+
updateDOM() {
|
|
752
|
+
return false;
|
|
753
|
+
}
|
|
754
|
+
isInline() {
|
|
755
|
+
return false;
|
|
756
|
+
}
|
|
757
|
+
getCols() {
|
|
758
|
+
return this.getLatest().__cols;
|
|
759
|
+
}
|
|
760
|
+
setCols(cols) {
|
|
761
|
+
const writable = this.getWritable();
|
|
762
|
+
const prev = writable.__cellStates.length;
|
|
763
|
+
writable.__cols = cols;
|
|
764
|
+
if (cols > prev) {
|
|
765
|
+
const emptyState = { root: {
|
|
766
|
+
children: [{
|
|
767
|
+
type: "paragraph",
|
|
768
|
+
children: [],
|
|
769
|
+
direction: null,
|
|
770
|
+
format: "",
|
|
771
|
+
indent: 0,
|
|
772
|
+
textFormat: 0,
|
|
773
|
+
textStyle: "",
|
|
774
|
+
version: 1
|
|
775
|
+
}],
|
|
776
|
+
direction: null,
|
|
777
|
+
format: "",
|
|
778
|
+
indent: 0,
|
|
779
|
+
type: "root",
|
|
780
|
+
version: 1
|
|
781
|
+
} };
|
|
782
|
+
for (let i = prev; i < cols; i++) writable.__cellStates.push(emptyState);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
getGap() {
|
|
786
|
+
return this.getLatest().__gap;
|
|
787
|
+
}
|
|
788
|
+
setGap(gap) {
|
|
789
|
+
const writable = this.getWritable();
|
|
790
|
+
writable.__gap = gap;
|
|
791
|
+
}
|
|
792
|
+
getCellStates() {
|
|
793
|
+
return this.getLatest().__cellStates;
|
|
794
|
+
}
|
|
795
|
+
addCells(count) {
|
|
796
|
+
const writable = this.getWritable();
|
|
797
|
+
const emptyState = { root: {
|
|
798
|
+
children: [{
|
|
799
|
+
type: "paragraph",
|
|
800
|
+
children: [],
|
|
801
|
+
direction: null,
|
|
802
|
+
format: "",
|
|
803
|
+
indent: 0,
|
|
804
|
+
textFormat: 0,
|
|
805
|
+
textStyle: "",
|
|
806
|
+
version: 1
|
|
807
|
+
}],
|
|
808
|
+
direction: null,
|
|
809
|
+
format: "",
|
|
810
|
+
indent: 0,
|
|
811
|
+
type: "root",
|
|
812
|
+
version: 1
|
|
813
|
+
} };
|
|
814
|
+
for (let i = 0; i < count; i++) writable.__cellStates.push(emptyState);
|
|
815
|
+
}
|
|
816
|
+
removeCells(count) {
|
|
817
|
+
const states = this.getWritable().__cellStates;
|
|
818
|
+
const toRemove = Math.min(count, states.length);
|
|
819
|
+
for (let i = 0; i < toRemove; i++) {
|
|
820
|
+
const state = states.at(-1);
|
|
821
|
+
if (!state) break;
|
|
822
|
+
if (!(extractTextContent(state).trim() === "")) break;
|
|
823
|
+
states.pop();
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
getTextContent() {
|
|
827
|
+
return this.__cellStates.map((s) => extractTextContent(s)).join("\n");
|
|
828
|
+
}
|
|
829
|
+
static importJSON(serializedNode) {
|
|
830
|
+
const legacy = serializedNode;
|
|
831
|
+
const cols = legacy.cols || 2;
|
|
832
|
+
const rawGap = legacy.gap;
|
|
833
|
+
const gap = typeof rawGap === "number" ? `${rawGap}px` : rawGap;
|
|
834
|
+
if (legacy.cells && legacy.cells.length > 0) return new GridContainerNode(cols, gap, legacy.cells);
|
|
835
|
+
if (legacy.children) return new GridContainerNode(cols, gap, legacy.children.map((child) => {
|
|
836
|
+
return { root: {
|
|
837
|
+
children: [child],
|
|
838
|
+
direction: null,
|
|
839
|
+
format: "",
|
|
840
|
+
indent: 0,
|
|
841
|
+
type: "root",
|
|
842
|
+
version: 1
|
|
843
|
+
} };
|
|
844
|
+
}));
|
|
845
|
+
return new GridContainerNode(cols, gap);
|
|
846
|
+
}
|
|
847
|
+
exportJSON() {
|
|
848
|
+
return {
|
|
849
|
+
...super.exportJSON(),
|
|
850
|
+
type: "grid-container",
|
|
851
|
+
cols: this.__cols,
|
|
852
|
+
gap: this.__gap,
|
|
853
|
+
cells: this.__cellStates,
|
|
854
|
+
version: 1
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
decorate(_editor, _config) {
|
|
858
|
+
return createElement(GridStaticDecorator, {
|
|
859
|
+
cols: this.__cols,
|
|
860
|
+
gap: this.__gap,
|
|
861
|
+
cellStates: this.__cellStates
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
};
|
|
865
|
+
function $createGridContainerNode(cols = 2, gap) {
|
|
866
|
+
return new GridContainerNode(cols, gap);
|
|
867
|
+
}
|
|
868
|
+
function $isGridContainerNode(node) {
|
|
869
|
+
return node instanceof GridContainerNode;
|
|
870
|
+
}
|
|
871
|
+
//#endregion
|
|
872
|
+
//#region src/nodes/LinkCardNode.ts
|
|
873
|
+
var LinkCardNode = class LinkCardNode extends DecoratorNode {
|
|
874
|
+
static getType() {
|
|
875
|
+
return "link-card";
|
|
876
|
+
}
|
|
877
|
+
static clone(node) {
|
|
878
|
+
return new LinkCardNode({
|
|
879
|
+
url: node.__url,
|
|
880
|
+
source: node.__source,
|
|
881
|
+
id: node.__id,
|
|
882
|
+
title: node.__title,
|
|
883
|
+
description: node.__description,
|
|
884
|
+
favicon: node.__favicon,
|
|
885
|
+
image: node.__image
|
|
886
|
+
}, node.__key);
|
|
887
|
+
}
|
|
888
|
+
constructor(payload, key) {
|
|
889
|
+
super(key);
|
|
890
|
+
_defineProperty(this, "__url", void 0);
|
|
891
|
+
_defineProperty(this, "__source", void 0);
|
|
892
|
+
_defineProperty(this, "__id", void 0);
|
|
893
|
+
_defineProperty(this, "__title", void 0);
|
|
894
|
+
_defineProperty(this, "__description", void 0);
|
|
895
|
+
_defineProperty(this, "__favicon", void 0);
|
|
896
|
+
_defineProperty(this, "__image", void 0);
|
|
897
|
+
this.__url = payload.url;
|
|
898
|
+
this.__source = payload.source;
|
|
899
|
+
this.__id = payload.id;
|
|
900
|
+
this.__title = payload.title;
|
|
901
|
+
this.__description = payload.description;
|
|
902
|
+
this.__favicon = payload.favicon;
|
|
903
|
+
this.__image = payload.image;
|
|
904
|
+
}
|
|
905
|
+
createDOM(_config) {
|
|
906
|
+
const div = document.createElement("div");
|
|
907
|
+
div.className = "rich-link-card-wrapper";
|
|
908
|
+
return div;
|
|
909
|
+
}
|
|
910
|
+
updateDOM() {
|
|
911
|
+
return false;
|
|
912
|
+
}
|
|
913
|
+
isInline() {
|
|
914
|
+
return false;
|
|
915
|
+
}
|
|
916
|
+
static importJSON(serializedNode) {
|
|
917
|
+
return $createLinkCardNode({
|
|
918
|
+
url: serializedNode.url,
|
|
919
|
+
source: serializedNode.source,
|
|
920
|
+
id: serializedNode.id,
|
|
921
|
+
title: serializedNode.title,
|
|
922
|
+
description: serializedNode.description,
|
|
923
|
+
favicon: serializedNode.favicon,
|
|
924
|
+
image: serializedNode.image
|
|
925
|
+
});
|
|
926
|
+
}
|
|
927
|
+
exportJSON() {
|
|
928
|
+
return {
|
|
929
|
+
...super.exportJSON(),
|
|
930
|
+
type: "link-card",
|
|
931
|
+
url: this.__url,
|
|
932
|
+
source: this.__source,
|
|
933
|
+
id: this.__id,
|
|
934
|
+
title: this.__title,
|
|
935
|
+
description: this.__description,
|
|
936
|
+
favicon: this.__favicon,
|
|
937
|
+
image: this.__image,
|
|
938
|
+
version: 1
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
getUrl() {
|
|
942
|
+
return this.getLatest().__url;
|
|
943
|
+
}
|
|
944
|
+
setUrl(url) {
|
|
945
|
+
const writable = this.getWritable();
|
|
946
|
+
writable.__url = url;
|
|
947
|
+
}
|
|
948
|
+
getSource() {
|
|
949
|
+
return this.getLatest().__source;
|
|
950
|
+
}
|
|
951
|
+
setSource(source) {
|
|
952
|
+
const writable = this.getWritable();
|
|
953
|
+
writable.__source = source;
|
|
954
|
+
}
|
|
955
|
+
getId() {
|
|
956
|
+
return this.getLatest().__id;
|
|
957
|
+
}
|
|
958
|
+
setId(id) {
|
|
959
|
+
const writable = this.getWritable();
|
|
960
|
+
writable.__id = id;
|
|
961
|
+
}
|
|
962
|
+
decorate(_editor, _config) {
|
|
963
|
+
return createRendererDecoration("LinkCard", LinkCardRenderer, {
|
|
964
|
+
url: this.__url,
|
|
965
|
+
source: this.__source,
|
|
966
|
+
id: this.__id,
|
|
967
|
+
title: this.__title,
|
|
968
|
+
description: this.__description,
|
|
969
|
+
favicon: this.__favicon,
|
|
970
|
+
image: this.__image
|
|
971
|
+
});
|
|
972
|
+
}
|
|
973
|
+
};
|
|
974
|
+
_defineProperty(LinkCardNode, "commandItems", [{
|
|
975
|
+
title: "Link Card",
|
|
976
|
+
icon: createElement(Link, { size: 20 }),
|
|
977
|
+
description: "Link preview card",
|
|
978
|
+
keywords: [
|
|
979
|
+
"link",
|
|
980
|
+
"card",
|
|
981
|
+
"bookmark",
|
|
982
|
+
"embed"
|
|
983
|
+
],
|
|
984
|
+
section: "MEDIA",
|
|
985
|
+
placement: ["slash", "toolbar"],
|
|
986
|
+
group: "insert",
|
|
987
|
+
onSelect: (editor) => {
|
|
988
|
+
editor.update(() => {
|
|
989
|
+
$insertNodes([$createLinkCardNode({ url: "" })]);
|
|
990
|
+
});
|
|
991
|
+
}
|
|
992
|
+
}]);
|
|
993
|
+
function $createLinkCardNode(payload) {
|
|
994
|
+
return new LinkCardNode(payload);
|
|
995
|
+
}
|
|
996
|
+
function $isLinkCardNode(node) {
|
|
997
|
+
return node instanceof LinkCardNode;
|
|
998
|
+
}
|
|
999
|
+
//#endregion
|
|
1000
|
+
//#region src/context/PollDataContext.tsx
|
|
1001
|
+
var PollDataContext = createContext({
|
|
1002
|
+
adapter: null,
|
|
1003
|
+
initialStates: {}
|
|
1004
|
+
});
|
|
1005
|
+
function PollDataProvider({ adapter, initialStates, children }) {
|
|
1006
|
+
const value = useMemo(() => ({
|
|
1007
|
+
adapter,
|
|
1008
|
+
initialStates: initialStates ?? {}
|
|
1009
|
+
}), [adapter, initialStates]);
|
|
1010
|
+
return /* @__PURE__ */ jsx(PollDataContext.Provider, {
|
|
1011
|
+
value,
|
|
1012
|
+
children
|
|
1013
|
+
});
|
|
1014
|
+
}
|
|
1015
|
+
function usePollDataAdapter() {
|
|
1016
|
+
return use(PollDataContext).adapter;
|
|
1017
|
+
}
|
|
1018
|
+
function useInitialPollState(pollId) {
|
|
1019
|
+
return use(PollDataContext).initialStates[pollId];
|
|
1020
|
+
}
|
|
1021
|
+
//#endregion
|
|
1022
|
+
//#region src/styles/poll.css.ts
|
|
1023
|
+
var pollClasses = {
|
|
1024
|
+
container: "_1m5xzan0",
|
|
1025
|
+
meta: "_1m5xzan1",
|
|
1026
|
+
question: "_1m5xzan2",
|
|
1027
|
+
optionList: "_1m5xzan3",
|
|
1028
|
+
option: "_1m5xzan4",
|
|
1029
|
+
optionInteractive: "_1m5xzan5",
|
|
1030
|
+
optionSelected: "_1m5xzan6",
|
|
1031
|
+
optionDisabled: "_1m5xzan7",
|
|
1032
|
+
tint: "_1m5xzan8",
|
|
1033
|
+
tintActive: "_1m5xzan9",
|
|
1034
|
+
optionRow: "_1m5xzana",
|
|
1035
|
+
optionLabel: "_1m5xzanb",
|
|
1036
|
+
optionPct: "_1m5xzanc",
|
|
1037
|
+
optionPctActive: "_1m5xzand",
|
|
1038
|
+
hint: "_1m5xzane",
|
|
1039
|
+
submit: "_1m5xzanf",
|
|
1040
|
+
submitActive: "_1m5xzang",
|
|
1041
|
+
footer: "_1m5xzanh",
|
|
1042
|
+
errorMessage: "_1m5xzani",
|
|
1043
|
+
skeleton: "_1m5xzanj"
|
|
1044
|
+
};
|
|
1045
|
+
//#endregion
|
|
1046
|
+
//#region src/components/renderers/PollRenderer.tsx
|
|
1047
|
+
function shouldShowTallies(state, showResults) {
|
|
1048
|
+
if (showResults === "after-vote") return state.userVote !== void 0 || state.closed;
|
|
1049
|
+
if (showResults === "after-close") return state.closed;
|
|
1050
|
+
return true;
|
|
1051
|
+
}
|
|
1052
|
+
function classNames(...names) {
|
|
1053
|
+
return names.filter((n) => Boolean(n)).join(" ");
|
|
1054
|
+
}
|
|
1055
|
+
function PollStaticFallback({ question, options }) {
|
|
1056
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1057
|
+
className: pollClasses.container,
|
|
1058
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
1059
|
+
className: pollClasses.question,
|
|
1060
|
+
children: question
|
|
1061
|
+
}), /* @__PURE__ */ jsx("ul", {
|
|
1062
|
+
className: pollClasses.optionList,
|
|
1063
|
+
children: options.map((option) => /* @__PURE__ */ jsx("li", {
|
|
1064
|
+
className: pollClasses.option,
|
|
1065
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
1066
|
+
className: pollClasses.optionRow,
|
|
1067
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
1068
|
+
className: pollClasses.optionLabel,
|
|
1069
|
+
children: option.label
|
|
1070
|
+
})
|
|
1071
|
+
})
|
|
1072
|
+
}, option.id))
|
|
1073
|
+
})]
|
|
1074
|
+
});
|
|
1075
|
+
}
|
|
1076
|
+
function PollInteractive({ adapter, closeAt, mode, options, pollId, question, showResults }) {
|
|
1077
|
+
const initialState = useInitialPollState(pollId);
|
|
1078
|
+
const liveState = adapter.usePollState(pollId);
|
|
1079
|
+
const submit = adapter.useSubmit(pollId);
|
|
1080
|
+
const state = liveState ?? initialState ?? {
|
|
1081
|
+
tallies: {},
|
|
1082
|
+
totalVotes: 0,
|
|
1083
|
+
status: "loading",
|
|
1084
|
+
closed: false,
|
|
1085
|
+
canVote: false
|
|
1086
|
+
};
|
|
1087
|
+
const [pendingSelection, setPendingSelection] = useState([]);
|
|
1088
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
1089
|
+
const showTallies = shouldShowTallies(state, showResults);
|
|
1090
|
+
const userVoted = state.userVote !== void 0;
|
|
1091
|
+
const isClosed = state.closed;
|
|
1092
|
+
const canInteract = !userVoted && !isClosed && state.canVote && state.status !== "loading";
|
|
1093
|
+
const tallyShare = useCallback((optionId) => {
|
|
1094
|
+
if (!showTallies || state.totalVotes <= 0) return 0;
|
|
1095
|
+
const tally = state.tallies[optionId] ?? 0;
|
|
1096
|
+
return Math.max(0, Math.min(1, tally / state.totalVotes));
|
|
1097
|
+
}, [
|
|
1098
|
+
showTallies,
|
|
1099
|
+
state.tallies,
|
|
1100
|
+
state.totalVotes
|
|
1101
|
+
]);
|
|
1102
|
+
const handleSingleClick = useCallback(async (optionId) => {
|
|
1103
|
+
if (!canInteract || isSubmitting) return;
|
|
1104
|
+
setIsSubmitting(true);
|
|
1105
|
+
try {
|
|
1106
|
+
await submit([optionId]);
|
|
1107
|
+
} finally {
|
|
1108
|
+
setIsSubmitting(false);
|
|
1109
|
+
}
|
|
1110
|
+
}, [
|
|
1111
|
+
canInteract,
|
|
1112
|
+
isSubmitting,
|
|
1113
|
+
submit
|
|
1114
|
+
]);
|
|
1115
|
+
const handleMultiToggle = useCallback((optionId) => {
|
|
1116
|
+
if (!canInteract || isSubmitting) return;
|
|
1117
|
+
setPendingSelection((prev) => prev.includes(optionId) ? prev.filter((id) => id !== optionId) : [...prev, optionId]);
|
|
1118
|
+
}, [canInteract, isSubmitting]);
|
|
1119
|
+
const handleMultiSubmit = useCallback(async () => {
|
|
1120
|
+
if (!canInteract || isSubmitting || pendingSelection.length === 0) return;
|
|
1121
|
+
setIsSubmitting(true);
|
|
1122
|
+
try {
|
|
1123
|
+
await submit([...pendingSelection]);
|
|
1124
|
+
} finally {
|
|
1125
|
+
setIsSubmitting(false);
|
|
1126
|
+
}
|
|
1127
|
+
}, [
|
|
1128
|
+
canInteract,
|
|
1129
|
+
isSubmitting,
|
|
1130
|
+
pendingSelection,
|
|
1131
|
+
submit
|
|
1132
|
+
]);
|
|
1133
|
+
const metaLabel = useMemo(() => {
|
|
1134
|
+
const parts = [mode === "single" ? "Single choice" : "Multiple choice"];
|
|
1135
|
+
if (isClosed) parts.push("Voting closed");
|
|
1136
|
+
else if (userVoted) parts.push("Voted");
|
|
1137
|
+
return parts.join(" · ");
|
|
1138
|
+
}, [
|
|
1139
|
+
mode,
|
|
1140
|
+
isClosed,
|
|
1141
|
+
userVoted
|
|
1142
|
+
]);
|
|
1143
|
+
if (state.status === "loading") return /* @__PURE__ */ jsxs("div", {
|
|
1144
|
+
className: pollClasses.container,
|
|
1145
|
+
children: [
|
|
1146
|
+
/* @__PURE__ */ jsx("p", {
|
|
1147
|
+
className: pollClasses.meta,
|
|
1148
|
+
children: metaLabel
|
|
1149
|
+
}),
|
|
1150
|
+
/* @__PURE__ */ jsx("p", {
|
|
1151
|
+
className: pollClasses.question,
|
|
1152
|
+
children: question
|
|
1153
|
+
}),
|
|
1154
|
+
/* @__PURE__ */ jsx("ul", {
|
|
1155
|
+
className: pollClasses.optionList,
|
|
1156
|
+
children: options.map((option) => /* @__PURE__ */ jsx("li", { className: pollClasses.skeleton }, option.id))
|
|
1157
|
+
})
|
|
1158
|
+
]
|
|
1159
|
+
});
|
|
1160
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1161
|
+
className: pollClasses.container,
|
|
1162
|
+
children: [
|
|
1163
|
+
/* @__PURE__ */ jsx("p", {
|
|
1164
|
+
className: pollClasses.meta,
|
|
1165
|
+
children: metaLabel
|
|
1166
|
+
}),
|
|
1167
|
+
/* @__PURE__ */ jsx("p", {
|
|
1168
|
+
className: pollClasses.question,
|
|
1169
|
+
children: question
|
|
1170
|
+
}),
|
|
1171
|
+
/* @__PURE__ */ jsx("ul", {
|
|
1172
|
+
className: pollClasses.optionList,
|
|
1173
|
+
children: options.map((option) => {
|
|
1174
|
+
const isUserChoice = state.userVote?.includes(option.id) ?? false;
|
|
1175
|
+
const isPending = pendingSelection.includes(option.id);
|
|
1176
|
+
const tintWidth = (() => {
|
|
1177
|
+
if (canInteract && mode === "multiple" && isPending) return 1;
|
|
1178
|
+
return tallyShare(option.id);
|
|
1179
|
+
})();
|
|
1180
|
+
const tintShouldBeActive = isUserChoice || isPending;
|
|
1181
|
+
const labelHighlighted = isUserChoice || isPending;
|
|
1182
|
+
const handleClick = canInteract ? mode === "single" ? () => handleSingleClick(option.id) : () => handleMultiToggle(option.id) : void 0;
|
|
1183
|
+
return /* @__PURE__ */ jsxs("li", {
|
|
1184
|
+
className: classNames(pollClasses.option, canInteract ? pollClasses.optionInteractive : pollClasses.optionDisabled, labelHighlighted && pollClasses.optionSelected),
|
|
1185
|
+
onClick: handleClick,
|
|
1186
|
+
...canInteract ? {
|
|
1187
|
+
role: "button",
|
|
1188
|
+
tabIndex: 0,
|
|
1189
|
+
onKeyDown: (event) => {
|
|
1190
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
1191
|
+
event.preventDefault();
|
|
1192
|
+
handleClick?.();
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
} : {},
|
|
1196
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
1197
|
+
"aria-hidden": true,
|
|
1198
|
+
style: { width: `${tintWidth * 100}%` },
|
|
1199
|
+
className: classNames(pollClasses.tint, tintShouldBeActive && pollClasses.tintActive)
|
|
1200
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
1201
|
+
className: pollClasses.optionRow,
|
|
1202
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
1203
|
+
className: pollClasses.optionLabel,
|
|
1204
|
+
children: [labelHighlighted && "✓ ", option.label]
|
|
1205
|
+
}), showTallies && (userVoted || isClosed) ? /* @__PURE__ */ jsxs("span", {
|
|
1206
|
+
className: classNames(pollClasses.optionPct, labelHighlighted && pollClasses.optionPctActive),
|
|
1207
|
+
children: [Math.round(tallyShare(option.id) * 100), "%"]
|
|
1208
|
+
}) : canInteract && mode === "single" ? /* @__PURE__ */ jsx("span", {
|
|
1209
|
+
className: pollClasses.hint,
|
|
1210
|
+
children: "Click to vote"
|
|
1211
|
+
}) : null]
|
|
1212
|
+
})]
|
|
1213
|
+
}, option.id);
|
|
1214
|
+
})
|
|
1215
|
+
}),
|
|
1216
|
+
canInteract && mode === "multiple" && /* @__PURE__ */ jsx("button", {
|
|
1217
|
+
disabled: pendingSelection.length === 0 || isSubmitting,
|
|
1218
|
+
type: "button",
|
|
1219
|
+
className: classNames(pollClasses.submit, pendingSelection.length > 0 && !isSubmitting && pollClasses.submitActive),
|
|
1220
|
+
onClick: handleMultiSubmit,
|
|
1221
|
+
children: isSubmitting ? "Submitting…" : pendingSelection.length > 0 ? `Submit ${pendingSelection.length} ${pendingSelection.length === 1 ? "item" : "items"}` : "Submit (select at least one)"
|
|
1222
|
+
}),
|
|
1223
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1224
|
+
className: pollClasses.footer,
|
|
1225
|
+
children: [(userVoted || isClosed) && state.totalVotes > 0 ? /* @__PURE__ */ jsxs("span", { children: [
|
|
1226
|
+
state.totalVotes.toLocaleString(),
|
|
1227
|
+
" ",
|
|
1228
|
+
state.totalVotes === 1 ? "vote" : "votes"
|
|
1229
|
+
] }) : /* @__PURE__ */ jsx("span", {}), closeAt && !isClosed ? /* @__PURE__ */ jsxs("span", { children: ["Closes ", closeAt] }) : null]
|
|
1230
|
+
}),
|
|
1231
|
+
state.status === "error" && state.errorMessage ? /* @__PURE__ */ jsx("p", {
|
|
1232
|
+
className: pollClasses.errorMessage,
|
|
1233
|
+
children: state.errorMessage
|
|
1234
|
+
}) : !state.canVote && !userVoted && !isClosed && state.errorMessage ? /* @__PURE__ */ jsx("p", {
|
|
1235
|
+
className: pollClasses.errorMessage,
|
|
1236
|
+
children: state.errorMessage
|
|
1237
|
+
}) : null
|
|
1238
|
+
]
|
|
1239
|
+
});
|
|
1240
|
+
}
|
|
1241
|
+
function PollRenderer(props) {
|
|
1242
|
+
const adapter = usePollDataAdapter();
|
|
1243
|
+
if (!adapter) return /* @__PURE__ */ jsx(PollStaticFallback, { ...props });
|
|
1244
|
+
return /* @__PURE__ */ jsx(PollInteractive, {
|
|
1245
|
+
adapter,
|
|
1246
|
+
...props
|
|
1247
|
+
});
|
|
1248
|
+
}
|
|
1249
|
+
//#endregion
|
|
1250
|
+
//#region src/nodes/PollNode.ts
|
|
1251
|
+
var idAlphabet = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
1252
|
+
var makePollIdSuffix = customAlphabet(idAlphabet, 10);
|
|
1253
|
+
var makeOptionIdSuffix = customAlphabet(idAlphabet, 6);
|
|
1254
|
+
function createPollId() {
|
|
1255
|
+
return `p_${makePollIdSuffix()}`;
|
|
1256
|
+
}
|
|
1257
|
+
function createOptionId() {
|
|
1258
|
+
return `o_${makeOptionIdSuffix()}`;
|
|
1259
|
+
}
|
|
1260
|
+
var PollNode = class PollNode extends DecoratorNode {
|
|
1261
|
+
static getType() {
|
|
1262
|
+
return "poll";
|
|
1263
|
+
}
|
|
1264
|
+
static clone(node) {
|
|
1265
|
+
return new PollNode({
|
|
1266
|
+
pollId: node.__pollId,
|
|
1267
|
+
question: node.__question,
|
|
1268
|
+
options: node.__options,
|
|
1269
|
+
mode: node.__mode,
|
|
1270
|
+
closeAt: node.__closeAt,
|
|
1271
|
+
showResults: node.__showResults
|
|
1272
|
+
}, node.__key);
|
|
1273
|
+
}
|
|
1274
|
+
constructor(payload = {}, key) {
|
|
1275
|
+
super(key);
|
|
1276
|
+
_defineProperty(this, "__pollId", void 0);
|
|
1277
|
+
_defineProperty(this, "__question", void 0);
|
|
1278
|
+
_defineProperty(this, "__options", void 0);
|
|
1279
|
+
_defineProperty(this, "__mode", void 0);
|
|
1280
|
+
_defineProperty(this, "__closeAt", void 0);
|
|
1281
|
+
_defineProperty(this, "__showResults", void 0);
|
|
1282
|
+
this.__pollId = payload.pollId ?? createPollId();
|
|
1283
|
+
this.__question = payload.question ?? "";
|
|
1284
|
+
this.__options = payload.options && payload.options.length > 0 ? payload.options : [{
|
|
1285
|
+
id: createOptionId(),
|
|
1286
|
+
label: ""
|
|
1287
|
+
}, {
|
|
1288
|
+
id: createOptionId(),
|
|
1289
|
+
label: ""
|
|
1290
|
+
}];
|
|
1291
|
+
this.__mode = payload.mode ?? "single";
|
|
1292
|
+
this.__closeAt = payload.closeAt;
|
|
1293
|
+
this.__showResults = payload.showResults;
|
|
1294
|
+
}
|
|
1295
|
+
createDOM(_config) {
|
|
1296
|
+
const div = document.createElement("div");
|
|
1297
|
+
div.className = "rich-poll-wrapper";
|
|
1298
|
+
return div;
|
|
1299
|
+
}
|
|
1300
|
+
updateDOM() {
|
|
1301
|
+
return false;
|
|
1302
|
+
}
|
|
1303
|
+
isInline() {
|
|
1304
|
+
return false;
|
|
1305
|
+
}
|
|
1306
|
+
getPollId() {
|
|
1307
|
+
return this.getLatest().__pollId;
|
|
1308
|
+
}
|
|
1309
|
+
getQuestion() {
|
|
1310
|
+
return this.getLatest().__question;
|
|
1311
|
+
}
|
|
1312
|
+
setQuestion(question) {
|
|
1313
|
+
const writable = this.getWritable();
|
|
1314
|
+
writable.__question = question;
|
|
1315
|
+
}
|
|
1316
|
+
getOptions() {
|
|
1317
|
+
return this.getLatest().__options;
|
|
1318
|
+
}
|
|
1319
|
+
setOptions(options) {
|
|
1320
|
+
const writable = this.getWritable();
|
|
1321
|
+
writable.__options = options;
|
|
1322
|
+
}
|
|
1323
|
+
getMode() {
|
|
1324
|
+
return this.getLatest().__mode;
|
|
1325
|
+
}
|
|
1326
|
+
setMode(mode) {
|
|
1327
|
+
const writable = this.getWritable();
|
|
1328
|
+
writable.__mode = mode;
|
|
1329
|
+
}
|
|
1330
|
+
getCloseAt() {
|
|
1331
|
+
return this.getLatest().__closeAt;
|
|
1332
|
+
}
|
|
1333
|
+
setCloseAt(closeAt) {
|
|
1334
|
+
const writable = this.getWritable();
|
|
1335
|
+
writable.__closeAt = closeAt;
|
|
1336
|
+
}
|
|
1337
|
+
getShowResults() {
|
|
1338
|
+
return this.getLatest().__showResults;
|
|
1339
|
+
}
|
|
1340
|
+
setShowResults(showResults) {
|
|
1341
|
+
const writable = this.getWritable();
|
|
1342
|
+
writable.__showResults = showResults;
|
|
1343
|
+
}
|
|
1344
|
+
static importJSON(serializedNode) {
|
|
1345
|
+
return new PollNode({
|
|
1346
|
+
pollId: serializedNode.pollId,
|
|
1347
|
+
question: serializedNode.question,
|
|
1348
|
+
options: serializedNode.options,
|
|
1349
|
+
mode: serializedNode.mode,
|
|
1350
|
+
closeAt: serializedNode.closeAt,
|
|
1351
|
+
showResults: serializedNode.showResults
|
|
1352
|
+
});
|
|
1353
|
+
}
|
|
1354
|
+
exportJSON() {
|
|
1355
|
+
return {
|
|
1356
|
+
...super.exportJSON(),
|
|
1357
|
+
type: "poll",
|
|
1358
|
+
pollId: this.__pollId,
|
|
1359
|
+
question: this.__question,
|
|
1360
|
+
options: this.__options,
|
|
1361
|
+
mode: this.__mode,
|
|
1362
|
+
...this.__closeAt ? { closeAt: this.__closeAt } : {},
|
|
1363
|
+
...this.__showResults ? { showResults: this.__showResults } : {},
|
|
1364
|
+
version: 1
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
1367
|
+
decorate(_editor, _config) {
|
|
1368
|
+
return createRendererDecoration("Poll", PollRenderer, {
|
|
1369
|
+
pollId: this.__pollId,
|
|
1370
|
+
question: this.__question,
|
|
1371
|
+
options: this.__options,
|
|
1372
|
+
mode: this.__mode,
|
|
1373
|
+
closeAt: this.__closeAt,
|
|
1374
|
+
showResults: this.__showResults
|
|
1375
|
+
});
|
|
1376
|
+
}
|
|
1377
|
+
};
|
|
1378
|
+
function $createPollNode(payload) {
|
|
1379
|
+
return new PollNode(payload);
|
|
1380
|
+
}
|
|
1381
|
+
function $isPollNode(node) {
|
|
1382
|
+
return node instanceof PollNode;
|
|
1383
|
+
}
|
|
1384
|
+
//#endregion
|
|
1385
|
+
//#region src/nodes/RubyNode.ts
|
|
1386
|
+
function readBaseTextFromRuby(element) {
|
|
1387
|
+
let base = "";
|
|
1388
|
+
for (const child of Array.from(element.childNodes)) {
|
|
1389
|
+
if (child.nodeType === Node.TEXT_NODE) {
|
|
1390
|
+
base += child.textContent ?? "";
|
|
1391
|
+
continue;
|
|
1392
|
+
}
|
|
1393
|
+
if (child.nodeType !== Node.ELEMENT_NODE) continue;
|
|
1394
|
+
const childElement = child;
|
|
1395
|
+
const tag = childElement.tagName.toLowerCase();
|
|
1396
|
+
if (tag === "rt" || tag === "rp") continue;
|
|
1397
|
+
base += childElement.textContent ?? "";
|
|
1398
|
+
}
|
|
1399
|
+
return base;
|
|
1400
|
+
}
|
|
1401
|
+
var RubyNode = class RubyNode extends ElementNode {
|
|
1402
|
+
static getType() {
|
|
1403
|
+
return "ruby";
|
|
1404
|
+
}
|
|
1405
|
+
static clone(node) {
|
|
1406
|
+
return new RubyNode(node.__reading, node.__key);
|
|
1407
|
+
}
|
|
1408
|
+
constructor(reading, key) {
|
|
1409
|
+
super(key);
|
|
1410
|
+
_defineProperty(this, "__reading", void 0);
|
|
1411
|
+
this.__reading = reading;
|
|
1412
|
+
}
|
|
1413
|
+
static importJSON(serializedNode) {
|
|
1414
|
+
return $createRubyNode(serializedNode.reading ?? "");
|
|
1415
|
+
}
|
|
1416
|
+
exportJSON() {
|
|
1417
|
+
return {
|
|
1418
|
+
...super.exportJSON(),
|
|
1419
|
+
type: "ruby",
|
|
1420
|
+
reading: this.__reading,
|
|
1421
|
+
version: 1
|
|
1422
|
+
};
|
|
1423
|
+
}
|
|
1424
|
+
static importDOM() {
|
|
1425
|
+
return { ruby: () => ({
|
|
1426
|
+
conversion: (domNode) => {
|
|
1427
|
+
if (!(domNode instanceof HTMLElement)) return null;
|
|
1428
|
+
const reading = domNode.querySelector("rt")?.textContent ?? "";
|
|
1429
|
+
const baseText = readBaseTextFromRuby(domNode);
|
|
1430
|
+
const node = $createRubyNode(reading);
|
|
1431
|
+
if (baseText) node.append($createTextNode(baseText));
|
|
1432
|
+
return { node };
|
|
1433
|
+
},
|
|
1434
|
+
priority: 2
|
|
1435
|
+
}) };
|
|
1436
|
+
}
|
|
1437
|
+
exportDOM() {
|
|
1438
|
+
const ruby = document.createElement("ruby");
|
|
1439
|
+
ruby.className = `${semanticClassNames.ruby} ${sharedStyles.ruby}`;
|
|
1440
|
+
const baseText = this.getTextContent();
|
|
1441
|
+
if (baseText) ruby.append(baseText);
|
|
1442
|
+
if (this.__reading) {
|
|
1443
|
+
const rt = document.createElement("rt");
|
|
1444
|
+
rt.className = `${semanticClassNames.rubyRt} ${sharedStyles.rubyRt}`;
|
|
1445
|
+
rt.textContent = this.__reading;
|
|
1446
|
+
ruby.append(rt);
|
|
1447
|
+
}
|
|
1448
|
+
return { element: ruby };
|
|
1449
|
+
}
|
|
1450
|
+
createDOM(_config) {
|
|
1451
|
+
const span = document.createElement("span");
|
|
1452
|
+
span.className = `${semanticClassNames.ruby} ${sharedStyles.ruby}`;
|
|
1453
|
+
if (this.__reading) span.dataset.ruby = this.__reading;
|
|
1454
|
+
return span;
|
|
1455
|
+
}
|
|
1456
|
+
updateDOM(prevNode, dom) {
|
|
1457
|
+
if (prevNode.__reading !== this.__reading) if (this.__reading) dom.dataset.ruby = this.__reading;
|
|
1458
|
+
else delete dom.dataset.ruby;
|
|
1459
|
+
return false;
|
|
1460
|
+
}
|
|
1461
|
+
canInsertTextBefore() {
|
|
1462
|
+
return true;
|
|
1463
|
+
}
|
|
1464
|
+
canInsertTextAfter() {
|
|
1465
|
+
return true;
|
|
1466
|
+
}
|
|
1467
|
+
isInline() {
|
|
1468
|
+
return true;
|
|
1469
|
+
}
|
|
1470
|
+
getReading() {
|
|
1471
|
+
return this.getLatest().__reading;
|
|
1472
|
+
}
|
|
1473
|
+
setReading(reading) {
|
|
1474
|
+
const writable = this.getWritable();
|
|
1475
|
+
writable.__reading = reading;
|
|
1476
|
+
}
|
|
1477
|
+
};
|
|
1478
|
+
function $createRubyNode(reading) {
|
|
1479
|
+
return new RubyNode(reading);
|
|
1480
|
+
}
|
|
1481
|
+
function $isRubyNode(node) {
|
|
1482
|
+
return node instanceof RubyNode;
|
|
1483
|
+
}
|
|
1484
|
+
//#endregion
|
|
1485
|
+
//#region src/components/renderers/VideoRenderer.tsx
|
|
1486
|
+
function VideoRenderer({ src, poster, width, height }) {
|
|
1487
|
+
return /* @__PURE__ */ jsx("figure", {
|
|
1488
|
+
className: "rich-video",
|
|
1489
|
+
children: /* @__PURE__ */ jsx("video", {
|
|
1490
|
+
controls: true,
|
|
1491
|
+
height,
|
|
1492
|
+
poster,
|
|
1493
|
+
preload: "metadata",
|
|
1494
|
+
src,
|
|
1495
|
+
style: {
|
|
1496
|
+
maxWidth: "100%",
|
|
1497
|
+
height: "auto"
|
|
1498
|
+
},
|
|
1499
|
+
width
|
|
1500
|
+
})
|
|
1501
|
+
});
|
|
1502
|
+
}
|
|
1503
|
+
//#endregion
|
|
1504
|
+
//#region src/nodes/VideoNode.ts
|
|
1505
|
+
var VideoNode = class VideoNode extends DecoratorNode {
|
|
1506
|
+
static getType() {
|
|
1507
|
+
return "video";
|
|
1508
|
+
}
|
|
1509
|
+
static clone(node) {
|
|
1510
|
+
return new VideoNode({
|
|
1511
|
+
src: node.__src,
|
|
1512
|
+
poster: node.__poster,
|
|
1513
|
+
width: node.__width,
|
|
1514
|
+
height: node.__height
|
|
1515
|
+
}, node.__key);
|
|
1516
|
+
}
|
|
1517
|
+
constructor(payload, key) {
|
|
1518
|
+
super(key);
|
|
1519
|
+
_defineProperty(this, "__src", void 0);
|
|
1520
|
+
_defineProperty(this, "__poster", void 0);
|
|
1521
|
+
_defineProperty(this, "__width", void 0);
|
|
1522
|
+
_defineProperty(this, "__height", void 0);
|
|
1523
|
+
this.__src = payload.src;
|
|
1524
|
+
this.__poster = payload.poster;
|
|
1525
|
+
this.__width = payload.width;
|
|
1526
|
+
this.__height = payload.height;
|
|
1527
|
+
}
|
|
1528
|
+
createDOM(_config) {
|
|
1529
|
+
const div = document.createElement("div");
|
|
1530
|
+
div.className = "rich-video-wrapper";
|
|
1531
|
+
return div;
|
|
1532
|
+
}
|
|
1533
|
+
updateDOM() {
|
|
1534
|
+
return false;
|
|
1535
|
+
}
|
|
1536
|
+
isInline() {
|
|
1537
|
+
return false;
|
|
1538
|
+
}
|
|
1539
|
+
static importJSON(serializedNode) {
|
|
1540
|
+
return $createVideoNode({
|
|
1541
|
+
src: serializedNode.src,
|
|
1542
|
+
poster: serializedNode.poster,
|
|
1543
|
+
width: serializedNode.width,
|
|
1544
|
+
height: serializedNode.height
|
|
1545
|
+
});
|
|
1546
|
+
}
|
|
1547
|
+
exportJSON() {
|
|
1548
|
+
return {
|
|
1549
|
+
...super.exportJSON(),
|
|
1550
|
+
type: "video",
|
|
1551
|
+
src: this.__src,
|
|
1552
|
+
poster: this.__poster,
|
|
1553
|
+
width: this.__width,
|
|
1554
|
+
height: this.__height,
|
|
1555
|
+
version: 1
|
|
1556
|
+
};
|
|
1557
|
+
}
|
|
1558
|
+
getSrc() {
|
|
1559
|
+
return this.getLatest().__src;
|
|
1560
|
+
}
|
|
1561
|
+
setSrc(src) {
|
|
1562
|
+
const writable = this.getWritable();
|
|
1563
|
+
writable.__src = src;
|
|
1564
|
+
}
|
|
1565
|
+
decorate(_editor, _config) {
|
|
1566
|
+
return createRendererDecoration("Video", VideoRenderer, {
|
|
1567
|
+
src: this.__src,
|
|
1568
|
+
poster: this.__poster,
|
|
1569
|
+
width: this.__width,
|
|
1570
|
+
height: this.__height
|
|
1571
|
+
});
|
|
1572
|
+
}
|
|
1573
|
+
};
|
|
1574
|
+
_defineProperty(VideoNode, "commandItems", [{
|
|
1575
|
+
title: "Video",
|
|
1576
|
+
icon: createElement(Video, { size: 20 }),
|
|
1577
|
+
description: "Embed a video",
|
|
1578
|
+
keywords: [
|
|
1579
|
+
"video",
|
|
1580
|
+
"media",
|
|
1581
|
+
"mp4"
|
|
1582
|
+
],
|
|
1583
|
+
section: "MEDIA",
|
|
1584
|
+
placement: ["slash", "toolbar"],
|
|
1585
|
+
group: "insert",
|
|
1586
|
+
onSelect: (editor) => {
|
|
1587
|
+
editor.update(() => {
|
|
1588
|
+
$insertNodes([$createVideoNode({ src: "" })]);
|
|
1589
|
+
});
|
|
1590
|
+
}
|
|
1591
|
+
}]);
|
|
1592
|
+
function $createVideoNode(payload) {
|
|
1593
|
+
return new VideoNode(payload);
|
|
1594
|
+
}
|
|
1595
|
+
//#endregion
|
|
1596
|
+
//#region src/config.ts
|
|
1597
|
+
var builtinNodes = [
|
|
1598
|
+
HeadingNode,
|
|
1599
|
+
QuoteNode,
|
|
1600
|
+
ListNode,
|
|
1601
|
+
ListItemNode,
|
|
1602
|
+
LinkNode,
|
|
1603
|
+
AutoLinkNode,
|
|
1604
|
+
HorizontalRuleNode,
|
|
1605
|
+
TableNode,
|
|
1606
|
+
TableCellNode,
|
|
1607
|
+
TableRowNode,
|
|
1608
|
+
CodeNode
|
|
1609
|
+
];
|
|
1610
|
+
var customNodes = [
|
|
1611
|
+
SpoilerNode,
|
|
1612
|
+
MentionNode,
|
|
1613
|
+
KaTeXInlineNode,
|
|
1614
|
+
KaTeXBlockNode,
|
|
1615
|
+
ImageNode,
|
|
1616
|
+
AlertQuoteNode,
|
|
1617
|
+
CodeBlockNode,
|
|
1618
|
+
FootnoteNode,
|
|
1619
|
+
FootnoteSectionNode,
|
|
1620
|
+
VideoNode,
|
|
1621
|
+
LinkCardNode,
|
|
1622
|
+
CommentNode,
|
|
1623
|
+
DetailsNode,
|
|
1624
|
+
GridContainerNode,
|
|
1625
|
+
BannerNode,
|
|
1626
|
+
MermaidNode,
|
|
1627
|
+
RubyNode,
|
|
1628
|
+
TagNode,
|
|
1629
|
+
PollNode
|
|
1630
|
+
];
|
|
1631
|
+
var allNodes = [...builtinNodes, ...customNodes];
|
|
1632
|
+
//#endregion
|
|
1633
|
+
export { $createCommentPlaceholderNode as A, BANNER_TYPES as B, GridContainerNode as C, $createDetailsNode as D, FootnoteSectionNode as E, CodeBlockRenderer as F, normalizeBannerType as H, ColorSchemeProvider as I, useColorScheme as L, CommentNode as M, $isCodeBlockNode as N, DetailsNode as O, CodeBlockNode as P, $isBannerNode as R, $isGridContainerNode as S, $isFootnoteSectionNode as T, BannerRenderer as U, BannerNode as V, usePollDataAdapter as _, $createRubyNode as a, LinkCardNode as b, $createPollNode as c, createOptionId as d, createPollId as f, useInitialPollState as g, PollDataProvider as h, VideoNode as i, $isCommentNode as j, $createCommentNode as k, $isPollNode as l, pollClasses as m, builtinNodes as n, $isRubyNode as o, PollRenderer as p, customNodes as r, RubyNode as s, allNodes as t, PollNode as u, $createLinkCardNode as v, $createFootnoteSectionNode as w, $createGridContainerNode as x, $isLinkCardNode as y, BANNER_LABELS as z };
|