@haklex/rich-ext-tldraw 0.0.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/LICENSE +28 -0
- package/README.md +59 -0
- package/dist/TldrawConfigContext.d.ts +10 -0
- package/dist/TldrawConfigContext.d.ts.map +1 -0
- package/dist/TldrawEditRenderer.d.ts +7 -0
- package/dist/TldrawEditRenderer.d.ts.map +1 -0
- package/dist/TldrawNode.d.ts +24 -0
- package/dist/TldrawNode.d.ts.map +1 -0
- package/dist/TldrawPlugin.d.ts +3 -0
- package/dist/TldrawPlugin.d.ts.map +1 -0
- package/dist/TldrawRenderer.d.ts +3 -0
- package/dist/TldrawRenderer.d.ts.map +1 -0
- package/dist/TldrawStaticRenderer.d.ts +6 -0
- package/dist/TldrawStaticRenderer.d.ts.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +1004 -0
- package/dist/rich-ext-tldraw.css +306 -0
- package/dist/styles.css.d.ts +25 -0
- package/dist/styles.css.d.ts.map +1 -0
- package/dist/useTldrawData.d.ts +8 -0
- package/dist/useTldrawData.d.ts.map +1 -0
- package/package.json +56 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1004 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
5
|
+
import { createContext, use, useMemo, forwardRef, createElement, useState, useEffect, useRef, useCallback, lazy, Suspense, Component } from "react";
|
|
6
|
+
import { useColorScheme } from "@haklex/rich-editor";
|
|
7
|
+
import { presentDialog, SegmentedControl } from "@haklex/rich-editor-ui";
|
|
8
|
+
import { DecoratorNode, $insertNodes, $getNodeByKey, createCommand, COMMAND_PRIORITY_EDITOR } from "lexical";
|
|
9
|
+
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
10
|
+
const TldrawConfigContext = createContext({});
|
|
11
|
+
function TldrawConfigProvider({
|
|
12
|
+
apiUrl,
|
|
13
|
+
saveSnapshot,
|
|
14
|
+
children
|
|
15
|
+
}) {
|
|
16
|
+
const value = useMemo(
|
|
17
|
+
() => ({ apiUrl, saveSnapshot }),
|
|
18
|
+
[apiUrl, saveSnapshot]
|
|
19
|
+
);
|
|
20
|
+
return /* @__PURE__ */ jsx(TldrawConfigContext.Provider, { value, children });
|
|
21
|
+
}
|
|
22
|
+
function useTldrawConfig() {
|
|
23
|
+
return use(TldrawConfigContext);
|
|
24
|
+
}
|
|
25
|
+
const PortalThemeContext = createContext({
|
|
26
|
+
className: ""
|
|
27
|
+
});
|
|
28
|
+
function usePortalTheme() {
|
|
29
|
+
return use(PortalThemeContext);
|
|
30
|
+
}
|
|
31
|
+
const mergeClasses = (...classes) => classes.filter((className, index, array) => {
|
|
32
|
+
return Boolean(className) && className.trim() !== "" && array.indexOf(className) === index;
|
|
33
|
+
}).join(" ").trim();
|
|
34
|
+
const toKebabCase = (string) => string.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
35
|
+
const toCamelCase = (string) => string.replace(
|
|
36
|
+
/^([A-Z])|[\s-_]+(\w)/g,
|
|
37
|
+
(match, p1, p2) => p2 ? p2.toUpperCase() : p1.toLowerCase()
|
|
38
|
+
);
|
|
39
|
+
const toPascalCase = (string) => {
|
|
40
|
+
const camelCase = toCamelCase(string);
|
|
41
|
+
return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);
|
|
42
|
+
};
|
|
43
|
+
var defaultAttributes = {
|
|
44
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
45
|
+
width: 24,
|
|
46
|
+
height: 24,
|
|
47
|
+
viewBox: "0 0 24 24",
|
|
48
|
+
fill: "none",
|
|
49
|
+
stroke: "currentColor",
|
|
50
|
+
strokeWidth: 2,
|
|
51
|
+
strokeLinecap: "round",
|
|
52
|
+
strokeLinejoin: "round"
|
|
53
|
+
};
|
|
54
|
+
const hasA11yProp = (props) => {
|
|
55
|
+
for (const prop in props) {
|
|
56
|
+
if (prop.startsWith("aria-") || prop === "role" || prop === "title") {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return false;
|
|
61
|
+
};
|
|
62
|
+
const Icon = forwardRef(
|
|
63
|
+
({
|
|
64
|
+
color = "currentColor",
|
|
65
|
+
size = 24,
|
|
66
|
+
strokeWidth = 2,
|
|
67
|
+
absoluteStrokeWidth,
|
|
68
|
+
className = "",
|
|
69
|
+
children,
|
|
70
|
+
iconNode,
|
|
71
|
+
...rest
|
|
72
|
+
}, ref) => createElement(
|
|
73
|
+
"svg",
|
|
74
|
+
{
|
|
75
|
+
ref,
|
|
76
|
+
...defaultAttributes,
|
|
77
|
+
width: size,
|
|
78
|
+
height: size,
|
|
79
|
+
stroke: color,
|
|
80
|
+
strokeWidth: absoluteStrokeWidth ? Number(strokeWidth) * 24 / Number(size) : strokeWidth,
|
|
81
|
+
className: mergeClasses("lucide", className),
|
|
82
|
+
...!children && !hasA11yProp(rest) && { "aria-hidden": "true" },
|
|
83
|
+
...rest
|
|
84
|
+
},
|
|
85
|
+
[
|
|
86
|
+
...iconNode.map(([tag, attrs]) => createElement(tag, attrs)),
|
|
87
|
+
...Array.isArray(children) ? children : [children]
|
|
88
|
+
]
|
|
89
|
+
)
|
|
90
|
+
);
|
|
91
|
+
const createLucideIcon = (iconName, iconNode) => {
|
|
92
|
+
const Component2 = forwardRef(
|
|
93
|
+
({ className, ...props }, ref) => createElement(Icon, {
|
|
94
|
+
ref,
|
|
95
|
+
iconNode,
|
|
96
|
+
className: mergeClasses(
|
|
97
|
+
`lucide-${toKebabCase(toPascalCase(iconName))}`,
|
|
98
|
+
`lucide-${iconName}`,
|
|
99
|
+
className
|
|
100
|
+
),
|
|
101
|
+
...props
|
|
102
|
+
})
|
|
103
|
+
);
|
|
104
|
+
Component2.displayName = toPascalCase(iconName);
|
|
105
|
+
return Component2;
|
|
106
|
+
};
|
|
107
|
+
const __iconNode$9 = [
|
|
108
|
+
["rect", { width: "8", height: "4", x: "8", y: "2", rx: "1", ry: "1", key: "tgr4d6" }],
|
|
109
|
+
[
|
|
110
|
+
"path",
|
|
111
|
+
{
|
|
112
|
+
d: "M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2",
|
|
113
|
+
key: "116196"
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
];
|
|
117
|
+
const Clipboard = createLucideIcon("clipboard", __iconNode$9);
|
|
118
|
+
const __iconNode$8 = [
|
|
119
|
+
["path", { d: "M12 15V3", key: "m9g1x1" }],
|
|
120
|
+
["path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4", key: "ih7n3h" }],
|
|
121
|
+
["path", { d: "m7 10 5 5 5-5", key: "brsn70" }]
|
|
122
|
+
];
|
|
123
|
+
const Download = createLucideIcon("download", __iconNode$8);
|
|
124
|
+
const __iconNode$7 = [
|
|
125
|
+
["path", { d: "M15 3h6v6", key: "1q9fwt" }],
|
|
126
|
+
["path", { d: "m21 3-7 7", key: "1l2asr" }],
|
|
127
|
+
["path", { d: "m3 21 7-7", key: "tjx5ai" }],
|
|
128
|
+
["path", { d: "M9 21H3v-6", key: "wtvkvv" }]
|
|
129
|
+
];
|
|
130
|
+
const Maximize2 = createLucideIcon("maximize-2", __iconNode$7);
|
|
131
|
+
const __iconNode$6 = [
|
|
132
|
+
[
|
|
133
|
+
"path",
|
|
134
|
+
{
|
|
135
|
+
d: "M15.707 21.293a1 1 0 0 1-1.414 0l-1.586-1.586a1 1 0 0 1 0-1.414l5.586-5.586a1 1 0 0 1 1.414 0l1.586 1.586a1 1 0 0 1 0 1.414z",
|
|
136
|
+
key: "nt11vn"
|
|
137
|
+
}
|
|
138
|
+
],
|
|
139
|
+
[
|
|
140
|
+
"path",
|
|
141
|
+
{
|
|
142
|
+
d: "m18 13-1.375-6.874a1 1 0 0 0-.746-.776L3.235 2.028a1 1 0 0 0-1.207 1.207L5.35 15.879a1 1 0 0 0 .776.746L13 18",
|
|
143
|
+
key: "15qc1e"
|
|
144
|
+
}
|
|
145
|
+
],
|
|
146
|
+
["path", { d: "m2.3 2.3 7.286 7.286", key: "1wuzzi" }],
|
|
147
|
+
["circle", { cx: "11", cy: "11", r: "2", key: "xmgehs" }]
|
|
148
|
+
];
|
|
149
|
+
const PenTool = createLucideIcon("pen-tool", __iconNode$6);
|
|
150
|
+
const __iconNode$5 = [
|
|
151
|
+
[
|
|
152
|
+
"path",
|
|
153
|
+
{
|
|
154
|
+
d: "M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z",
|
|
155
|
+
key: "1a8usu"
|
|
156
|
+
}
|
|
157
|
+
],
|
|
158
|
+
["path", { d: "m15 5 4 4", key: "1mk7zo" }]
|
|
159
|
+
];
|
|
160
|
+
const Pencil = createLucideIcon("pencil", __iconNode$5);
|
|
161
|
+
const __iconNode$4 = [
|
|
162
|
+
["path", { d: "M3 7V5a2 2 0 0 1 2-2h2", key: "aa7l1z" }],
|
|
163
|
+
["path", { d: "M17 3h2a2 2 0 0 1 2 2v2", key: "4qcy5o" }],
|
|
164
|
+
["path", { d: "M21 17v2a2 2 0 0 1-2 2h-2", key: "6vwrx8" }],
|
|
165
|
+
["path", { d: "M7 21H5a2 2 0 0 1-2-2v-2", key: "ioqczr" }],
|
|
166
|
+
["circle", { cx: "12", cy: "12", r: "3", key: "1v7zrd" }],
|
|
167
|
+
["path", { d: "m16 16-1.9-1.9", key: "1dq9hf" }]
|
|
168
|
+
];
|
|
169
|
+
const ScanSearch = createLucideIcon("scan-search", __iconNode$4);
|
|
170
|
+
const __iconNode$3 = [
|
|
171
|
+
["path", { d: "M12 3v12", key: "1x0j5s" }],
|
|
172
|
+
["path", { d: "m17 8-5-5-5 5", key: "7q97r8" }],
|
|
173
|
+
["path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4", key: "ih7n3h" }]
|
|
174
|
+
];
|
|
175
|
+
const Upload = createLucideIcon("upload", __iconNode$3);
|
|
176
|
+
const __iconNode$2 = [
|
|
177
|
+
["path", { d: "M18 6 6 18", key: "1bl5f8" }],
|
|
178
|
+
["path", { d: "m6 6 12 12", key: "d8bk6v" }]
|
|
179
|
+
];
|
|
180
|
+
const X = createLucideIcon("x", __iconNode$2);
|
|
181
|
+
const __iconNode$1 = [
|
|
182
|
+
["circle", { cx: "11", cy: "11", r: "8", key: "4ej97u" }],
|
|
183
|
+
["line", { x1: "21", x2: "16.65", y1: "21", y2: "16.65", key: "13gj7c" }],
|
|
184
|
+
["line", { x1: "11", x2: "11", y1: "8", y2: "14", key: "1vmskp" }],
|
|
185
|
+
["line", { x1: "8", x2: "14", y1: "11", y2: "11", key: "durymu" }]
|
|
186
|
+
];
|
|
187
|
+
const ZoomIn = createLucideIcon("zoom-in", __iconNode$1);
|
|
188
|
+
const __iconNode = [
|
|
189
|
+
["circle", { cx: "11", cy: "11", r: "8", key: "4ej97u" }],
|
|
190
|
+
["line", { x1: "21", x2: "16.65", y1: "21", y2: "16.65", key: "13gj7c" }],
|
|
191
|
+
["line", { x1: "8", x2: "14", y1: "11", y2: "11", key: "durymu" }]
|
|
192
|
+
];
|
|
193
|
+
const ZoomOut = createLucideIcon("zoom-out", __iconNode);
|
|
194
|
+
var tldrawStaticContainer = "_1vqtdre0";
|
|
195
|
+
var tldrawEditorContainer = "_1vqtdre1";
|
|
196
|
+
var tldrawLoading = "_1vqtdre2";
|
|
197
|
+
var tldrawError = "_1vqtdre4";
|
|
198
|
+
var tldrawActionGroup = "_1vqtdre5";
|
|
199
|
+
var tldrawActionButton = "_1vqtdre6";
|
|
200
|
+
var tldrawFullscreenPopup = "_1vqtdre7";
|
|
201
|
+
var tldrawDialogHeader = "_1vqtdre8";
|
|
202
|
+
var tldrawStatusDot = "_1vqtdre9";
|
|
203
|
+
var tldrawDialogTitle = "_1vqtdrea";
|
|
204
|
+
var tldrawDialogMeta = "_1vqtdreb";
|
|
205
|
+
var tldrawHeaderActions = "_1vqtdrec";
|
|
206
|
+
var tldrawHeaderClose = "_1vqtdred";
|
|
207
|
+
var tldrawActionBarBtn = "_1vqtdree";
|
|
208
|
+
var tldrawActionBarSep = "_1vqtdref";
|
|
209
|
+
var tldrawActionBarUrl = "_1vqtdreg";
|
|
210
|
+
var tldrawDialogCanvas = "_1vqtdreh";
|
|
211
|
+
var tldrawConfirmActions = "_1vqtdrei";
|
|
212
|
+
var tldrawConfirmBtn = "_1vqtdrej";
|
|
213
|
+
var tldrawConfirmBtnPrimary = "_1vqtdrek";
|
|
214
|
+
var tldrawConfirmBtnDanger = "_1vqtdrel";
|
|
215
|
+
var tldrawEditOverlay = "_1vqtdrem";
|
|
216
|
+
var tldrawEditLabel = "_1vqtdren";
|
|
217
|
+
function useTldrawData(data) {
|
|
218
|
+
const { apiUrl } = useTldrawConfig();
|
|
219
|
+
const parsed = useMemo(() => {
|
|
220
|
+
if (!data) return { type: "empty" };
|
|
221
|
+
try {
|
|
222
|
+
const json = JSON.parse(data);
|
|
223
|
+
if (json && typeof json === "object") {
|
|
224
|
+
return {
|
|
225
|
+
type: "inline",
|
|
226
|
+
snapshot: json
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
} catch {
|
|
230
|
+
}
|
|
231
|
+
const firstLine = data.split("\n")[0].trim();
|
|
232
|
+
let fetchUrl2;
|
|
233
|
+
if (firstLine.startsWith("http")) {
|
|
234
|
+
fetchUrl2 = firstLine;
|
|
235
|
+
} else if (firstLine.startsWith("ref:") && apiUrl) {
|
|
236
|
+
fetchUrl2 = `${apiUrl}/objects/${firstLine.slice(4)}`;
|
|
237
|
+
}
|
|
238
|
+
if (fetchUrl2) {
|
|
239
|
+
return { type: "remote", fetchUrl: fetchUrl2 };
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
type: "error",
|
|
243
|
+
error: firstLine.startsWith("ref:") ? "Missing apiUrl for ref resolution" : "Unrecognized snapshot format"
|
|
244
|
+
};
|
|
245
|
+
}, [data, apiUrl]);
|
|
246
|
+
const fetchUrl = parsed.type === "remote" ? parsed.fetchUrl : void 0;
|
|
247
|
+
const [remoteSnapshot, setRemoteSnapshot] = useState();
|
|
248
|
+
const [loading, setLoading] = useState(!!fetchUrl);
|
|
249
|
+
const [error, setError] = useState("");
|
|
250
|
+
useEffect(() => {
|
|
251
|
+
if (!fetchUrl) return;
|
|
252
|
+
let cancelled = false;
|
|
253
|
+
setLoading(true);
|
|
254
|
+
setError("");
|
|
255
|
+
fetch(fetchUrl).then((res) => {
|
|
256
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
257
|
+
return res.json();
|
|
258
|
+
}).then((json) => {
|
|
259
|
+
if (!cancelled) {
|
|
260
|
+
setRemoteSnapshot(json);
|
|
261
|
+
setLoading(false);
|
|
262
|
+
}
|
|
263
|
+
}).catch((err) => {
|
|
264
|
+
if (!cancelled) {
|
|
265
|
+
setError(err instanceof Error ? err.message : "Failed to load");
|
|
266
|
+
setLoading(false);
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
return () => {
|
|
270
|
+
cancelled = true;
|
|
271
|
+
};
|
|
272
|
+
}, [fetchUrl]);
|
|
273
|
+
if (parsed.type === "inline") {
|
|
274
|
+
return { snapshot: parsed.snapshot, loading: false, error: "" };
|
|
275
|
+
}
|
|
276
|
+
if (parsed.type === "remote") {
|
|
277
|
+
return { snapshot: remoteSnapshot, loading, error };
|
|
278
|
+
}
|
|
279
|
+
if (parsed.type === "error") {
|
|
280
|
+
return { snapshot: void 0, loading: false, error: parsed.error };
|
|
281
|
+
}
|
|
282
|
+
return { snapshot: void 0, loading: false, error: "" };
|
|
283
|
+
}
|
|
284
|
+
const SAVE_DEBOUNCE_MS = 2e3;
|
|
285
|
+
const TldrawEditorDialogContent = ({
|
|
286
|
+
dismiss,
|
|
287
|
+
TldrawComponent,
|
|
288
|
+
initialData,
|
|
289
|
+
initialSnapshot,
|
|
290
|
+
theme,
|
|
291
|
+
onSave,
|
|
292
|
+
saveSnapshot,
|
|
293
|
+
onClose
|
|
294
|
+
}) => {
|
|
295
|
+
const { className: portalClassName } = usePortalTheme();
|
|
296
|
+
const editorRef = useRef(null);
|
|
297
|
+
const saveTimerRef = useRef(void 0);
|
|
298
|
+
const [isDirty, setIsDirty] = useState(false);
|
|
299
|
+
const isDirtyRef = useRef(false);
|
|
300
|
+
const [storageMode, setStorageMode] = useState(() => {
|
|
301
|
+
if (!saveSnapshot || !initialSnapshot || !initialSnapshot.trim())
|
|
302
|
+
return "inline";
|
|
303
|
+
try {
|
|
304
|
+
JSON.parse(initialSnapshot);
|
|
305
|
+
return "inline";
|
|
306
|
+
} catch {
|
|
307
|
+
return "remote";
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
const storageModeRef = useRef(storageMode);
|
|
311
|
+
const [savedRef, setSavedRef] = useState(() => {
|
|
312
|
+
if (!saveSnapshot || !initialSnapshot || !initialSnapshot.trim()) return "";
|
|
313
|
+
try {
|
|
314
|
+
JSON.parse(initialSnapshot);
|
|
315
|
+
return "";
|
|
316
|
+
} catch {
|
|
317
|
+
return initialSnapshot;
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
const [isSaving, setIsSaving] = useState(false);
|
|
321
|
+
useEffect(() => {
|
|
322
|
+
return () => {
|
|
323
|
+
if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
|
|
324
|
+
};
|
|
325
|
+
}, []);
|
|
326
|
+
const performSave = useCallback(() => {
|
|
327
|
+
if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
|
|
328
|
+
const editor = editorRef.current;
|
|
329
|
+
if (!editor) return Promise.resolve();
|
|
330
|
+
try {
|
|
331
|
+
const doc = editor.store.getSnapshot();
|
|
332
|
+
if (storageModeRef.current === "remote" && saveSnapshot) {
|
|
333
|
+
setIsSaving(true);
|
|
334
|
+
return saveSnapshot(doc).then(
|
|
335
|
+
(ref) => {
|
|
336
|
+
onSave(ref);
|
|
337
|
+
isDirtyRef.current = false;
|
|
338
|
+
setIsDirty(false);
|
|
339
|
+
setIsSaving(false);
|
|
340
|
+
setSavedRef(ref);
|
|
341
|
+
},
|
|
342
|
+
() => {
|
|
343
|
+
setIsSaving(false);
|
|
344
|
+
}
|
|
345
|
+
);
|
|
346
|
+
} else {
|
|
347
|
+
onSave(JSON.stringify(doc));
|
|
348
|
+
isDirtyRef.current = false;
|
|
349
|
+
setIsDirty(false);
|
|
350
|
+
}
|
|
351
|
+
} catch {
|
|
352
|
+
}
|
|
353
|
+
return Promise.resolve();
|
|
354
|
+
}, [onSave, saveSnapshot]);
|
|
355
|
+
const handleMount = useCallback(
|
|
356
|
+
(editor) => {
|
|
357
|
+
editorRef.current = editor;
|
|
358
|
+
editor.user.updateUserPreferences({ colorScheme: theme });
|
|
359
|
+
editor.store.listen(
|
|
360
|
+
() => {
|
|
361
|
+
isDirtyRef.current = true;
|
|
362
|
+
setIsDirty(true);
|
|
363
|
+
if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
|
|
364
|
+
saveTimerRef.current = setTimeout(
|
|
365
|
+
() => performSave(),
|
|
366
|
+
SAVE_DEBOUNCE_MS
|
|
367
|
+
);
|
|
368
|
+
},
|
|
369
|
+
{ scope: "document" }
|
|
370
|
+
);
|
|
371
|
+
},
|
|
372
|
+
[theme, performSave]
|
|
373
|
+
);
|
|
374
|
+
const handleSaveAndClose = useCallback(() => {
|
|
375
|
+
void performSave().then(() => {
|
|
376
|
+
const editor = editorRef.current;
|
|
377
|
+
onClose(editor?.store.getSnapshot());
|
|
378
|
+
dismiss();
|
|
379
|
+
});
|
|
380
|
+
}, [performSave, onClose, dismiss]);
|
|
381
|
+
const handleDiscard = useCallback(() => {
|
|
382
|
+
if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
|
|
383
|
+
dismiss();
|
|
384
|
+
}, [dismiss]);
|
|
385
|
+
const showConfirmDialog = useCallback(() => {
|
|
386
|
+
presentDialog({
|
|
387
|
+
title: "Unsaved Changes",
|
|
388
|
+
description: "You have unsaved changes that will be lost if you close now.",
|
|
389
|
+
content: ({ dismiss: dismissConfirm }) => /* @__PURE__ */ jsxs("div", { className: tldrawConfirmActions, children: [
|
|
390
|
+
/* @__PURE__ */ jsx(
|
|
391
|
+
"button",
|
|
392
|
+
{
|
|
393
|
+
type: "button",
|
|
394
|
+
className: tldrawConfirmBtn,
|
|
395
|
+
onClick: dismissConfirm,
|
|
396
|
+
children: "Continue Editing"
|
|
397
|
+
}
|
|
398
|
+
),
|
|
399
|
+
/* @__PURE__ */ jsx(
|
|
400
|
+
"button",
|
|
401
|
+
{
|
|
402
|
+
type: "button",
|
|
403
|
+
className: tldrawConfirmBtnDanger,
|
|
404
|
+
onClick: () => {
|
|
405
|
+
dismissConfirm();
|
|
406
|
+
handleDiscard();
|
|
407
|
+
},
|
|
408
|
+
children: "Discard"
|
|
409
|
+
}
|
|
410
|
+
),
|
|
411
|
+
/* @__PURE__ */ jsx(
|
|
412
|
+
"button",
|
|
413
|
+
{
|
|
414
|
+
type: "button",
|
|
415
|
+
className: tldrawConfirmBtnPrimary,
|
|
416
|
+
onClick: () => {
|
|
417
|
+
dismissConfirm();
|
|
418
|
+
handleSaveAndClose();
|
|
419
|
+
},
|
|
420
|
+
children: "Save & Close"
|
|
421
|
+
}
|
|
422
|
+
)
|
|
423
|
+
] }),
|
|
424
|
+
portalClassName,
|
|
425
|
+
showCloseButton: false,
|
|
426
|
+
clickOutsideToDismiss: false
|
|
427
|
+
});
|
|
428
|
+
}, [handleDiscard, handleSaveAndClose, portalClassName]);
|
|
429
|
+
useEffect(() => {
|
|
430
|
+
const handleKeyDown = (e) => {
|
|
431
|
+
if (e.key === "Escape") {
|
|
432
|
+
e.preventDefault();
|
|
433
|
+
e.stopImmediatePropagation();
|
|
434
|
+
if (isDirtyRef.current) {
|
|
435
|
+
showConfirmDialog();
|
|
436
|
+
} else {
|
|
437
|
+
dismiss();
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
document.addEventListener("keydown", handleKeyDown, true);
|
|
442
|
+
return () => document.removeEventListener("keydown", handleKeyDown, true);
|
|
443
|
+
}, [dismiss, showConfirmDialog]);
|
|
444
|
+
const attemptClose = useCallback(() => {
|
|
445
|
+
if (isDirtyRef.current) {
|
|
446
|
+
showConfirmDialog();
|
|
447
|
+
} else {
|
|
448
|
+
dismiss();
|
|
449
|
+
}
|
|
450
|
+
}, [dismiss, showConfirmDialog]);
|
|
451
|
+
const handleManualUpload = useCallback(() => {
|
|
452
|
+
performSave();
|
|
453
|
+
}, [performSave]);
|
|
454
|
+
const handleExportJson = useCallback(() => {
|
|
455
|
+
const editor = editorRef.current;
|
|
456
|
+
if (!editor) return;
|
|
457
|
+
const doc = editor.store.getSnapshot();
|
|
458
|
+
const blob = new Blob([JSON.stringify(doc, null, 2)], {
|
|
459
|
+
type: "application/json"
|
|
460
|
+
});
|
|
461
|
+
const url = URL.createObjectURL(blob);
|
|
462
|
+
const a = document.createElement("a");
|
|
463
|
+
a.href = url;
|
|
464
|
+
a.download = "tldraw-snapshot.json";
|
|
465
|
+
a.click();
|
|
466
|
+
URL.revokeObjectURL(url);
|
|
467
|
+
}, []);
|
|
468
|
+
const handleCopyJson = useCallback(() => {
|
|
469
|
+
const editor = editorRef.current;
|
|
470
|
+
if (!editor) return;
|
|
471
|
+
const doc = editor.store.getSnapshot();
|
|
472
|
+
void navigator.clipboard.writeText(JSON.stringify(doc));
|
|
473
|
+
}, []);
|
|
474
|
+
const handleCopyRef = useCallback(() => {
|
|
475
|
+
if (savedRef) void navigator.clipboard.writeText(savedRef);
|
|
476
|
+
}, [savedRef]);
|
|
477
|
+
const handleStorageModeChange = useCallback(
|
|
478
|
+
(newMode) => {
|
|
479
|
+
setStorageMode(newMode);
|
|
480
|
+
storageModeRef.current = newMode;
|
|
481
|
+
if (newMode === "inline") setSavedRef("");
|
|
482
|
+
performSave();
|
|
483
|
+
},
|
|
484
|
+
[performSave]
|
|
485
|
+
);
|
|
486
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
487
|
+
/* @__PURE__ */ jsxs("div", { className: tldrawDialogHeader, children: [
|
|
488
|
+
/* @__PURE__ */ jsx(
|
|
489
|
+
"span",
|
|
490
|
+
{
|
|
491
|
+
className: tldrawStatusDot,
|
|
492
|
+
style: { backgroundColor: isDirty ? "#f59e0b" : "#22c55e" }
|
|
493
|
+
}
|
|
494
|
+
),
|
|
495
|
+
/* @__PURE__ */ jsx("span", { className: tldrawDialogTitle, children: "Canvas Editor" }),
|
|
496
|
+
/* @__PURE__ */ jsx("span", { className: tldrawDialogMeta, children: "tldraw" }),
|
|
497
|
+
/* @__PURE__ */ jsxs("div", { className: tldrawHeaderActions, children: [
|
|
498
|
+
saveSnapshot && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
499
|
+
/* @__PURE__ */ jsx(
|
|
500
|
+
SegmentedControl,
|
|
501
|
+
{
|
|
502
|
+
items: [
|
|
503
|
+
{ value: "inline", label: "Inline" },
|
|
504
|
+
{ value: "remote", label: "Remote" }
|
|
505
|
+
],
|
|
506
|
+
value: storageMode,
|
|
507
|
+
onChange: handleStorageModeChange
|
|
508
|
+
}
|
|
509
|
+
),
|
|
510
|
+
/* @__PURE__ */ jsx("div", { className: tldrawActionBarSep }),
|
|
511
|
+
/* @__PURE__ */ jsxs(
|
|
512
|
+
"button",
|
|
513
|
+
{
|
|
514
|
+
type: "button",
|
|
515
|
+
className: tldrawActionBarBtn,
|
|
516
|
+
onClick: handleManualUpload,
|
|
517
|
+
disabled: isSaving,
|
|
518
|
+
children: [
|
|
519
|
+
/* @__PURE__ */ jsx(Upload, { size: 14 }),
|
|
520
|
+
/* @__PURE__ */ jsx("span", { children: isSaving ? "Saving..." : "Save" })
|
|
521
|
+
]
|
|
522
|
+
}
|
|
523
|
+
),
|
|
524
|
+
/* @__PURE__ */ jsx("div", { className: tldrawActionBarSep })
|
|
525
|
+
] }),
|
|
526
|
+
/* @__PURE__ */ jsxs(
|
|
527
|
+
"button",
|
|
528
|
+
{
|
|
529
|
+
type: "button",
|
|
530
|
+
className: tldrawActionBarBtn,
|
|
531
|
+
onClick: handleExportJson,
|
|
532
|
+
children: [
|
|
533
|
+
/* @__PURE__ */ jsx(Download, { size: 14 }),
|
|
534
|
+
"JSON"
|
|
535
|
+
]
|
|
536
|
+
}
|
|
537
|
+
),
|
|
538
|
+
/* @__PURE__ */ jsxs(
|
|
539
|
+
"button",
|
|
540
|
+
{
|
|
541
|
+
type: "button",
|
|
542
|
+
className: tldrawActionBarBtn,
|
|
543
|
+
onClick: handleCopyJson,
|
|
544
|
+
children: [
|
|
545
|
+
/* @__PURE__ */ jsx(Clipboard, { size: 14 }),
|
|
546
|
+
"Copy"
|
|
547
|
+
]
|
|
548
|
+
}
|
|
549
|
+
),
|
|
550
|
+
storageMode === "remote" && savedRef && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
551
|
+
/* @__PURE__ */ jsx("div", { className: tldrawActionBarSep }),
|
|
552
|
+
/* @__PURE__ */ jsx(
|
|
553
|
+
"button",
|
|
554
|
+
{
|
|
555
|
+
type: "button",
|
|
556
|
+
className: tldrawActionBarUrl,
|
|
557
|
+
onClick: handleCopyRef,
|
|
558
|
+
title: savedRef,
|
|
559
|
+
children: savedRef
|
|
560
|
+
}
|
|
561
|
+
)
|
|
562
|
+
] })
|
|
563
|
+
] }),
|
|
564
|
+
/* @__PURE__ */ jsx(
|
|
565
|
+
"button",
|
|
566
|
+
{
|
|
567
|
+
type: "button",
|
|
568
|
+
className: tldrawHeaderClose,
|
|
569
|
+
onClick: attemptClose,
|
|
570
|
+
children: /* @__PURE__ */ jsx(X, { size: 18 })
|
|
571
|
+
}
|
|
572
|
+
)
|
|
573
|
+
] }),
|
|
574
|
+
/* @__PURE__ */ jsx("div", { className: tldrawDialogCanvas, children: /* @__PURE__ */ jsx(TldrawComponent, { snapshot: initialData, onMount: handleMount }) })
|
|
575
|
+
] });
|
|
576
|
+
};
|
|
577
|
+
const TldrawEditRenderer = ({
|
|
578
|
+
snapshot,
|
|
579
|
+
onSnapshotChange
|
|
580
|
+
}) => {
|
|
581
|
+
const theme = useColorScheme();
|
|
582
|
+
const { saveSnapshot } = useTldrawConfig();
|
|
583
|
+
const {
|
|
584
|
+
snapshot: initialData,
|
|
585
|
+
loading: dataLoading,
|
|
586
|
+
error: dataError
|
|
587
|
+
} = useTldrawData(snapshot);
|
|
588
|
+
const [TldrawComponent, setTldrawComponent] = useState(null);
|
|
589
|
+
const [libLoading, setLibLoading] = useState(true);
|
|
590
|
+
const previewEditorRef = useRef(null);
|
|
591
|
+
const initialDataRef = useRef(void 0);
|
|
592
|
+
const onSnapshotChangeRef = useRef(onSnapshotChange);
|
|
593
|
+
onSnapshotChangeRef.current = onSnapshotChange;
|
|
594
|
+
const saveSnapshotRef = useRef(saveSnapshot);
|
|
595
|
+
saveSnapshotRef.current = saveSnapshot;
|
|
596
|
+
const snapshotRef = useRef(snapshot);
|
|
597
|
+
snapshotRef.current = snapshot;
|
|
598
|
+
const { className: portalClassName } = usePortalTheme();
|
|
599
|
+
useEffect(() => {
|
|
600
|
+
if (initialData && !initialDataRef.current) {
|
|
601
|
+
initialDataRef.current = initialData;
|
|
602
|
+
}
|
|
603
|
+
}, [initialData]);
|
|
604
|
+
useEffect(() => {
|
|
605
|
+
import("tldraw").then((mod) => {
|
|
606
|
+
setTldrawComponent(() => mod.Tldraw);
|
|
607
|
+
setLibLoading(false);
|
|
608
|
+
}).catch(() => setLibLoading(false));
|
|
609
|
+
}, []);
|
|
610
|
+
useEffect(() => {
|
|
611
|
+
if (previewEditorRef.current?.user?.updateUserPreferences) {
|
|
612
|
+
previewEditorRef.current.user.updateUserPreferences({
|
|
613
|
+
colorScheme: theme
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
}, [theme]);
|
|
617
|
+
const handlePreviewMount = useCallback(
|
|
618
|
+
(editor) => {
|
|
619
|
+
previewEditorRef.current = editor;
|
|
620
|
+
editor.updateInstanceState({ isReadonly: true });
|
|
621
|
+
editor.setCurrentTool("hand");
|
|
622
|
+
editor.user.updateUserPreferences({ colorScheme: theme });
|
|
623
|
+
setTimeout(() => editor.zoomToFit({ animate: false }), 100);
|
|
624
|
+
},
|
|
625
|
+
[theme]
|
|
626
|
+
);
|
|
627
|
+
const handleOpenEditor = useCallback(() => {
|
|
628
|
+
if (!TldrawComponent) return;
|
|
629
|
+
presentDialog({
|
|
630
|
+
content: ({ dismiss }) => /* @__PURE__ */ jsx(
|
|
631
|
+
TldrawEditorDialogContent,
|
|
632
|
+
{
|
|
633
|
+
dismiss,
|
|
634
|
+
TldrawComponent,
|
|
635
|
+
initialData: initialDataRef.current,
|
|
636
|
+
initialSnapshot: snapshotRef.current,
|
|
637
|
+
theme,
|
|
638
|
+
onSave: (ref) => onSnapshotChangeRef.current(ref),
|
|
639
|
+
saveSnapshot: saveSnapshotRef.current,
|
|
640
|
+
onClose: (doc) => {
|
|
641
|
+
if (doc) initialDataRef.current = doc;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
),
|
|
645
|
+
className: tldrawFullscreenPopup,
|
|
646
|
+
portalClassName,
|
|
647
|
+
showCloseButton: false,
|
|
648
|
+
clickOutsideToDismiss: false
|
|
649
|
+
});
|
|
650
|
+
}, [TldrawComponent, theme, portalClassName]);
|
|
651
|
+
const loading = dataLoading || libLoading;
|
|
652
|
+
if (loading || !TldrawComponent) {
|
|
653
|
+
return /* @__PURE__ */ jsx("div", { className: tldrawEditorContainer, children: /* @__PURE__ */ jsx("div", { className: tldrawLoading, children: "Loading tldraw editor..." }) });
|
|
654
|
+
}
|
|
655
|
+
if (dataError) {
|
|
656
|
+
return /* @__PURE__ */ jsx("div", { className: tldrawEditorContainer, children: /* @__PURE__ */ jsx("div", { className: tldrawError, children: dataError }) });
|
|
657
|
+
}
|
|
658
|
+
return /* @__PURE__ */ jsxs("div", { className: tldrawEditorContainer, children: [
|
|
659
|
+
/* @__PURE__ */ jsx(
|
|
660
|
+
TldrawComponent,
|
|
661
|
+
{
|
|
662
|
+
snapshot: initialDataRef.current,
|
|
663
|
+
hideUi: true,
|
|
664
|
+
onMount: handlePreviewMount
|
|
665
|
+
}
|
|
666
|
+
),
|
|
667
|
+
/* @__PURE__ */ jsx(
|
|
668
|
+
"button",
|
|
669
|
+
{
|
|
670
|
+
type: "button",
|
|
671
|
+
className: tldrawEditOverlay,
|
|
672
|
+
onClick: handleOpenEditor,
|
|
673
|
+
children: /* @__PURE__ */ jsxs("span", { className: tldrawEditLabel, children: [
|
|
674
|
+
/* @__PURE__ */ jsx(Pencil, { size: 16 }),
|
|
675
|
+
"Edit Whiteboard"
|
|
676
|
+
] })
|
|
677
|
+
}
|
|
678
|
+
)
|
|
679
|
+
] });
|
|
680
|
+
};
|
|
681
|
+
const TldrawEditRenderer$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
682
|
+
__proto__: null,
|
|
683
|
+
TldrawEditRenderer
|
|
684
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
685
|
+
const LazyStaticRenderer = lazy(
|
|
686
|
+
() => Promise.resolve().then(() => TldrawStaticRenderer$1).then((m) => ({
|
|
687
|
+
default: m.TldrawStaticRenderer
|
|
688
|
+
}))
|
|
689
|
+
);
|
|
690
|
+
const LazyEditRenderer = lazy(
|
|
691
|
+
() => Promise.resolve().then(() => TldrawEditRenderer$1).then((m) => ({
|
|
692
|
+
default: m.TldrawEditRenderer
|
|
693
|
+
}))
|
|
694
|
+
);
|
|
695
|
+
const _TldrawNode = class _TldrawNode extends DecoratorNode {
|
|
696
|
+
constructor(snapshot, key) {
|
|
697
|
+
super(key);
|
|
698
|
+
__publicField(this, "__snapshot");
|
|
699
|
+
this.__snapshot = snapshot;
|
|
700
|
+
}
|
|
701
|
+
static getType() {
|
|
702
|
+
return "tldraw";
|
|
703
|
+
}
|
|
704
|
+
static clone(node) {
|
|
705
|
+
return new _TldrawNode(node.__snapshot, node.__key);
|
|
706
|
+
}
|
|
707
|
+
createDOM(_config) {
|
|
708
|
+
const div = document.createElement("div");
|
|
709
|
+
div.className = "rich-tldraw-wrapper";
|
|
710
|
+
return div;
|
|
711
|
+
}
|
|
712
|
+
updateDOM() {
|
|
713
|
+
return false;
|
|
714
|
+
}
|
|
715
|
+
isInline() {
|
|
716
|
+
return false;
|
|
717
|
+
}
|
|
718
|
+
static importJSON(serializedNode) {
|
|
719
|
+
return $createTldrawNode(serializedNode.snapshot);
|
|
720
|
+
}
|
|
721
|
+
exportJSON() {
|
|
722
|
+
return {
|
|
723
|
+
...super.exportJSON(),
|
|
724
|
+
type: "tldraw",
|
|
725
|
+
snapshot: this.__snapshot,
|
|
726
|
+
version: 1
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
getSnapshot() {
|
|
730
|
+
return this.__snapshot;
|
|
731
|
+
}
|
|
732
|
+
setSnapshot(snapshot) {
|
|
733
|
+
const writable = this.getWritable();
|
|
734
|
+
writable.__snapshot = snapshot;
|
|
735
|
+
}
|
|
736
|
+
decorate(editor, _config) {
|
|
737
|
+
const nodeKey = this.__key;
|
|
738
|
+
const isEditable = editor.isEditable();
|
|
739
|
+
const fallback = createElement("div", {
|
|
740
|
+
className: "rich-tldraw-loading",
|
|
741
|
+
style: {
|
|
742
|
+
position: "relative",
|
|
743
|
+
width: "100%",
|
|
744
|
+
aspectRatio: "16 / 10",
|
|
745
|
+
margin: "1rem 0"
|
|
746
|
+
}
|
|
747
|
+
});
|
|
748
|
+
if (isEditable) {
|
|
749
|
+
return createElement(
|
|
750
|
+
Suspense,
|
|
751
|
+
{ fallback },
|
|
752
|
+
createElement(LazyEditRenderer, {
|
|
753
|
+
snapshot: this.__snapshot,
|
|
754
|
+
onSnapshotChange: (newSnapshot) => {
|
|
755
|
+
editor.update(() => {
|
|
756
|
+
const node = $getNodeByKey(nodeKey);
|
|
757
|
+
if (node) {
|
|
758
|
+
node.setSnapshot(newSnapshot);
|
|
759
|
+
}
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
})
|
|
763
|
+
);
|
|
764
|
+
}
|
|
765
|
+
return createElement(
|
|
766
|
+
Suspense,
|
|
767
|
+
{ fallback },
|
|
768
|
+
createElement(LazyStaticRenderer, { snapshot: this.__snapshot })
|
|
769
|
+
);
|
|
770
|
+
}
|
|
771
|
+
};
|
|
772
|
+
__publicField(_TldrawNode, "slashMenuItems", [
|
|
773
|
+
{
|
|
774
|
+
title: "Whiteboard",
|
|
775
|
+
icon: createElement(PenTool, { size: 20 }),
|
|
776
|
+
description: "Tldraw whiteboard canvas",
|
|
777
|
+
keywords: ["tldraw", "whiteboard", "draw", "canvas"],
|
|
778
|
+
section: "MEDIA",
|
|
779
|
+
onSelect: (editor) => {
|
|
780
|
+
editor.update(() => {
|
|
781
|
+
$insertNodes([$createTldrawNode("{}")]);
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
]);
|
|
786
|
+
let TldrawNode = _TldrawNode;
|
|
787
|
+
function $createTldrawNode(snapshot) {
|
|
788
|
+
return new TldrawNode(snapshot);
|
|
789
|
+
}
|
|
790
|
+
function $isTldrawNode(node) {
|
|
791
|
+
return node instanceof TldrawNode;
|
|
792
|
+
}
|
|
793
|
+
const INSERT_TLDRAW_COMMAND = createCommand("INSERT_TLDRAW");
|
|
794
|
+
function TldrawPlugin() {
|
|
795
|
+
const [editor] = useLexicalComposerContext();
|
|
796
|
+
useEffect(() => {
|
|
797
|
+
return editor.registerCommand(
|
|
798
|
+
INSERT_TLDRAW_COMMAND,
|
|
799
|
+
(snapshot) => {
|
|
800
|
+
const node = $createTldrawNode(snapshot);
|
|
801
|
+
$insertNodes([node]);
|
|
802
|
+
return true;
|
|
803
|
+
},
|
|
804
|
+
COMMAND_PRIORITY_EDITOR
|
|
805
|
+
);
|
|
806
|
+
}, [editor]);
|
|
807
|
+
return null;
|
|
808
|
+
}
|
|
809
|
+
const TldrawStaticRenderer = ({
|
|
810
|
+
snapshot
|
|
811
|
+
}) => {
|
|
812
|
+
const theme = useColorScheme();
|
|
813
|
+
const containerRef = useRef(null);
|
|
814
|
+
const [hasEnteredView, setHasEnteredView] = useState(false);
|
|
815
|
+
useEffect(() => {
|
|
816
|
+
if (hasEnteredView) return;
|
|
817
|
+
const target = containerRef.current;
|
|
818
|
+
if (!target) return;
|
|
819
|
+
const observer = new IntersectionObserver(
|
|
820
|
+
(entries) => {
|
|
821
|
+
if (entries.some((e) => e.isIntersecting)) {
|
|
822
|
+
setHasEnteredView(true);
|
|
823
|
+
observer.disconnect();
|
|
824
|
+
}
|
|
825
|
+
},
|
|
826
|
+
{ rootMargin: "200px 0px", threshold: 0.1 }
|
|
827
|
+
);
|
|
828
|
+
observer.observe(target);
|
|
829
|
+
return () => observer.disconnect();
|
|
830
|
+
}, [hasEnteredView]);
|
|
831
|
+
if (!hasEnteredView) {
|
|
832
|
+
return /* @__PURE__ */ jsx("div", { ref: containerRef, className: tldrawStaticContainer, children: /* @__PURE__ */ jsx("div", { className: tldrawLoading, children: "Loading tldraw..." }) });
|
|
833
|
+
}
|
|
834
|
+
return /* @__PURE__ */ jsx("div", { ref: containerRef, children: /* @__PURE__ */ jsx(TldrawStaticCanvas, { snapshot, theme }) });
|
|
835
|
+
};
|
|
836
|
+
const TldrawExpandContent = ({ dismiss, TldrawComponent, data, theme }) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
837
|
+
/* @__PURE__ */ jsxs("div", { className: tldrawDialogHeader, children: [
|
|
838
|
+
/* @__PURE__ */ jsx("span", { className: tldrawDialogTitle, children: "Whiteboard" }),
|
|
839
|
+
/* @__PURE__ */ jsx("span", { className: tldrawDialogMeta, children: "tldraw" }),
|
|
840
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: tldrawHeaderClose, onClick: dismiss, children: /* @__PURE__ */ jsx(X, { size: 18 }) })
|
|
841
|
+
] }),
|
|
842
|
+
/* @__PURE__ */ jsx("div", { className: tldrawDialogCanvas, children: /* @__PURE__ */ jsx(
|
|
843
|
+
TldrawComponent,
|
|
844
|
+
{
|
|
845
|
+
snapshot: data,
|
|
846
|
+
hideUi: true,
|
|
847
|
+
onMount: (editor) => {
|
|
848
|
+
editor.updateInstanceState({ isReadonly: true });
|
|
849
|
+
editor.setCurrentTool("hand");
|
|
850
|
+
editor.user.updateUserPreferences({ colorScheme: theme });
|
|
851
|
+
setTimeout(() => editor.zoomToFit({ animate: false }), 100);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
) })
|
|
855
|
+
] });
|
|
856
|
+
const TldrawStaticCanvas = ({ snapshot, theme }) => {
|
|
857
|
+
const {
|
|
858
|
+
snapshot: data,
|
|
859
|
+
loading: dataLoading,
|
|
860
|
+
error: dataError
|
|
861
|
+
} = useTldrawData(snapshot);
|
|
862
|
+
const [TldrawComponent, setTldrawComponent] = useState(null);
|
|
863
|
+
const [libLoading, setLibLoading] = useState(true);
|
|
864
|
+
const editorRef = useRef(null);
|
|
865
|
+
const { className: portalClassName } = usePortalTheme();
|
|
866
|
+
useEffect(() => {
|
|
867
|
+
import("tldraw").then((mod) => {
|
|
868
|
+
setTldrawComponent(() => mod.Tldraw);
|
|
869
|
+
setLibLoading(false);
|
|
870
|
+
}).catch(() => setLibLoading(false));
|
|
871
|
+
}, []);
|
|
872
|
+
useEffect(() => {
|
|
873
|
+
if (editorRef.current?.user?.updateUserPreferences) {
|
|
874
|
+
editorRef.current.user.updateUserPreferences({ colorScheme: theme });
|
|
875
|
+
}
|
|
876
|
+
}, [theme]);
|
|
877
|
+
const handleMount = useCallback(
|
|
878
|
+
(editor) => {
|
|
879
|
+
editorRef.current = editor;
|
|
880
|
+
editor.updateInstanceState({ isReadonly: true });
|
|
881
|
+
editor.setCurrentTool("hand");
|
|
882
|
+
editor.user.updateUserPreferences({ colorScheme: theme });
|
|
883
|
+
setTimeout(() => {
|
|
884
|
+
editor.zoomToFit({ animate: false });
|
|
885
|
+
}, 100);
|
|
886
|
+
},
|
|
887
|
+
[theme]
|
|
888
|
+
);
|
|
889
|
+
const handleExpand = useCallback(() => {
|
|
890
|
+
if (!TldrawComponent || !data) return;
|
|
891
|
+
presentDialog({
|
|
892
|
+
content: ({ dismiss }) => /* @__PURE__ */ jsx(
|
|
893
|
+
TldrawExpandContent,
|
|
894
|
+
{
|
|
895
|
+
dismiss,
|
|
896
|
+
TldrawComponent,
|
|
897
|
+
data,
|
|
898
|
+
theme
|
|
899
|
+
}
|
|
900
|
+
),
|
|
901
|
+
className: tldrawFullscreenPopup,
|
|
902
|
+
portalClassName,
|
|
903
|
+
showCloseButton: false,
|
|
904
|
+
clickOutsideToDismiss: true
|
|
905
|
+
});
|
|
906
|
+
}, [TldrawComponent, data, theme, portalClassName]);
|
|
907
|
+
const loading = dataLoading || libLoading;
|
|
908
|
+
if (loading) {
|
|
909
|
+
return /* @__PURE__ */ jsx("div", { className: tldrawStaticContainer, children: /* @__PURE__ */ jsx("div", { className: tldrawLoading, children: "Loading tldraw..." }) });
|
|
910
|
+
}
|
|
911
|
+
if (dataError || !data) {
|
|
912
|
+
return /* @__PURE__ */ jsx("div", { className: tldrawStaticContainer, children: /* @__PURE__ */ jsx("div", { className: tldrawError, children: dataError || "No data" }) });
|
|
913
|
+
}
|
|
914
|
+
if (!TldrawComponent) {
|
|
915
|
+
return /* @__PURE__ */ jsx("div", { className: tldrawStaticContainer, children: /* @__PURE__ */ jsx("div", { className: tldrawError, children: "Failed to load tldraw" }) });
|
|
916
|
+
}
|
|
917
|
+
return /* @__PURE__ */ jsxs(
|
|
918
|
+
"div",
|
|
919
|
+
{
|
|
920
|
+
className: tldrawStaticContainer,
|
|
921
|
+
"data-theme": theme,
|
|
922
|
+
"data-color-scheme": theme,
|
|
923
|
+
children: [
|
|
924
|
+
/* @__PURE__ */ jsx(
|
|
925
|
+
TldrawErrorBoundary,
|
|
926
|
+
{
|
|
927
|
+
fallback: /* @__PURE__ */ jsx("div", { className: tldrawError, children: "Failed to render tldraw" }),
|
|
928
|
+
children: /* @__PURE__ */ jsx(TldrawComponent, { snapshot: data, hideUi: true, onMount: handleMount })
|
|
929
|
+
}
|
|
930
|
+
),
|
|
931
|
+
/* @__PURE__ */ jsxs("div", { className: tldrawActionGroup, children: [
|
|
932
|
+
/* @__PURE__ */ jsx(
|
|
933
|
+
"button",
|
|
934
|
+
{
|
|
935
|
+
type: "button",
|
|
936
|
+
className: tldrawActionButton,
|
|
937
|
+
title: "Zoom In",
|
|
938
|
+
onClick: () => editorRef.current?.zoomIn(),
|
|
939
|
+
children: /* @__PURE__ */ jsx(ZoomIn, { size: 20 })
|
|
940
|
+
}
|
|
941
|
+
),
|
|
942
|
+
/* @__PURE__ */ jsx(
|
|
943
|
+
"button",
|
|
944
|
+
{
|
|
945
|
+
type: "button",
|
|
946
|
+
className: tldrawActionButton,
|
|
947
|
+
title: "Zoom Out",
|
|
948
|
+
onClick: () => editorRef.current?.zoomOut(),
|
|
949
|
+
children: /* @__PURE__ */ jsx(ZoomOut, { size: 20 })
|
|
950
|
+
}
|
|
951
|
+
),
|
|
952
|
+
/* @__PURE__ */ jsx(
|
|
953
|
+
"button",
|
|
954
|
+
{
|
|
955
|
+
type: "button",
|
|
956
|
+
className: tldrawActionButton,
|
|
957
|
+
title: "Fit to Content",
|
|
958
|
+
onClick: () => editorRef.current?.zoomToFit(),
|
|
959
|
+
children: /* @__PURE__ */ jsx(ScanSearch, { size: 20 })
|
|
960
|
+
}
|
|
961
|
+
),
|
|
962
|
+
/* @__PURE__ */ jsx(
|
|
963
|
+
"button",
|
|
964
|
+
{
|
|
965
|
+
type: "button",
|
|
966
|
+
className: tldrawActionButton,
|
|
967
|
+
title: "Expand",
|
|
968
|
+
onClick: handleExpand,
|
|
969
|
+
children: /* @__PURE__ */ jsx(Maximize2, { size: 20 })
|
|
970
|
+
}
|
|
971
|
+
)
|
|
972
|
+
] })
|
|
973
|
+
]
|
|
974
|
+
}
|
|
975
|
+
);
|
|
976
|
+
};
|
|
977
|
+
class TldrawErrorBoundary extends Component {
|
|
978
|
+
constructor() {
|
|
979
|
+
super(...arguments);
|
|
980
|
+
__publicField(this, "state", { hasError: false });
|
|
981
|
+
}
|
|
982
|
+
static getDerivedStateFromError() {
|
|
983
|
+
return { hasError: true };
|
|
984
|
+
}
|
|
985
|
+
render() {
|
|
986
|
+
return this.state.hasError ? this.props.fallback : this.props.children;
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
const TldrawStaticRenderer$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
990
|
+
__proto__: null,
|
|
991
|
+
TldrawStaticRenderer
|
|
992
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
993
|
+
export {
|
|
994
|
+
$createTldrawNode,
|
|
995
|
+
$isTldrawNode,
|
|
996
|
+
INSERT_TLDRAW_COMMAND,
|
|
997
|
+
TldrawConfigProvider,
|
|
998
|
+
TldrawEditRenderer,
|
|
999
|
+
TldrawNode,
|
|
1000
|
+
TldrawPlugin,
|
|
1001
|
+
TldrawStaticRenderer as TldrawRenderer,
|
|
1002
|
+
TldrawStaticRenderer,
|
|
1003
|
+
useTldrawConfig
|
|
1004
|
+
};
|