@haklex/rich-ext-excalidraw 0.14.1 → 0.15.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/dist/index.mjs CHANGED
@@ -1,14 +1,15 @@
1
- import { A as ExcalidrawConfigProvider, S as excalidrawFullscreenPopup, j as useExcalidrawConfig, n as parseSnapshot, r as serializeSnapshot } from "./useExcalidrawData-DcXa8vqV.js";
2
- import { r as _defineProperty, t as ExcalidrawDisplayRenderer } from "./ExcalidrawDisplayRenderer-BWsFJSQ7.js";
3
- import { a as ExcalidrawNode, i as $isExcalidrawNode, n as ExcalidrawExpandShell, o as EXCALIDRAW_NODE_KEY, r as $createExcalidrawNode, s as ExcalidrawSSRRenderer, t as EXCALIDRAW_BLOCK_TRANSFORMER } from "./transformer-zBd4ibNw.js";
4
- import { t as ExcalidrawEditRenderer } from "./ExcalidrawEditRenderer-BlJTjnpV.js";
5
- import { Suspense, createElement, lazy, useEffect } from "react";
6
- import { PenTool } from "lucide-react";
7
- import { ViewportGate } from "@haklex/rich-editor-ui";
1
+ import { a as ExcalidrawNode, c as ExcalidrawDisplayRenderer, d as __exportAll, i as $isExcalidrawNode, n as ExcalidrawExpandShell, o as EXCALIDRAW_NODE_KEY, r as $createExcalidrawNode, s as ExcalidrawSSRRenderer, t as EXCALIDRAW_BLOCK_TRANSFORMER, u as _defineProperty } from "./transformer-B0euMW7k.js";
2
+ import { A as ExcalidrawConfigProvider, C as excalidrawHeaderActions, O as excalidrawStatusDot, S as excalidrawFullscreenPopup, T as excalidrawLoading, _ as excalidrawDialogTitle, b as excalidrawEditorContainer, d as excalidrawConfirmBtnDanger, f as excalidrawConfirmBtnPrimary, g as excalidrawDialogMeta, i as excalidrawActionBarBtn, j as useExcalidrawConfig, k as readonlyUIOptions, l as excalidrawConfirmActions, m as excalidrawDialogHeader, n as parseSnapshot, p as excalidrawDialogCanvas, r as serializeSnapshot, t as useExcalidrawData, u as excalidrawConfirmBtn, v as excalidrawEditLabel, w as excalidrawHeaderClose, x as excalidrawError, y as excalidrawEditOverlay } from "./useExcalidrawData-DcXa8vqV.js";
3
+ import { Suspense, createElement, lazy, useCallback, useEffect, useRef, useState } from "react";
4
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
+ import { useColorScheme } from "@haklex/rich-editor/static";
6
+ import { Clipboard, Download, PenTool, Pencil, Save, X } from "lucide-react";
7
+ import { SegmentedControl, ViewportGate, dismissTopDialog, presentDialog } from "@haklex/rich-editor-ui";
8
8
  import { $getNodeByKey, $insertNodes, COMMAND_PRIORITY_EDITOR, createCommand } from "lexical";
9
+ import { usePortalTheme } from "@haklex/rich-style-token";
9
10
  import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
10
11
  //#region src/ExcalidrawEditNode.ts
11
- var LazyEditRenderer = lazy(() => import("./ExcalidrawEditRenderer-BlJTjnpV.js").then((n) => n.n).then((m) => ({ default: m.ExcalidrawEditRenderer })));
12
+ var LazyEditRenderer = lazy(() => Promise.resolve().then(() => ExcalidrawEditRenderer_exports).then((m) => ({ default: m.ExcalidrawEditRenderer })));
12
13
  var ExcalidrawEditNode = class ExcalidrawEditNode extends ExcalidrawNode {
13
14
  static clone(node) {
14
15
  return new ExcalidrawEditNode(node.__snapshot, node.__key);
@@ -71,6 +72,481 @@ function $isExcalidrawEditNode(node) {
71
72
  return node instanceof ExcalidrawEditNode;
72
73
  }
73
74
  //#endregion
75
+ //#region src/ExcalidrawEditRenderer.tsx
76
+ var ExcalidrawEditRenderer_exports = /* @__PURE__ */ __exportAll({ ExcalidrawEditRenderer: () => ExcalidrawEditRenderer });
77
+ var SAVE_DEBOUNCE_MS = 2e3;
78
+ var ExcalidrawEditorDialogContent = ({ dismiss, ExcalidrawComponent, initialData, initialSnapshot, theme, onSave, saveSnapshot, onClose, baseRef, baseData }) => {
79
+ const { className: portalClassName } = usePortalTheme();
80
+ const apiRef = useRef(null);
81
+ const saveTimerRef = useRef(void 0);
82
+ const [isDirty, setIsDirty] = useState(false);
83
+ const isDirtyRef = useRef(false);
84
+ const confirmDialogOpenRef = useRef(false);
85
+ const initializedRef = useRef(false);
86
+ const stableRef = useRef(false);
87
+ const baseRefRef = useRef(baseRef);
88
+ const baseDataRef = useRef(baseData);
89
+ const [storageMode, setStorageMode] = useState(() => {
90
+ if (!saveSnapshot || !initialSnapshot) return "inline";
91
+ return initialSnapshot.type === "delta" ? "delta" : initialSnapshot.type === "remote" ? "remote" : "inline";
92
+ });
93
+ const storageModeRef = useRef(storageMode);
94
+ const [savedRef, setSavedRef] = useState(() => {
95
+ if (!saveSnapshot || !initialSnapshot) return "";
96
+ if (initialSnapshot.type === "remote") return initialSnapshot.url;
97
+ if (initialSnapshot.type === "delta") return initialSnapshot.baseUrl;
98
+ return "";
99
+ });
100
+ const [isSaving, setIsSaving] = useState(false);
101
+ useEffect(() => {
102
+ const stableTimer = setTimeout(() => {
103
+ stableRef.current = true;
104
+ }, 500);
105
+ return () => {
106
+ clearTimeout(stableTimer);
107
+ if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
108
+ };
109
+ }, []);
110
+ const getSnapshot = useCallback(() => {
111
+ const api = apiRef.current;
112
+ if (!api) return;
113
+ const appState = api.getAppState();
114
+ return {
115
+ elements: api.getSceneElements(),
116
+ appState: {
117
+ viewBackgroundColor: appState.viewBackgroundColor,
118
+ gridSize: appState.gridSize
119
+ },
120
+ files: api.getFiles()
121
+ };
122
+ }, []);
123
+ const performSave = useCallback(() => {
124
+ if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
125
+ const doc = getSnapshot();
126
+ if (!doc) return Promise.resolve();
127
+ const mode = storageModeRef.current;
128
+ if (mode === "delta" && saveSnapshot) {
129
+ setIsSaving(true);
130
+ return (async () => {
131
+ try {
132
+ const currentBaseUrl = baseRefRef.current;
133
+ const currentBaseData = baseDataRef.current;
134
+ if (!currentBaseUrl || !currentBaseData) {
135
+ const ref = await saveSnapshot(doc, baseRefRef.current || void 0);
136
+ baseRefRef.current = ref;
137
+ baseDataRef.current = doc;
138
+ setSavedRef(ref);
139
+ }
140
+ isDirtyRef.current = false;
141
+ setIsDirty(false);
142
+ } finally {
143
+ setIsSaving(false);
144
+ }
145
+ })();
146
+ }
147
+ if (mode === "remote" && saveSnapshot) {
148
+ if (baseRefRef.current && baseDataRef.current && JSON.stringify(doc) === JSON.stringify(baseDataRef.current)) {
149
+ isDirtyRef.current = false;
150
+ setIsDirty(false);
151
+ return Promise.resolve();
152
+ }
153
+ setIsSaving(true);
154
+ return saveSnapshot(doc, baseRefRef.current || void 0).then((ref) => {
155
+ baseRefRef.current = ref;
156
+ baseDataRef.current = doc;
157
+ isDirtyRef.current = false;
158
+ setIsDirty(false);
159
+ setIsSaving(false);
160
+ setSavedRef(ref);
161
+ }, () => {
162
+ setIsSaving(false);
163
+ });
164
+ }
165
+ isDirtyRef.current = false;
166
+ setIsDirty(false);
167
+ return Promise.resolve();
168
+ }, [saveSnapshot, getSnapshot]);
169
+ const emitSnapshot = useCallback(async () => {
170
+ const doc = getSnapshot();
171
+ if (!doc) return;
172
+ const mode = storageModeRef.current;
173
+ if (mode === "delta" && saveSnapshot) {
174
+ const currentBaseUrl = baseRefRef.current;
175
+ const currentBaseData = baseDataRef.current;
176
+ if (currentBaseUrl && currentBaseData) {
177
+ const { diff } = await import("jsondiffpatch");
178
+ const delta = diff(currentBaseData, structuredClone(doc));
179
+ onSave(delta ? {
180
+ type: "delta",
181
+ baseUrl: currentBaseUrl,
182
+ delta
183
+ } : {
184
+ type: "remote",
185
+ url: currentBaseUrl
186
+ });
187
+ } else onSave({
188
+ type: "remote",
189
+ url: await saveSnapshot(doc, baseRefRef.current || void 0)
190
+ });
191
+ return;
192
+ }
193
+ if (mode === "remote") {
194
+ const ref = baseRefRef.current;
195
+ if (ref) onSave({
196
+ type: "remote",
197
+ url: ref
198
+ });
199
+ else if (saveSnapshot) onSave({
200
+ type: "remote",
201
+ url: await saveSnapshot(doc)
202
+ });
203
+ return;
204
+ }
205
+ onSave({
206
+ type: "inline",
207
+ data: doc
208
+ });
209
+ }, [
210
+ onSave,
211
+ saveSnapshot,
212
+ getSnapshot
213
+ ]);
214
+ const handleChange = useCallback((elements) => {
215
+ if (!initializedRef.current) {
216
+ initializedRef.current = true;
217
+ return;
218
+ }
219
+ if (!stableRef.current) return;
220
+ if (elements.length === 0) return;
221
+ if (!isDirtyRef.current) {
222
+ isDirtyRef.current = true;
223
+ setIsDirty(true);
224
+ }
225
+ if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
226
+ saveTimerRef.current = setTimeout(() => performSave(), SAVE_DEBOUNCE_MS);
227
+ }, [performSave]);
228
+ const handleSaveAndClose = useCallback(() => {
229
+ performSave().then(async () => {
230
+ await emitSnapshot();
231
+ onClose(getSnapshot());
232
+ dismiss();
233
+ });
234
+ }, [
235
+ performSave,
236
+ emitSnapshot,
237
+ getSnapshot,
238
+ onClose,
239
+ dismiss
240
+ ]);
241
+ const handleDiscard = useCallback(() => {
242
+ if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
243
+ dismiss();
244
+ }, [dismiss]);
245
+ const showConfirmDialog = useCallback(() => {
246
+ if (confirmDialogOpenRef.current) return;
247
+ confirmDialogOpenRef.current = true;
248
+ presentDialog({
249
+ title: "Unsaved Changes",
250
+ description: "You have unsaved changes that will be lost if you close now.",
251
+ content: ({ dismiss: dismissConfirm }) => {
252
+ const wrappedDismiss = () => {
253
+ confirmDialogOpenRef.current = false;
254
+ dismissConfirm();
255
+ };
256
+ return /* @__PURE__ */ jsxs("div", {
257
+ className: excalidrawConfirmActions,
258
+ children: [
259
+ /* @__PURE__ */ jsx("button", {
260
+ className: excalidrawConfirmBtn,
261
+ type: "button",
262
+ onClick: wrappedDismiss,
263
+ children: "Continue Editing"
264
+ }),
265
+ /* @__PURE__ */ jsx("button", {
266
+ className: excalidrawConfirmBtnDanger,
267
+ type: "button",
268
+ onClick: () => {
269
+ wrappedDismiss();
270
+ handleDiscard();
271
+ },
272
+ children: "Discard"
273
+ }),
274
+ /* @__PURE__ */ jsx("button", {
275
+ className: excalidrawConfirmBtnPrimary,
276
+ type: "button",
277
+ onClick: () => {
278
+ wrappedDismiss();
279
+ handleSaveAndClose();
280
+ },
281
+ children: "Save & Close"
282
+ })
283
+ ]
284
+ });
285
+ },
286
+ portalClassName,
287
+ theme,
288
+ showCloseButton: false,
289
+ clickOutsideToDismiss: false
290
+ });
291
+ }, [
292
+ handleDiscard,
293
+ handleSaveAndClose,
294
+ portalClassName,
295
+ theme
296
+ ]);
297
+ const attemptClose = useCallback(() => {
298
+ if (isDirtyRef.current) showConfirmDialog();
299
+ else emitSnapshot().then(() => {
300
+ onClose(getSnapshot());
301
+ dismiss();
302
+ });
303
+ }, [
304
+ dismiss,
305
+ showConfirmDialog,
306
+ emitSnapshot,
307
+ getSnapshot,
308
+ onClose
309
+ ]);
310
+ useEffect(() => {
311
+ const handleKeyDown = (e) => {
312
+ if (e.key === "Escape") {
313
+ e.preventDefault();
314
+ e.stopImmediatePropagation();
315
+ if (confirmDialogOpenRef.current) {
316
+ confirmDialogOpenRef.current = false;
317
+ dismissTopDialog();
318
+ } else attemptClose();
319
+ }
320
+ };
321
+ document.addEventListener("keydown", handleKeyDown, true);
322
+ return () => document.removeEventListener("keydown", handleKeyDown, true);
323
+ }, [attemptClose]);
324
+ const handleManualUpload = useCallback(() => {
325
+ performSave();
326
+ }, [performSave]);
327
+ const handleExportJson = useCallback(() => {
328
+ const doc = getSnapshot();
329
+ if (!doc) return;
330
+ const blob = new Blob([JSON.stringify(doc, null, 2)], { type: "application/json" });
331
+ const url = URL.createObjectURL(blob);
332
+ const a = document.createElement("a");
333
+ a.href = url;
334
+ a.download = "excalidraw-snapshot.json";
335
+ a.click();
336
+ URL.revokeObjectURL(url);
337
+ }, [getSnapshot]);
338
+ const handleCopyJson = useCallback(() => {
339
+ const doc = getSnapshot();
340
+ if (!doc) return;
341
+ navigator.clipboard.writeText(JSON.stringify(doc));
342
+ }, [getSnapshot]);
343
+ const handleRefChange = useCallback((e) => {
344
+ const { value } = e.target;
345
+ setSavedRef(value);
346
+ baseRefRef.current = value;
347
+ baseDataRef.current = void 0;
348
+ }, []);
349
+ const handleStorageModeChange = useCallback((newMode) => {
350
+ setStorageMode(newMode);
351
+ storageModeRef.current = newMode;
352
+ if (newMode === "inline") setSavedRef("");
353
+ performSave();
354
+ }, [performSave]);
355
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
356
+ className: excalidrawDialogHeader,
357
+ children: [
358
+ /* @__PURE__ */ jsx("span", {
359
+ className: excalidrawStatusDot,
360
+ style: { backgroundColor: isDirty ? "#f59e0b" : "#22c55e" }
361
+ }),
362
+ /* @__PURE__ */ jsx("span", {
363
+ className: excalidrawDialogTitle,
364
+ children: "Canvas Editor"
365
+ }),
366
+ /* @__PURE__ */ jsx("span", {
367
+ className: excalidrawDialogMeta,
368
+ children: "excalidraw"
369
+ }),
370
+ /* @__PURE__ */ jsxs("div", {
371
+ className: excalidrawHeaderActions,
372
+ children: [
373
+ saveSnapshot && /* @__PURE__ */ jsxs(Fragment, { children: [
374
+ /* @__PURE__ */ jsx(SegmentedControl, {
375
+ value: storageMode,
376
+ items: [
377
+ {
378
+ value: "inline",
379
+ label: "Inline"
380
+ },
381
+ {
382
+ value: "remote",
383
+ label: "Remote"
384
+ },
385
+ {
386
+ value: "delta",
387
+ label: "Delta"
388
+ }
389
+ ],
390
+ onChange: handleStorageModeChange
391
+ }),
392
+ /* @__PURE__ */ jsx("div", { className: "_1c3wdzlh" }),
393
+ /* @__PURE__ */ jsxs("button", {
394
+ className: "_1c3wdzlg",
395
+ disabled: isSaving,
396
+ type: "button",
397
+ onClick: handleManualUpload,
398
+ children: [/* @__PURE__ */ jsx(Save, { size: 14 }), /* @__PURE__ */ jsx("span", { children: isSaving ? "Saving..." : "Save" })]
399
+ }),
400
+ /* @__PURE__ */ jsx("div", { className: "_1c3wdzlh" })
401
+ ] }),
402
+ /* @__PURE__ */ jsxs("button", {
403
+ className: excalidrawActionBarBtn,
404
+ type: "button",
405
+ onClick: handleExportJson,
406
+ children: [/* @__PURE__ */ jsx(Download, { size: 14 }), "JSON"]
407
+ }),
408
+ /* @__PURE__ */ jsxs("button", {
409
+ className: excalidrawActionBarBtn,
410
+ type: "button",
411
+ onClick: handleCopyJson,
412
+ children: [/* @__PURE__ */ jsx(Clipboard, { size: 14 }), "Copy"]
413
+ }),
414
+ storageMode !== "inline" && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", { className: "_1c3wdzlh" }), /* @__PURE__ */ jsx("input", {
415
+ className: "_1c3wdzli",
416
+ placeholder: "base URL / ref",
417
+ spellCheck: false,
418
+ type: "text",
419
+ value: savedRef,
420
+ onChange: handleRefChange
421
+ })] })
422
+ ]
423
+ }),
424
+ /* @__PURE__ */ jsx("button", {
425
+ className: excalidrawHeaderClose,
426
+ type: "button",
427
+ onClick: attemptClose,
428
+ children: /* @__PURE__ */ jsx(X, { size: 18 })
429
+ })
430
+ ]
431
+ }), /* @__PURE__ */ jsx("div", {
432
+ className: excalidrawDialogCanvas,
433
+ children: /* @__PURE__ */ jsx(ExcalidrawComponent, {
434
+ initialData,
435
+ theme,
436
+ excalidrawAPI: (api) => {
437
+ apiRef.current = api;
438
+ },
439
+ onChange: handleChange
440
+ })
441
+ })] });
442
+ };
443
+ var ExcalidrawEditRenderer = ({ snapshot, onSnapshotChange }) => {
444
+ const theme = useColorScheme();
445
+ const { saveSnapshot } = useExcalidrawConfig();
446
+ const { snapshot: initialData, loading: dataLoading, error: dataError, baseRef, baseData } = useExcalidrawData(snapshot);
447
+ const [ExcalidrawComponent, setExcalidrawComponent] = useState(null);
448
+ const [libLoading, setLibLoading] = useState(true);
449
+ const previewApiRef = useRef(null);
450
+ const initialDataRef = useRef(void 0);
451
+ const [previewKey, setPreviewKey] = useState(0);
452
+ const onSnapshotChangeRef = useRef(onSnapshotChange);
453
+ onSnapshotChangeRef.current = onSnapshotChange;
454
+ const saveSnapshotRef = useRef(saveSnapshot);
455
+ saveSnapshotRef.current = saveSnapshot;
456
+ const snapshotRef = useRef(snapshot);
457
+ snapshotRef.current = snapshot;
458
+ const { className: portalClassName } = usePortalTheme();
459
+ useEffect(() => {
460
+ if (initialData && !initialDataRef.current) initialDataRef.current = initialData;
461
+ }, [initialData]);
462
+ useEffect(() => {
463
+ Promise.all([import("@excalidraw/excalidraw"), import("@excalidraw/excalidraw/index.css")]).then(([mod]) => {
464
+ const Comp = mod.Excalidraw || mod.default?.Excalidraw;
465
+ if (Comp) setExcalidrawComponent(() => Comp);
466
+ setLibLoading(false);
467
+ }).catch((err) => {
468
+ console.error("[excalidraw] failed to load", err);
469
+ setLibLoading(false);
470
+ });
471
+ }, []);
472
+ const baseRefRef = useRef(baseRef);
473
+ const baseDataRefOuter = useRef(baseData);
474
+ useEffect(() => {
475
+ if (baseRef) baseRefRef.current = baseRef;
476
+ }, [baseRef]);
477
+ useEffect(() => {
478
+ if (baseData) baseDataRefOuter.current = baseData;
479
+ }, [baseData]);
480
+ const handleOpenEditor = useCallback(() => {
481
+ if (!ExcalidrawComponent || dataLoading) return;
482
+ presentDialog({
483
+ content: ({ dismiss }) => /* @__PURE__ */ jsx(ExcalidrawEditorDialogContent, {
484
+ ExcalidrawComponent,
485
+ baseData: baseDataRefOuter.current,
486
+ baseRef: baseRefRef.current,
487
+ dismiss,
488
+ initialData: initialDataRef.current,
489
+ initialSnapshot: snapshotRef.current,
490
+ saveSnapshot: saveSnapshotRef.current,
491
+ theme,
492
+ onSave: (ref) => onSnapshotChangeRef.current(ref),
493
+ onClose: (doc) => {
494
+ if (doc) {
495
+ initialDataRef.current = doc;
496
+ setPreviewKey((k) => k + 1);
497
+ }
498
+ }
499
+ }),
500
+ className: excalidrawFullscreenPopup,
501
+ portalClassName,
502
+ theme,
503
+ showCloseButton: false,
504
+ clickOutsideToDismiss: false
505
+ });
506
+ }, [
507
+ ExcalidrawComponent,
508
+ dataLoading,
509
+ theme,
510
+ portalClassName
511
+ ]);
512
+ if (dataLoading || libLoading || !ExcalidrawComponent) return /* @__PURE__ */ jsx("div", {
513
+ className: excalidrawEditorContainer,
514
+ children: /* @__PURE__ */ jsx("div", {
515
+ className: excalidrawLoading,
516
+ children: "Loading excalidraw editor..."
517
+ })
518
+ });
519
+ if (dataError) return /* @__PURE__ */ jsx("div", {
520
+ className: excalidrawEditorContainer,
521
+ children: /* @__PURE__ */ jsx("div", {
522
+ className: excalidrawError,
523
+ children: dataError
524
+ })
525
+ });
526
+ return /* @__PURE__ */ jsxs("div", {
527
+ className: excalidrawEditorContainer,
528
+ children: [/* @__PURE__ */ jsx(ExcalidrawComponent, {
529
+ viewModeEnabled: true,
530
+ zenModeEnabled: true,
531
+ UIOptions: readonlyUIOptions,
532
+ initialData: initialDataRef.current,
533
+ theme,
534
+ excalidrawAPI: (api) => {
535
+ previewApiRef.current = api;
536
+ setTimeout(() => api.scrollToContent(), 100);
537
+ }
538
+ }, previewKey), /* @__PURE__ */ jsx("button", {
539
+ className: excalidrawEditOverlay,
540
+ type: "button",
541
+ onClick: handleOpenEditor,
542
+ children: /* @__PURE__ */ jsxs("span", {
543
+ className: excalidrawEditLabel,
544
+ children: [/* @__PURE__ */ jsx(Pencil, { size: 16 }), "Edit Whiteboard"]
545
+ })
546
+ })]
547
+ });
548
+ };
549
+ //#endregion
74
550
  //#region src/ExcalidrawPlugin.tsx
75
551
  var INSERT_EXCALIDRAW_COMMAND = createCommand("INSERT_EXCALIDRAW");
76
552
  function ExcalidrawPlugin() {
package/dist/slot.d.ts CHANGED
@@ -3,7 +3,7 @@ export declare const EXCALIDRAW_NODE_KEY: "Excalidraw";
3
3
  export interface ExcalidrawSlotProps {
4
4
  snapshot: string;
5
5
  }
6
- declare module '@haklex/rich-editor' {
6
+ declare module '@haklex/rich-editor/static' {
7
7
  interface RendererConfig {
8
8
  Excalidraw?: ComponentType<ExcalidrawSlotProps>;
9
9
  }
@@ -1 +1 @@
1
- {"version":3,"file":"slot.d.ts","sourceRoot":"","sources":["../src/slot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAE3C,eAAO,MAAM,mBAAmB,EAAG,YAAqB,CAAC;AAEzD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,cAAc;QACtB,UAAU,CAAC,EAAE,aAAa,CAAC,mBAAmB,CAAC,CAAC;KACjD;CACF"}
1
+ {"version":3,"file":"slot.d.ts","sourceRoot":"","sources":["../src/slot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAE3C,eAAO,MAAM,mBAAmB,EAAG,YAAqB,CAAC;AAEzD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,OAAO,QAAQ,4BAA4B,CAAC;IAC1C,UAAU,cAAc;QACtB,UAAU,CAAC,EAAE,aAAa,CAAC,mBAAmB,CAAC,CAAC;KACjD;CACF"}
package/dist/static.mjs CHANGED
@@ -1,4 +1,3 @@
1
+ import { a as ExcalidrawNode, c as ExcalidrawDisplayRenderer, i as $isExcalidrawNode, n as ExcalidrawExpandShell, o as EXCALIDRAW_NODE_KEY, r as $createExcalidrawNode, s as ExcalidrawSSRRenderer, t as EXCALIDRAW_BLOCK_TRANSFORMER } from "./transformer-B0euMW7k.js";
1
2
  import { A as ExcalidrawConfigProvider, S as excalidrawFullscreenPopup, j as useExcalidrawConfig } from "./useExcalidrawData-DcXa8vqV.js";
2
- import { t as ExcalidrawDisplayRenderer } from "./ExcalidrawDisplayRenderer-BWsFJSQ7.js";
3
- import { a as ExcalidrawNode, i as $isExcalidrawNode, n as ExcalidrawExpandShell, o as EXCALIDRAW_NODE_KEY, r as $createExcalidrawNode, s as ExcalidrawSSRRenderer, t as EXCALIDRAW_BLOCK_TRANSFORMER } from "./transformer-zBd4ibNw.js";
4
3
  export { $createExcalidrawNode, $isExcalidrawNode, EXCALIDRAW_BLOCK_TRANSFORMER, EXCALIDRAW_NODE_KEY, ExcalidrawConfigProvider, ExcalidrawDisplayRenderer, ExcalidrawDisplayRenderer as ExcalidrawRenderer, ExcalidrawDisplayRenderer as ExcalidrawStaticRenderer, ExcalidrawExpandShell, ExcalidrawNode, ExcalidrawSSRRenderer, excalidrawFullscreenPopup, useExcalidrawConfig };
@@ -1,10 +1,24 @@
1
- import { t as __exportAll } from "./rolldown-runtime-CiIaOW0V.js";
2
- import { D as excalidrawStaticContainer, T as excalidrawLoading, c as excalidrawActionGroup, k as readonlyUIOptions, s as excalidrawActionButton, t as useExcalidrawData, x as excalidrawError } from "./useExcalidrawData-DcXa8vqV.js";
3
- import { Component, useCallback, useEffect, useRef, useState } from "react";
4
- import { jsx, jsxs } from "react/jsx-runtime";
5
- import { useColorScheme } from "@haklex/rich-editor";
6
- import { Maximize2, ScanSearch, ZoomIn, ZoomOut } from "lucide-react";
7
- //#region \0@oxc-project+runtime@0.127.0/helpers/typeof.js
1
+ import { D as excalidrawStaticContainer, E as excalidrawPlaceholder, T as excalidrawLoading, _ as excalidrawDialogTitle, c as excalidrawActionGroup, h as excalidrawDialogHeaderTitle, k as readonlyUIOptions, m as excalidrawDialogHeader, p as excalidrawDialogCanvas, s as excalidrawActionButton, t as useExcalidrawData, w as excalidrawHeaderClose, x as excalidrawError } from "./useExcalidrawData-DcXa8vqV.js";
2
+ import { Component, Suspense, lazy, useCallback, useEffect, useRef, useState } from "react";
3
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
+ import { createRendererDecoration, useColorScheme } from "@haklex/rich-editor/static";
5
+ import { Maximize2, ScanSearch, X, ZoomIn, ZoomOut } from "lucide-react";
6
+ import { ViewportGate } from "@haklex/rich-editor-ui";
7
+ import { DecoratorNode } from "lexical";
8
+ import { EXCALIDRAW_BLOCK_TRANSFORMER } from "@haklex/rich-headless/transformers";
9
+ //#region \0rolldown/runtime.js
10
+ var __defProp = Object.defineProperty;
11
+ var __exportAll = (all, no_symbols) => {
12
+ let target = {};
13
+ for (var name in all) __defProp(target, name, {
14
+ get: all[name],
15
+ enumerable: true
16
+ });
17
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
18
+ return target;
19
+ };
20
+ //#endregion
21
+ //#region \0@oxc-project+runtime@0.130.0/helpers/typeof.js
8
22
  function _typeof(o) {
9
23
  "@babel/helpers - typeof";
10
24
  return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
@@ -14,7 +28,7 @@ function _typeof(o) {
14
28
  }, _typeof(o);
15
29
  }
16
30
  //#endregion
17
- //#region \0@oxc-project+runtime@0.127.0/helpers/toPrimitive.js
31
+ //#region \0@oxc-project+runtime@0.130.0/helpers/toPrimitive.js
18
32
  function toPrimitive(t, r) {
19
33
  if ("object" != _typeof(t) || !t) return t;
20
34
  var e = t[Symbol.toPrimitive];
@@ -26,13 +40,13 @@ function toPrimitive(t, r) {
26
40
  return ("string" === r ? String : Number)(t);
27
41
  }
28
42
  //#endregion
29
- //#region \0@oxc-project+runtime@0.127.0/helpers/toPropertyKey.js
43
+ //#region \0@oxc-project+runtime@0.130.0/helpers/toPropertyKey.js
30
44
  function toPropertyKey(t) {
31
45
  var i = toPrimitive(t, "string");
32
46
  return "symbol" == _typeof(i) ? i : i + "";
33
47
  }
34
48
  //#endregion
35
- //#region \0@oxc-project+runtime@0.127.0/helpers/defineProperty.js
49
+ //#region \0@oxc-project+runtime@0.130.0/helpers/defineProperty.js
36
50
  function _defineProperty(e, r, t) {
37
51
  return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
38
52
  value: t,
@@ -188,4 +202,113 @@ var ExcalidrawErrorBoundary = class extends Component {
188
202
  }
189
203
  };
190
204
  //#endregion
191
- export { ExcalidrawDisplayRenderer_exports as n, _defineProperty as r, ExcalidrawDisplayRenderer as t };
205
+ //#region src/ExcalidrawSSRRenderer.tsx
206
+ var LazyDisplayRenderer = lazy(() => Promise.resolve().then(() => ExcalidrawDisplayRenderer_exports).then((m) => ({ default: m.ExcalidrawDisplayRenderer })));
207
+ var ExcalidrawPlaceholder = ({ snapshot }) => {
208
+ let label = "Excalidraw Whiteboard";
209
+ try {
210
+ const data = JSON.parse(snapshot);
211
+ if (data && typeof data === "object") {
212
+ const elementCount = Array.isArray(data.elements) ? data.elements.length : 0;
213
+ if (elementCount > 0) label = `Excalidraw Whiteboard (${elementCount} elements)`;
214
+ }
215
+ } catch {}
216
+ return /* @__PURE__ */ jsx("div", {
217
+ "aria-label": label,
218
+ className: excalidrawPlaceholder,
219
+ children: /* @__PURE__ */ jsx("span", { children: label })
220
+ });
221
+ };
222
+ var ExcalidrawSSRRenderer = ({ onExpand, snapshot }) => {
223
+ return /* @__PURE__ */ jsx(ViewportGate, {
224
+ fallback: /* @__PURE__ */ jsx(ExcalidrawPlaceholder, { snapshot }),
225
+ children: /* @__PURE__ */ jsx(Suspense, {
226
+ fallback: /* @__PURE__ */ jsx(ExcalidrawPlaceholder, { snapshot }),
227
+ children: /* @__PURE__ */ jsx(LazyDisplayRenderer, {
228
+ snapshot,
229
+ onExpand
230
+ })
231
+ })
232
+ });
233
+ };
234
+ //#endregion
235
+ //#region src/slot.ts
236
+ var EXCALIDRAW_NODE_KEY = "Excalidraw";
237
+ //#endregion
238
+ //#region src/ExcalidrawNode.ts
239
+ var ExcalidrawNode = class ExcalidrawNode extends DecoratorNode {
240
+ static getType() {
241
+ return "excalidraw";
242
+ }
243
+ static clone(node) {
244
+ return new ExcalidrawNode(node.__snapshot, node.__key);
245
+ }
246
+ constructor(snapshot, key) {
247
+ super(key);
248
+ _defineProperty(this, "__snapshot", void 0);
249
+ this.__snapshot = snapshot;
250
+ }
251
+ createDOM(_config) {
252
+ const div = document.createElement("div");
253
+ div.className = "rich-excalidraw-wrapper";
254
+ return div;
255
+ }
256
+ updateDOM() {
257
+ return false;
258
+ }
259
+ isInline() {
260
+ return false;
261
+ }
262
+ static importJSON(serializedNode) {
263
+ return $createExcalidrawNode(serializedNode.snapshot);
264
+ }
265
+ exportJSON() {
266
+ return {
267
+ ...super.exportJSON(),
268
+ type: "excalidraw",
269
+ snapshot: this.__snapshot,
270
+ version: 1
271
+ };
272
+ }
273
+ getSnapshot() {
274
+ return this.__snapshot;
275
+ }
276
+ setSnapshot(snapshot) {
277
+ const writable = this.getWritable();
278
+ writable.__snapshot = snapshot;
279
+ }
280
+ decorate(_editor, _config) {
281
+ return createRendererDecoration(EXCALIDRAW_NODE_KEY, ExcalidrawSSRRenderer, { snapshot: this.__snapshot });
282
+ }
283
+ };
284
+ function $createExcalidrawNode(snapshot) {
285
+ return new ExcalidrawNode(snapshot);
286
+ }
287
+ function $isExcalidrawNode(node) {
288
+ return node instanceof ExcalidrawNode;
289
+ }
290
+ //#endregion
291
+ //#region src/ExcalidrawExpandShell.tsx
292
+ var ExcalidrawExpandShell = ({ children, dismiss, meta = "excalidraw", title = "Whiteboard" }) => /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
293
+ className: excalidrawDialogHeader,
294
+ children: [/* @__PURE__ */ jsxs("div", {
295
+ className: excalidrawDialogHeaderTitle,
296
+ children: [/* @__PURE__ */ jsx("span", {
297
+ className: excalidrawDialogTitle,
298
+ children: title
299
+ }), meta != null && /* @__PURE__ */ jsx("span", {
300
+ className: "_1c3wdzld",
301
+ children: meta
302
+ })]
303
+ }), /* @__PURE__ */ jsx("button", {
304
+ className: excalidrawHeaderClose,
305
+ type: "button",
306
+ onClick: dismiss,
307
+ children: /* @__PURE__ */ jsx(X, { size: 18 })
308
+ })]
309
+ }), /* @__PURE__ */ jsx("div", {
310
+ className: excalidrawDialogCanvas,
311
+ children
312
+ })] });
313
+ //#endregion
314
+ export { ExcalidrawNode as a, ExcalidrawDisplayRenderer as c, __exportAll as d, $isExcalidrawNode as i, ExcalidrawDisplayRenderer_exports as l, ExcalidrawExpandShell as n, EXCALIDRAW_NODE_KEY as o, $createExcalidrawNode as r, ExcalidrawSSRRenderer as s, EXCALIDRAW_BLOCK_TRANSFORMER as t, _defineProperty as u };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haklex/rich-ext-excalidraw",
3
- "version": "0.14.1",
3
+ "version": "0.15.1",
4
4
  "description": "Excalidraw whiteboard extension",
5
5
  "repository": {
6
6
  "type": "git",
@@ -25,23 +25,23 @@
25
25
  "dist"
26
26
  ],
27
27
  "dependencies": {
28
- "jsondiffpatch": "^0.7.3",
29
- "@haklex/rich-headless": "0.14.1"
28
+ "jsondiffpatch": "^0.7.6",
29
+ "@haklex/rich-headless": "0.15.1"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@excalidraw/excalidraw": "^0.18.0",
33
33
  "@lexical/react": "^0.44.0",
34
- "@types/react": "^19.2.14",
34
+ "@types/react": "^19.2.15",
35
35
  "@types/react-dom": "^19.2.3",
36
36
  "@vanilla-extract/css": "^1.20.1",
37
37
  "@vanilla-extract/vite-plugin": "^5.2.2",
38
38
  "lexical": "^0.44.0",
39
- "lucide-react": "^1.12.0",
39
+ "lucide-react": "^1.16.0",
40
40
  "react": "19.2.5",
41
41
  "react-dom": "19.2.5",
42
42
  "typescript": "^5.9.3",
43
- "vite": "^8.0.10",
44
- "vite-plugin-dts": "^4.5.4"
43
+ "unplugin-dts": "^1.0.1",
44
+ "vite": "^8.0.13"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "@excalidraw/excalidraw": ">=0.18.0",
@@ -50,9 +50,9 @@
50
50
  "lucide-react": "^1.0.0",
51
51
  "react": ">=19",
52
52
  "react-dom": ">=19",
53
- "@haklex/rich-editor": "0.14.1",
54
- "@haklex/rich-style-token": "0.14.1",
55
- "@haklex/rich-editor-ui": "0.14.1"
53
+ "@haklex/rich-editor": "0.15.1",
54
+ "@haklex/rich-editor-ui": "0.15.1",
55
+ "@haklex/rich-style-token": "0.15.1"
56
56
  },
57
57
  "publishConfig": {
58
58
  "access": "public"
@@ -1,484 +0,0 @@
1
- import { t as __exportAll } from "./rolldown-runtime-CiIaOW0V.js";
2
- import { C as excalidrawHeaderActions, O as excalidrawStatusDot, S as excalidrawFullscreenPopup, T as excalidrawLoading, _ as excalidrawDialogTitle, b as excalidrawEditorContainer, d as excalidrawConfirmBtnDanger, f as excalidrawConfirmBtnPrimary, g as excalidrawDialogMeta, i as excalidrawActionBarBtn, j as useExcalidrawConfig, k as readonlyUIOptions, l as excalidrawConfirmActions, m as excalidrawDialogHeader, p as excalidrawDialogCanvas, t as useExcalidrawData, u as excalidrawConfirmBtn, v as excalidrawEditLabel, w as excalidrawHeaderClose, x as excalidrawError, y as excalidrawEditOverlay } from "./useExcalidrawData-DcXa8vqV.js";
3
- import { useCallback, useEffect, useRef, useState } from "react";
4
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
- import { useColorScheme } from "@haklex/rich-editor";
6
- import { Clipboard, Download, Pencil, Save, X } from "lucide-react";
7
- import { SegmentedControl, dismissTopDialog, presentDialog } from "@haklex/rich-editor-ui";
8
- import { usePortalTheme } from "@haklex/rich-style-token";
9
- //#region src/ExcalidrawEditRenderer.tsx
10
- var ExcalidrawEditRenderer_exports = /* @__PURE__ */ __exportAll({ ExcalidrawEditRenderer: () => ExcalidrawEditRenderer });
11
- var SAVE_DEBOUNCE_MS = 2e3;
12
- var ExcalidrawEditorDialogContent = ({ dismiss, ExcalidrawComponent, initialData, initialSnapshot, theme, onSave, saveSnapshot, onClose, baseRef, baseData }) => {
13
- const { className: portalClassName } = usePortalTheme();
14
- const apiRef = useRef(null);
15
- const saveTimerRef = useRef(void 0);
16
- const [isDirty, setIsDirty] = useState(false);
17
- const isDirtyRef = useRef(false);
18
- const confirmDialogOpenRef = useRef(false);
19
- const initializedRef = useRef(false);
20
- const stableRef = useRef(false);
21
- const baseRefRef = useRef(baseRef);
22
- const baseDataRef = useRef(baseData);
23
- const [storageMode, setStorageMode] = useState(() => {
24
- if (!saveSnapshot || !initialSnapshot) return "inline";
25
- return initialSnapshot.type === "delta" ? "delta" : initialSnapshot.type === "remote" ? "remote" : "inline";
26
- });
27
- const storageModeRef = useRef(storageMode);
28
- const [savedRef, setSavedRef] = useState(() => {
29
- if (!saveSnapshot || !initialSnapshot) return "";
30
- if (initialSnapshot.type === "remote") return initialSnapshot.url;
31
- if (initialSnapshot.type === "delta") return initialSnapshot.baseUrl;
32
- return "";
33
- });
34
- const [isSaving, setIsSaving] = useState(false);
35
- useEffect(() => {
36
- const stableTimer = setTimeout(() => {
37
- stableRef.current = true;
38
- }, 500);
39
- return () => {
40
- clearTimeout(stableTimer);
41
- if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
42
- };
43
- }, []);
44
- const getSnapshot = useCallback(() => {
45
- const api = apiRef.current;
46
- if (!api) return;
47
- const appState = api.getAppState();
48
- return {
49
- elements: api.getSceneElements(),
50
- appState: {
51
- viewBackgroundColor: appState.viewBackgroundColor,
52
- gridSize: appState.gridSize
53
- },
54
- files: api.getFiles()
55
- };
56
- }, []);
57
- const performSave = useCallback(() => {
58
- if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
59
- const doc = getSnapshot();
60
- if (!doc) return Promise.resolve();
61
- const mode = storageModeRef.current;
62
- if (mode === "delta" && saveSnapshot) {
63
- setIsSaving(true);
64
- return (async () => {
65
- try {
66
- const currentBaseUrl = baseRefRef.current;
67
- const currentBaseData = baseDataRef.current;
68
- if (!currentBaseUrl || !currentBaseData) {
69
- const ref = await saveSnapshot(doc, baseRefRef.current || void 0);
70
- baseRefRef.current = ref;
71
- baseDataRef.current = doc;
72
- setSavedRef(ref);
73
- }
74
- isDirtyRef.current = false;
75
- setIsDirty(false);
76
- } finally {
77
- setIsSaving(false);
78
- }
79
- })();
80
- }
81
- if (mode === "remote" && saveSnapshot) {
82
- if (baseRefRef.current && baseDataRef.current && JSON.stringify(doc) === JSON.stringify(baseDataRef.current)) {
83
- isDirtyRef.current = false;
84
- setIsDirty(false);
85
- return Promise.resolve();
86
- }
87
- setIsSaving(true);
88
- return saveSnapshot(doc, baseRefRef.current || void 0).then((ref) => {
89
- baseRefRef.current = ref;
90
- baseDataRef.current = doc;
91
- isDirtyRef.current = false;
92
- setIsDirty(false);
93
- setIsSaving(false);
94
- setSavedRef(ref);
95
- }, () => {
96
- setIsSaving(false);
97
- });
98
- }
99
- isDirtyRef.current = false;
100
- setIsDirty(false);
101
- return Promise.resolve();
102
- }, [saveSnapshot, getSnapshot]);
103
- const emitSnapshot = useCallback(async () => {
104
- const doc = getSnapshot();
105
- if (!doc) return;
106
- const mode = storageModeRef.current;
107
- if (mode === "delta" && saveSnapshot) {
108
- const currentBaseUrl = baseRefRef.current;
109
- const currentBaseData = baseDataRef.current;
110
- if (currentBaseUrl && currentBaseData) {
111
- const { diff } = await import("jsondiffpatch");
112
- const delta = diff(currentBaseData, structuredClone(doc));
113
- onSave(delta ? {
114
- type: "delta",
115
- baseUrl: currentBaseUrl,
116
- delta
117
- } : {
118
- type: "remote",
119
- url: currentBaseUrl
120
- });
121
- } else onSave({
122
- type: "remote",
123
- url: await saveSnapshot(doc, baseRefRef.current || void 0)
124
- });
125
- return;
126
- }
127
- if (mode === "remote") {
128
- const ref = baseRefRef.current;
129
- if (ref) onSave({
130
- type: "remote",
131
- url: ref
132
- });
133
- else if (saveSnapshot) onSave({
134
- type: "remote",
135
- url: await saveSnapshot(doc)
136
- });
137
- return;
138
- }
139
- onSave({
140
- type: "inline",
141
- data: doc
142
- });
143
- }, [
144
- onSave,
145
- saveSnapshot,
146
- getSnapshot
147
- ]);
148
- const handleChange = useCallback((elements) => {
149
- if (!initializedRef.current) {
150
- initializedRef.current = true;
151
- return;
152
- }
153
- if (!stableRef.current) return;
154
- if (elements.length === 0) return;
155
- if (!isDirtyRef.current) {
156
- isDirtyRef.current = true;
157
- setIsDirty(true);
158
- }
159
- if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
160
- saveTimerRef.current = setTimeout(() => performSave(), SAVE_DEBOUNCE_MS);
161
- }, [performSave]);
162
- const handleSaveAndClose = useCallback(() => {
163
- performSave().then(async () => {
164
- await emitSnapshot();
165
- onClose(getSnapshot());
166
- dismiss();
167
- });
168
- }, [
169
- performSave,
170
- emitSnapshot,
171
- getSnapshot,
172
- onClose,
173
- dismiss
174
- ]);
175
- const handleDiscard = useCallback(() => {
176
- if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
177
- dismiss();
178
- }, [dismiss]);
179
- const showConfirmDialog = useCallback(() => {
180
- if (confirmDialogOpenRef.current) return;
181
- confirmDialogOpenRef.current = true;
182
- presentDialog({
183
- title: "Unsaved Changes",
184
- description: "You have unsaved changes that will be lost if you close now.",
185
- content: ({ dismiss: dismissConfirm }) => {
186
- const wrappedDismiss = () => {
187
- confirmDialogOpenRef.current = false;
188
- dismissConfirm();
189
- };
190
- return /* @__PURE__ */ jsxs("div", {
191
- className: excalidrawConfirmActions,
192
- children: [
193
- /* @__PURE__ */ jsx("button", {
194
- className: excalidrawConfirmBtn,
195
- type: "button",
196
- onClick: wrappedDismiss,
197
- children: "Continue Editing"
198
- }),
199
- /* @__PURE__ */ jsx("button", {
200
- className: excalidrawConfirmBtnDanger,
201
- type: "button",
202
- onClick: () => {
203
- wrappedDismiss();
204
- handleDiscard();
205
- },
206
- children: "Discard"
207
- }),
208
- /* @__PURE__ */ jsx("button", {
209
- className: excalidrawConfirmBtnPrimary,
210
- type: "button",
211
- onClick: () => {
212
- wrappedDismiss();
213
- handleSaveAndClose();
214
- },
215
- children: "Save & Close"
216
- })
217
- ]
218
- });
219
- },
220
- portalClassName,
221
- theme,
222
- showCloseButton: false,
223
- clickOutsideToDismiss: false
224
- });
225
- }, [
226
- handleDiscard,
227
- handleSaveAndClose,
228
- portalClassName,
229
- theme
230
- ]);
231
- const attemptClose = useCallback(() => {
232
- if (isDirtyRef.current) showConfirmDialog();
233
- else emitSnapshot().then(() => {
234
- onClose(getSnapshot());
235
- dismiss();
236
- });
237
- }, [
238
- dismiss,
239
- showConfirmDialog,
240
- emitSnapshot,
241
- getSnapshot,
242
- onClose
243
- ]);
244
- useEffect(() => {
245
- const handleKeyDown = (e) => {
246
- if (e.key === "Escape") {
247
- e.preventDefault();
248
- e.stopImmediatePropagation();
249
- if (confirmDialogOpenRef.current) {
250
- confirmDialogOpenRef.current = false;
251
- dismissTopDialog();
252
- } else attemptClose();
253
- }
254
- };
255
- document.addEventListener("keydown", handleKeyDown, true);
256
- return () => document.removeEventListener("keydown", handleKeyDown, true);
257
- }, [attemptClose]);
258
- const handleManualUpload = useCallback(() => {
259
- performSave();
260
- }, [performSave]);
261
- const handleExportJson = useCallback(() => {
262
- const doc = getSnapshot();
263
- if (!doc) return;
264
- const blob = new Blob([JSON.stringify(doc, null, 2)], { type: "application/json" });
265
- const url = URL.createObjectURL(blob);
266
- const a = document.createElement("a");
267
- a.href = url;
268
- a.download = "excalidraw-snapshot.json";
269
- a.click();
270
- URL.revokeObjectURL(url);
271
- }, [getSnapshot]);
272
- const handleCopyJson = useCallback(() => {
273
- const doc = getSnapshot();
274
- if (!doc) return;
275
- navigator.clipboard.writeText(JSON.stringify(doc));
276
- }, [getSnapshot]);
277
- const handleRefChange = useCallback((e) => {
278
- const { value } = e.target;
279
- setSavedRef(value);
280
- baseRefRef.current = value;
281
- baseDataRef.current = void 0;
282
- }, []);
283
- const handleStorageModeChange = useCallback((newMode) => {
284
- setStorageMode(newMode);
285
- storageModeRef.current = newMode;
286
- if (newMode === "inline") setSavedRef("");
287
- performSave();
288
- }, [performSave]);
289
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
290
- className: excalidrawDialogHeader,
291
- children: [
292
- /* @__PURE__ */ jsx("span", {
293
- className: excalidrawStatusDot,
294
- style: { backgroundColor: isDirty ? "#f59e0b" : "#22c55e" }
295
- }),
296
- /* @__PURE__ */ jsx("span", {
297
- className: excalidrawDialogTitle,
298
- children: "Canvas Editor"
299
- }),
300
- /* @__PURE__ */ jsx("span", {
301
- className: excalidrawDialogMeta,
302
- children: "excalidraw"
303
- }),
304
- /* @__PURE__ */ jsxs("div", {
305
- className: excalidrawHeaderActions,
306
- children: [
307
- saveSnapshot && /* @__PURE__ */ jsxs(Fragment, { children: [
308
- /* @__PURE__ */ jsx(SegmentedControl, {
309
- value: storageMode,
310
- items: [
311
- {
312
- value: "inline",
313
- label: "Inline"
314
- },
315
- {
316
- value: "remote",
317
- label: "Remote"
318
- },
319
- {
320
- value: "delta",
321
- label: "Delta"
322
- }
323
- ],
324
- onChange: handleStorageModeChange
325
- }),
326
- /* @__PURE__ */ jsx("div", { className: "_1c3wdzlh" }),
327
- /* @__PURE__ */ jsxs("button", {
328
- className: "_1c3wdzlg",
329
- disabled: isSaving,
330
- type: "button",
331
- onClick: handleManualUpload,
332
- children: [/* @__PURE__ */ jsx(Save, { size: 14 }), /* @__PURE__ */ jsx("span", { children: isSaving ? "Saving..." : "Save" })]
333
- }),
334
- /* @__PURE__ */ jsx("div", { className: "_1c3wdzlh" })
335
- ] }),
336
- /* @__PURE__ */ jsxs("button", {
337
- className: excalidrawActionBarBtn,
338
- type: "button",
339
- onClick: handleExportJson,
340
- children: [/* @__PURE__ */ jsx(Download, { size: 14 }), "JSON"]
341
- }),
342
- /* @__PURE__ */ jsxs("button", {
343
- className: excalidrawActionBarBtn,
344
- type: "button",
345
- onClick: handleCopyJson,
346
- children: [/* @__PURE__ */ jsx(Clipboard, { size: 14 }), "Copy"]
347
- }),
348
- storageMode !== "inline" && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", { className: "_1c3wdzlh" }), /* @__PURE__ */ jsx("input", {
349
- className: "_1c3wdzli",
350
- placeholder: "base URL / ref",
351
- spellCheck: false,
352
- type: "text",
353
- value: savedRef,
354
- onChange: handleRefChange
355
- })] })
356
- ]
357
- }),
358
- /* @__PURE__ */ jsx("button", {
359
- className: excalidrawHeaderClose,
360
- type: "button",
361
- onClick: attemptClose,
362
- children: /* @__PURE__ */ jsx(X, { size: 18 })
363
- })
364
- ]
365
- }), /* @__PURE__ */ jsx("div", {
366
- className: excalidrawDialogCanvas,
367
- children: /* @__PURE__ */ jsx(ExcalidrawComponent, {
368
- initialData,
369
- theme,
370
- excalidrawAPI: (api) => {
371
- apiRef.current = api;
372
- },
373
- onChange: handleChange
374
- })
375
- })] });
376
- };
377
- var ExcalidrawEditRenderer = ({ snapshot, onSnapshotChange }) => {
378
- const theme = useColorScheme();
379
- const { saveSnapshot } = useExcalidrawConfig();
380
- const { snapshot: initialData, loading: dataLoading, error: dataError, baseRef, baseData } = useExcalidrawData(snapshot);
381
- const [ExcalidrawComponent, setExcalidrawComponent] = useState(null);
382
- const [libLoading, setLibLoading] = useState(true);
383
- const previewApiRef = useRef(null);
384
- const initialDataRef = useRef(void 0);
385
- const [previewKey, setPreviewKey] = useState(0);
386
- const onSnapshotChangeRef = useRef(onSnapshotChange);
387
- onSnapshotChangeRef.current = onSnapshotChange;
388
- const saveSnapshotRef = useRef(saveSnapshot);
389
- saveSnapshotRef.current = saveSnapshot;
390
- const snapshotRef = useRef(snapshot);
391
- snapshotRef.current = snapshot;
392
- const { className: portalClassName } = usePortalTheme();
393
- useEffect(() => {
394
- if (initialData && !initialDataRef.current) initialDataRef.current = initialData;
395
- }, [initialData]);
396
- useEffect(() => {
397
- Promise.all([import("@excalidraw/excalidraw"), import("@excalidraw/excalidraw/index.css")]).then(([mod]) => {
398
- const Comp = mod.Excalidraw || mod.default?.Excalidraw;
399
- if (Comp) setExcalidrawComponent(() => Comp);
400
- setLibLoading(false);
401
- }).catch((err) => {
402
- console.error("[excalidraw] failed to load", err);
403
- setLibLoading(false);
404
- });
405
- }, []);
406
- const baseRefRef = useRef(baseRef);
407
- const baseDataRefOuter = useRef(baseData);
408
- useEffect(() => {
409
- if (baseRef) baseRefRef.current = baseRef;
410
- }, [baseRef]);
411
- useEffect(() => {
412
- if (baseData) baseDataRefOuter.current = baseData;
413
- }, [baseData]);
414
- const handleOpenEditor = useCallback(() => {
415
- if (!ExcalidrawComponent || dataLoading) return;
416
- presentDialog({
417
- content: ({ dismiss }) => /* @__PURE__ */ jsx(ExcalidrawEditorDialogContent, {
418
- ExcalidrawComponent,
419
- baseData: baseDataRefOuter.current,
420
- baseRef: baseRefRef.current,
421
- dismiss,
422
- initialData: initialDataRef.current,
423
- initialSnapshot: snapshotRef.current,
424
- saveSnapshot: saveSnapshotRef.current,
425
- theme,
426
- onSave: (ref) => onSnapshotChangeRef.current(ref),
427
- onClose: (doc) => {
428
- if (doc) {
429
- initialDataRef.current = doc;
430
- setPreviewKey((k) => k + 1);
431
- }
432
- }
433
- }),
434
- className: excalidrawFullscreenPopup,
435
- portalClassName,
436
- theme,
437
- showCloseButton: false,
438
- clickOutsideToDismiss: false
439
- });
440
- }, [
441
- ExcalidrawComponent,
442
- dataLoading,
443
- theme,
444
- portalClassName
445
- ]);
446
- if (dataLoading || libLoading || !ExcalidrawComponent) return /* @__PURE__ */ jsx("div", {
447
- className: excalidrawEditorContainer,
448
- children: /* @__PURE__ */ jsx("div", {
449
- className: excalidrawLoading,
450
- children: "Loading excalidraw editor..."
451
- })
452
- });
453
- if (dataError) return /* @__PURE__ */ jsx("div", {
454
- className: excalidrawEditorContainer,
455
- children: /* @__PURE__ */ jsx("div", {
456
- className: excalidrawError,
457
- children: dataError
458
- })
459
- });
460
- return /* @__PURE__ */ jsxs("div", {
461
- className: excalidrawEditorContainer,
462
- children: [/* @__PURE__ */ jsx(ExcalidrawComponent, {
463
- viewModeEnabled: true,
464
- zenModeEnabled: true,
465
- UIOptions: readonlyUIOptions,
466
- initialData: initialDataRef.current,
467
- theme,
468
- excalidrawAPI: (api) => {
469
- previewApiRef.current = api;
470
- setTimeout(() => api.scrollToContent(), 100);
471
- }
472
- }, previewKey), /* @__PURE__ */ jsx("button", {
473
- className: excalidrawEditOverlay,
474
- type: "button",
475
- onClick: handleOpenEditor,
476
- children: /* @__PURE__ */ jsxs("span", {
477
- className: excalidrawEditLabel,
478
- children: [/* @__PURE__ */ jsx(Pencil, { size: 16 }), "Edit Whiteboard"]
479
- })
480
- })]
481
- });
482
- };
483
- //#endregion
484
- export { ExcalidrawEditRenderer_exports as n, ExcalidrawEditRenderer as t };
@@ -1,13 +0,0 @@
1
- //#region \0rolldown/runtime.js
2
- var __defProp = Object.defineProperty;
3
- var __exportAll = (all, no_symbols) => {
4
- let target = {};
5
- for (var name in all) __defProp(target, name, {
6
- get: all[name],
7
- enumerable: true
8
- });
9
- if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
10
- return target;
11
- };
12
- //#endregion
13
- export { __exportAll as t };
@@ -1,119 +0,0 @@
1
- import { E as excalidrawPlaceholder, _ as excalidrawDialogTitle, h as excalidrawDialogHeaderTitle, m as excalidrawDialogHeader, p as excalidrawDialogCanvas, w as excalidrawHeaderClose } from "./useExcalidrawData-DcXa8vqV.js";
2
- import { r as _defineProperty } from "./ExcalidrawDisplayRenderer-BWsFJSQ7.js";
3
- import { Suspense, lazy } from "react";
4
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
- import { X } from "lucide-react";
6
- import { ViewportGate } from "@haklex/rich-editor-ui";
7
- import { DecoratorNode } from "lexical";
8
- import { createRendererDecoration } from "@haklex/rich-editor/static";
9
- import { EXCALIDRAW_BLOCK_TRANSFORMER } from "@haklex/rich-headless/transformers";
10
- //#region src/ExcalidrawSSRRenderer.tsx
11
- var LazyDisplayRenderer = lazy(() => import("./ExcalidrawDisplayRenderer-BWsFJSQ7.js").then((n) => n.n).then((m) => ({ default: m.ExcalidrawDisplayRenderer })));
12
- var ExcalidrawPlaceholder = ({ snapshot }) => {
13
- let label = "Excalidraw Whiteboard";
14
- try {
15
- const data = JSON.parse(snapshot);
16
- if (data && typeof data === "object") {
17
- const elementCount = Array.isArray(data.elements) ? data.elements.length : 0;
18
- if (elementCount > 0) label = `Excalidraw Whiteboard (${elementCount} elements)`;
19
- }
20
- } catch {}
21
- return /* @__PURE__ */ jsx("div", {
22
- "aria-label": label,
23
- className: excalidrawPlaceholder,
24
- children: /* @__PURE__ */ jsx("span", { children: label })
25
- });
26
- };
27
- var ExcalidrawSSRRenderer = ({ onExpand, snapshot }) => {
28
- return /* @__PURE__ */ jsx(ViewportGate, {
29
- fallback: /* @__PURE__ */ jsx(ExcalidrawPlaceholder, { snapshot }),
30
- children: /* @__PURE__ */ jsx(Suspense, {
31
- fallback: /* @__PURE__ */ jsx(ExcalidrawPlaceholder, { snapshot }),
32
- children: /* @__PURE__ */ jsx(LazyDisplayRenderer, {
33
- snapshot,
34
- onExpand
35
- })
36
- })
37
- });
38
- };
39
- //#endregion
40
- //#region src/slot.ts
41
- var EXCALIDRAW_NODE_KEY = "Excalidraw";
42
- //#endregion
43
- //#region src/ExcalidrawNode.ts
44
- var ExcalidrawNode = class ExcalidrawNode extends DecoratorNode {
45
- static getType() {
46
- return "excalidraw";
47
- }
48
- static clone(node) {
49
- return new ExcalidrawNode(node.__snapshot, node.__key);
50
- }
51
- constructor(snapshot, key) {
52
- super(key);
53
- _defineProperty(this, "__snapshot", void 0);
54
- this.__snapshot = snapshot;
55
- }
56
- createDOM(_config) {
57
- const div = document.createElement("div");
58
- div.className = "rich-excalidraw-wrapper";
59
- return div;
60
- }
61
- updateDOM() {
62
- return false;
63
- }
64
- isInline() {
65
- return false;
66
- }
67
- static importJSON(serializedNode) {
68
- return $createExcalidrawNode(serializedNode.snapshot);
69
- }
70
- exportJSON() {
71
- return {
72
- ...super.exportJSON(),
73
- type: "excalidraw",
74
- snapshot: this.__snapshot,
75
- version: 1
76
- };
77
- }
78
- getSnapshot() {
79
- return this.__snapshot;
80
- }
81
- setSnapshot(snapshot) {
82
- const writable = this.getWritable();
83
- writable.__snapshot = snapshot;
84
- }
85
- decorate(_editor, _config) {
86
- return createRendererDecoration(EXCALIDRAW_NODE_KEY, ExcalidrawSSRRenderer, { snapshot: this.__snapshot });
87
- }
88
- };
89
- function $createExcalidrawNode(snapshot) {
90
- return new ExcalidrawNode(snapshot);
91
- }
92
- function $isExcalidrawNode(node) {
93
- return node instanceof ExcalidrawNode;
94
- }
95
- //#endregion
96
- //#region src/ExcalidrawExpandShell.tsx
97
- var ExcalidrawExpandShell = ({ children, dismiss, meta = "excalidraw", title = "Whiteboard" }) => /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
98
- className: excalidrawDialogHeader,
99
- children: [/* @__PURE__ */ jsxs("div", {
100
- className: excalidrawDialogHeaderTitle,
101
- children: [/* @__PURE__ */ jsx("span", {
102
- className: excalidrawDialogTitle,
103
- children: title
104
- }), meta != null && /* @__PURE__ */ jsx("span", {
105
- className: "_1c3wdzld",
106
- children: meta
107
- })]
108
- }), /* @__PURE__ */ jsx("button", {
109
- className: excalidrawHeaderClose,
110
- type: "button",
111
- onClick: dismiss,
112
- children: /* @__PURE__ */ jsx(X, { size: 18 })
113
- })]
114
- }), /* @__PURE__ */ jsx("div", {
115
- className: excalidrawDialogCanvas,
116
- children
117
- })] });
118
- //#endregion
119
- export { ExcalidrawNode as a, $isExcalidrawNode as i, ExcalidrawExpandShell as n, EXCALIDRAW_NODE_KEY as o, $createExcalidrawNode as r, ExcalidrawSSRRenderer as s, EXCALIDRAW_BLOCK_TRANSFORMER as t };