@haklex/rich-ext-excalidraw 0.0.82 → 0.0.84

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.
@@ -1,475 +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 { SegmentedControl, dismissTopDialog, presentDialog } from "@haklex/rich-editor-ui";
7
- import { usePortalTheme } from "@haklex/rich-style-token";
8
- import { Clipboard, Download, Pencil, Save, X } from "lucide-react";
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 baseRefRef = useRef(baseRef);
21
- const baseDataRef = useRef(baseData);
22
- const [storageMode, setStorageMode] = useState(() => {
23
- if (!saveSnapshot || !initialSnapshot) return "inline";
24
- return initialSnapshot.type === "delta" ? "delta" : initialSnapshot.type === "remote" ? "remote" : "inline";
25
- });
26
- const storageModeRef = useRef(storageMode);
27
- const [savedRef, setSavedRef] = useState(() => {
28
- if (!saveSnapshot || !initialSnapshot) return "";
29
- if (initialSnapshot.type === "remote") return initialSnapshot.url;
30
- if (initialSnapshot.type === "delta") return initialSnapshot.baseUrl;
31
- return "";
32
- });
33
- const [isSaving, setIsSaving] = useState(false);
34
- useEffect(() => {
35
- return () => {
36
- if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
37
- };
38
- }, []);
39
- const getSnapshot = useCallback(() => {
40
- const api = apiRef.current;
41
- if (!api) return;
42
- const appState = api.getAppState();
43
- return {
44
- elements: api.getSceneElements(),
45
- appState: {
46
- viewBackgroundColor: appState.viewBackgroundColor,
47
- gridSize: appState.gridSize
48
- },
49
- files: api.getFiles()
50
- };
51
- }, []);
52
- const performSave = useCallback(() => {
53
- if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
54
- const doc = getSnapshot();
55
- if (!doc) return Promise.resolve();
56
- const mode = storageModeRef.current;
57
- if (mode === "delta" && saveSnapshot) {
58
- setIsSaving(true);
59
- return (async () => {
60
- try {
61
- const currentBaseUrl = baseRefRef.current;
62
- const currentBaseData = baseDataRef.current;
63
- if (!currentBaseUrl || !currentBaseData) {
64
- const ref = await saveSnapshot(doc, baseRefRef.current || void 0);
65
- baseRefRef.current = ref;
66
- baseDataRef.current = doc;
67
- setSavedRef(ref);
68
- }
69
- isDirtyRef.current = false;
70
- setIsDirty(false);
71
- } finally {
72
- setIsSaving(false);
73
- }
74
- })();
75
- }
76
- if (mode === "remote" && saveSnapshot) {
77
- if (baseRefRef.current && baseDataRef.current && JSON.stringify(doc) === JSON.stringify(baseDataRef.current)) {
78
- isDirtyRef.current = false;
79
- setIsDirty(false);
80
- return Promise.resolve();
81
- }
82
- setIsSaving(true);
83
- return saveSnapshot(doc, baseRefRef.current || void 0).then((ref) => {
84
- baseRefRef.current = ref;
85
- baseDataRef.current = doc;
86
- isDirtyRef.current = false;
87
- setIsDirty(false);
88
- setIsSaving(false);
89
- setSavedRef(ref);
90
- }, () => {
91
- setIsSaving(false);
92
- });
93
- }
94
- isDirtyRef.current = false;
95
- setIsDirty(false);
96
- return Promise.resolve();
97
- }, [saveSnapshot, getSnapshot]);
98
- const emitSnapshot = useCallback(async () => {
99
- const doc = getSnapshot();
100
- if (!doc) return;
101
- const mode = storageModeRef.current;
102
- if (mode === "delta" && saveSnapshot) {
103
- const currentBaseUrl = baseRefRef.current;
104
- const currentBaseData = baseDataRef.current;
105
- if (currentBaseUrl && currentBaseData) {
106
- const { diff } = await import("jsondiffpatch");
107
- const delta = diff(currentBaseData, structuredClone(doc));
108
- onSave(delta ? {
109
- type: "delta",
110
- baseUrl: currentBaseUrl,
111
- delta
112
- } : {
113
- type: "remote",
114
- url: currentBaseUrl
115
- });
116
- } else onSave({
117
- type: "remote",
118
- url: await saveSnapshot(doc, baseRefRef.current || void 0)
119
- });
120
- return;
121
- }
122
- if (mode === "remote") {
123
- const ref = baseRefRef.current;
124
- if (ref) onSave({
125
- type: "remote",
126
- url: ref
127
- });
128
- else if (saveSnapshot) onSave({
129
- type: "remote",
130
- url: await saveSnapshot(doc)
131
- });
132
- return;
133
- }
134
- onSave({
135
- type: "inline",
136
- data: doc
137
- });
138
- }, [
139
- onSave,
140
- saveSnapshot,
141
- getSnapshot
142
- ]);
143
- const handleChange = useCallback((elements) => {
144
- if (!initializedRef.current) {
145
- initializedRef.current = true;
146
- return;
147
- }
148
- if (elements.length === 0) return;
149
- if (!isDirtyRef.current) {
150
- isDirtyRef.current = true;
151
- setIsDirty(true);
152
- }
153
- if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
154
- saveTimerRef.current = setTimeout(() => performSave(), SAVE_DEBOUNCE_MS);
155
- }, [performSave]);
156
- const handleSaveAndClose = useCallback(() => {
157
- performSave().then(async () => {
158
- await emitSnapshot();
159
- onClose(getSnapshot());
160
- dismiss();
161
- });
162
- }, [
163
- performSave,
164
- emitSnapshot,
165
- getSnapshot,
166
- onClose,
167
- dismiss
168
- ]);
169
- const handleDiscard = useCallback(() => {
170
- if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
171
- dismiss();
172
- }, [dismiss]);
173
- const showConfirmDialog = useCallback(() => {
174
- if (confirmDialogOpenRef.current) return;
175
- confirmDialogOpenRef.current = true;
176
- presentDialog({
177
- title: "Unsaved Changes",
178
- description: "You have unsaved changes that will be lost if you close now.",
179
- content: ({ dismiss: dismissConfirm }) => {
180
- const wrappedDismiss = () => {
181
- confirmDialogOpenRef.current = false;
182
- dismissConfirm();
183
- };
184
- return /* @__PURE__ */ jsxs("div", {
185
- className: excalidrawConfirmActions,
186
- children: [
187
- /* @__PURE__ */ jsx("button", {
188
- className: excalidrawConfirmBtn,
189
- type: "button",
190
- onClick: wrappedDismiss,
191
- children: "Continue Editing"
192
- }),
193
- /* @__PURE__ */ jsx("button", {
194
- className: excalidrawConfirmBtnDanger,
195
- type: "button",
196
- onClick: () => {
197
- wrappedDismiss();
198
- handleDiscard();
199
- },
200
- children: "Discard"
201
- }),
202
- /* @__PURE__ */ jsx("button", {
203
- className: excalidrawConfirmBtnPrimary,
204
- type: "button",
205
- onClick: () => {
206
- wrappedDismiss();
207
- handleSaveAndClose();
208
- },
209
- children: "Save & Close"
210
- })
211
- ]
212
- });
213
- },
214
- portalClassName,
215
- theme,
216
- showCloseButton: false,
217
- clickOutsideToDismiss: false
218
- });
219
- }, [
220
- handleDiscard,
221
- handleSaveAndClose,
222
- portalClassName,
223
- theme
224
- ]);
225
- const attemptClose = useCallback(() => {
226
- if (isDirtyRef.current) showConfirmDialog();
227
- else emitSnapshot().then(() => {
228
- onClose(getSnapshot());
229
- dismiss();
230
- });
231
- }, [
232
- dismiss,
233
- showConfirmDialog,
234
- emitSnapshot,
235
- getSnapshot,
236
- onClose
237
- ]);
238
- useEffect(() => {
239
- const handleKeyDown = (e) => {
240
- if (e.key === "Escape") {
241
- e.preventDefault();
242
- e.stopImmediatePropagation();
243
- if (confirmDialogOpenRef.current) {
244
- confirmDialogOpenRef.current = false;
245
- dismissTopDialog();
246
- } else attemptClose();
247
- }
248
- };
249
- document.addEventListener("keydown", handleKeyDown, true);
250
- return () => document.removeEventListener("keydown", handleKeyDown, true);
251
- }, [attemptClose]);
252
- const handleManualUpload = useCallback(() => {
253
- performSave();
254
- }, [performSave]);
255
- const handleExportJson = useCallback(() => {
256
- const doc = getSnapshot();
257
- if (!doc) return;
258
- const blob = new Blob([JSON.stringify(doc, null, 2)], { type: "application/json" });
259
- const url = URL.createObjectURL(blob);
260
- const a = document.createElement("a");
261
- a.href = url;
262
- a.download = "excalidraw-snapshot.json";
263
- a.click();
264
- URL.revokeObjectURL(url);
265
- }, [getSnapshot]);
266
- const handleCopyJson = useCallback(() => {
267
- const doc = getSnapshot();
268
- if (!doc) return;
269
- navigator.clipboard.writeText(JSON.stringify(doc));
270
- }, [getSnapshot]);
271
- const handleRefChange = useCallback((e) => {
272
- const { value } = e.target;
273
- setSavedRef(value);
274
- baseRefRef.current = value;
275
- baseDataRef.current = void 0;
276
- }, []);
277
- const handleStorageModeChange = useCallback((newMode) => {
278
- setStorageMode(newMode);
279
- storageModeRef.current = newMode;
280
- if (newMode === "inline") setSavedRef("");
281
- performSave();
282
- }, [performSave]);
283
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
284
- className: excalidrawDialogHeader,
285
- children: [
286
- /* @__PURE__ */ jsx("span", {
287
- className: excalidrawStatusDot,
288
- style: { backgroundColor: isDirty ? "#f59e0b" : "#22c55e" }
289
- }),
290
- /* @__PURE__ */ jsx("span", {
291
- className: excalidrawDialogTitle,
292
- children: "Canvas Editor"
293
- }),
294
- /* @__PURE__ */ jsx("span", {
295
- className: excalidrawDialogMeta,
296
- children: "excalidraw"
297
- }),
298
- /* @__PURE__ */ jsxs("div", {
299
- className: excalidrawHeaderActions,
300
- children: [
301
- saveSnapshot && /* @__PURE__ */ jsxs(Fragment, { children: [
302
- /* @__PURE__ */ jsx(SegmentedControl, {
303
- value: storageMode,
304
- items: [
305
- {
306
- value: "inline",
307
- label: "Inline"
308
- },
309
- {
310
- value: "remote",
311
- label: "Remote"
312
- },
313
- {
314
- value: "delta",
315
- label: "Delta"
316
- }
317
- ],
318
- onChange: handleStorageModeChange
319
- }),
320
- /* @__PURE__ */ jsx("div", { className: "_1c3wdzlh" }),
321
- /* @__PURE__ */ jsxs("button", {
322
- className: "_1c3wdzlg",
323
- disabled: isSaving,
324
- type: "button",
325
- onClick: handleManualUpload,
326
- children: [/* @__PURE__ */ jsx(Save, { size: 14 }), /* @__PURE__ */ jsx("span", { children: isSaving ? "Saving..." : "Save" })]
327
- }),
328
- /* @__PURE__ */ jsx("div", { className: "_1c3wdzlh" })
329
- ] }),
330
- /* @__PURE__ */ jsxs("button", {
331
- className: excalidrawActionBarBtn,
332
- type: "button",
333
- onClick: handleExportJson,
334
- children: [/* @__PURE__ */ jsx(Download, { size: 14 }), "JSON"]
335
- }),
336
- /* @__PURE__ */ jsxs("button", {
337
- className: excalidrawActionBarBtn,
338
- type: "button",
339
- onClick: handleCopyJson,
340
- children: [/* @__PURE__ */ jsx(Clipboard, { size: 14 }), "Copy"]
341
- }),
342
- storageMode !== "inline" && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", { className: "_1c3wdzlh" }), /* @__PURE__ */ jsx("input", {
343
- className: "_1c3wdzli",
344
- placeholder: "base URL / ref",
345
- spellCheck: false,
346
- type: "text",
347
- value: savedRef,
348
- onChange: handleRefChange
349
- })] })
350
- ]
351
- }),
352
- /* @__PURE__ */ jsx("button", {
353
- className: excalidrawHeaderClose,
354
- type: "button",
355
- onClick: attemptClose,
356
- children: /* @__PURE__ */ jsx(X, { size: 18 })
357
- })
358
- ]
359
- }), /* @__PURE__ */ jsx("div", {
360
- className: excalidrawDialogCanvas,
361
- children: /* @__PURE__ */ jsx(ExcalidrawComponent, {
362
- initialData,
363
- theme,
364
- excalidrawAPI: (api) => {
365
- apiRef.current = api;
366
- },
367
- onChange: handleChange
368
- })
369
- })] });
370
- };
371
- var ExcalidrawEditRenderer = ({ snapshot, onSnapshotChange }) => {
372
- const theme = useColorScheme();
373
- const { saveSnapshot } = useExcalidrawConfig();
374
- const { snapshot: initialData, loading: dataLoading, error: dataError, baseRef, baseData } = useExcalidrawData(snapshot);
375
- const [ExcalidrawComponent, setExcalidrawComponent] = useState(null);
376
- const [libLoading, setLibLoading] = useState(true);
377
- const previewApiRef = useRef(null);
378
- const initialDataRef = useRef(void 0);
379
- const [previewKey, setPreviewKey] = useState(0);
380
- const onSnapshotChangeRef = useRef(onSnapshotChange);
381
- onSnapshotChangeRef.current = onSnapshotChange;
382
- const saveSnapshotRef = useRef(saveSnapshot);
383
- saveSnapshotRef.current = saveSnapshot;
384
- const snapshotRef = useRef(snapshot);
385
- snapshotRef.current = snapshot;
386
- const { className: portalClassName } = usePortalTheme();
387
- useEffect(() => {
388
- if (initialData && !initialDataRef.current) initialDataRef.current = initialData;
389
- }, [initialData]);
390
- useEffect(() => {
391
- import("@excalidraw/excalidraw").then((mod) => {
392
- const Comp = mod.Excalidraw || mod.default?.Excalidraw;
393
- if (Comp) setExcalidrawComponent(() => Comp);
394
- setLibLoading(false);
395
- }).catch(() => setLibLoading(false));
396
- }, []);
397
- const baseRefRef = useRef(baseRef);
398
- const baseDataRefOuter = useRef(baseData);
399
- useEffect(() => {
400
- if (baseRef) baseRefRef.current = baseRef;
401
- }, [baseRef]);
402
- useEffect(() => {
403
- if (baseData) baseDataRefOuter.current = baseData;
404
- }, [baseData]);
405
- const handleOpenEditor = useCallback(() => {
406
- if (!ExcalidrawComponent || dataLoading) return;
407
- presentDialog({
408
- content: ({ dismiss }) => /* @__PURE__ */ jsx(ExcalidrawEditorDialogContent, {
409
- ExcalidrawComponent,
410
- baseData: baseDataRefOuter.current,
411
- baseRef: baseRefRef.current,
412
- dismiss,
413
- initialData: initialDataRef.current,
414
- initialSnapshot: snapshotRef.current,
415
- saveSnapshot: saveSnapshotRef.current,
416
- theme,
417
- onSave: (ref) => onSnapshotChangeRef.current(ref),
418
- onClose: (doc) => {
419
- if (doc) {
420
- initialDataRef.current = doc;
421
- setPreviewKey((k) => k + 1);
422
- }
423
- }
424
- }),
425
- className: excalidrawFullscreenPopup,
426
- portalClassName,
427
- theme,
428
- showCloseButton: false,
429
- clickOutsideToDismiss: false
430
- });
431
- }, [
432
- ExcalidrawComponent,
433
- dataLoading,
434
- theme,
435
- portalClassName
436
- ]);
437
- if (dataLoading || libLoading || !ExcalidrawComponent) return /* @__PURE__ */ jsx("div", {
438
- className: excalidrawEditorContainer,
439
- children: /* @__PURE__ */ jsx("div", {
440
- className: excalidrawLoading,
441
- children: "Loading excalidraw editor..."
442
- })
443
- });
444
- if (dataError) return /* @__PURE__ */ jsx("div", {
445
- className: excalidrawEditorContainer,
446
- children: /* @__PURE__ */ jsx("div", {
447
- className: excalidrawError,
448
- children: dataError
449
- })
450
- });
451
- return /* @__PURE__ */ jsxs("div", {
452
- className: excalidrawEditorContainer,
453
- children: [/* @__PURE__ */ jsx(ExcalidrawComponent, {
454
- viewModeEnabled: true,
455
- zenModeEnabled: true,
456
- UIOptions: readonlyUIOptions,
457
- initialData: initialDataRef.current,
458
- theme,
459
- excalidrawAPI: (api) => {
460
- previewApiRef.current = api;
461
- setTimeout(() => api.scrollToContent(), 100);
462
- }
463
- }, previewKey), /* @__PURE__ */ jsx("button", {
464
- className: excalidrawEditOverlay,
465
- type: "button",
466
- onClick: handleOpenEditor,
467
- children: /* @__PURE__ */ jsxs("span", {
468
- className: excalidrawEditLabel,
469
- children: [/* @__PURE__ */ jsx(Pencil, { size: 16 }), "Edit Whiteboard"]
470
- })
471
- })]
472
- });
473
- };
474
- //#endregion
475
- 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,88 +0,0 @@
1
- import { E as excalidrawPlaceholder } from "./useExcalidrawData-DcXa8vqV.js";
2
- import { r as _defineProperty } from "./ExcalidrawDisplayRenderer-C4fbKYqL.js";
3
- import { Suspense, createElement, lazy } from "react";
4
- import { jsx } from "react/jsx-runtime";
5
- import { ViewportGate } from "@haklex/rich-editor-ui";
6
- import { DecoratorNode } from "lexical";
7
- import { EXCALIDRAW_BLOCK_TRANSFORMER } from "@haklex/rich-headless/transformers";
8
- //#region src/ExcalidrawSSRRenderer.tsx
9
- var LazyDisplayRenderer = lazy(() => import("./ExcalidrawDisplayRenderer-C4fbKYqL.js").then((n) => n.n).then((m) => ({ default: m.ExcalidrawDisplayRenderer })));
10
- var ExcalidrawPlaceholder = ({ snapshot }) => {
11
- let label = "Excalidraw Whiteboard";
12
- try {
13
- const data = JSON.parse(snapshot);
14
- if (data && typeof data === "object") {
15
- const elementCount = Array.isArray(data.elements) ? data.elements.length : 0;
16
- if (elementCount > 0) label = `Excalidraw Whiteboard (${elementCount} elements)`;
17
- }
18
- } catch {}
19
- return /* @__PURE__ */ jsx("div", {
20
- "aria-label": label,
21
- className: excalidrawPlaceholder,
22
- children: /* @__PURE__ */ jsx("span", { children: label })
23
- });
24
- };
25
- var ExcalidrawSSRRenderer = ({ snapshot }) => {
26
- return /* @__PURE__ */ jsx(ViewportGate, {
27
- fallback: /* @__PURE__ */ jsx(ExcalidrawPlaceholder, { snapshot }),
28
- children: /* @__PURE__ */ jsx(Suspense, {
29
- fallback: /* @__PURE__ */ jsx(ExcalidrawPlaceholder, { snapshot }),
30
- children: /* @__PURE__ */ jsx(LazyDisplayRenderer, { snapshot })
31
- })
32
- });
33
- };
34
- //#endregion
35
- //#region src/ExcalidrawNode.ts
36
- var ExcalidrawNode = class ExcalidrawNode extends DecoratorNode {
37
- static getType() {
38
- return "excalidraw";
39
- }
40
- static clone(node) {
41
- return new ExcalidrawNode(node.__snapshot, node.__key);
42
- }
43
- constructor(snapshot, key) {
44
- super(key);
45
- _defineProperty(this, "__snapshot", void 0);
46
- this.__snapshot = snapshot;
47
- }
48
- createDOM(_config) {
49
- const div = document.createElement("div");
50
- div.className = "rich-excalidraw-wrapper";
51
- return div;
52
- }
53
- updateDOM() {
54
- return false;
55
- }
56
- isInline() {
57
- return false;
58
- }
59
- static importJSON(serializedNode) {
60
- return $createExcalidrawNode(serializedNode.snapshot);
61
- }
62
- exportJSON() {
63
- return {
64
- ...super.exportJSON(),
65
- type: "excalidraw",
66
- snapshot: this.__snapshot,
67
- version: 1
68
- };
69
- }
70
- getSnapshot() {
71
- return this.__snapshot;
72
- }
73
- setSnapshot(snapshot) {
74
- const writable = this.getWritable();
75
- writable.__snapshot = snapshot;
76
- }
77
- decorate(_editor, _config) {
78
- return createElement(ExcalidrawSSRRenderer, { snapshot: this.__snapshot });
79
- }
80
- };
81
- function $createExcalidrawNode(snapshot) {
82
- return new ExcalidrawNode(snapshot);
83
- }
84
- function $isExcalidrawNode(node) {
85
- return node instanceof ExcalidrawNode;
86
- }
87
- //#endregion
88
- export { ExcalidrawSSRRenderer as a, ExcalidrawNode as i, $createExcalidrawNode as n, $isExcalidrawNode as r, EXCALIDRAW_BLOCK_TRANSFORMER as t };