@zuilib/text-editor 0.2.1 → 0.3.1
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/CHANGELOG.md +36 -0
- package/DRAWING_FORMAT.md +7 -2
- package/README.md +122 -3
- package/dist/index.d.ts +156 -10
- package/dist/index.js +1134 -778
- package/dist/styles.css +44 -0
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
import {
|
|
1
|
+
// src/EditorRoot.tsx
|
|
2
|
+
import {
|
|
3
|
+
createContext,
|
|
4
|
+
useCallback as useCallback2,
|
|
5
|
+
useContext,
|
|
6
|
+
useEffect as useEffect6,
|
|
7
|
+
useMemo,
|
|
8
|
+
useRef as useRef3,
|
|
9
|
+
useState as useState2
|
|
10
|
+
} from "react";
|
|
3
11
|
import { LexicalComposer } from "@lexical/react/LexicalComposer";
|
|
4
|
-
import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
|
|
5
|
-
import { ContentEditable } from "@lexical/react/LexicalContentEditable";
|
|
6
12
|
import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin";
|
|
7
|
-
import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary";
|
|
8
13
|
import { ListPlugin } from "@lexical/react/LexicalListPlugin";
|
|
9
14
|
import { CheckListPlugin } from "@lexical/react/LexicalCheckListPlugin";
|
|
10
15
|
import { LinkPlugin } from "@lexical/react/LexicalLinkPlugin";
|
|
@@ -12,7 +17,7 @@ import { MarkdownShortcutPlugin } from "@lexical/react/LexicalMarkdownShortcutPl
|
|
|
12
17
|
import { AutoFocusPlugin } from "@lexical/react/LexicalAutoFocusPlugin";
|
|
13
18
|
import { TablePlugin } from "@lexical/react/LexicalTablePlugin";
|
|
14
19
|
import { CHECK_LIST, CODE, TRANSFORMERS as TRANSFORMERS2 } from "@lexical/markdown";
|
|
15
|
-
import { useLexicalComposerContext as
|
|
20
|
+
import { useLexicalComposerContext as useLexicalComposerContext6 } from "@lexical/react/LexicalComposerContext";
|
|
16
21
|
import { HeadingNode, QuoteNode } from "@lexical/rich-text";
|
|
17
22
|
import { ListItemNode, ListNode as ListNode2 } from "@lexical/list";
|
|
18
23
|
import { CodeHighlightNode, CodeNode } from "@lexical/code";
|
|
@@ -166,146 +171,9 @@ function CodeHighlightPlugin() {
|
|
|
166
171
|
return null;
|
|
167
172
|
}
|
|
168
173
|
|
|
169
|
-
// src/plugins/FoldingPlugin.tsx
|
|
170
|
-
import {
|
|
171
|
-
useCallback,
|
|
172
|
-
useEffect as useEffect4,
|
|
173
|
-
useRef,
|
|
174
|
-
useState
|
|
175
|
-
} from "react";
|
|
176
|
-
import { $getRoot, $getSelection as $getSelection2, $isRangeSelection as $isRangeSelection2 } from "lexical";
|
|
177
|
-
import { useLexicalComposerContext as useLexicalComposerContext4 } from "@lexical/react/LexicalComposerContext";
|
|
178
|
-
import { $isHeadingNode } from "@lexical/rich-text";
|
|
179
|
-
import { jsx } from "react/jsx-runtime";
|
|
180
|
-
var HEADING_LEVELS = {
|
|
181
|
-
h1: 1,
|
|
182
|
-
h2: 2,
|
|
183
|
-
h3: 3,
|
|
184
|
-
h4: 4,
|
|
185
|
-
h5: 5,
|
|
186
|
-
h6: 6
|
|
187
|
-
};
|
|
188
|
-
function FoldingPlugin() {
|
|
189
|
-
const [editor] = useLexicalComposerContext4();
|
|
190
|
-
const [foldedKeys, setFoldedKeys] = useState(/* @__PURE__ */ new Set());
|
|
191
|
-
const [buttons, setButtons] = useState([]);
|
|
192
|
-
const foldedRef = useRef(foldedKeys);
|
|
193
|
-
foldedRef.current = foldedKeys;
|
|
194
|
-
const sync = useCallback(() => {
|
|
195
|
-
editor.getEditorState().read(() => {
|
|
196
|
-
const children = $getRoot().getChildren();
|
|
197
|
-
const folded = foldedRef.current;
|
|
198
|
-
const hidden = /* @__PURE__ */ new Set();
|
|
199
|
-
const headings = [];
|
|
200
|
-
for (let i = 0; i < children.length; i++) {
|
|
201
|
-
const node = children[i];
|
|
202
|
-
if (!$isHeadingNode(node)) continue;
|
|
203
|
-
const level = HEADING_LEVELS[node.getTag()] ?? 6;
|
|
204
|
-
const isFolded = folded.has(node.getKey());
|
|
205
|
-
headings.push({ key: node.getKey(), folded: isFolded });
|
|
206
|
-
if (!isFolded) continue;
|
|
207
|
-
for (let j = i + 1; j < children.length; j++) {
|
|
208
|
-
const sibling = children[j];
|
|
209
|
-
if ($isHeadingNode(sibling) && (HEADING_LEVELS[sibling.getTag()] ?? 6) <= level) {
|
|
210
|
-
break;
|
|
211
|
-
}
|
|
212
|
-
hidden.add(sibling.getKey());
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
const selection = $getSelection2();
|
|
216
|
-
if ($isRangeSelection2(selection)) {
|
|
217
|
-
const topLevel = selection.anchor.getNode().getTopLevelElement();
|
|
218
|
-
if (topLevel && hidden.has(topLevel.getKey())) {
|
|
219
|
-
let node = topLevel.getPreviousSibling();
|
|
220
|
-
while (node) {
|
|
221
|
-
if ($isHeadingNode(node) && folded.has(node.getKey())) {
|
|
222
|
-
const unfoldKey = node.getKey();
|
|
223
|
-
setFoldedKeys((prev) => {
|
|
224
|
-
const next = new Set(prev);
|
|
225
|
-
next.delete(unfoldKey);
|
|
226
|
-
return next;
|
|
227
|
-
});
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
node = node.getPreviousSibling();
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
for (const child of children) {
|
|
235
|
-
const element = editor.getElementByKey(child.getKey());
|
|
236
|
-
if (!element) continue;
|
|
237
|
-
const shouldHide = hidden.has(child.getKey());
|
|
238
|
-
if (shouldHide) {
|
|
239
|
-
element.style.display = "none";
|
|
240
|
-
} else if (element.style.display === "none") {
|
|
241
|
-
element.style.display = "";
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
setButtons(
|
|
245
|
-
headings.map(({ key, folded: isFolded }) => {
|
|
246
|
-
const element = editor.getElementByKey(key);
|
|
247
|
-
element?.classList.toggle("zui-heading-folded", isFolded);
|
|
248
|
-
return {
|
|
249
|
-
key,
|
|
250
|
-
folded: isFolded,
|
|
251
|
-
top: element?.offsetTop ?? 0,
|
|
252
|
-
height: element?.offsetHeight ?? 0
|
|
253
|
-
};
|
|
254
|
-
})
|
|
255
|
-
);
|
|
256
|
-
});
|
|
257
|
-
}, [editor]);
|
|
258
|
-
useEffect4(() => {
|
|
259
|
-
sync();
|
|
260
|
-
const unregister = editor.registerUpdateListener(sync);
|
|
261
|
-
const rootElement = editor.getRootElement();
|
|
262
|
-
const observer = typeof ResizeObserver !== "undefined" ? new ResizeObserver(sync) : null;
|
|
263
|
-
if (rootElement && observer) observer.observe(rootElement);
|
|
264
|
-
return () => {
|
|
265
|
-
unregister();
|
|
266
|
-
observer?.disconnect();
|
|
267
|
-
};
|
|
268
|
-
}, [editor, sync, foldedKeys]);
|
|
269
|
-
const toggle = useCallback((key) => {
|
|
270
|
-
setFoldedKeys((prev) => {
|
|
271
|
-
const next = new Set(prev);
|
|
272
|
-
if (next.has(key)) next.delete(key);
|
|
273
|
-
else next.add(key);
|
|
274
|
-
return next;
|
|
275
|
-
});
|
|
276
|
-
}, []);
|
|
277
|
-
return /* @__PURE__ */ jsx("div", { className: "zui-text-editor-fold-gutter", children: buttons.map(({ key, folded, top, height }) => /* @__PURE__ */ jsx(
|
|
278
|
-
"button",
|
|
279
|
-
{
|
|
280
|
-
type: "button",
|
|
281
|
-
className: `zui-text-editor-fold ${folded ? "is-folded" : ""}`,
|
|
282
|
-
style: { top: top + Math.max(0, (Math.min(height, 44) - 18) / 2) },
|
|
283
|
-
title: folded ? "Expand section" : "Collapse section",
|
|
284
|
-
"aria-label": folded ? "Expand section" : "Collapse section",
|
|
285
|
-
"aria-expanded": !folded,
|
|
286
|
-
onClick: () => toggle(key),
|
|
287
|
-
children: /* @__PURE__ */ jsx(
|
|
288
|
-
"svg",
|
|
289
|
-
{
|
|
290
|
-
viewBox: "0 0 20 20",
|
|
291
|
-
width: "12",
|
|
292
|
-
height: "12",
|
|
293
|
-
fill: "none",
|
|
294
|
-
stroke: "currentColor",
|
|
295
|
-
strokeWidth: "2",
|
|
296
|
-
strokeLinecap: "round",
|
|
297
|
-
strokeLinejoin: "round",
|
|
298
|
-
children: /* @__PURE__ */ jsx("path", { d: "M6 8l4 4 4-4" })
|
|
299
|
-
}
|
|
300
|
-
)
|
|
301
|
-
},
|
|
302
|
-
key
|
|
303
|
-
)) });
|
|
304
|
-
}
|
|
305
|
-
|
|
306
174
|
// src/plugins/MarkdownSyncPlugin.tsx
|
|
307
|
-
import { useEffect as
|
|
308
|
-
import { useLexicalComposerContext as
|
|
175
|
+
import { useEffect as useEffect4, useRef } from "react";
|
|
176
|
+
import { useLexicalComposerContext as useLexicalComposerContext4 } from "@lexical/react/LexicalComposerContext";
|
|
309
177
|
import {
|
|
310
178
|
$convertFromMarkdownString,
|
|
311
179
|
$convertToMarkdownString
|
|
@@ -315,11 +183,11 @@ function MarkdownSyncPlugin({
|
|
|
315
183
|
onChange,
|
|
316
184
|
transformers
|
|
317
185
|
}) {
|
|
318
|
-
const [editor] =
|
|
319
|
-
const lastMarkdownRef =
|
|
320
|
-
const onChangeRef =
|
|
186
|
+
const [editor] = useLexicalComposerContext4();
|
|
187
|
+
const lastMarkdownRef = useRef("");
|
|
188
|
+
const onChangeRef = useRef(onChange);
|
|
321
189
|
onChangeRef.current = onChange;
|
|
322
|
-
|
|
190
|
+
useEffect4(() => {
|
|
323
191
|
if (initialMarkdown && initialMarkdown !== lastMarkdownRef.current) {
|
|
324
192
|
lastMarkdownRef.current = initialMarkdown;
|
|
325
193
|
editor.update(() => {
|
|
@@ -327,7 +195,7 @@ function MarkdownSyncPlugin({
|
|
|
327
195
|
}, { tag: "initial-load" });
|
|
328
196
|
}
|
|
329
197
|
}, [editor, initialMarkdown]);
|
|
330
|
-
|
|
198
|
+
useEffect4(() => {
|
|
331
199
|
return editor.registerUpdateListener(
|
|
332
200
|
({ editorState, dirtyElements, dirtyLeaves, tags }) => {
|
|
333
201
|
if (dirtyElements.size === 0 && dirtyLeaves.size === 0) return;
|
|
@@ -342,145 +210,101 @@ function MarkdownSyncPlugin({
|
|
|
342
210
|
return null;
|
|
343
211
|
}
|
|
344
212
|
|
|
345
|
-
// src/
|
|
346
|
-
import { useCallback as useCallback2, useEffect as useEffect6, useState as useState2 } from "react";
|
|
347
|
-
import { useLexicalComposerContext as useLexicalComposerContext6 } from "@lexical/react/LexicalComposerContext";
|
|
348
|
-
import {
|
|
349
|
-
TableOfContentsPlugin
|
|
350
|
-
} from "@lexical/react/LexicalTableOfContentsPlugin";
|
|
351
|
-
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
352
|
-
var INDENT_PER_LEVEL = {
|
|
353
|
-
h1: 0,
|
|
354
|
-
h2: 1,
|
|
355
|
-
h3: 2,
|
|
356
|
-
h4: 3,
|
|
357
|
-
h5: 4,
|
|
358
|
-
h6: 5
|
|
359
|
-
};
|
|
360
|
-
function OutlinePlugin() {
|
|
361
|
-
const [editor] = useLexicalComposerContext6();
|
|
362
|
-
const [collapsed, setCollapsed] = useState2(false);
|
|
363
|
-
const [activeKey, setActiveKey] = useState2(null);
|
|
364
|
-
useEffect6(() => {
|
|
365
|
-
const rootElement = editor.getRootElement();
|
|
366
|
-
const scroller = rootElement?.closest(".zui-text-editor");
|
|
367
|
-
if (!rootElement || !(scroller instanceof HTMLElement)) return;
|
|
368
|
-
let frame = 0;
|
|
369
|
-
const updateActive = () => {
|
|
370
|
-
cancelAnimationFrame(frame);
|
|
371
|
-
frame = requestAnimationFrame(() => {
|
|
372
|
-
const headings = rootElement.querySelectorAll(
|
|
373
|
-
"h1, h2, h3, h4, h5, h6"
|
|
374
|
-
);
|
|
375
|
-
const threshold = scroller.scrollTop + 80;
|
|
376
|
-
let current = null;
|
|
377
|
-
for (const el of headings) {
|
|
378
|
-
if (el.offsetTop <= threshold) {
|
|
379
|
-
current = el.getAttribute("data-outline-key");
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
setActiveKey(current);
|
|
383
|
-
});
|
|
384
|
-
};
|
|
385
|
-
updateActive();
|
|
386
|
-
scroller.addEventListener("scroll", updateActive, { passive: true });
|
|
387
|
-
const unregister = editor.registerUpdateListener(updateActive);
|
|
388
|
-
return () => {
|
|
389
|
-
cancelAnimationFrame(frame);
|
|
390
|
-
scroller.removeEventListener("scroll", updateActive);
|
|
391
|
-
unregister();
|
|
392
|
-
};
|
|
393
|
-
}, [editor]);
|
|
394
|
-
const scrollTo = useCallback2(
|
|
395
|
-
(key) => {
|
|
396
|
-
editor.getEditorState().read(() => {
|
|
397
|
-
const element = editor.getElementByKey(key);
|
|
398
|
-
element?.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
399
|
-
});
|
|
400
|
-
},
|
|
401
|
-
[editor]
|
|
402
|
-
);
|
|
403
|
-
return /* @__PURE__ */ jsx2(TableOfContentsPlugin, { children: (entries) => {
|
|
404
|
-
for (const [key] of entries) {
|
|
405
|
-
editor.getElementByKey(key)?.setAttribute("data-outline-key", key);
|
|
406
|
-
}
|
|
407
|
-
return /* @__PURE__ */ jsxs(
|
|
408
|
-
"div",
|
|
409
|
-
{
|
|
410
|
-
className: `zui-text-editor-outline ${collapsed ? "is-collapsed" : ""}`,
|
|
411
|
-
children: [
|
|
412
|
-
/* @__PURE__ */ jsx2(
|
|
413
|
-
"button",
|
|
414
|
-
{
|
|
415
|
-
type: "button",
|
|
416
|
-
className: "zui-text-editor-outline-toggle",
|
|
417
|
-
title: collapsed ? "Show outline" : "Hide outline",
|
|
418
|
-
"aria-label": collapsed ? "Show outline" : "Hide outline",
|
|
419
|
-
"aria-expanded": !collapsed,
|
|
420
|
-
onClick: () => setCollapsed((c) => !c),
|
|
421
|
-
children: /* @__PURE__ */ jsx2(
|
|
422
|
-
"svg",
|
|
423
|
-
{
|
|
424
|
-
viewBox: "0 0 20 20",
|
|
425
|
-
width: "14",
|
|
426
|
-
height: "14",
|
|
427
|
-
fill: "none",
|
|
428
|
-
stroke: "currentColor",
|
|
429
|
-
strokeWidth: "1.8",
|
|
430
|
-
strokeLinecap: "round",
|
|
431
|
-
children: /* @__PURE__ */ jsx2("path", { d: "M4 5h12M4 10h8M4 15h10" })
|
|
432
|
-
}
|
|
433
|
-
)
|
|
434
|
-
}
|
|
435
|
-
),
|
|
436
|
-
!collapsed && /* @__PURE__ */ jsxs("nav", { className: "zui-text-editor-outline-list", "aria-label": "Table of contents", children: [
|
|
437
|
-
entries.length === 0 && /* @__PURE__ */ jsx2("div", { className: "zui-text-editor-outline-empty", children: "No headings" }),
|
|
438
|
-
entries.map(([key, text, tag]) => /* @__PURE__ */ jsx2(
|
|
439
|
-
"button",
|
|
440
|
-
{
|
|
441
|
-
type: "button",
|
|
442
|
-
className: `zui-text-editor-outline-item ${key === activeKey ? "is-active" : ""}`,
|
|
443
|
-
style: {
|
|
444
|
-
paddingLeft: `${0.5 + INDENT_PER_LEVEL[tag] * 0.75}rem`
|
|
445
|
-
},
|
|
446
|
-
onClick: () => scrollTo(key),
|
|
447
|
-
children: text || "Untitled"
|
|
448
|
-
},
|
|
449
|
-
key
|
|
450
|
-
))
|
|
451
|
-
] })
|
|
452
|
-
]
|
|
453
|
-
}
|
|
454
|
-
);
|
|
455
|
-
} });
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
// src/plugins/ToolbarPlugin.tsx
|
|
459
|
-
import { useCallback as useCallback4, useEffect as useEffect8, useState as useState4 } from "react";
|
|
460
|
-
import {
|
|
461
|
-
$getSelection as $getSelection3,
|
|
462
|
-
$isRangeSelection as $isRangeSelection3,
|
|
463
|
-
FORMAT_TEXT_COMMAND
|
|
464
|
-
} from "lexical";
|
|
465
|
-
import { useLexicalComposerContext as useLexicalComposerContext8 } from "@lexical/react/LexicalComposerContext";
|
|
466
|
-
import { INSERT_TABLE_COMMAND } from "@lexical/table";
|
|
467
|
-
import { $insertNodeToNearestRoot } from "@lexical/utils";
|
|
468
|
-
|
|
469
|
-
// src/nodes/DrawingNode.tsx
|
|
213
|
+
// src/nodes/FrontmatterNode.ts
|
|
470
214
|
import {
|
|
471
215
|
$applyNodeReplacement,
|
|
472
|
-
|
|
216
|
+
$createLineBreakNode,
|
|
217
|
+
$createTextNode,
|
|
218
|
+
ElementNode
|
|
473
219
|
} from "lexical";
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
220
|
+
var FrontmatterNode = class _FrontmatterNode extends ElementNode {
|
|
221
|
+
static getType() {
|
|
222
|
+
return "frontmatter";
|
|
223
|
+
}
|
|
224
|
+
static clone(node) {
|
|
225
|
+
return new _FrontmatterNode(node.__key);
|
|
226
|
+
}
|
|
227
|
+
constructor(key) {
|
|
228
|
+
super(key);
|
|
229
|
+
}
|
|
230
|
+
createDOM(config) {
|
|
231
|
+
const dom = document.createElement("div");
|
|
232
|
+
const className = config.theme.frontmatter;
|
|
233
|
+
dom.className = typeof className === "string" ? className : "zui-frontmatter";
|
|
234
|
+
return dom;
|
|
235
|
+
}
|
|
236
|
+
updateDOM() {
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
static importJSON(_serializedNode) {
|
|
240
|
+
return $createFrontmatterNode();
|
|
241
|
+
}
|
|
242
|
+
exportJSON() {
|
|
243
|
+
return {
|
|
244
|
+
...super.exportJSON(),
|
|
245
|
+
type: "frontmatter",
|
|
246
|
+
version: 1
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
isInline() {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
canBeEmpty() {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
canIndent() {
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
function $createFrontmatterNode() {
|
|
260
|
+
return $applyNodeReplacement(new FrontmatterNode());
|
|
261
|
+
}
|
|
262
|
+
function $isFrontmatterNode(node) {
|
|
263
|
+
return node instanceof FrontmatterNode;
|
|
264
|
+
}
|
|
265
|
+
var FRONTMATTER_DELIMITER = /^---\s*$/;
|
|
266
|
+
var FRONTMATTER = {
|
|
267
|
+
dependencies: [FrontmatterNode],
|
|
268
|
+
export: (node) => {
|
|
269
|
+
if (!$isFrontmatterNode(node)) return null;
|
|
270
|
+
const body = node.getTextContent();
|
|
271
|
+
return body ? `---
|
|
272
|
+
${body}
|
|
273
|
+
---` : "---\n---";
|
|
274
|
+
},
|
|
275
|
+
regExpStart: FRONTMATTER_DELIMITER,
|
|
276
|
+
regExpEnd: FRONTMATTER_DELIMITER,
|
|
277
|
+
replace: (rootNode, children, _startMatch, _endMatch, linesInBetween, isImport) => {
|
|
278
|
+
if (!isImport || children != null || linesInBetween == null) return false;
|
|
279
|
+
if (rootNode.getChildrenSize() !== 0) return false;
|
|
280
|
+
const lines = [...linesInBetween];
|
|
281
|
+
while (lines.length > 0 && lines[0].length === 0) lines.shift();
|
|
282
|
+
while (lines.length > 0 && lines[lines.length - 1].length === 0) lines.pop();
|
|
283
|
+
const node = $createFrontmatterNode();
|
|
284
|
+
lines.forEach((line, index) => {
|
|
285
|
+
if (index > 0) node.append($createLineBreakNode());
|
|
286
|
+
node.append($createTextNode(line));
|
|
287
|
+
});
|
|
288
|
+
rootNode.append(node);
|
|
289
|
+
},
|
|
290
|
+
type: "multiline-element"
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
// src/nodes/DrawingNode.tsx
|
|
294
|
+
import {
|
|
295
|
+
$applyNodeReplacement as $applyNodeReplacement2,
|
|
296
|
+
DecoratorNode
|
|
297
|
+
} from "lexical";
|
|
298
|
+
|
|
299
|
+
// src/components/DrawingCanvas.tsx
|
|
300
|
+
import {
|
|
301
|
+
useCallback,
|
|
302
|
+
useEffect as useEffect5,
|
|
303
|
+
useRef as useRef2,
|
|
304
|
+
useState
|
|
481
305
|
} from "react";
|
|
482
306
|
import { $getNodeByKey as $getNodeByKey2 } from "lexical";
|
|
483
|
-
import { useLexicalComposerContext as
|
|
307
|
+
import { useLexicalComposerContext as useLexicalComposerContext5 } from "@lexical/react/LexicalComposerContext";
|
|
484
308
|
import { useLexicalEditable } from "@lexical/react/useLexicalEditable";
|
|
485
309
|
|
|
486
310
|
// src/components/drawingTypes.ts
|
|
@@ -533,6 +357,9 @@ function parseDrawingData(json) {
|
|
|
533
357
|
return {
|
|
534
358
|
version: 1,
|
|
535
359
|
height: typeof raw.height === "number" && raw.height >= 80 ? raw.height : DEFAULT_DRAWING_HEIGHT,
|
|
360
|
+
// `full` is the default and stays implicit so existing payloads are
|
|
361
|
+
// serialized byte-for-byte unchanged
|
|
362
|
+
width: raw.width === "content" ? "content" : void 0,
|
|
536
363
|
shapes
|
|
537
364
|
};
|
|
538
365
|
} catch {
|
|
@@ -549,6 +376,27 @@ function isValidShape(value) {
|
|
|
549
376
|
));
|
|
550
377
|
}
|
|
551
378
|
|
|
379
|
+
// src/components/blockWidthOptions.tsx
|
|
380
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
381
|
+
var BLOCK_WIDTH_OPTIONS = [
|
|
382
|
+
{
|
|
383
|
+
width: "full",
|
|
384
|
+
label: "Full width",
|
|
385
|
+
icon: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
386
|
+
/* @__PURE__ */ jsx("path", { d: "M3 4v12M17 4v12" }),
|
|
387
|
+
/* @__PURE__ */ jsx("path", { d: "M6 10h8M8.5 7.5L6 10l2.5 2.5M11.5 7.5L14 10l-2.5 2.5" })
|
|
388
|
+
] })
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
width: "content",
|
|
392
|
+
label: "Content width",
|
|
393
|
+
icon: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
394
|
+
/* @__PURE__ */ jsx("path", { d: "M3 4v12M17 4v12" }),
|
|
395
|
+
/* @__PURE__ */ jsx("path", { d: "M5.5 10h3M11.5 10h3M6.5 7.5L9 10l-2.5 2.5M13.5 7.5L11 10l2.5 2.5" })
|
|
396
|
+
] })
|
|
397
|
+
}
|
|
398
|
+
];
|
|
399
|
+
|
|
552
400
|
// src/components/drawingGeometry.ts
|
|
553
401
|
var BINDING_GAP = 6;
|
|
554
402
|
var BINDING_TOLERANCE = 8;
|
|
@@ -791,10 +639,12 @@ function resolveBindings(shapes) {
|
|
|
791
639
|
}
|
|
792
640
|
|
|
793
641
|
// src/components/DrawingCanvas.tsx
|
|
794
|
-
import { jsx as
|
|
642
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
795
643
|
import { createElement } from "react";
|
|
796
644
|
var MIN_HEIGHT = 120;
|
|
797
645
|
var MAX_HEIGHT = 900;
|
|
646
|
+
var MIN_CONTENT_WIDTH = 240;
|
|
647
|
+
var CONTENT_WIDTH_PADDING = 40;
|
|
798
648
|
var SELECTION_COLOR = "#6965db";
|
|
799
649
|
var CONNECTOR_FONT_SIZE = 12;
|
|
800
650
|
var CONNECTOR_LABEL_MAX_WIDTH = 160;
|
|
@@ -838,7 +688,7 @@ function BoxTexts({
|
|
|
838
688
|
opacity: 0.85
|
|
839
689
|
},
|
|
840
690
|
line
|
|
841
|
-
)) : showHints && /* @__PURE__ */
|
|
691
|
+
)) : showHints && /* @__PURE__ */ jsx2("text", { ...hint, x: cx, y: b.y + SMALL_FONT_SIZE + 7, fontSize: SMALL_FONT_SIZE, children: "Label" })),
|
|
842
692
|
hideField !== "text" && (shape.text ? mainLines.map((line, i) => /* @__PURE__ */ createElement(
|
|
843
693
|
"text",
|
|
844
694
|
{
|
|
@@ -849,7 +699,7 @@ function BoxTexts({
|
|
|
849
699
|
fontSize: FONT_SIZE
|
|
850
700
|
},
|
|
851
701
|
line
|
|
852
|
-
)) : showHints && /* @__PURE__ */
|
|
702
|
+
)) : showHints && /* @__PURE__ */ jsx2("text", { ...hint, x: cx, y: mainStart, fontSize: FONT_SIZE, children: "Text" })),
|
|
853
703
|
hideField !== "footer" && (shape.footer ? footerLines.map((line, i) => /* @__PURE__ */ createElement(
|
|
854
704
|
"text",
|
|
855
705
|
{
|
|
@@ -861,7 +711,7 @@ function BoxTexts({
|
|
|
861
711
|
opacity: 0.65
|
|
862
712
|
},
|
|
863
713
|
line
|
|
864
|
-
)) : showHints && /* @__PURE__ */
|
|
714
|
+
)) : showHints && /* @__PURE__ */ jsx2("text", { ...hint, x: cx, y: b.y + b.h - 9, fontSize: SMALL_FONT_SIZE, children: "Footer" }))
|
|
865
715
|
] });
|
|
866
716
|
}
|
|
867
717
|
function ShapeView({
|
|
@@ -879,7 +729,7 @@ function ShapeView({
|
|
|
879
729
|
case "rect": {
|
|
880
730
|
const b = bbox(shape);
|
|
881
731
|
return /* @__PURE__ */ jsxs2("g", { children: [
|
|
882
|
-
/* @__PURE__ */
|
|
732
|
+
/* @__PURE__ */ jsx2(
|
|
883
733
|
"rect",
|
|
884
734
|
{
|
|
885
735
|
...common,
|
|
@@ -891,13 +741,13 @@ function ShapeView({
|
|
|
891
741
|
fill: shape.fill
|
|
892
742
|
}
|
|
893
743
|
),
|
|
894
|
-
/* @__PURE__ */
|
|
744
|
+
/* @__PURE__ */ jsx2(BoxTexts, { shape, hideField, showHints })
|
|
895
745
|
] });
|
|
896
746
|
}
|
|
897
747
|
case "ellipse": {
|
|
898
748
|
const b = bbox(shape);
|
|
899
749
|
return /* @__PURE__ */ jsxs2("g", { children: [
|
|
900
|
-
/* @__PURE__ */
|
|
750
|
+
/* @__PURE__ */ jsx2(
|
|
901
751
|
"ellipse",
|
|
902
752
|
{
|
|
903
753
|
...common,
|
|
@@ -908,13 +758,13 @@ function ShapeView({
|
|
|
908
758
|
fill: shape.fill
|
|
909
759
|
}
|
|
910
760
|
),
|
|
911
|
-
/* @__PURE__ */
|
|
761
|
+
/* @__PURE__ */ jsx2(BoxTexts, { shape, hideField, showHints })
|
|
912
762
|
] });
|
|
913
763
|
}
|
|
914
764
|
case "triangle":
|
|
915
765
|
case "pentagon":
|
|
916
766
|
return /* @__PURE__ */ jsxs2("g", { children: [
|
|
917
|
-
/* @__PURE__ */
|
|
767
|
+
/* @__PURE__ */ jsx2(
|
|
918
768
|
"polygon",
|
|
919
769
|
{
|
|
920
770
|
...common,
|
|
@@ -922,12 +772,12 @@ function ShapeView({
|
|
|
922
772
|
fill: shape.fill
|
|
923
773
|
}
|
|
924
774
|
),
|
|
925
|
-
/* @__PURE__ */
|
|
775
|
+
/* @__PURE__ */ jsx2(BoxTexts, { shape, hideField, showHints })
|
|
926
776
|
] });
|
|
927
777
|
case "line":
|
|
928
778
|
case "arrow":
|
|
929
779
|
return /* @__PURE__ */ jsxs2("g", { children: [
|
|
930
|
-
/* @__PURE__ */
|
|
780
|
+
/* @__PURE__ */ jsx2(
|
|
931
781
|
"path",
|
|
932
782
|
{
|
|
933
783
|
...common,
|
|
@@ -935,10 +785,10 @@ function ShapeView({
|
|
|
935
785
|
fill: "none"
|
|
936
786
|
}
|
|
937
787
|
),
|
|
938
|
-
shape.text && hideField !== "text" && /* @__PURE__ */
|
|
788
|
+
shape.text && hideField !== "text" && /* @__PURE__ */ jsx2(ConnectorLabel, { shape })
|
|
939
789
|
] });
|
|
940
790
|
case "text":
|
|
941
|
-
return /* @__PURE__ */
|
|
791
|
+
return /* @__PURE__ */ jsx2(
|
|
942
792
|
"text",
|
|
943
793
|
{
|
|
944
794
|
x: shape.x,
|
|
@@ -947,7 +797,7 @@ function ShapeView({
|
|
|
947
797
|
fontSize: FONT_SIZE,
|
|
948
798
|
fontFamily: "ui-sans-serif, system-ui, sans-serif",
|
|
949
799
|
style: { userSelect: "none", whiteSpace: "pre" },
|
|
950
|
-
children: (shape.text ?? "").split("\n").map((line, i) => /* @__PURE__ */
|
|
800
|
+
children: (shape.text ?? "").split("\n").map((line, i) => /* @__PURE__ */ jsx2("tspan", { x: shape.x, dy: i === 0 ? 0 : FONT_SIZE * 1.35, children: line }, i))
|
|
951
801
|
}
|
|
952
802
|
);
|
|
953
803
|
}
|
|
@@ -960,7 +810,7 @@ function ConnectorLabel({ shape }) {
|
|
|
960
810
|
const lineH = CONNECTOR_FONT_SIZE * LINE_HEIGHT;
|
|
961
811
|
const startY = midY - (lines.length - 1) * lineH / 2 + CONNECTOR_FONT_SIZE * 0.35;
|
|
962
812
|
return /* @__PURE__ */ jsxs2("g", { children: [
|
|
963
|
-
/* @__PURE__ */
|
|
813
|
+
/* @__PURE__ */ jsx2(
|
|
964
814
|
"rect",
|
|
965
815
|
{
|
|
966
816
|
x: midX - size.w / 2 - 5,
|
|
@@ -972,7 +822,7 @@ function ConnectorLabel({ shape }) {
|
|
|
972
822
|
opacity: 0.92
|
|
973
823
|
}
|
|
974
824
|
),
|
|
975
|
-
lines.map((line, i) => /* @__PURE__ */
|
|
825
|
+
lines.map((line, i) => /* @__PURE__ */ jsx2(
|
|
976
826
|
"text",
|
|
977
827
|
{
|
|
978
828
|
x: midX,
|
|
@@ -989,20 +839,20 @@ function ConnectorLabel({ shape }) {
|
|
|
989
839
|
] });
|
|
990
840
|
}
|
|
991
841
|
var TOOL_ICONS = {
|
|
992
|
-
select: /* @__PURE__ */
|
|
993
|
-
rect: /* @__PURE__ */
|
|
994
|
-
ellipse: /* @__PURE__ */
|
|
995
|
-
triangle: /* @__PURE__ */
|
|
996
|
-
pentagon: /* @__PURE__ */
|
|
842
|
+
select: /* @__PURE__ */ jsx2("path", { d: "M4 2l12 6.5-5.2 1.6L8 16z", fill: "currentColor", stroke: "none" }),
|
|
843
|
+
rect: /* @__PURE__ */ jsx2("rect", { x: "3", y: "4.5", width: "14", height: "11", rx: "2" }),
|
|
844
|
+
ellipse: /* @__PURE__ */ jsx2("ellipse", { cx: "10", cy: "10", rx: "7", ry: "5.5" }),
|
|
845
|
+
triangle: /* @__PURE__ */ jsx2("path", { d: "M10 3.5L17 16.5H3z", strokeLinejoin: "round" }),
|
|
846
|
+
pentagon: /* @__PURE__ */ jsx2("path", { d: "M10 3L16.8 8.1L14.2 16.2H5.8L3.2 8.1z", strokeLinejoin: "round" }),
|
|
997
847
|
arrow: /* @__PURE__ */ jsxs2("g", { children: [
|
|
998
|
-
/* @__PURE__ */
|
|
999
|
-
/* @__PURE__ */
|
|
848
|
+
/* @__PURE__ */ jsx2("path", { d: "M4 16L16 4" }),
|
|
849
|
+
/* @__PURE__ */ jsx2("path", { d: "M9 4h7v7" })
|
|
1000
850
|
] }),
|
|
1001
|
-
line: /* @__PURE__ */
|
|
851
|
+
line: /* @__PURE__ */ jsx2("path", { d: "M4 16L16 4" }),
|
|
1002
852
|
text: /* @__PURE__ */ jsxs2("g", { children: [
|
|
1003
|
-
/* @__PURE__ */
|
|
1004
|
-
/* @__PURE__ */
|
|
1005
|
-
/* @__PURE__ */
|
|
853
|
+
/* @__PURE__ */ jsx2("path", { d: "M4 5V3.5h12V5" }),
|
|
854
|
+
/* @__PURE__ */ jsx2("path", { d: "M10 3.5V16.5" }),
|
|
855
|
+
/* @__PURE__ */ jsx2("path", { d: "M7 16.5h6" })
|
|
1006
856
|
] })
|
|
1007
857
|
};
|
|
1008
858
|
function bindEndpoints(shape, shapes) {
|
|
@@ -1025,38 +875,55 @@ var TOOLS = [
|
|
|
1025
875
|
{ tool: "line", label: "Line" },
|
|
1026
876
|
{ tool: "text", label: "Text" }
|
|
1027
877
|
];
|
|
878
|
+
function contentWidth(shapes) {
|
|
879
|
+
let right = 0;
|
|
880
|
+
for (const shape of shapes) {
|
|
881
|
+
if (isConnectorType(shape.type)) {
|
|
882
|
+
for (const p of connectorPoints(shape)) right = Math.max(right, p.x);
|
|
883
|
+
} else {
|
|
884
|
+
const b = bbox(shape);
|
|
885
|
+
right = Math.max(right, b.x + b.w);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
return Math.max(MIN_CONTENT_WIDTH, Math.ceil(right + CONTENT_WIDTH_PADDING));
|
|
889
|
+
}
|
|
1028
890
|
function DrawingCanvas({ nodeKey, data }) {
|
|
1029
|
-
const [editor] =
|
|
891
|
+
const [editor] = useLexicalComposerContext5();
|
|
1030
892
|
const isEditable = useLexicalEditable();
|
|
1031
|
-
const [shapes, setShapes] =
|
|
1032
|
-
const [height, setHeight] =
|
|
1033
|
-
const [
|
|
1034
|
-
const [
|
|
1035
|
-
const [
|
|
1036
|
-
const [
|
|
1037
|
-
const [
|
|
1038
|
-
const
|
|
1039
|
-
const
|
|
1040
|
-
const
|
|
1041
|
-
const
|
|
893
|
+
const [shapes, setShapes] = useState(data.shapes);
|
|
894
|
+
const [height, setHeight] = useState(data.height);
|
|
895
|
+
const [width, setWidth] = useState(data.width ?? "full");
|
|
896
|
+
const [tool, setTool] = useState("select");
|
|
897
|
+
const [selectedId, setSelectedId] = useState(null);
|
|
898
|
+
const [editingText, setEditingText] = useState(null);
|
|
899
|
+
const [stroke, setStroke] = useState(STROKE_COLORS[0]);
|
|
900
|
+
const [fill, setFill] = useState(FILL_COLORS[0]);
|
|
901
|
+
const svgRef = useRef2(null);
|
|
902
|
+
const dragRef = useRef2(null);
|
|
903
|
+
const lastCommittedRef = useRef2(serializeDrawingData(data));
|
|
904
|
+
const shapesRef = useRef2(shapes);
|
|
1042
905
|
shapesRef.current = shapes;
|
|
1043
|
-
const heightRef =
|
|
906
|
+
const heightRef = useRef2(height);
|
|
1044
907
|
heightRef.current = height;
|
|
1045
|
-
|
|
908
|
+
const widthRef = useRef2(width);
|
|
909
|
+
widthRef.current = width;
|
|
910
|
+
useEffect5(() => {
|
|
1046
911
|
const incoming = serializeDrawingData(data);
|
|
1047
912
|
if (incoming !== lastCommittedRef.current) {
|
|
1048
913
|
lastCommittedRef.current = incoming;
|
|
1049
914
|
setShapes(data.shapes);
|
|
1050
915
|
setHeight(data.height);
|
|
916
|
+
setWidth(data.width ?? "full");
|
|
1051
917
|
setSelectedId(null);
|
|
1052
918
|
setEditingText(null);
|
|
1053
919
|
}
|
|
1054
920
|
}, [data]);
|
|
1055
|
-
const commit =
|
|
1056
|
-
(nextShapes, nextHeight) => {
|
|
921
|
+
const commit = useCallback(
|
|
922
|
+
(nextShapes, nextHeight, nextWidth) => {
|
|
1057
923
|
const payload = {
|
|
1058
924
|
version: 1,
|
|
1059
925
|
height: nextHeight ?? heightRef.current,
|
|
926
|
+
width: (nextWidth ?? widthRef.current) === "content" ? "content" : void 0,
|
|
1060
927
|
shapes: nextShapes
|
|
1061
928
|
};
|
|
1062
929
|
const json = serializeDrawingData(payload);
|
|
@@ -1071,7 +938,14 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1071
938
|
},
|
|
1072
939
|
[editor, nodeKey]
|
|
1073
940
|
);
|
|
1074
|
-
const
|
|
941
|
+
const applyWidth = useCallback(
|
|
942
|
+
(next) => {
|
|
943
|
+
setWidth(next);
|
|
944
|
+
commit(shapesRef.current, void 0, next);
|
|
945
|
+
},
|
|
946
|
+
[commit]
|
|
947
|
+
);
|
|
948
|
+
const updateShapes = useCallback(
|
|
1075
949
|
(updater, options) => {
|
|
1076
950
|
setShapes((prev) => {
|
|
1077
951
|
const next = resolveBindings(updater(prev));
|
|
@@ -1081,17 +955,17 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1081
955
|
},
|
|
1082
956
|
[commit]
|
|
1083
957
|
);
|
|
1084
|
-
const getPoint =
|
|
958
|
+
const getPoint = useCallback((e) => {
|
|
1085
959
|
const rect = svgRef.current?.getBoundingClientRect();
|
|
1086
960
|
if (!rect) return { x: 0, y: 0 };
|
|
1087
961
|
return { x: e.clientX - rect.left, y: e.clientY - rect.top };
|
|
1088
962
|
}, []);
|
|
1089
963
|
const selectedShape = shapes.find((s) => s.id === selectedId) ?? null;
|
|
1090
|
-
const startTextEditing =
|
|
964
|
+
const startTextEditing = useCallback((id, field) => {
|
|
1091
965
|
setEditingText({ id, field });
|
|
1092
966
|
setSelectedId(id);
|
|
1093
967
|
}, []);
|
|
1094
|
-
const handleBackgroundPointerDown =
|
|
968
|
+
const handleBackgroundPointerDown = useCallback(
|
|
1095
969
|
(e) => {
|
|
1096
970
|
if (!isEditable || editingText) return;
|
|
1097
971
|
if (e.button !== 0) return;
|
|
@@ -1151,7 +1025,7 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1151
1025
|
startTextEditing
|
|
1152
1026
|
]
|
|
1153
1027
|
);
|
|
1154
|
-
const handleShapePointerDown =
|
|
1028
|
+
const handleShapePointerDown = useCallback(
|
|
1155
1029
|
(e, shape) => {
|
|
1156
1030
|
if (!isEditable || tool !== "select" || editingText) return;
|
|
1157
1031
|
if (e.button !== 0) return;
|
|
@@ -1170,7 +1044,7 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1170
1044
|
},
|
|
1171
1045
|
[isEditable, tool, editingText, getPoint, selectedId]
|
|
1172
1046
|
);
|
|
1173
|
-
const handleHandlePointerDown =
|
|
1047
|
+
const handleHandlePointerDown = useCallback(
|
|
1174
1048
|
(e, drag) => {
|
|
1175
1049
|
if (!isEditable) return;
|
|
1176
1050
|
if (e.button !== 0) return;
|
|
@@ -1186,7 +1060,7 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1186
1060
|
},
|
|
1187
1061
|
[isEditable, updateShapes]
|
|
1188
1062
|
);
|
|
1189
|
-
const handlePointerMove =
|
|
1063
|
+
const handlePointerMove = useCallback(
|
|
1190
1064
|
(e) => {
|
|
1191
1065
|
const drag = dragRef.current;
|
|
1192
1066
|
if (!drag) return;
|
|
@@ -1277,7 +1151,7 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1277
1151
|
},
|
|
1278
1152
|
[getPoint, updateShapes]
|
|
1279
1153
|
);
|
|
1280
|
-
const handlePointerUp =
|
|
1154
|
+
const handlePointerUp = useCallback(() => {
|
|
1281
1155
|
const drag = dragRef.current;
|
|
1282
1156
|
dragRef.current = null;
|
|
1283
1157
|
if (!drag) return;
|
|
@@ -1330,7 +1204,7 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1330
1204
|
}
|
|
1331
1205
|
updateShapes((prev) => prev.map(normalize), { commit: true });
|
|
1332
1206
|
}, [commit, updateShapes, startTextEditing]);
|
|
1333
|
-
const deleteSelected =
|
|
1207
|
+
const deleteSelected = useCallback(() => {
|
|
1334
1208
|
if (!selectedId) return;
|
|
1335
1209
|
setEditingText(null);
|
|
1336
1210
|
setSelectedId(null);
|
|
@@ -1338,7 +1212,7 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1338
1212
|
commit: true
|
|
1339
1213
|
});
|
|
1340
1214
|
}, [selectedId, updateShapes]);
|
|
1341
|
-
const handleKeyDown =
|
|
1215
|
+
const handleKeyDown = useCallback(
|
|
1342
1216
|
(e) => {
|
|
1343
1217
|
if (!isEditable || editingText) return;
|
|
1344
1218
|
if ((e.key === "Delete" || e.key === "Backspace") && selectedId) {
|
|
@@ -1366,7 +1240,7 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1366
1240
|
startTextEditing
|
|
1367
1241
|
]
|
|
1368
1242
|
);
|
|
1369
|
-
const applyStroke =
|
|
1243
|
+
const applyStroke = useCallback(
|
|
1370
1244
|
(color) => {
|
|
1371
1245
|
setStroke(color);
|
|
1372
1246
|
if (selectedId) {
|
|
@@ -1378,7 +1252,7 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1378
1252
|
},
|
|
1379
1253
|
[selectedId, updateShapes]
|
|
1380
1254
|
);
|
|
1381
|
-
const applyFill =
|
|
1255
|
+
const applyFill = useCallback(
|
|
1382
1256
|
(color) => {
|
|
1383
1257
|
setFill(color);
|
|
1384
1258
|
if (selectedId) {
|
|
@@ -1390,7 +1264,7 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1390
1264
|
},
|
|
1391
1265
|
[selectedId, updateShapes]
|
|
1392
1266
|
);
|
|
1393
|
-
const applyRouting =
|
|
1267
|
+
const applyRouting = useCallback(
|
|
1394
1268
|
(elbow) => {
|
|
1395
1269
|
if (!selectedId) return;
|
|
1396
1270
|
updateShapes(
|
|
@@ -1407,7 +1281,7 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1407
1281
|
},
|
|
1408
1282
|
[selectedId, updateShapes]
|
|
1409
1283
|
);
|
|
1410
|
-
const addWaypoint =
|
|
1284
|
+
const addWaypoint = useCallback(
|
|
1411
1285
|
(e, shapeId, segmentIndex, point) => {
|
|
1412
1286
|
if (!isEditable) return;
|
|
1413
1287
|
if (e.button !== 0) return;
|
|
@@ -1429,7 +1303,7 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1429
1303
|
},
|
|
1430
1304
|
[isEditable, updateShapes]
|
|
1431
1305
|
);
|
|
1432
|
-
const removeWaypoint =
|
|
1306
|
+
const removeWaypoint = useCallback(
|
|
1433
1307
|
(shapeId, index) => {
|
|
1434
1308
|
updateShapes(
|
|
1435
1309
|
(prev) => prev.map((s) => {
|
|
@@ -1442,7 +1316,7 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1442
1316
|
},
|
|
1443
1317
|
[updateShapes]
|
|
1444
1318
|
);
|
|
1445
|
-
const applyDirection =
|
|
1319
|
+
const applyDirection = useCallback(
|
|
1446
1320
|
(bidirectional) => {
|
|
1447
1321
|
if (!selectedId) return;
|
|
1448
1322
|
updateShapes(
|
|
@@ -1454,7 +1328,7 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1454
1328
|
},
|
|
1455
1329
|
[selectedId, updateShapes]
|
|
1456
1330
|
);
|
|
1457
|
-
const commitText =
|
|
1331
|
+
const commitText = useCallback(
|
|
1458
1332
|
(id, field, value) => {
|
|
1459
1333
|
setEditingText(null);
|
|
1460
1334
|
updateShapes(
|
|
@@ -1478,12 +1352,12 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1478
1352
|
return /* @__PURE__ */ jsxs2(
|
|
1479
1353
|
"div",
|
|
1480
1354
|
{
|
|
1481
|
-
className:
|
|
1355
|
+
className: `zui-drawing-canvas ${width === "content" ? "is-content-width" : ""}`,
|
|
1482
1356
|
tabIndex: isEditable ? 0 : void 0,
|
|
1483
1357
|
onKeyDown: handleKeyDown,
|
|
1484
1358
|
children: [
|
|
1485
1359
|
isEditable && /* @__PURE__ */ jsxs2("div", { className: "zui-drawing-toolbar", onPointerDown: (e) => e.stopPropagation(), children: [
|
|
1486
|
-
/* @__PURE__ */
|
|
1360
|
+
/* @__PURE__ */ jsx2("div", { className: "zui-drawing-toolbar-group", children: TOOLS.map(({ tool: t, label }) => /* @__PURE__ */ jsx2(
|
|
1487
1361
|
"button",
|
|
1488
1362
|
{
|
|
1489
1363
|
type: "button",
|
|
@@ -1494,7 +1368,7 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1494
1368
|
setTool(t);
|
|
1495
1369
|
if (t !== "select") setSelectedId(null);
|
|
1496
1370
|
},
|
|
1497
|
-
children: /* @__PURE__ */
|
|
1371
|
+
children: /* @__PURE__ */ jsx2(
|
|
1498
1372
|
"svg",
|
|
1499
1373
|
{
|
|
1500
1374
|
viewBox: "0 0 20 20",
|
|
@@ -1512,8 +1386,8 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1512
1386
|
t
|
|
1513
1387
|
)) }),
|
|
1514
1388
|
/* @__PURE__ */ jsxs2("div", { className: "zui-drawing-toolbar-group", children: [
|
|
1515
|
-
/* @__PURE__ */
|
|
1516
|
-
STROKE_COLORS.map((color) => /* @__PURE__ */
|
|
1389
|
+
/* @__PURE__ */ jsx2("span", { className: "zui-drawing-swatch-label", children: "Stroke" }),
|
|
1390
|
+
STROKE_COLORS.map((color) => /* @__PURE__ */ jsx2(
|
|
1517
1391
|
"button",
|
|
1518
1392
|
{
|
|
1519
1393
|
type: "button",
|
|
@@ -1527,8 +1401,8 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1527
1401
|
))
|
|
1528
1402
|
] }),
|
|
1529
1403
|
showFill && /* @__PURE__ */ jsxs2("div", { className: "zui-drawing-toolbar-group", children: [
|
|
1530
|
-
/* @__PURE__ */
|
|
1531
|
-
FILL_COLORS.map((color) => /* @__PURE__ */
|
|
1404
|
+
/* @__PURE__ */ jsx2("span", { className: "zui-drawing-swatch-label", children: "Fill" }),
|
|
1405
|
+
FILL_COLORS.map((color) => /* @__PURE__ */ jsx2(
|
|
1532
1406
|
"button",
|
|
1533
1407
|
{
|
|
1534
1408
|
type: "button",
|
|
@@ -1542,7 +1416,7 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1542
1416
|
))
|
|
1543
1417
|
] }),
|
|
1544
1418
|
selectedShape && isConnectorType(selectedShape.type) && /* @__PURE__ */ jsxs2("div", { className: "zui-drawing-toolbar-group", children: [
|
|
1545
|
-
/* @__PURE__ */
|
|
1419
|
+
/* @__PURE__ */ jsx2(
|
|
1546
1420
|
"button",
|
|
1547
1421
|
{
|
|
1548
1422
|
type: "button",
|
|
@@ -1550,7 +1424,7 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1550
1424
|
"aria-label": "Straight connector",
|
|
1551
1425
|
className: `zui-drawing-tool ${selectedShape.routing !== "elbow" ? "is-active" : ""}`,
|
|
1552
1426
|
onClick: () => applyRouting(false),
|
|
1553
|
-
children: /* @__PURE__ */
|
|
1427
|
+
children: /* @__PURE__ */ jsx2(
|
|
1554
1428
|
"svg",
|
|
1555
1429
|
{
|
|
1556
1430
|
viewBox: "0 0 20 20",
|
|
@@ -1560,12 +1434,12 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1560
1434
|
stroke: "currentColor",
|
|
1561
1435
|
strokeWidth: "1.6",
|
|
1562
1436
|
strokeLinecap: "round",
|
|
1563
|
-
children: /* @__PURE__ */
|
|
1437
|
+
children: /* @__PURE__ */ jsx2("path", { d: "M4 16L16 4" })
|
|
1564
1438
|
}
|
|
1565
1439
|
)
|
|
1566
1440
|
}
|
|
1567
1441
|
),
|
|
1568
|
-
/* @__PURE__ */
|
|
1442
|
+
/* @__PURE__ */ jsx2(
|
|
1569
1443
|
"button",
|
|
1570
1444
|
{
|
|
1571
1445
|
type: "button",
|
|
@@ -1573,7 +1447,7 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1573
1447
|
"aria-label": "Elbow connector",
|
|
1574
1448
|
className: `zui-drawing-tool ${selectedShape.routing === "elbow" ? "is-active" : ""}`,
|
|
1575
1449
|
onClick: () => applyRouting(true),
|
|
1576
|
-
children: /* @__PURE__ */
|
|
1450
|
+
children: /* @__PURE__ */ jsx2(
|
|
1577
1451
|
"svg",
|
|
1578
1452
|
{
|
|
1579
1453
|
viewBox: "0 0 20 20",
|
|
@@ -1584,14 +1458,14 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1584
1458
|
strokeWidth: "1.6",
|
|
1585
1459
|
strokeLinecap: "round",
|
|
1586
1460
|
strokeLinejoin: "round",
|
|
1587
|
-
children: /* @__PURE__ */
|
|
1461
|
+
children: /* @__PURE__ */ jsx2("path", { d: "M4 16v-6h12V4" })
|
|
1588
1462
|
}
|
|
1589
1463
|
)
|
|
1590
1464
|
}
|
|
1591
1465
|
)
|
|
1592
1466
|
] }),
|
|
1593
1467
|
selectedShape?.type === "arrow" && /* @__PURE__ */ jsxs2("div", { className: "zui-drawing-toolbar-group", children: [
|
|
1594
|
-
/* @__PURE__ */
|
|
1468
|
+
/* @__PURE__ */ jsx2(
|
|
1595
1469
|
"button",
|
|
1596
1470
|
{
|
|
1597
1471
|
type: "button",
|
|
@@ -1599,7 +1473,7 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1599
1473
|
"aria-label": "One-way arrow",
|
|
1600
1474
|
className: `zui-drawing-tool ${!selectedShape.bidirectional ? "is-active" : ""}`,
|
|
1601
1475
|
onClick: () => applyDirection(false),
|
|
1602
|
-
children: /* @__PURE__ */
|
|
1476
|
+
children: /* @__PURE__ */ jsx2(
|
|
1603
1477
|
"svg",
|
|
1604
1478
|
{
|
|
1605
1479
|
viewBox: "0 0 20 20",
|
|
@@ -1610,12 +1484,12 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1610
1484
|
strokeWidth: "1.6",
|
|
1611
1485
|
strokeLinecap: "round",
|
|
1612
1486
|
strokeLinejoin: "round",
|
|
1613
|
-
children: /* @__PURE__ */
|
|
1487
|
+
children: /* @__PURE__ */ jsx2("path", { d: "M3 10h13M12 5.5L16.5 10L12 14.5" })
|
|
1614
1488
|
}
|
|
1615
1489
|
)
|
|
1616
1490
|
}
|
|
1617
1491
|
),
|
|
1618
|
-
/* @__PURE__ */
|
|
1492
|
+
/* @__PURE__ */ jsx2(
|
|
1619
1493
|
"button",
|
|
1620
1494
|
{
|
|
1621
1495
|
type: "button",
|
|
@@ -1623,7 +1497,7 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1623
1497
|
"aria-label": "Two-way arrow",
|
|
1624
1498
|
className: `zui-drawing-tool ${selectedShape.bidirectional ? "is-active" : ""}`,
|
|
1625
1499
|
onClick: () => applyDirection(true),
|
|
1626
|
-
children: /* @__PURE__ */
|
|
1500
|
+
children: /* @__PURE__ */ jsx2(
|
|
1627
1501
|
"svg",
|
|
1628
1502
|
{
|
|
1629
1503
|
viewBox: "0 0 20 20",
|
|
@@ -1634,13 +1508,13 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1634
1508
|
strokeWidth: "1.6",
|
|
1635
1509
|
strokeLinecap: "round",
|
|
1636
1510
|
strokeLinejoin: "round",
|
|
1637
|
-
children: /* @__PURE__ */
|
|
1511
|
+
children: /* @__PURE__ */ jsx2("path", { d: "M3.5 10h13M8 5.5L3.5 10L8 14.5M12 5.5L16.5 10L12 14.5" })
|
|
1638
1512
|
}
|
|
1639
1513
|
)
|
|
1640
1514
|
}
|
|
1641
1515
|
)
|
|
1642
1516
|
] }),
|
|
1643
|
-
selectedShape && /* @__PURE__ */
|
|
1517
|
+
selectedShape && /* @__PURE__ */ jsx2("div", { className: "zui-drawing-toolbar-group", children: /* @__PURE__ */ jsx2(
|
|
1644
1518
|
"button",
|
|
1645
1519
|
{
|
|
1646
1520
|
type: "button",
|
|
@@ -1648,7 +1522,7 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1648
1522
|
"aria-label": "Delete shape",
|
|
1649
1523
|
className: "zui-drawing-tool zui-drawing-tool-danger",
|
|
1650
1524
|
onClick: deleteSelected,
|
|
1651
|
-
children: /* @__PURE__ */
|
|
1525
|
+
children: /* @__PURE__ */ jsx2(
|
|
1652
1526
|
"svg",
|
|
1653
1527
|
{
|
|
1654
1528
|
viewBox: "0 0 20 20",
|
|
@@ -1658,18 +1532,48 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1658
1532
|
stroke: "currentColor",
|
|
1659
1533
|
strokeWidth: "1.6",
|
|
1660
1534
|
strokeLinecap: "round",
|
|
1661
|
-
children: /* @__PURE__ */
|
|
1535
|
+
children: /* @__PURE__ */ jsx2("path", { d: "M4 6h12M8 6V4h4v2M6 6l1 10h6l1-10M8.5 9v4M11.5 9v4" })
|
|
1662
1536
|
}
|
|
1663
1537
|
)
|
|
1664
1538
|
}
|
|
1665
|
-
) })
|
|
1539
|
+
) }),
|
|
1540
|
+
/* @__PURE__ */ jsx2("div", { className: "zui-drawing-toolbar-group zui-drawing-toolbar-group-end", children: BLOCK_WIDTH_OPTIONS.map(({ width: option, label, icon }) => /* @__PURE__ */ jsx2(
|
|
1541
|
+
"button",
|
|
1542
|
+
{
|
|
1543
|
+
type: "button",
|
|
1544
|
+
title: label,
|
|
1545
|
+
"aria-label": label,
|
|
1546
|
+
"aria-pressed": width === option,
|
|
1547
|
+
className: `zui-drawing-tool ${width === option ? "is-active" : ""}`,
|
|
1548
|
+
onClick: () => applyWidth(option),
|
|
1549
|
+
children: /* @__PURE__ */ jsx2(
|
|
1550
|
+
"svg",
|
|
1551
|
+
{
|
|
1552
|
+
viewBox: "0 0 20 20",
|
|
1553
|
+
width: "16",
|
|
1554
|
+
height: "16",
|
|
1555
|
+
fill: "none",
|
|
1556
|
+
stroke: "currentColor",
|
|
1557
|
+
strokeWidth: "1.6",
|
|
1558
|
+
strokeLinecap: "round",
|
|
1559
|
+
strokeLinejoin: "round",
|
|
1560
|
+
children: icon
|
|
1561
|
+
}
|
|
1562
|
+
)
|
|
1563
|
+
},
|
|
1564
|
+
option
|
|
1565
|
+
)) })
|
|
1666
1566
|
] }),
|
|
1667
1567
|
/* @__PURE__ */ jsxs2(
|
|
1668
1568
|
"svg",
|
|
1669
1569
|
{
|
|
1670
1570
|
ref: svgRef,
|
|
1671
1571
|
className: "zui-drawing-surface",
|
|
1672
|
-
style: {
|
|
1572
|
+
style: {
|
|
1573
|
+
height,
|
|
1574
|
+
width: width === "content" ? contentWidth(shapes) : void 0,
|
|
1575
|
+
cursor: canvasCursor
|
|
1576
|
+
},
|
|
1673
1577
|
onPointerDown: handleBackgroundPointerDown,
|
|
1674
1578
|
onPointerMove: handlePointerMove,
|
|
1675
1579
|
onPointerUp: handlePointerUp,
|
|
@@ -1700,7 +1604,7 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1700
1604
|
},
|
|
1701
1605
|
onPointerDown: (e) => handleShapePointerDown(e, shape),
|
|
1702
1606
|
children: [
|
|
1703
|
-
isConnectorType(shape.type) && /* @__PURE__ */
|
|
1607
|
+
isConnectorType(shape.type) && /* @__PURE__ */ jsx2(
|
|
1704
1608
|
"path",
|
|
1705
1609
|
{
|
|
1706
1610
|
d: connectorPath(shape),
|
|
@@ -1709,8 +1613,8 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1709
1613
|
strokeWidth: 14
|
|
1710
1614
|
}
|
|
1711
1615
|
),
|
|
1712
|
-
(isBoxType(shape.type) || shape.type === "text") && /* @__PURE__ */
|
|
1713
|
-
shape.id === editingText?.id && shape.type === "text" ? null : /* @__PURE__ */
|
|
1616
|
+
(isBoxType(shape.type) || shape.type === "text") && /* @__PURE__ */ jsx2(HitArea, { shape }),
|
|
1617
|
+
shape.id === editingText?.id && shape.type === "text" ? null : /* @__PURE__ */ jsx2(
|
|
1714
1618
|
ShapeView,
|
|
1715
1619
|
{
|
|
1716
1620
|
shape,
|
|
@@ -1722,7 +1626,7 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1722
1626
|
},
|
|
1723
1627
|
shape.id
|
|
1724
1628
|
)),
|
|
1725
|
-
selectedShape && isEditable && !editingText && /* @__PURE__ */
|
|
1629
|
+
selectedShape && isEditable && !editingText && /* @__PURE__ */ jsx2(
|
|
1726
1630
|
SelectionOverlay,
|
|
1727
1631
|
{
|
|
1728
1632
|
shape: selectedShape,
|
|
@@ -1734,7 +1638,7 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1734
1638
|
]
|
|
1735
1639
|
}
|
|
1736
1640
|
),
|
|
1737
|
-
editingShape && editingText && /* @__PURE__ */
|
|
1641
|
+
editingShape && editingText && /* @__PURE__ */ jsx2(
|
|
1738
1642
|
TextEditOverlay,
|
|
1739
1643
|
{
|
|
1740
1644
|
shape: editingShape,
|
|
@@ -1743,7 +1647,7 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1743
1647
|
},
|
|
1744
1648
|
`${editingShape.id}:${editingText.field}`
|
|
1745
1649
|
),
|
|
1746
|
-
isEditable && /* @__PURE__ */
|
|
1650
|
+
isEditable && /* @__PURE__ */ jsx2(
|
|
1747
1651
|
"div",
|
|
1748
1652
|
{
|
|
1749
1653
|
className: "zui-drawing-resize",
|
|
@@ -1782,7 +1686,7 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
1782
1686
|
}
|
|
1783
1687
|
function HitArea({ shape }) {
|
|
1784
1688
|
const b = bbox(shape);
|
|
1785
|
-
return /* @__PURE__ */
|
|
1689
|
+
return /* @__PURE__ */ jsx2(
|
|
1786
1690
|
"rect",
|
|
1787
1691
|
{
|
|
1788
1692
|
x: b.x,
|
|
@@ -1817,7 +1721,7 @@ function SelectionOverlay({
|
|
|
1817
1721
|
return [{ index: i, x: (p.x + q.x) / 2, y: (p.y + q.y) / 2 }];
|
|
1818
1722
|
});
|
|
1819
1723
|
return /* @__PURE__ */ jsxs2("g", { children: [
|
|
1820
|
-
ghosts.map(({ index, x, y }) => /* @__PURE__ */
|
|
1724
|
+
ghosts.map(({ index, x, y }) => /* @__PURE__ */ jsx2(
|
|
1821
1725
|
"circle",
|
|
1822
1726
|
{
|
|
1823
1727
|
cx: x,
|
|
@@ -1830,11 +1734,11 @@ function SelectionOverlay({
|
|
|
1830
1734
|
opacity: 0.6,
|
|
1831
1735
|
style: { cursor: "copy" },
|
|
1832
1736
|
onPointerDown: (e) => onWaypointAdd(e, shape.id, index, { x, y }),
|
|
1833
|
-
children: /* @__PURE__ */
|
|
1737
|
+
children: /* @__PURE__ */ jsx2("title", { children: "Drag to add a point" })
|
|
1834
1738
|
},
|
|
1835
1739
|
`ghost-${index}`
|
|
1836
1740
|
)),
|
|
1837
|
-
waypoints.map((p, i) => /* @__PURE__ */
|
|
1741
|
+
waypoints.map((p, i) => /* @__PURE__ */ jsx2(
|
|
1838
1742
|
"rect",
|
|
1839
1743
|
{
|
|
1840
1744
|
x: p.x - 4,
|
|
@@ -1855,11 +1759,11 @@ function SelectionOverlay({
|
|
|
1855
1759
|
e.stopPropagation();
|
|
1856
1760
|
onWaypointRemove(shape.id, i);
|
|
1857
1761
|
},
|
|
1858
|
-
children: /* @__PURE__ */
|
|
1762
|
+
children: /* @__PURE__ */ jsx2("title", { children: "Drag to move, double-click to remove" })
|
|
1859
1763
|
},
|
|
1860
1764
|
`wp-${i}`
|
|
1861
1765
|
)),
|
|
1862
|
-
ends.map(({ end, x, y }) => /* @__PURE__ */
|
|
1766
|
+
ends.map(({ end, x, y }) => /* @__PURE__ */ jsx2(
|
|
1863
1767
|
"circle",
|
|
1864
1768
|
{
|
|
1865
1769
|
cx: x,
|
|
@@ -1878,7 +1782,7 @@ function SelectionOverlay({
|
|
|
1878
1782
|
},
|
|
1879
1783
|
end
|
|
1880
1784
|
)),
|
|
1881
|
-
elbowMid && /* @__PURE__ */
|
|
1785
|
+
elbowMid && /* @__PURE__ */ jsx2(
|
|
1882
1786
|
"rect",
|
|
1883
1787
|
{
|
|
1884
1788
|
x: elbowMid.x - 4,
|
|
@@ -1909,7 +1813,7 @@ function SelectionOverlay({
|
|
|
1909
1813
|
];
|
|
1910
1814
|
const resizable = shape.type !== "text";
|
|
1911
1815
|
return /* @__PURE__ */ jsxs2("g", { children: [
|
|
1912
|
-
/* @__PURE__ */
|
|
1816
|
+
/* @__PURE__ */ jsx2(
|
|
1913
1817
|
"rect",
|
|
1914
1818
|
{
|
|
1915
1819
|
x: b.x - pad,
|
|
@@ -1923,7 +1827,7 @@ function SelectionOverlay({
|
|
|
1923
1827
|
pointerEvents: "none"
|
|
1924
1828
|
}
|
|
1925
1829
|
),
|
|
1926
|
-
resizable && corners.map(({ corner, x, y }) => /* @__PURE__ */
|
|
1830
|
+
resizable && corners.map(({ corner, x, y }) => /* @__PURE__ */ jsx2(
|
|
1927
1831
|
"rect",
|
|
1928
1832
|
{
|
|
1929
1833
|
x: x - 4,
|
|
@@ -1949,9 +1853,9 @@ function TextEditOverlay({
|
|
|
1949
1853
|
onCommit
|
|
1950
1854
|
}) {
|
|
1951
1855
|
const original = shape[field] ?? "";
|
|
1952
|
-
const [value, setValue] =
|
|
1953
|
-
const ref =
|
|
1954
|
-
|
|
1856
|
+
const [value, setValue] = useState(original);
|
|
1857
|
+
const ref = useRef2(null);
|
|
1858
|
+
useEffect5(() => {
|
|
1955
1859
|
ref.current?.focus();
|
|
1956
1860
|
ref.current?.select();
|
|
1957
1861
|
}, []);
|
|
@@ -1999,7 +1903,7 @@ function TextEditOverlay({
|
|
|
1999
1903
|
});
|
|
2000
1904
|
}
|
|
2001
1905
|
const placeholder = field === "label" ? "Label" : field === "footer" ? "Footer" : "Text";
|
|
2002
|
-
return /* @__PURE__ */
|
|
1906
|
+
return /* @__PURE__ */ jsx2(
|
|
2003
1907
|
"textarea",
|
|
2004
1908
|
{
|
|
2005
1909
|
ref,
|
|
@@ -2025,7 +1929,7 @@ function TextEditOverlay({
|
|
|
2025
1929
|
}
|
|
2026
1930
|
|
|
2027
1931
|
// src/nodes/DrawingNode.tsx
|
|
2028
|
-
import { jsx as
|
|
1932
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
2029
1933
|
var DrawingNode = class _DrawingNode extends DecoratorNode {
|
|
2030
1934
|
static getType() {
|
|
2031
1935
|
return "drawing";
|
|
@@ -2089,11 +1993,11 @@ var DrawingNode = class _DrawingNode extends DecoratorNode {
|
|
|
2089
1993
|
return false;
|
|
2090
1994
|
}
|
|
2091
1995
|
decorate(_editor, _config) {
|
|
2092
|
-
return /* @__PURE__ */
|
|
1996
|
+
return /* @__PURE__ */ jsx3(DrawingCanvas, { nodeKey: this.getKey(), data: this.getData() });
|
|
2093
1997
|
}
|
|
2094
1998
|
};
|
|
2095
1999
|
function $createDrawingNode(data = serializeDrawingData(EMPTY_DRAWING)) {
|
|
2096
|
-
return $
|
|
2000
|
+
return $applyNodeReplacement2(new DrawingNode(data));
|
|
2097
2001
|
}
|
|
2098
2002
|
function $isDrawingNode(node) {
|
|
2099
2003
|
return node instanceof DrawingNode;
|
|
@@ -2119,303 +2023,106 @@ ${node.getLatest().__data}
|
|
|
2119
2023
|
type: "multiline-element"
|
|
2120
2024
|
};
|
|
2121
2025
|
|
|
2122
|
-
// src/
|
|
2123
|
-
import {
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2026
|
+
// src/transformers/tableTransformer.ts
|
|
2027
|
+
import {
|
|
2028
|
+
$isParagraphNode as $isParagraphNode2,
|
|
2029
|
+
$isTextNode
|
|
2030
|
+
} from "lexical";
|
|
2031
|
+
import {
|
|
2032
|
+
$convertFromMarkdownString as $convertFromMarkdownString2,
|
|
2033
|
+
$convertToMarkdownString as $convertToMarkdownString2,
|
|
2034
|
+
TRANSFORMERS
|
|
2035
|
+
} from "@lexical/markdown";
|
|
2036
|
+
import {
|
|
2037
|
+
$createTableCellNode,
|
|
2038
|
+
$createTableNode,
|
|
2039
|
+
$createTableRowNode,
|
|
2040
|
+
$isTableCellNode,
|
|
2041
|
+
$isTableNode as $isTableNode2,
|
|
2042
|
+
$isTableRowNode,
|
|
2043
|
+
TableCellHeaderStates,
|
|
2044
|
+
TableCellNode,
|
|
2045
|
+
TableNode,
|
|
2046
|
+
TableRowNode
|
|
2047
|
+
} from "@lexical/table";
|
|
2048
|
+
|
|
2049
|
+
// src/transformers/tableWidth.ts
|
|
2050
|
+
import {
|
|
2051
|
+
$getNodeByKey as $getNodeByKey3,
|
|
2052
|
+
$getSelection as $getSelection2,
|
|
2053
|
+
$isRangeSelection as $isRangeSelection2
|
|
2054
|
+
} from "lexical";
|
|
2055
|
+
import { $findMatchingParent } from "@lexical/utils";
|
|
2056
|
+
import { $isTableNode, $isTableSelection } from "@lexical/table";
|
|
2057
|
+
var TABLE_WIDTH_MARKER = "<!-- width: content -->";
|
|
2058
|
+
var TABLE_WIDTH_MARKER_REG_EXP = /^<!--\s*width:\s*content\s*-->\s*$/;
|
|
2059
|
+
var CONTENT_WIDTH_STYLE = "width: auto; table-layout: auto";
|
|
2060
|
+
var CONTENT_WIDTH_REG_EXP = /(^|;)\s*width:\s*auto\s*(;|$)/;
|
|
2061
|
+
function $getTableWidth(table) {
|
|
2062
|
+
return CONTENT_WIDTH_REG_EXP.test(table.getStyle()) ? "content" : "full";
|
|
2063
|
+
}
|
|
2064
|
+
function $setTableWidth(table, width) {
|
|
2065
|
+
table.setStyle(width === "content" ? CONTENT_WIDTH_STYLE : "");
|
|
2066
|
+
}
|
|
2067
|
+
function $getSelectedTable() {
|
|
2068
|
+
const selection = $getSelection2();
|
|
2069
|
+
if ($isTableSelection(selection)) {
|
|
2070
|
+
const node = $getNodeByKey3(selection.tableKey);
|
|
2071
|
+
return $isTableNode(node) ? node : null;
|
|
2156
2072
|
}
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
/* @__PURE__ */ new Set()
|
|
2073
|
+
if (!$isRangeSelection2(selection)) return null;
|
|
2074
|
+
const table = $findMatchingParent(
|
|
2075
|
+
selection.anchor.getNode(),
|
|
2076
|
+
(node) => $isTableNode(node)
|
|
2162
2077
|
);
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2078
|
+
return $isTableNode(table) ? table : null;
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
// src/transformers/tableTransformer.ts
|
|
2082
|
+
var TABLE_ROW_REG_EXP = /^\|(.+)\|\s?$/;
|
|
2083
|
+
var TABLE_ROW_DIVIDER_REG_EXP = /^(\| ?:?-*:? ?)+\|\s?$/;
|
|
2084
|
+
function $isTableWidthMarker(node) {
|
|
2085
|
+
if (!$isParagraphNode2(node) || node.getChildrenSize() !== 1) return false;
|
|
2086
|
+
const child = node.getFirstChild();
|
|
2087
|
+
return $isTextNode(child) && TABLE_WIDTH_MARKER_REG_EXP.test(child.getTextContent());
|
|
2088
|
+
}
|
|
2089
|
+
function getTableColumnsSize(table) {
|
|
2090
|
+
const row = table.getFirstChild();
|
|
2091
|
+
return $isTableRowNode(row) ? row.getChildrenSize() : 0;
|
|
2092
|
+
}
|
|
2093
|
+
function createTableCell(textContent) {
|
|
2094
|
+
const unescaped = textContent.replace(/\\n/g, "\n");
|
|
2095
|
+
const cell = $createTableCellNode(TableCellHeaderStates.NO_STATUS);
|
|
2096
|
+
$convertFromMarkdownString2(unescaped, TRANSFORMERS, cell);
|
|
2097
|
+
return cell;
|
|
2098
|
+
}
|
|
2099
|
+
function mapToTableCells(textContent) {
|
|
2100
|
+
const match = textContent.match(TABLE_ROW_REG_EXP);
|
|
2101
|
+
if (!match || !match[1]) return null;
|
|
2102
|
+
return match[1].split("|").map((text) => createTableCell(text.trim()));
|
|
2103
|
+
}
|
|
2104
|
+
var TABLE = {
|
|
2105
|
+
dependencies: [TableNode, TableRowNode, TableCellNode],
|
|
2106
|
+
export: (node) => {
|
|
2107
|
+
if (!$isTableNode2(node)) return null;
|
|
2108
|
+
const output = [];
|
|
2109
|
+
if ($getTableWidth(node) === "content") output.push(TABLE_WIDTH_MARKER);
|
|
2110
|
+
for (const row of node.getChildren()) {
|
|
2111
|
+
if (!$isTableRowNode(row)) continue;
|
|
2112
|
+
const rowOutput = [];
|
|
2113
|
+
let isHeaderRow = false;
|
|
2114
|
+
for (const cell of row.getChildren()) {
|
|
2115
|
+
if (!$isTableCellNode(cell)) continue;
|
|
2116
|
+
rowOutput.push(
|
|
2117
|
+
$convertToMarkdownString2(TRANSFORMERS, cell).replace(/\n/g, "\\n")
|
|
2118
|
+
);
|
|
2119
|
+
if (cell.__headerState === TableCellHeaderStates.ROW) {
|
|
2120
|
+
isHeaderRow = true;
|
|
2174
2121
|
}
|
|
2175
|
-
setActiveFormats(next);
|
|
2176
|
-
});
|
|
2177
|
-
});
|
|
2178
|
-
}, [editor]);
|
|
2179
|
-
const insertTable = useCallback4(() => {
|
|
2180
|
-
editor.dispatchCommand(INSERT_TABLE_COMMAND, {
|
|
2181
|
-
columns: "3",
|
|
2182
|
-
rows: "3",
|
|
2183
|
-
includeHeaders: { rows: true, columns: false }
|
|
2184
|
-
});
|
|
2185
|
-
}, [editor]);
|
|
2186
|
-
const insertDrawing = useCallback4(() => {
|
|
2187
|
-
editor.update(() => {
|
|
2188
|
-
const drawing = $createDrawingNode();
|
|
2189
|
-
$insertNodeToNearestRoot(drawing);
|
|
2190
|
-
});
|
|
2191
|
-
}, [editor]);
|
|
2192
|
-
return /* @__PURE__ */ jsxs3("div", { className: "zui-text-editor-toolbar", children: [
|
|
2193
|
-
FORMAT_BUTTONS.map(({ format, label, icon }) => /* @__PURE__ */ jsx5(
|
|
2194
|
-
"button",
|
|
2195
|
-
{
|
|
2196
|
-
type: "button",
|
|
2197
|
-
title: label,
|
|
2198
|
-
"aria-label": label,
|
|
2199
|
-
"aria-pressed": activeFormats.has(format),
|
|
2200
|
-
className: `zui-text-editor-toolbar-button ${activeFormats.has(format) ? "is-active" : ""}`,
|
|
2201
|
-
onMouseDown: (e) => e.preventDefault(),
|
|
2202
|
-
onClick: () => editor.dispatchCommand(FORMAT_TEXT_COMMAND, format),
|
|
2203
|
-
children: /* @__PURE__ */ jsx5(
|
|
2204
|
-
"svg",
|
|
2205
|
-
{
|
|
2206
|
-
viewBox: "0 0 20 20",
|
|
2207
|
-
width: "16",
|
|
2208
|
-
height: "16",
|
|
2209
|
-
fill: "none",
|
|
2210
|
-
stroke: "currentColor",
|
|
2211
|
-
strokeLinecap: "round",
|
|
2212
|
-
strokeLinejoin: "round",
|
|
2213
|
-
children: icon
|
|
2214
|
-
}
|
|
2215
|
-
)
|
|
2216
|
-
},
|
|
2217
|
-
format
|
|
2218
|
-
)),
|
|
2219
|
-
/* @__PURE__ */ jsx5("div", { className: "zui-text-editor-toolbar-divider" }),
|
|
2220
|
-
/* @__PURE__ */ jsx5(
|
|
2221
|
-
"button",
|
|
2222
|
-
{
|
|
2223
|
-
type: "button",
|
|
2224
|
-
title: "Insert table",
|
|
2225
|
-
"aria-label": "Insert table",
|
|
2226
|
-
className: "zui-text-editor-toolbar-button",
|
|
2227
|
-
onMouseDown: (e) => e.preventDefault(),
|
|
2228
|
-
onClick: insertTable,
|
|
2229
|
-
children: /* @__PURE__ */ jsxs3(
|
|
2230
|
-
"svg",
|
|
2231
|
-
{
|
|
2232
|
-
viewBox: "0 0 20 20",
|
|
2233
|
-
width: "16",
|
|
2234
|
-
height: "16",
|
|
2235
|
-
fill: "none",
|
|
2236
|
-
stroke: "currentColor",
|
|
2237
|
-
strokeWidth: "1.5",
|
|
2238
|
-
strokeLinecap: "round",
|
|
2239
|
-
children: [
|
|
2240
|
-
/* @__PURE__ */ jsx5("rect", { x: "3", y: "3.5", width: "14", height: "13", rx: "1.5" }),
|
|
2241
|
-
/* @__PURE__ */ jsx5("path", { d: "M3 8h14M8 8v8.5M13 8v8.5" })
|
|
2242
|
-
]
|
|
2243
|
-
}
|
|
2244
|
-
)
|
|
2245
2122
|
}
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
{
|
|
2250
|
-
type: "button",
|
|
2251
|
-
title: "Insert drawing",
|
|
2252
|
-
"aria-label": "Insert drawing",
|
|
2253
|
-
className: "zui-text-editor-toolbar-button",
|
|
2254
|
-
onMouseDown: (e) => e.preventDefault(),
|
|
2255
|
-
onClick: insertDrawing,
|
|
2256
|
-
children: /* @__PURE__ */ jsxs3(
|
|
2257
|
-
"svg",
|
|
2258
|
-
{
|
|
2259
|
-
viewBox: "0 0 20 20",
|
|
2260
|
-
width: "16",
|
|
2261
|
-
height: "16",
|
|
2262
|
-
fill: "none",
|
|
2263
|
-
stroke: "currentColor",
|
|
2264
|
-
strokeWidth: "1.5",
|
|
2265
|
-
strokeLinecap: "round",
|
|
2266
|
-
strokeLinejoin: "round",
|
|
2267
|
-
children: [
|
|
2268
|
-
/* @__PURE__ */ jsx5("rect", { x: "3", y: "5", width: "8", height: "6", rx: "1.5" }),
|
|
2269
|
-
/* @__PURE__ */ jsx5("path", { d: "M13.5 8h3M14.5 6.5L17.5 8l-3 1.5" }),
|
|
2270
|
-
/* @__PURE__ */ jsx5("ellipse", { cx: "13", cy: "14.5", rx: "4", ry: "2.8" })
|
|
2271
|
-
]
|
|
2272
|
-
}
|
|
2273
|
-
)
|
|
2274
|
-
}
|
|
2275
|
-
)
|
|
2276
|
-
] });
|
|
2277
|
-
}
|
|
2278
|
-
|
|
2279
|
-
// src/nodes/FrontmatterNode.ts
|
|
2280
|
-
import {
|
|
2281
|
-
$applyNodeReplacement as $applyNodeReplacement2,
|
|
2282
|
-
$createLineBreakNode,
|
|
2283
|
-
$createTextNode,
|
|
2284
|
-
ElementNode
|
|
2285
|
-
} from "lexical";
|
|
2286
|
-
var FrontmatterNode = class _FrontmatterNode extends ElementNode {
|
|
2287
|
-
static getType() {
|
|
2288
|
-
return "frontmatter";
|
|
2289
|
-
}
|
|
2290
|
-
static clone(node) {
|
|
2291
|
-
return new _FrontmatterNode(node.__key);
|
|
2292
|
-
}
|
|
2293
|
-
constructor(key) {
|
|
2294
|
-
super(key);
|
|
2295
|
-
}
|
|
2296
|
-
createDOM(config) {
|
|
2297
|
-
const dom = document.createElement("div");
|
|
2298
|
-
const className = config.theme.frontmatter;
|
|
2299
|
-
dom.className = typeof className === "string" ? className : "zui-frontmatter";
|
|
2300
|
-
return dom;
|
|
2301
|
-
}
|
|
2302
|
-
updateDOM() {
|
|
2303
|
-
return false;
|
|
2304
|
-
}
|
|
2305
|
-
static importJSON(_serializedNode) {
|
|
2306
|
-
return $createFrontmatterNode();
|
|
2307
|
-
}
|
|
2308
|
-
exportJSON() {
|
|
2309
|
-
return {
|
|
2310
|
-
...super.exportJSON(),
|
|
2311
|
-
type: "frontmatter",
|
|
2312
|
-
version: 1
|
|
2313
|
-
};
|
|
2314
|
-
}
|
|
2315
|
-
isInline() {
|
|
2316
|
-
return false;
|
|
2317
|
-
}
|
|
2318
|
-
canBeEmpty() {
|
|
2319
|
-
return false;
|
|
2320
|
-
}
|
|
2321
|
-
canIndent() {
|
|
2322
|
-
return false;
|
|
2323
|
-
}
|
|
2324
|
-
};
|
|
2325
|
-
function $createFrontmatterNode() {
|
|
2326
|
-
return $applyNodeReplacement2(new FrontmatterNode());
|
|
2327
|
-
}
|
|
2328
|
-
function $isFrontmatterNode(node) {
|
|
2329
|
-
return node instanceof FrontmatterNode;
|
|
2330
|
-
}
|
|
2331
|
-
var FRONTMATTER_DELIMITER = /^---\s*$/;
|
|
2332
|
-
var FRONTMATTER = {
|
|
2333
|
-
dependencies: [FrontmatterNode],
|
|
2334
|
-
export: (node) => {
|
|
2335
|
-
if (!$isFrontmatterNode(node)) return null;
|
|
2336
|
-
const body = node.getTextContent();
|
|
2337
|
-
return body ? `---
|
|
2338
|
-
${body}
|
|
2339
|
-
---` : "---\n---";
|
|
2340
|
-
},
|
|
2341
|
-
regExpStart: FRONTMATTER_DELIMITER,
|
|
2342
|
-
regExpEnd: FRONTMATTER_DELIMITER,
|
|
2343
|
-
replace: (rootNode, children, _startMatch, _endMatch, linesInBetween, isImport) => {
|
|
2344
|
-
if (!isImport || children != null || linesInBetween == null) return false;
|
|
2345
|
-
if (rootNode.getChildrenSize() !== 0) return false;
|
|
2346
|
-
const lines = [...linesInBetween];
|
|
2347
|
-
while (lines.length > 0 && lines[0].length === 0) lines.shift();
|
|
2348
|
-
while (lines.length > 0 && lines[lines.length - 1].length === 0) lines.pop();
|
|
2349
|
-
const node = $createFrontmatterNode();
|
|
2350
|
-
lines.forEach((line, index) => {
|
|
2351
|
-
if (index > 0) node.append($createLineBreakNode());
|
|
2352
|
-
node.append($createTextNode(line));
|
|
2353
|
-
});
|
|
2354
|
-
rootNode.append(node);
|
|
2355
|
-
},
|
|
2356
|
-
type: "multiline-element"
|
|
2357
|
-
};
|
|
2358
|
-
|
|
2359
|
-
// src/transformers/tableTransformer.ts
|
|
2360
|
-
import {
|
|
2361
|
-
$isParagraphNode as $isParagraphNode2,
|
|
2362
|
-
$isTextNode
|
|
2363
|
-
} from "lexical";
|
|
2364
|
-
import {
|
|
2365
|
-
$convertFromMarkdownString as $convertFromMarkdownString2,
|
|
2366
|
-
$convertToMarkdownString as $convertToMarkdownString2,
|
|
2367
|
-
TRANSFORMERS
|
|
2368
|
-
} from "@lexical/markdown";
|
|
2369
|
-
import {
|
|
2370
|
-
$createTableCellNode,
|
|
2371
|
-
$createTableNode,
|
|
2372
|
-
$createTableRowNode,
|
|
2373
|
-
$isTableCellNode,
|
|
2374
|
-
$isTableNode,
|
|
2375
|
-
$isTableRowNode,
|
|
2376
|
-
TableCellHeaderStates,
|
|
2377
|
-
TableCellNode,
|
|
2378
|
-
TableNode,
|
|
2379
|
-
TableRowNode
|
|
2380
|
-
} from "@lexical/table";
|
|
2381
|
-
var TABLE_ROW_REG_EXP = /^\|(.+)\|\s?$/;
|
|
2382
|
-
var TABLE_ROW_DIVIDER_REG_EXP = /^(\| ?:?-*:? ?)+\|\s?$/;
|
|
2383
|
-
function getTableColumnsSize(table) {
|
|
2384
|
-
const row = table.getFirstChild();
|
|
2385
|
-
return $isTableRowNode(row) ? row.getChildrenSize() : 0;
|
|
2386
|
-
}
|
|
2387
|
-
function createTableCell(textContent) {
|
|
2388
|
-
const unescaped = textContent.replace(/\\n/g, "\n");
|
|
2389
|
-
const cell = $createTableCellNode(TableCellHeaderStates.NO_STATUS);
|
|
2390
|
-
$convertFromMarkdownString2(unescaped, TRANSFORMERS, cell);
|
|
2391
|
-
return cell;
|
|
2392
|
-
}
|
|
2393
|
-
function mapToTableCells(textContent) {
|
|
2394
|
-
const match = textContent.match(TABLE_ROW_REG_EXP);
|
|
2395
|
-
if (!match || !match[1]) return null;
|
|
2396
|
-
return match[1].split("|").map((text) => createTableCell(text.trim()));
|
|
2397
|
-
}
|
|
2398
|
-
var TABLE = {
|
|
2399
|
-
dependencies: [TableNode, TableRowNode, TableCellNode],
|
|
2400
|
-
export: (node) => {
|
|
2401
|
-
if (!$isTableNode(node)) return null;
|
|
2402
|
-
const output = [];
|
|
2403
|
-
for (const row of node.getChildren()) {
|
|
2404
|
-
if (!$isTableRowNode(row)) continue;
|
|
2405
|
-
const rowOutput = [];
|
|
2406
|
-
let isHeaderRow = false;
|
|
2407
|
-
for (const cell of row.getChildren()) {
|
|
2408
|
-
if (!$isTableCellNode(cell)) continue;
|
|
2409
|
-
rowOutput.push(
|
|
2410
|
-
$convertToMarkdownString2(TRANSFORMERS, cell).replace(/\n/g, "\\n")
|
|
2411
|
-
);
|
|
2412
|
-
if (cell.__headerState === TableCellHeaderStates.ROW) {
|
|
2413
|
-
isHeaderRow = true;
|
|
2414
|
-
}
|
|
2415
|
-
}
|
|
2416
|
-
output.push(`| ${rowOutput.join(" | ")} |`);
|
|
2417
|
-
if (isHeaderRow) {
|
|
2418
|
-
output.push(`| ${rowOutput.map(() => "---").join(" | ")} |`);
|
|
2123
|
+
output.push(`| ${rowOutput.join(" | ")} |`);
|
|
2124
|
+
if (isHeaderRow) {
|
|
2125
|
+
output.push(`| ${rowOutput.map(() => "---").join(" | ")} |`);
|
|
2419
2126
|
}
|
|
2420
2127
|
}
|
|
2421
2128
|
return output.join("\n");
|
|
@@ -2424,7 +2131,7 @@ var TABLE = {
|
|
|
2424
2131
|
replace: (parentNode, _children, match) => {
|
|
2425
2132
|
if (TABLE_ROW_DIVIDER_REG_EXP.test(match[0])) {
|
|
2426
2133
|
const table2 = parentNode.getPreviousSibling();
|
|
2427
|
-
if (!table2 || !$
|
|
2134
|
+
if (!table2 || !$isTableNode2(table2)) return;
|
|
2428
2135
|
const lastRow = table2.getLastChild();
|
|
2429
2136
|
if (!lastRow || !$isTableRowNode(lastRow)) return;
|
|
2430
2137
|
lastRow.getChildren().forEach((cell) => {
|
|
@@ -2461,10 +2168,14 @@ var TABLE = {
|
|
|
2461
2168
|
}
|
|
2462
2169
|
}
|
|
2463
2170
|
const previousSibling = parentNode.getPreviousSibling();
|
|
2464
|
-
if ($
|
|
2171
|
+
if ($isTableNode2(previousSibling) && getTableColumnsSize(previousSibling) === maxCells) {
|
|
2465
2172
|
previousSibling.append(...table.getChildren());
|
|
2466
2173
|
parentNode.remove();
|
|
2467
2174
|
} else {
|
|
2175
|
+
if ($isTableWidthMarker(previousSibling)) {
|
|
2176
|
+
$setTableWidth(table, "content");
|
|
2177
|
+
previousSibling?.remove();
|
|
2178
|
+
}
|
|
2468
2179
|
parentNode.replace(table);
|
|
2469
2180
|
}
|
|
2470
2181
|
table.selectEnd();
|
|
@@ -2551,10 +2262,20 @@ var editorTheme = {
|
|
|
2551
2262
|
drawing: "zui-drawing"
|
|
2552
2263
|
};
|
|
2553
2264
|
|
|
2554
|
-
// src/
|
|
2555
|
-
import { jsx as
|
|
2265
|
+
// src/EditorRoot.tsx
|
|
2266
|
+
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
2556
2267
|
var SYNC_TRANSFORMERS = [FRONTMATTER, DRAWING, CHECK_LIST, TABLE, ...TRANSFORMERS2];
|
|
2557
2268
|
var SHORTCUT_TRANSFORMERS = [TABLE, ...TRANSFORMERS2.filter((t) => t !== CODE)];
|
|
2269
|
+
var EditorContext = createContext(null);
|
|
2270
|
+
function useEditorContext() {
|
|
2271
|
+
const ctx = useContext(EditorContext);
|
|
2272
|
+
if (!ctx) {
|
|
2273
|
+
throw new Error(
|
|
2274
|
+
"@zuilib/text-editor: this component must be rendered inside <MarkdownEditor> or <MarkdownEditor.Root>"
|
|
2275
|
+
);
|
|
2276
|
+
}
|
|
2277
|
+
return ctx;
|
|
2278
|
+
}
|
|
2558
2279
|
function onError(error) {
|
|
2559
2280
|
console.error(error);
|
|
2560
2281
|
}
|
|
@@ -2574,49 +2295,38 @@ var editorNodes = [
|
|
|
2574
2295
|
DrawingNode
|
|
2575
2296
|
];
|
|
2576
2297
|
function ReadOnlyPlugin({ readOnly }) {
|
|
2577
|
-
const [editor] =
|
|
2578
|
-
|
|
2298
|
+
const [editor] = useLexicalComposerContext6();
|
|
2299
|
+
useEffect6(() => {
|
|
2579
2300
|
editor.setEditable(!readOnly);
|
|
2580
2301
|
}, [editor, readOnly]);
|
|
2581
2302
|
return null;
|
|
2582
2303
|
}
|
|
2583
|
-
function
|
|
2304
|
+
function EditorRoot({
|
|
2584
2305
|
value,
|
|
2585
2306
|
onChange,
|
|
2586
|
-
placeholder = "Start writing...",
|
|
2587
2307
|
readOnly = false,
|
|
2588
2308
|
className,
|
|
2589
2309
|
mode = "edit-md",
|
|
2590
2310
|
autoFocus = false,
|
|
2591
|
-
|
|
2592
|
-
outline = false,
|
|
2593
|
-
foldable = true
|
|
2311
|
+
children
|
|
2594
2312
|
}) {
|
|
2595
|
-
const latestValueRef =
|
|
2596
|
-
const [mountKey, setMountKey] =
|
|
2597
|
-
const [capturedMarkdown, setCapturedMarkdown] =
|
|
2598
|
-
|
|
2313
|
+
const latestValueRef = useRef3(value ?? "");
|
|
2314
|
+
const [mountKey, setMountKey] = useState2(0);
|
|
2315
|
+
const [capturedMarkdown, setCapturedMarkdown] = useState2(value ?? "");
|
|
2316
|
+
useEffect6(() => {
|
|
2599
2317
|
if (value !== void 0) {
|
|
2600
2318
|
latestValueRef.current = value;
|
|
2601
2319
|
}
|
|
2602
2320
|
}, [value]);
|
|
2603
|
-
const
|
|
2604
|
-
(e) => {
|
|
2605
|
-
const newValue = e.target.value;
|
|
2606
|
-
latestValueRef.current = newValue;
|
|
2607
|
-
onChange?.(newValue);
|
|
2608
|
-
},
|
|
2609
|
-
[onChange]
|
|
2610
|
-
);
|
|
2611
|
-
const handleLexicalChange = useCallback5(
|
|
2321
|
+
const handleChange = useCallback2(
|
|
2612
2322
|
(newValue) => {
|
|
2613
2323
|
latestValueRef.current = newValue;
|
|
2614
2324
|
onChange?.(newValue);
|
|
2615
2325
|
},
|
|
2616
2326
|
[onChange]
|
|
2617
2327
|
);
|
|
2618
|
-
const prevModeRef =
|
|
2619
|
-
|
|
2328
|
+
const prevModeRef = useRef3(mode);
|
|
2329
|
+
useEffect6(() => {
|
|
2620
2330
|
if (prevModeRef.current === "edit-raw" && mode !== "edit-raw") {
|
|
2621
2331
|
setCapturedMarkdown(latestValueRef.current);
|
|
2622
2332
|
setMountKey((k) => k + 1);
|
|
@@ -2632,78 +2342,713 @@ function MarkdownEditor({
|
|
|
2632
2342
|
}),
|
|
2633
2343
|
[]
|
|
2634
2344
|
);
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2345
|
+
const context = useMemo(
|
|
2346
|
+
() => ({
|
|
2347
|
+
mode,
|
|
2348
|
+
readOnly,
|
|
2349
|
+
autoFocus,
|
|
2350
|
+
rawValue: value ?? "",
|
|
2351
|
+
onRawChange: handleChange
|
|
2352
|
+
}),
|
|
2353
|
+
[mode, readOnly, autoFocus, value, handleChange]
|
|
2354
|
+
);
|
|
2355
|
+
const isRaw = mode === "edit-raw";
|
|
2356
|
+
return /* @__PURE__ */ jsx4(LexicalComposer, { initialConfig, children: /* @__PURE__ */ jsx4(EditorContext.Provider, { value: context, children: /* @__PURE__ */ jsxs3("div", { className: `zui-text-editor ${className ?? ""}`, children: [
|
|
2357
|
+
children,
|
|
2358
|
+
!isRaw && /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
2359
|
+
/* @__PURE__ */ jsx4(
|
|
2360
|
+
MarkdownSyncPlugin,
|
|
2361
|
+
{
|
|
2362
|
+
initialMarkdown: mode === "view" ? value ?? "" : capturedMarkdown,
|
|
2363
|
+
onChange: handleChange,
|
|
2364
|
+
transformers: SYNC_TRANSFORMERS
|
|
2365
|
+
}
|
|
2366
|
+
),
|
|
2367
|
+
/* @__PURE__ */ jsx4(HistoryPlugin, {}),
|
|
2368
|
+
/* @__PURE__ */ jsx4(ListPlugin, {}),
|
|
2369
|
+
/* @__PURE__ */ jsx4(CheckListPlugin, {}),
|
|
2370
|
+
/* @__PURE__ */ jsx4(ChecklistShortcutPlugin, {}),
|
|
2371
|
+
/* @__PURE__ */ jsx4(CodeBlockShortcutPlugin, {}),
|
|
2372
|
+
/* @__PURE__ */ jsx4(CodeHighlightPlugin, {}),
|
|
2373
|
+
/* @__PURE__ */ jsx4(TablePlugin, {}),
|
|
2374
|
+
/* @__PURE__ */ jsx4(LinkPlugin, {}),
|
|
2375
|
+
/* @__PURE__ */ jsx4(MarkdownShortcutPlugin, { transformers: SHORTCUT_TRANSFORMERS }),
|
|
2376
|
+
/* @__PURE__ */ jsx4(ReadOnlyPlugin, { readOnly: readOnly || mode === "view" }),
|
|
2377
|
+
autoFocus && /* @__PURE__ */ jsx4(AutoFocusPlugin, {})
|
|
2378
|
+
] })
|
|
2379
|
+
] }) }) }, mountKey);
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2382
|
+
// src/EditorContent.tsx
|
|
2383
|
+
import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
|
|
2384
|
+
import { ContentEditable } from "@lexical/react/LexicalContentEditable";
|
|
2385
|
+
import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary";
|
|
2386
|
+
|
|
2387
|
+
// src/plugins/FoldingPlugin.tsx
|
|
2388
|
+
import {
|
|
2389
|
+
useCallback as useCallback3,
|
|
2390
|
+
useEffect as useEffect7,
|
|
2391
|
+
useRef as useRef4,
|
|
2392
|
+
useState as useState3
|
|
2393
|
+
} from "react";
|
|
2394
|
+
import { $getRoot, $getSelection as $getSelection3, $isRangeSelection as $isRangeSelection3 } from "lexical";
|
|
2395
|
+
import { useLexicalComposerContext as useLexicalComposerContext7 } from "@lexical/react/LexicalComposerContext";
|
|
2396
|
+
import { $isHeadingNode } from "@lexical/rich-text";
|
|
2397
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
2398
|
+
var HEADING_LEVELS = {
|
|
2399
|
+
h1: 1,
|
|
2400
|
+
h2: 2,
|
|
2401
|
+
h3: 3,
|
|
2402
|
+
h4: 4,
|
|
2403
|
+
h5: 5,
|
|
2404
|
+
h6: 6
|
|
2405
|
+
};
|
|
2406
|
+
function FoldingPlugin() {
|
|
2407
|
+
const [editor] = useLexicalComposerContext7();
|
|
2408
|
+
const [foldedKeys, setFoldedKeys] = useState3(/* @__PURE__ */ new Set());
|
|
2409
|
+
const [buttons, setButtons] = useState3([]);
|
|
2410
|
+
const foldedRef = useRef4(foldedKeys);
|
|
2411
|
+
foldedRef.current = foldedKeys;
|
|
2412
|
+
const sync = useCallback3(() => {
|
|
2413
|
+
editor.getEditorState().read(() => {
|
|
2414
|
+
const children = $getRoot().getChildren();
|
|
2415
|
+
const folded = foldedRef.current;
|
|
2416
|
+
const hidden = /* @__PURE__ */ new Set();
|
|
2417
|
+
const headings = [];
|
|
2418
|
+
for (let i = 0; i < children.length; i++) {
|
|
2419
|
+
const node = children[i];
|
|
2420
|
+
if (!$isHeadingNode(node)) continue;
|
|
2421
|
+
const level = HEADING_LEVELS[node.getTag()] ?? 6;
|
|
2422
|
+
const isFolded = folded.has(node.getKey());
|
|
2423
|
+
headings.push({ key: node.getKey(), folded: isFolded });
|
|
2424
|
+
if (!isFolded) continue;
|
|
2425
|
+
for (let j = i + 1; j < children.length; j++) {
|
|
2426
|
+
const sibling = children[j];
|
|
2427
|
+
if ($isHeadingNode(sibling) && (HEADING_LEVELS[sibling.getTag()] ?? 6) <= level) {
|
|
2428
|
+
break;
|
|
2429
|
+
}
|
|
2430
|
+
hidden.add(sibling.getKey());
|
|
2431
|
+
}
|
|
2646
2432
|
}
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
),
|
|
2664
|
-
ErrorBoundary: LexicalErrorBoundary
|
|
2433
|
+
const selection = $getSelection3();
|
|
2434
|
+
if ($isRangeSelection3(selection)) {
|
|
2435
|
+
const topLevel = selection.anchor.getNode().getTopLevelElement();
|
|
2436
|
+
if (topLevel && hidden.has(topLevel.getKey())) {
|
|
2437
|
+
let node = topLevel.getPreviousSibling();
|
|
2438
|
+
while (node) {
|
|
2439
|
+
if ($isHeadingNode(node) && folded.has(node.getKey())) {
|
|
2440
|
+
const unfoldKey = node.getKey();
|
|
2441
|
+
setFoldedKeys((prev) => {
|
|
2442
|
+
const next = new Set(prev);
|
|
2443
|
+
next.delete(unfoldKey);
|
|
2444
|
+
return next;
|
|
2445
|
+
});
|
|
2446
|
+
return;
|
|
2447
|
+
}
|
|
2448
|
+
node = node.getPreviousSibling();
|
|
2665
2449
|
}
|
|
2666
|
-
|
|
2667
|
-
foldable && /* @__PURE__ */ jsx6(FoldingPlugin, {})
|
|
2668
|
-
] }),
|
|
2669
|
-
outline && /* @__PURE__ */ jsx6(OutlinePlugin, {})
|
|
2670
|
-
] }),
|
|
2671
|
-
/* @__PURE__ */ jsx6(
|
|
2672
|
-
MarkdownSyncPlugin,
|
|
2673
|
-
{
|
|
2674
|
-
initialMarkdown: mode === "view" ? value ?? "" : capturedMarkdown,
|
|
2675
|
-
onChange: handleLexicalChange,
|
|
2676
|
-
transformers: SYNC_TRANSFORMERS
|
|
2450
|
+
}
|
|
2677
2451
|
}
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2452
|
+
for (const child of children) {
|
|
2453
|
+
const element = editor.getElementByKey(child.getKey());
|
|
2454
|
+
if (!element) continue;
|
|
2455
|
+
const shouldHide = hidden.has(child.getKey());
|
|
2456
|
+
if (shouldHide) {
|
|
2457
|
+
element.style.display = "none";
|
|
2458
|
+
} else if (element.style.display === "none") {
|
|
2459
|
+
element.style.display = "";
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
setButtons(
|
|
2463
|
+
headings.map(({ key, folded: isFolded }) => {
|
|
2464
|
+
const element = editor.getElementByKey(key);
|
|
2465
|
+
element?.classList.toggle("zui-heading-folded", isFolded);
|
|
2466
|
+
return {
|
|
2467
|
+
key,
|
|
2468
|
+
folded: isFolded,
|
|
2469
|
+
top: element?.offsetTop ?? 0,
|
|
2470
|
+
height: element?.offsetHeight ?? 0
|
|
2471
|
+
};
|
|
2472
|
+
})
|
|
2473
|
+
);
|
|
2474
|
+
});
|
|
2475
|
+
}, [editor]);
|
|
2476
|
+
useEffect7(() => {
|
|
2477
|
+
sync();
|
|
2478
|
+
const unregister = editor.registerUpdateListener(sync);
|
|
2479
|
+
const rootElement = editor.getRootElement();
|
|
2480
|
+
const observer = typeof ResizeObserver !== "undefined" ? new ResizeObserver(sync) : null;
|
|
2481
|
+
if (rootElement && observer) observer.observe(rootElement);
|
|
2482
|
+
return () => {
|
|
2483
|
+
unregister();
|
|
2484
|
+
observer?.disconnect();
|
|
2485
|
+
};
|
|
2486
|
+
}, [editor, sync, foldedKeys]);
|
|
2487
|
+
const toggle = useCallback3((key) => {
|
|
2488
|
+
setFoldedKeys((prev) => {
|
|
2489
|
+
const next = new Set(prev);
|
|
2490
|
+
if (next.has(key)) next.delete(key);
|
|
2491
|
+
else next.add(key);
|
|
2492
|
+
return next;
|
|
2493
|
+
});
|
|
2494
|
+
}, []);
|
|
2495
|
+
return /* @__PURE__ */ jsx5("div", { className: "zui-text-editor-fold-gutter", children: buttons.map(({ key, folded, top, height }) => /* @__PURE__ */ jsx5(
|
|
2496
|
+
"button",
|
|
2497
|
+
{
|
|
2498
|
+
type: "button",
|
|
2499
|
+
className: `zui-text-editor-fold ${folded ? "is-folded" : ""}`,
|
|
2500
|
+
style: { top: top + Math.max(0, (Math.min(height, 44) - 18) / 2) },
|
|
2501
|
+
title: folded ? "Expand section" : "Collapse section",
|
|
2502
|
+
"aria-label": folded ? "Expand section" : "Collapse section",
|
|
2503
|
+
"aria-expanded": !folded,
|
|
2504
|
+
onClick: () => toggle(key),
|
|
2505
|
+
children: /* @__PURE__ */ jsx5(
|
|
2506
|
+
"svg",
|
|
2507
|
+
{
|
|
2508
|
+
viewBox: "0 0 20 20",
|
|
2509
|
+
width: "12",
|
|
2510
|
+
height: "12",
|
|
2511
|
+
fill: "none",
|
|
2512
|
+
stroke: "currentColor",
|
|
2513
|
+
strokeWidth: "2",
|
|
2514
|
+
strokeLinecap: "round",
|
|
2515
|
+
strokeLinejoin: "round",
|
|
2516
|
+
children: /* @__PURE__ */ jsx5("path", { d: "M6 8l4 4 4-4" })
|
|
2517
|
+
}
|
|
2518
|
+
)
|
|
2519
|
+
},
|
|
2520
|
+
key
|
|
2521
|
+
)) });
|
|
2522
|
+
}
|
|
2523
|
+
|
|
2524
|
+
// src/plugins/TableWidthPlugin.tsx
|
|
2525
|
+
import { useCallback as useCallback5, useEffect as useEffect9, useState as useState5 } from "react";
|
|
2526
|
+
import { useLexicalComposerContext as useLexicalComposerContext9 } from "@lexical/react/LexicalComposerContext";
|
|
2527
|
+
import { useLexicalEditable as useLexicalEditable2 } from "@lexical/react/useLexicalEditable";
|
|
2528
|
+
|
|
2529
|
+
// src/useMarkdownEditor.ts
|
|
2530
|
+
import { useCallback as useCallback4, useEffect as useEffect8, useState as useState4 } from "react";
|
|
2531
|
+
import {
|
|
2532
|
+
$getSelection as $getSelection4,
|
|
2533
|
+
$isRangeSelection as $isRangeSelection4,
|
|
2534
|
+
CAN_REDO_COMMAND,
|
|
2535
|
+
CAN_UNDO_COMMAND,
|
|
2536
|
+
COMMAND_PRIORITY_LOW as COMMAND_PRIORITY_LOW2,
|
|
2537
|
+
FORMAT_TEXT_COMMAND,
|
|
2538
|
+
REDO_COMMAND,
|
|
2539
|
+
UNDO_COMMAND
|
|
2540
|
+
} from "lexical";
|
|
2541
|
+
import { useLexicalComposerContext as useLexicalComposerContext8 } from "@lexical/react/LexicalComposerContext";
|
|
2542
|
+
import { INSERT_TABLE_COMMAND } from "@lexical/table";
|
|
2543
|
+
import { $insertNodeToNearestRoot, mergeRegister } from "@lexical/utils";
|
|
2544
|
+
var TRACKED_FORMATS = [
|
|
2545
|
+
"bold",
|
|
2546
|
+
"italic",
|
|
2547
|
+
"underline",
|
|
2548
|
+
"strikethrough",
|
|
2549
|
+
"code"
|
|
2550
|
+
];
|
|
2551
|
+
function useMarkdownEditor() {
|
|
2552
|
+
const [editor] = useLexicalComposerContext8();
|
|
2553
|
+
const [activeFormats, setActiveFormats] = useState4(
|
|
2554
|
+
/* @__PURE__ */ new Set()
|
|
2555
|
+
);
|
|
2556
|
+
const [canUndo, setCanUndo] = useState4(false);
|
|
2557
|
+
const [canRedo, setCanRedo] = useState4(false);
|
|
2558
|
+
const [tableWidth, setTableWidthState] = useState4(null);
|
|
2559
|
+
useEffect8(
|
|
2560
|
+
() => mergeRegister(
|
|
2561
|
+
editor.registerUpdateListener(({ editorState }) => {
|
|
2562
|
+
editorState.read(() => {
|
|
2563
|
+
const table = $getSelectedTable();
|
|
2564
|
+
setTableWidthState(table ? $getTableWidth(table) : null);
|
|
2565
|
+
const selection = $getSelection4();
|
|
2566
|
+
if (!$isRangeSelection4(selection)) {
|
|
2567
|
+
setActiveFormats(/* @__PURE__ */ new Set());
|
|
2568
|
+
return;
|
|
2569
|
+
}
|
|
2570
|
+
const next = /* @__PURE__ */ new Set();
|
|
2571
|
+
for (const format of TRACKED_FORMATS) {
|
|
2572
|
+
if (selection.hasFormat(format)) next.add(format);
|
|
2573
|
+
}
|
|
2574
|
+
setActiveFormats(next);
|
|
2575
|
+
});
|
|
2576
|
+
}),
|
|
2577
|
+
editor.registerCommand(
|
|
2578
|
+
CAN_UNDO_COMMAND,
|
|
2579
|
+
(payload) => {
|
|
2580
|
+
setCanUndo(payload);
|
|
2581
|
+
return false;
|
|
2582
|
+
},
|
|
2583
|
+
COMMAND_PRIORITY_LOW2
|
|
2584
|
+
),
|
|
2585
|
+
editor.registerCommand(
|
|
2586
|
+
CAN_REDO_COMMAND,
|
|
2587
|
+
(payload) => {
|
|
2588
|
+
setCanRedo(payload);
|
|
2589
|
+
return false;
|
|
2590
|
+
},
|
|
2591
|
+
COMMAND_PRIORITY_LOW2
|
|
2592
|
+
)
|
|
2593
|
+
),
|
|
2594
|
+
[editor]
|
|
2595
|
+
);
|
|
2596
|
+
const toggleFormat = useCallback4(
|
|
2597
|
+
(format) => {
|
|
2598
|
+
editor.dispatchCommand(FORMAT_TEXT_COMMAND, format);
|
|
2599
|
+
},
|
|
2600
|
+
[editor]
|
|
2601
|
+
);
|
|
2602
|
+
const insertTable = useCallback4(
|
|
2603
|
+
({ rows = 3, columns = 3 } = {}) => {
|
|
2604
|
+
editor.dispatchCommand(INSERT_TABLE_COMMAND, {
|
|
2605
|
+
columns: String(columns),
|
|
2606
|
+
rows: String(rows),
|
|
2607
|
+
includeHeaders: { rows: true, columns: false }
|
|
2608
|
+
});
|
|
2609
|
+
},
|
|
2610
|
+
[editor]
|
|
2611
|
+
);
|
|
2612
|
+
const insertDrawing = useCallback4(() => {
|
|
2613
|
+
editor.update(() => {
|
|
2614
|
+
$insertNodeToNearestRoot($createDrawingNode());
|
|
2615
|
+
});
|
|
2616
|
+
}, [editor]);
|
|
2617
|
+
const setTableWidth = useCallback4(
|
|
2618
|
+
(width) => {
|
|
2619
|
+
editor.update(() => {
|
|
2620
|
+
const table = $getSelectedTable();
|
|
2621
|
+
if (table) $setTableWidth(table, width);
|
|
2622
|
+
});
|
|
2623
|
+
},
|
|
2624
|
+
[editor]
|
|
2625
|
+
);
|
|
2626
|
+
const undo = useCallback4(() => {
|
|
2627
|
+
editor.dispatchCommand(UNDO_COMMAND, void 0);
|
|
2628
|
+
}, [editor]);
|
|
2629
|
+
const redo = useCallback4(() => {
|
|
2630
|
+
editor.dispatchCommand(REDO_COMMAND, void 0);
|
|
2631
|
+
}, [editor]);
|
|
2632
|
+
return {
|
|
2633
|
+
editor,
|
|
2634
|
+
activeFormats,
|
|
2635
|
+
toggleFormat,
|
|
2636
|
+
insertTable,
|
|
2637
|
+
insertDrawing,
|
|
2638
|
+
tableWidth,
|
|
2639
|
+
setTableWidth,
|
|
2640
|
+
canUndo,
|
|
2641
|
+
canRedo,
|
|
2642
|
+
undo,
|
|
2643
|
+
redo
|
|
2644
|
+
};
|
|
2645
|
+
}
|
|
2646
|
+
|
|
2647
|
+
// src/components/Toolbar.tsx
|
|
2648
|
+
import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
2649
|
+
function Toolbar({ children, className }) {
|
|
2650
|
+
return /* @__PURE__ */ jsx6(
|
|
2651
|
+
"div",
|
|
2652
|
+
{
|
|
2653
|
+
className: `zui-text-editor-toolbar ${className ?? ""}`,
|
|
2654
|
+
role: "toolbar",
|
|
2655
|
+
children: children ?? /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
2656
|
+
/* @__PURE__ */ jsx6(FormatButtons, {}),
|
|
2657
|
+
/* @__PURE__ */ jsx6(ToolbarDivider, {}),
|
|
2658
|
+
/* @__PURE__ */ jsx6(InsertButtons, {})
|
|
2659
|
+
] })
|
|
2660
|
+
}
|
|
2661
|
+
);
|
|
2662
|
+
}
|
|
2663
|
+
function ToolbarDivider() {
|
|
2664
|
+
return /* @__PURE__ */ jsx6("div", { className: "zui-text-editor-toolbar-divider" });
|
|
2665
|
+
}
|
|
2666
|
+
function ToolbarButton({
|
|
2667
|
+
label,
|
|
2668
|
+
onClick,
|
|
2669
|
+
children,
|
|
2670
|
+
active = false,
|
|
2671
|
+
disabled = false,
|
|
2672
|
+
className
|
|
2673
|
+
}) {
|
|
2674
|
+
return /* @__PURE__ */ jsx6(
|
|
2675
|
+
"button",
|
|
2676
|
+
{
|
|
2677
|
+
type: "button",
|
|
2678
|
+
title: label,
|
|
2679
|
+
"aria-label": label,
|
|
2680
|
+
"aria-pressed": active,
|
|
2681
|
+
disabled,
|
|
2682
|
+
className: `zui-text-editor-toolbar-button ${active ? "is-active" : ""} ${className ?? ""}`,
|
|
2683
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
2684
|
+
onClick,
|
|
2685
|
+
children
|
|
2686
|
+
}
|
|
2687
|
+
);
|
|
2688
|
+
}
|
|
2689
|
+
function Icon({ children, strokeWidth = 1.5 }) {
|
|
2690
|
+
return /* @__PURE__ */ jsx6(
|
|
2691
|
+
"svg",
|
|
2692
|
+
{
|
|
2693
|
+
viewBox: "0 0 20 20",
|
|
2694
|
+
width: "16",
|
|
2695
|
+
height: "16",
|
|
2696
|
+
fill: "none",
|
|
2697
|
+
stroke: "currentColor",
|
|
2698
|
+
strokeWidth,
|
|
2699
|
+
strokeLinecap: "round",
|
|
2700
|
+
strokeLinejoin: "round",
|
|
2701
|
+
children
|
|
2702
|
+
}
|
|
2703
|
+
);
|
|
2704
|
+
}
|
|
2705
|
+
var FORMAT_BUTTONS = [
|
|
2706
|
+
{
|
|
2707
|
+
format: "bold",
|
|
2708
|
+
label: "Bold",
|
|
2709
|
+
icon: /* @__PURE__ */ jsx6(Icon, { strokeWidth: 1.8, children: /* @__PURE__ */ jsx6("path", { d: "M6 3.5h5a3 3 0 0 1 0 6H6zm0 6h6a3 3 0 0 1 0 6H6z" }) })
|
|
2710
|
+
},
|
|
2711
|
+
{
|
|
2712
|
+
format: "italic",
|
|
2713
|
+
label: "Italic",
|
|
2714
|
+
icon: /* @__PURE__ */ jsx6(Icon, { strokeWidth: 1.6, children: /* @__PURE__ */ jsx6("path", { d: "M8.5 3.5h6M5.5 16.5h6M11.5 3.5l-3 13" }) })
|
|
2715
|
+
},
|
|
2716
|
+
{
|
|
2717
|
+
format: "strikethrough",
|
|
2718
|
+
label: "Strikethrough",
|
|
2719
|
+
icon: /* @__PURE__ */ jsx6(Icon, { strokeWidth: 1.6, children: /* @__PURE__ */ jsx6("path", { d: "M4 10h12M13.5 5.5c-.6-1.2-2-2-3.5-2-2 0-3.5 1.2-3.5 2.8 0 .5.1.9.4 1.3m-.4 5c.5 1.6 2 2.9 3.9 2.9 2 0 3.6-1.2 3.6-2.9 0-.4-.1-.8-.2-1.1" }) })
|
|
2720
|
+
},
|
|
2721
|
+
{
|
|
2722
|
+
format: "code",
|
|
2723
|
+
label: "Inline code",
|
|
2724
|
+
icon: /* @__PURE__ */ jsx6(Icon, { strokeWidth: 1.6, children: /* @__PURE__ */ jsx6("path", { d: "M7.5 6L4 10l3.5 4M12.5 6L16 10l-3.5 4" }) })
|
|
2725
|
+
}
|
|
2726
|
+
];
|
|
2727
|
+
function FormatButtons() {
|
|
2728
|
+
const { activeFormats, toggleFormat } = useMarkdownEditor();
|
|
2729
|
+
return /* @__PURE__ */ jsx6(Fragment3, { children: FORMAT_BUTTONS.map(({ format, label, icon }) => /* @__PURE__ */ jsx6(
|
|
2730
|
+
ToolbarButton,
|
|
2731
|
+
{
|
|
2732
|
+
label,
|
|
2733
|
+
active: activeFormats.has(format),
|
|
2734
|
+
onClick: () => toggleFormat(format),
|
|
2735
|
+
children: icon
|
|
2736
|
+
},
|
|
2737
|
+
format
|
|
2738
|
+
)) });
|
|
2739
|
+
}
|
|
2740
|
+
function InsertButtons() {
|
|
2741
|
+
const { insertTable, insertDrawing } = useMarkdownEditor();
|
|
2742
|
+
return /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
2743
|
+
/* @__PURE__ */ jsx6(ToolbarButton, { label: "Insert table", onClick: () => insertTable(), children: /* @__PURE__ */ jsxs4(Icon, { children: [
|
|
2744
|
+
/* @__PURE__ */ jsx6("rect", { x: "3", y: "3.5", width: "14", height: "13", rx: "1.5" }),
|
|
2745
|
+
/* @__PURE__ */ jsx6("path", { d: "M3 8h14M8 8v8.5M13 8v8.5" })
|
|
2746
|
+
] }) }),
|
|
2747
|
+
/* @__PURE__ */ jsx6(ToolbarButton, { label: "Insert drawing", onClick: insertDrawing, children: /* @__PURE__ */ jsxs4(Icon, { children: [
|
|
2748
|
+
/* @__PURE__ */ jsx6("rect", { x: "3", y: "5", width: "8", height: "6", rx: "1.5" }),
|
|
2749
|
+
/* @__PURE__ */ jsx6("path", { d: "M13.5 8h3M14.5 6.5L17.5 8l-3 1.5" }),
|
|
2750
|
+
/* @__PURE__ */ jsx6("ellipse", { cx: "13", cy: "14.5", rx: "4", ry: "2.8" })
|
|
2751
|
+
] }) })
|
|
2752
|
+
] });
|
|
2753
|
+
}
|
|
2754
|
+
function HistoryButtons() {
|
|
2755
|
+
const { canUndo, canRedo, undo, redo } = useMarkdownEditor();
|
|
2756
|
+
return /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
2757
|
+
/* @__PURE__ */ jsx6(ToolbarButton, { label: "Undo", onClick: undo, disabled: !canUndo, children: /* @__PURE__ */ jsxs4(Icon, { children: [
|
|
2758
|
+
/* @__PURE__ */ jsx6("path", { d: "M7 6L3.5 9.5 7 13" }),
|
|
2759
|
+
/* @__PURE__ */ jsx6("path", { d: "M3.5 9.5H12a4 4 0 0 1 0 8h-1" })
|
|
2760
|
+
] }) }),
|
|
2761
|
+
/* @__PURE__ */ jsx6(ToolbarButton, { label: "Redo", onClick: redo, disabled: !canRedo, children: /* @__PURE__ */ jsxs4(Icon, { children: [
|
|
2762
|
+
/* @__PURE__ */ jsx6("path", { d: "M13 6l3.5 3.5L13 13" }),
|
|
2763
|
+
/* @__PURE__ */ jsx6("path", { d: "M16.5 9.5H8a4 4 0 0 0 0 8h1" })
|
|
2764
|
+
] }) })
|
|
2765
|
+
] });
|
|
2766
|
+
}
|
|
2767
|
+
|
|
2768
|
+
// src/plugins/TableWidthPlugin.tsx
|
|
2769
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
2770
|
+
function TableWidthPlugin() {
|
|
2771
|
+
const [editor] = useLexicalComposerContext9();
|
|
2772
|
+
const isEditable = useLexicalEditable2();
|
|
2773
|
+
const { tableWidth, setTableWidth } = useMarkdownEditor();
|
|
2774
|
+
const [anchor, setAnchor] = useState5(null);
|
|
2775
|
+
const sync = useCallback5(() => {
|
|
2776
|
+
const tableKey = editor.getEditorState().read(() => $getSelectedTable()?.getKey());
|
|
2777
|
+
const element = tableKey ? editor.getElementByKey(tableKey) : null;
|
|
2778
|
+
const main = editor.getRootElement()?.parentElement;
|
|
2779
|
+
if (!element || !main) {
|
|
2780
|
+
setAnchor(null);
|
|
2781
|
+
return;
|
|
2782
|
+
}
|
|
2783
|
+
const rect = element.getBoundingClientRect();
|
|
2784
|
+
const mainRect = main.getBoundingClientRect();
|
|
2785
|
+
setAnchor({
|
|
2786
|
+
top: rect.top - mainRect.top,
|
|
2787
|
+
right: mainRect.right - rect.right + 8
|
|
2788
|
+
});
|
|
2789
|
+
}, [editor]);
|
|
2790
|
+
useEffect9(() => {
|
|
2791
|
+
sync();
|
|
2792
|
+
const unregister = editor.registerUpdateListener(sync);
|
|
2793
|
+
const rootElement = editor.getRootElement();
|
|
2794
|
+
const observer = typeof ResizeObserver !== "undefined" ? new ResizeObserver(sync) : null;
|
|
2795
|
+
if (rootElement && observer) observer.observe(rootElement);
|
|
2796
|
+
return () => {
|
|
2797
|
+
unregister();
|
|
2798
|
+
observer?.disconnect();
|
|
2799
|
+
};
|
|
2800
|
+
}, [editor, sync]);
|
|
2801
|
+
if (!isEditable || !anchor || !tableWidth) return null;
|
|
2802
|
+
return /* @__PURE__ */ jsx7(
|
|
2803
|
+
"div",
|
|
2804
|
+
{
|
|
2805
|
+
className: "zui-table-width",
|
|
2806
|
+
role: "group",
|
|
2807
|
+
"aria-label": "Table width",
|
|
2808
|
+
style: { top: anchor.top, right: anchor.right },
|
|
2809
|
+
children: BLOCK_WIDTH_OPTIONS.map(({ width, label, icon }) => /* @__PURE__ */ jsx7(
|
|
2810
|
+
ToolbarButton,
|
|
2811
|
+
{
|
|
2812
|
+
label,
|
|
2813
|
+
active: tableWidth === width,
|
|
2814
|
+
onClick: () => setTableWidth(width),
|
|
2815
|
+
children: /* @__PURE__ */ jsx7(
|
|
2816
|
+
"svg",
|
|
2817
|
+
{
|
|
2818
|
+
viewBox: "0 0 20 20",
|
|
2819
|
+
width: "16",
|
|
2820
|
+
height: "16",
|
|
2821
|
+
fill: "none",
|
|
2822
|
+
stroke: "currentColor",
|
|
2823
|
+
strokeWidth: "1.6",
|
|
2824
|
+
strokeLinecap: "round",
|
|
2825
|
+
strokeLinejoin: "round",
|
|
2826
|
+
children: icon
|
|
2827
|
+
}
|
|
2828
|
+
)
|
|
2829
|
+
},
|
|
2830
|
+
width
|
|
2831
|
+
))
|
|
2832
|
+
}
|
|
2833
|
+
);
|
|
2834
|
+
}
|
|
2835
|
+
|
|
2836
|
+
// src/EditorContent.tsx
|
|
2837
|
+
import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2838
|
+
function EditorContent({
|
|
2839
|
+
placeholder = "Start writing...",
|
|
2840
|
+
foldable = true,
|
|
2841
|
+
children
|
|
2842
|
+
}) {
|
|
2843
|
+
const { mode, readOnly, autoFocus, rawValue, onRawChange } = useEditorContext();
|
|
2844
|
+
if (mode === "edit-raw") {
|
|
2845
|
+
return /* @__PURE__ */ jsx8(
|
|
2846
|
+
"textarea",
|
|
2847
|
+
{
|
|
2848
|
+
className: "zui-text-editor-textarea",
|
|
2849
|
+
value: rawValue,
|
|
2850
|
+
onChange: (e) => onRawChange(e.target.value),
|
|
2851
|
+
placeholder,
|
|
2852
|
+
readOnly,
|
|
2853
|
+
autoFocus,
|
|
2854
|
+
spellCheck: false
|
|
2855
|
+
}
|
|
2856
|
+
);
|
|
2857
|
+
}
|
|
2858
|
+
return /* @__PURE__ */ jsxs5("div", { className: "zui-text-editor-body", children: [
|
|
2859
|
+
/* @__PURE__ */ jsxs5("div", { className: "zui-text-editor-main", children: [
|
|
2860
|
+
/* @__PURE__ */ jsx8(
|
|
2861
|
+
RichTextPlugin,
|
|
2862
|
+
{
|
|
2863
|
+
contentEditable: /* @__PURE__ */ jsx8(
|
|
2864
|
+
ContentEditable,
|
|
2865
|
+
{
|
|
2866
|
+
className: "zui-text-editor-content",
|
|
2867
|
+
"aria-placeholder": placeholder,
|
|
2868
|
+
placeholder: /* @__PURE__ */ jsx8("div", { className: "zui-text-editor-placeholder", children: placeholder })
|
|
2869
|
+
}
|
|
2870
|
+
),
|
|
2871
|
+
ErrorBoundary: LexicalErrorBoundary
|
|
2872
|
+
}
|
|
2873
|
+
),
|
|
2874
|
+
foldable && /* @__PURE__ */ jsx8(FoldingPlugin, {}),
|
|
2875
|
+
/* @__PURE__ */ jsx8(TableWidthPlugin, {})
|
|
2876
|
+
] }),
|
|
2877
|
+
children
|
|
2878
|
+
] });
|
|
2879
|
+
}
|
|
2880
|
+
|
|
2881
|
+
// src/plugins/OutlinePlugin.tsx
|
|
2882
|
+
import { useCallback as useCallback6, useEffect as useEffect10, useState as useState6 } from "react";
|
|
2883
|
+
import { useLexicalComposerContext as useLexicalComposerContext10 } from "@lexical/react/LexicalComposerContext";
|
|
2884
|
+
import {
|
|
2885
|
+
TableOfContentsPlugin
|
|
2886
|
+
} from "@lexical/react/LexicalTableOfContentsPlugin";
|
|
2887
|
+
import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2888
|
+
var INDENT_PER_LEVEL = {
|
|
2889
|
+
h1: 0,
|
|
2890
|
+
h2: 1,
|
|
2891
|
+
h3: 2,
|
|
2892
|
+
h4: 3,
|
|
2893
|
+
h5: 4,
|
|
2894
|
+
h6: 5
|
|
2895
|
+
};
|
|
2896
|
+
function OutlinePlugin() {
|
|
2897
|
+
const [editor] = useLexicalComposerContext10();
|
|
2898
|
+
const [collapsed, setCollapsed] = useState6(false);
|
|
2899
|
+
const [activeKey, setActiveKey] = useState6(null);
|
|
2900
|
+
useEffect10(() => {
|
|
2901
|
+
const rootElement = editor.getRootElement();
|
|
2902
|
+
const scroller = rootElement?.closest(".zui-text-editor");
|
|
2903
|
+
if (!rootElement || !(scroller instanceof HTMLElement)) return;
|
|
2904
|
+
let frame = 0;
|
|
2905
|
+
const updateActive = () => {
|
|
2906
|
+
cancelAnimationFrame(frame);
|
|
2907
|
+
frame = requestAnimationFrame(() => {
|
|
2908
|
+
const headings = rootElement.querySelectorAll(
|
|
2909
|
+
"h1, h2, h3, h4, h5, h6"
|
|
2910
|
+
);
|
|
2911
|
+
const threshold = scroller.scrollTop + 80;
|
|
2912
|
+
let current = null;
|
|
2913
|
+
for (const el of headings) {
|
|
2914
|
+
if (el.offsetTop <= threshold) {
|
|
2915
|
+
current = el.getAttribute("data-outline-key");
|
|
2916
|
+
}
|
|
2917
|
+
}
|
|
2918
|
+
setActiveKey(current);
|
|
2919
|
+
});
|
|
2920
|
+
};
|
|
2921
|
+
updateActive();
|
|
2922
|
+
scroller.addEventListener("scroll", updateActive, { passive: true });
|
|
2923
|
+
const unregister = editor.registerUpdateListener(updateActive);
|
|
2924
|
+
return () => {
|
|
2925
|
+
cancelAnimationFrame(frame);
|
|
2926
|
+
scroller.removeEventListener("scroll", updateActive);
|
|
2927
|
+
unregister();
|
|
2928
|
+
};
|
|
2929
|
+
}, [editor]);
|
|
2930
|
+
const scrollTo = useCallback6(
|
|
2931
|
+
(key) => {
|
|
2932
|
+
editor.getEditorState().read(() => {
|
|
2933
|
+
const element = editor.getElementByKey(key);
|
|
2934
|
+
element?.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
2935
|
+
});
|
|
2936
|
+
},
|
|
2937
|
+
[editor]
|
|
2938
|
+
);
|
|
2939
|
+
return /* @__PURE__ */ jsx9(TableOfContentsPlugin, { children: (entries) => {
|
|
2940
|
+
for (const [key] of entries) {
|
|
2941
|
+
editor.getElementByKey(key)?.setAttribute("data-outline-key", key);
|
|
2942
|
+
}
|
|
2943
|
+
return /* @__PURE__ */ jsxs6(
|
|
2944
|
+
"div",
|
|
2945
|
+
{
|
|
2946
|
+
className: `zui-text-editor-outline ${collapsed ? "is-collapsed" : ""}`,
|
|
2947
|
+
children: [
|
|
2948
|
+
/* @__PURE__ */ jsx9(
|
|
2949
|
+
"button",
|
|
2950
|
+
{
|
|
2951
|
+
type: "button",
|
|
2952
|
+
className: "zui-text-editor-outline-toggle",
|
|
2953
|
+
title: collapsed ? "Show outline" : "Hide outline",
|
|
2954
|
+
"aria-label": collapsed ? "Show outline" : "Hide outline",
|
|
2955
|
+
"aria-expanded": !collapsed,
|
|
2956
|
+
onClick: () => setCollapsed((c) => !c),
|
|
2957
|
+
children: /* @__PURE__ */ jsx9(
|
|
2958
|
+
"svg",
|
|
2959
|
+
{
|
|
2960
|
+
viewBox: "0 0 20 20",
|
|
2961
|
+
width: "14",
|
|
2962
|
+
height: "14",
|
|
2963
|
+
fill: "none",
|
|
2964
|
+
stroke: "currentColor",
|
|
2965
|
+
strokeWidth: "1.8",
|
|
2966
|
+
strokeLinecap: "round",
|
|
2967
|
+
children: /* @__PURE__ */ jsx9("path", { d: "M4 5h12M4 10h8M4 15h10" })
|
|
2968
|
+
}
|
|
2969
|
+
)
|
|
2970
|
+
}
|
|
2971
|
+
),
|
|
2972
|
+
!collapsed && /* @__PURE__ */ jsxs6("nav", { className: "zui-text-editor-outline-list", "aria-label": "Table of contents", children: [
|
|
2973
|
+
entries.length === 0 && /* @__PURE__ */ jsx9("div", { className: "zui-text-editor-outline-empty", children: "No headings" }),
|
|
2974
|
+
entries.map(([key, text, tag]) => /* @__PURE__ */ jsx9(
|
|
2975
|
+
"button",
|
|
2976
|
+
{
|
|
2977
|
+
type: "button",
|
|
2978
|
+
className: `zui-text-editor-outline-item ${key === activeKey ? "is-active" : ""}`,
|
|
2979
|
+
style: {
|
|
2980
|
+
paddingLeft: `${0.5 + INDENT_PER_LEVEL[tag] * 0.75}rem`
|
|
2981
|
+
},
|
|
2982
|
+
onClick: () => scrollTo(key),
|
|
2983
|
+
children: text || "Untitled"
|
|
2984
|
+
},
|
|
2985
|
+
key
|
|
2986
|
+
))
|
|
2987
|
+
] })
|
|
2988
|
+
]
|
|
2989
|
+
}
|
|
2990
|
+
);
|
|
2991
|
+
} });
|
|
2992
|
+
}
|
|
2993
|
+
|
|
2994
|
+
// src/MarkdownEditor.tsx
|
|
2995
|
+
import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2996
|
+
var DEFAULT_ITEMS = {
|
|
2997
|
+
format: /* @__PURE__ */ jsx10(FormatButtons, {}),
|
|
2998
|
+
insert: /* @__PURE__ */ jsx10(InsertButtons, {}),
|
|
2999
|
+
history: /* @__PURE__ */ jsx10(HistoryButtons, {})
|
|
3000
|
+
};
|
|
3001
|
+
function EditorToolbar({
|
|
3002
|
+
children,
|
|
3003
|
+
className
|
|
3004
|
+
}) {
|
|
3005
|
+
const { mode, readOnly } = useEditorContext();
|
|
3006
|
+
if (mode !== "edit-md" || readOnly) return null;
|
|
3007
|
+
return /* @__PURE__ */ jsx10(Toolbar, { className, children });
|
|
3008
|
+
}
|
|
3009
|
+
function MarkdownEditor({
|
|
3010
|
+
placeholder,
|
|
3011
|
+
toolbar = true,
|
|
3012
|
+
outline = false,
|
|
3013
|
+
foldable = true,
|
|
3014
|
+
...rootProps
|
|
3015
|
+
}) {
|
|
3016
|
+
return /* @__PURE__ */ jsxs7(EditorRoot, { ...rootProps, children: [
|
|
3017
|
+
toolbar === true && /* @__PURE__ */ jsx10(EditorToolbar, {}),
|
|
3018
|
+
typeof toolbar === "function" && toolbar(DEFAULT_ITEMS),
|
|
3019
|
+
/* @__PURE__ */ jsx10(EditorContent, { placeholder, foldable, children: outline && /* @__PURE__ */ jsx10(OutlinePlugin, {}) })
|
|
3020
|
+
] });
|
|
3021
|
+
}
|
|
3022
|
+
MarkdownEditor.Root = EditorRoot;
|
|
3023
|
+
MarkdownEditor.Content = EditorContent;
|
|
3024
|
+
MarkdownEditor.Toolbar = EditorToolbar;
|
|
3025
|
+
MarkdownEditor.ToolbarButton = ToolbarButton;
|
|
3026
|
+
MarkdownEditor.ToolbarDivider = ToolbarDivider;
|
|
3027
|
+
MarkdownEditor.FormatButtons = FormatButtons;
|
|
3028
|
+
MarkdownEditor.InsertButtons = InsertButtons;
|
|
3029
|
+
MarkdownEditor.HistoryButtons = HistoryButtons;
|
|
3030
|
+
MarkdownEditor.Outline = OutlinePlugin;
|
|
3031
|
+
MarkdownEditor.useEditor = useMarkdownEditor;
|
|
3032
|
+
var MarkdownEditor_default = MarkdownEditor;
|
|
3033
|
+
|
|
3034
|
+
// src/components/drawingSchema.ts
|
|
3035
|
+
var DRAWING_DATA_JSON_SCHEMA = {
|
|
3036
|
+
$schema: "http://json-schema.org/draft-07/schema#",
|
|
3037
|
+
title: "DrawingData",
|
|
3038
|
+
description: "Vector drawing embedded in markdown as a ```drawing fenced code block",
|
|
3039
|
+
type: "object",
|
|
3040
|
+
required: ["version", "height", "shapes"],
|
|
3041
|
+
additionalProperties: false,
|
|
3042
|
+
properties: {
|
|
3043
|
+
version: { const: 1 },
|
|
3044
|
+
height: {
|
|
2704
3045
|
type: "number",
|
|
2705
3046
|
minimum: 80,
|
|
2706
|
-
description: "Canvas height in pixels
|
|
3047
|
+
description: "Canvas height in pixels"
|
|
3048
|
+
},
|
|
3049
|
+
width: {
|
|
3050
|
+
enum: ["full", "content"],
|
|
3051
|
+
description: `Canvas width: "full" (default) spans the editor; "content" fits the shapes' horizontal extent and left-aligns with the text`
|
|
2707
3052
|
},
|
|
2708
3053
|
shapes: {
|
|
2709
3054
|
type: "array",
|
|
@@ -2801,15 +3146,26 @@ var DRAWING_DATA_JSON_SCHEMA = {
|
|
|
2801
3146
|
export {
|
|
2802
3147
|
$createDrawingNode,
|
|
2803
3148
|
$createFrontmatterNode,
|
|
3149
|
+
$getSelectedTable,
|
|
3150
|
+
$getTableWidth,
|
|
2804
3151
|
$isDrawingNode,
|
|
2805
3152
|
$isFrontmatterNode,
|
|
3153
|
+
$setTableWidth,
|
|
2806
3154
|
DRAWING,
|
|
2807
3155
|
DRAWING_DATA_JSON_SCHEMA,
|
|
2808
3156
|
DrawingNode,
|
|
2809
3157
|
FRONTMATTER,
|
|
3158
|
+
FormatButtons,
|
|
2810
3159
|
FrontmatterNode,
|
|
2811
|
-
|
|
3160
|
+
HistoryButtons,
|
|
3161
|
+
InsertButtons,
|
|
3162
|
+
MarkdownEditor_default as MarkdownEditor,
|
|
2812
3163
|
TABLE,
|
|
3164
|
+
TABLE_WIDTH_MARKER,
|
|
3165
|
+
Toolbar,
|
|
3166
|
+
ToolbarButton,
|
|
3167
|
+
ToolbarDivider,
|
|
2813
3168
|
parseDrawingData,
|
|
2814
|
-
serializeDrawingData
|
|
3169
|
+
serializeDrawingData,
|
|
3170
|
+
useMarkdownEditor
|
|
2815
3171
|
};
|