rita-workspace 0.1.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/README.md +104 -0
- package/dist/index.css +168 -0
- package/dist/index.d.mts +145 -0
- package/dist/index.d.ts +145 -0
- package/dist/index.js +657 -0
- package/dist/index.mjs +610 -0
- package/package.json +40 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,657 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
DrawingList: () => DrawingList,
|
|
24
|
+
DrawingListItem: () => DrawingListItem,
|
|
25
|
+
Sidebar: () => Sidebar,
|
|
26
|
+
WorkspacePlugin: () => WorkspacePlugin,
|
|
27
|
+
WorkspaceProvider: () => WorkspaceProvider,
|
|
28
|
+
addDrawingToWorkspace: () => addDrawingToWorkspace,
|
|
29
|
+
closeDB: () => closeDB,
|
|
30
|
+
createDrawing: () => createDrawing,
|
|
31
|
+
deleteDrawing: () => deleteDrawing,
|
|
32
|
+
duplicateDrawing: () => duplicateDrawing,
|
|
33
|
+
getAllDrawings: () => getAllDrawings,
|
|
34
|
+
getDB: () => getDB,
|
|
35
|
+
getDrawing: () => getDrawing,
|
|
36
|
+
getOrCreateDefaultWorkspace: () => getOrCreateDefaultWorkspace,
|
|
37
|
+
getWorkspace: () => getWorkspace,
|
|
38
|
+
removeDrawingFromWorkspace: () => removeDrawingFromWorkspace,
|
|
39
|
+
setActiveDrawing: () => setActiveDrawing,
|
|
40
|
+
updateDrawing: () => updateDrawing,
|
|
41
|
+
updateWorkspace: () => updateWorkspace,
|
|
42
|
+
useExcalidrawBridge: () => useExcalidrawBridge,
|
|
43
|
+
useWorkspace: () => useWorkspace
|
|
44
|
+
});
|
|
45
|
+
module.exports = __toCommonJS(index_exports);
|
|
46
|
+
|
|
47
|
+
// src/storage/db.ts
|
|
48
|
+
var import_idb = require("idb");
|
|
49
|
+
var DB_NAME = "rita-workspace";
|
|
50
|
+
var DB_VERSION = 1;
|
|
51
|
+
var dbInstance = null;
|
|
52
|
+
async function getDB() {
|
|
53
|
+
if (dbInstance) return dbInstance;
|
|
54
|
+
dbInstance = await (0, import_idb.openDB)(DB_NAME, DB_VERSION, {
|
|
55
|
+
upgrade(db) {
|
|
56
|
+
if (!db.objectStoreNames.contains("workspaces")) {
|
|
57
|
+
const workspaceStore = db.createObjectStore("workspaces", { keyPath: "id" });
|
|
58
|
+
workspaceStore.createIndex("by-updated", "updatedAt");
|
|
59
|
+
}
|
|
60
|
+
if (!db.objectStoreNames.contains("drawings")) {
|
|
61
|
+
const drawingStore = db.createObjectStore("drawings", { keyPath: "id" });
|
|
62
|
+
drawingStore.createIndex("by-updated", "updatedAt");
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
return dbInstance;
|
|
67
|
+
}
|
|
68
|
+
async function closeDB() {
|
|
69
|
+
if (dbInstance) {
|
|
70
|
+
dbInstance.close();
|
|
71
|
+
dbInstance = null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// src/storage/drawingStore.ts
|
|
76
|
+
var import_nanoid = require("nanoid");
|
|
77
|
+
async function createDrawing(name = "Untitled", elements = [], appState = {}) {
|
|
78
|
+
const db = await getDB();
|
|
79
|
+
const now = Date.now();
|
|
80
|
+
const drawing = {
|
|
81
|
+
id: (0, import_nanoid.nanoid)(),
|
|
82
|
+
name,
|
|
83
|
+
elements,
|
|
84
|
+
appState,
|
|
85
|
+
files: {},
|
|
86
|
+
createdAt: now,
|
|
87
|
+
updatedAt: now
|
|
88
|
+
};
|
|
89
|
+
await db.put("drawings", drawing);
|
|
90
|
+
return drawing;
|
|
91
|
+
}
|
|
92
|
+
async function getDrawing(id) {
|
|
93
|
+
const db = await getDB();
|
|
94
|
+
return db.get("drawings", id);
|
|
95
|
+
}
|
|
96
|
+
async function getAllDrawings() {
|
|
97
|
+
const db = await getDB();
|
|
98
|
+
return db.getAllFromIndex("drawings", "by-updated");
|
|
99
|
+
}
|
|
100
|
+
async function updateDrawing(id, updates) {
|
|
101
|
+
const db = await getDB();
|
|
102
|
+
const existing = await db.get("drawings", id);
|
|
103
|
+
if (!existing) return void 0;
|
|
104
|
+
const updated = {
|
|
105
|
+
...existing,
|
|
106
|
+
...updates,
|
|
107
|
+
updatedAt: Date.now()
|
|
108
|
+
};
|
|
109
|
+
await db.put("drawings", updated);
|
|
110
|
+
return updated;
|
|
111
|
+
}
|
|
112
|
+
async function deleteDrawing(id) {
|
|
113
|
+
const db = await getDB();
|
|
114
|
+
const existing = await db.get("drawings", id);
|
|
115
|
+
if (!existing) return false;
|
|
116
|
+
await db.delete("drawings", id);
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
async function duplicateDrawing(id, newName) {
|
|
120
|
+
const existing = await getDrawing(id);
|
|
121
|
+
if (!existing) return void 0;
|
|
122
|
+
return createDrawing(
|
|
123
|
+
newName || `${existing.name} (copy)`,
|
|
124
|
+
existing.elements,
|
|
125
|
+
existing.appState
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// src/storage/workspaceStore.ts
|
|
130
|
+
var DEFAULT_WORKSPACE_ID = "default";
|
|
131
|
+
async function getOrCreateDefaultWorkspace() {
|
|
132
|
+
const db = await getDB();
|
|
133
|
+
let workspace = await db.get("workspaces", DEFAULT_WORKSPACE_ID);
|
|
134
|
+
if (!workspace) {
|
|
135
|
+
const firstDrawing = await createDrawing("Ritning 1");
|
|
136
|
+
const now = Date.now();
|
|
137
|
+
workspace = {
|
|
138
|
+
id: DEFAULT_WORKSPACE_ID,
|
|
139
|
+
name: "My Workspace",
|
|
140
|
+
drawingIds: [firstDrawing.id],
|
|
141
|
+
activeDrawingId: firstDrawing.id,
|
|
142
|
+
createdAt: now,
|
|
143
|
+
updatedAt: now
|
|
144
|
+
};
|
|
145
|
+
await db.put("workspaces", workspace);
|
|
146
|
+
}
|
|
147
|
+
return workspace;
|
|
148
|
+
}
|
|
149
|
+
async function getWorkspace(id) {
|
|
150
|
+
const db = await getDB();
|
|
151
|
+
return db.get("workspaces", id);
|
|
152
|
+
}
|
|
153
|
+
async function updateWorkspace(id, updates) {
|
|
154
|
+
const db = await getDB();
|
|
155
|
+
const existing = await db.get("workspaces", id);
|
|
156
|
+
if (!existing) return void 0;
|
|
157
|
+
const updated = {
|
|
158
|
+
...existing,
|
|
159
|
+
...updates,
|
|
160
|
+
updatedAt: Date.now()
|
|
161
|
+
};
|
|
162
|
+
await db.put("workspaces", updated);
|
|
163
|
+
return updated;
|
|
164
|
+
}
|
|
165
|
+
async function addDrawingToWorkspace(workspaceId, drawingId) {
|
|
166
|
+
const workspace = await getWorkspace(workspaceId);
|
|
167
|
+
if (!workspace) return void 0;
|
|
168
|
+
if (!workspace.drawingIds.includes(drawingId)) {
|
|
169
|
+
workspace.drawingIds.push(drawingId);
|
|
170
|
+
return updateWorkspace(workspaceId, {
|
|
171
|
+
drawingIds: workspace.drawingIds
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
return workspace;
|
|
175
|
+
}
|
|
176
|
+
async function removeDrawingFromWorkspace(workspaceId, drawingId) {
|
|
177
|
+
const workspace = await getWorkspace(workspaceId);
|
|
178
|
+
if (!workspace) return void 0;
|
|
179
|
+
const newDrawingIds = workspace.drawingIds.filter((id) => id !== drawingId);
|
|
180
|
+
if (newDrawingIds.length === 0) {
|
|
181
|
+
return workspace;
|
|
182
|
+
}
|
|
183
|
+
const newActiveId = workspace.activeDrawingId === drawingId ? newDrawingIds[0] : workspace.activeDrawingId;
|
|
184
|
+
return updateWorkspace(workspaceId, {
|
|
185
|
+
drawingIds: newDrawingIds,
|
|
186
|
+
activeDrawingId: newActiveId
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
async function setActiveDrawing(workspaceId, drawingId) {
|
|
190
|
+
return updateWorkspace(workspaceId, { activeDrawingId: drawingId });
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// src/state/WorkspaceContext.tsx
|
|
194
|
+
var import_react = require("react");
|
|
195
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
196
|
+
var WorkspaceContext = (0, import_react.createContext)(null);
|
|
197
|
+
function useWorkspace() {
|
|
198
|
+
const context = (0, import_react.useContext)(WorkspaceContext);
|
|
199
|
+
if (!context) {
|
|
200
|
+
throw new Error("useWorkspace must be used within a WorkspaceProvider");
|
|
201
|
+
}
|
|
202
|
+
return context;
|
|
203
|
+
}
|
|
204
|
+
function WorkspaceProvider({ children }) {
|
|
205
|
+
const [workspace, setWorkspace] = (0, import_react.useState)(null);
|
|
206
|
+
const [drawings, setDrawings] = (0, import_react.useState)([]);
|
|
207
|
+
const [activeDrawing, setActiveDrawing2] = (0, import_react.useState)(null);
|
|
208
|
+
const [isLoading, setIsLoading] = (0, import_react.useState)(true);
|
|
209
|
+
const [error, setError] = (0, import_react.useState)(null);
|
|
210
|
+
(0, import_react.useEffect)(() => {
|
|
211
|
+
async function init() {
|
|
212
|
+
try {
|
|
213
|
+
setIsLoading(true);
|
|
214
|
+
const ws = await getOrCreateDefaultWorkspace();
|
|
215
|
+
setWorkspace(ws);
|
|
216
|
+
const allDrawings = await getAllDrawings();
|
|
217
|
+
const wsDrawings = allDrawings.filter((d) => ws.drawingIds.includes(d.id));
|
|
218
|
+
setDrawings(wsDrawings);
|
|
219
|
+
if (ws.activeDrawingId) {
|
|
220
|
+
const active = await getDrawing(ws.activeDrawingId);
|
|
221
|
+
setActiveDrawing2(active || null);
|
|
222
|
+
}
|
|
223
|
+
} catch (err) {
|
|
224
|
+
setError(err instanceof Error ? err.message : "Failed to load workspace");
|
|
225
|
+
} finally {
|
|
226
|
+
setIsLoading(false);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
init();
|
|
230
|
+
}, []);
|
|
231
|
+
const createNewDrawing = (0, import_react.useCallback)(async (name) => {
|
|
232
|
+
if (!workspace) return null;
|
|
233
|
+
try {
|
|
234
|
+
const drawing = await createDrawing(name || `Ritning ${drawings.length + 1}`);
|
|
235
|
+
await addDrawingToWorkspace(workspace.id, drawing.id);
|
|
236
|
+
await setActiveDrawing(workspace.id, drawing.id);
|
|
237
|
+
setDrawings((prev) => [...prev, drawing]);
|
|
238
|
+
setActiveDrawing2(drawing);
|
|
239
|
+
setWorkspace((prev) => prev ? {
|
|
240
|
+
...prev,
|
|
241
|
+
drawingIds: [...prev.drawingIds, drawing.id],
|
|
242
|
+
activeDrawingId: drawing.id
|
|
243
|
+
} : null);
|
|
244
|
+
return drawing;
|
|
245
|
+
} catch (err) {
|
|
246
|
+
setError(err instanceof Error ? err.message : "Failed to create drawing");
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
}, [workspace, drawings.length]);
|
|
250
|
+
const switchDrawing = (0, import_react.useCallback)(async (id) => {
|
|
251
|
+
if (!workspace) return;
|
|
252
|
+
try {
|
|
253
|
+
const drawing = await getDrawing(id);
|
|
254
|
+
if (drawing) {
|
|
255
|
+
await setActiveDrawing(workspace.id, id);
|
|
256
|
+
setActiveDrawing2(drawing);
|
|
257
|
+
setWorkspace((prev) => prev ? { ...prev, activeDrawingId: id } : null);
|
|
258
|
+
}
|
|
259
|
+
} catch (err) {
|
|
260
|
+
setError(err instanceof Error ? err.message : "Failed to switch drawing");
|
|
261
|
+
}
|
|
262
|
+
}, [workspace]);
|
|
263
|
+
const renameDrawing = (0, import_react.useCallback)(async (id, name) => {
|
|
264
|
+
try {
|
|
265
|
+
const updated = await updateDrawing(id, { name });
|
|
266
|
+
if (updated) {
|
|
267
|
+
setDrawings((prev) => prev.map((d) => d.id === id ? updated : d));
|
|
268
|
+
if (activeDrawing?.id === id) {
|
|
269
|
+
setActiveDrawing2(updated);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
} catch (err) {
|
|
273
|
+
setError(err instanceof Error ? err.message : "Failed to rename drawing");
|
|
274
|
+
}
|
|
275
|
+
}, [activeDrawing]);
|
|
276
|
+
const removeDrawing = (0, import_react.useCallback)(async (id) => {
|
|
277
|
+
if (!workspace || drawings.length <= 1) return;
|
|
278
|
+
try {
|
|
279
|
+
await deleteDrawing(id);
|
|
280
|
+
const updatedWorkspace = await removeDrawingFromWorkspace(workspace.id, id);
|
|
281
|
+
setDrawings((prev) => prev.filter((d) => d.id !== id));
|
|
282
|
+
if (updatedWorkspace) {
|
|
283
|
+
setWorkspace(updatedWorkspace);
|
|
284
|
+
if (activeDrawing?.id === id && updatedWorkspace.activeDrawingId) {
|
|
285
|
+
const newActive = await getDrawing(updatedWorkspace.activeDrawingId);
|
|
286
|
+
setActiveDrawing2(newActive || null);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
} catch (err) {
|
|
290
|
+
setError(err instanceof Error ? err.message : "Failed to delete drawing");
|
|
291
|
+
}
|
|
292
|
+
}, [workspace, drawings.length, activeDrawing]);
|
|
293
|
+
const duplicateCurrentDrawing = (0, import_react.useCallback)(async () => {
|
|
294
|
+
if (!activeDrawing || !workspace) return null;
|
|
295
|
+
try {
|
|
296
|
+
const duplicate = await duplicateDrawing(activeDrawing.id);
|
|
297
|
+
if (duplicate) {
|
|
298
|
+
await addDrawingToWorkspace(workspace.id, duplicate.id);
|
|
299
|
+
setDrawings((prev) => [...prev, duplicate]);
|
|
300
|
+
setWorkspace((prev) => prev ? {
|
|
301
|
+
...prev,
|
|
302
|
+
drawingIds: [...prev.drawingIds, duplicate.id]
|
|
303
|
+
} : null);
|
|
304
|
+
return duplicate;
|
|
305
|
+
}
|
|
306
|
+
return null;
|
|
307
|
+
} catch (err) {
|
|
308
|
+
setError(err instanceof Error ? err.message : "Failed to duplicate drawing");
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
}, [activeDrawing, workspace]);
|
|
312
|
+
const saveCurrentDrawing = (0, import_react.useCallback)(async (elements, appState) => {
|
|
313
|
+
if (!activeDrawing) return;
|
|
314
|
+
try {
|
|
315
|
+
const updated = await updateDrawing(activeDrawing.id, { elements, appState });
|
|
316
|
+
if (updated) {
|
|
317
|
+
setActiveDrawing2(updated);
|
|
318
|
+
setDrawings((prev) => prev.map((d) => d.id === updated.id ? updated : d));
|
|
319
|
+
}
|
|
320
|
+
} catch (err) {
|
|
321
|
+
setError(err instanceof Error ? err.message : "Failed to save drawing");
|
|
322
|
+
}
|
|
323
|
+
}, [activeDrawing]);
|
|
324
|
+
const value = {
|
|
325
|
+
workspace,
|
|
326
|
+
drawings,
|
|
327
|
+
activeDrawing,
|
|
328
|
+
isLoading,
|
|
329
|
+
error,
|
|
330
|
+
createNewDrawing,
|
|
331
|
+
switchDrawing,
|
|
332
|
+
renameDrawing,
|
|
333
|
+
removeDrawing,
|
|
334
|
+
duplicateCurrentDrawing,
|
|
335
|
+
saveCurrentDrawing
|
|
336
|
+
};
|
|
337
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WorkspaceContext.Provider, { value, children });
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// src/ui/Sidebar/Sidebar.tsx
|
|
341
|
+
var import_react3 = require("react");
|
|
342
|
+
|
|
343
|
+
// src/ui/DrawingList/DrawingListItem.tsx
|
|
344
|
+
var import_react2 = require("react");
|
|
345
|
+
|
|
346
|
+
// src/ui/DrawingList/DrawingList.module.css
|
|
347
|
+
var DrawingList_default = {};
|
|
348
|
+
|
|
349
|
+
// src/ui/DrawingList/DrawingListItem.tsx
|
|
350
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
351
|
+
function DrawingListItem({
|
|
352
|
+
drawing,
|
|
353
|
+
isActive,
|
|
354
|
+
onSelect,
|
|
355
|
+
onRename,
|
|
356
|
+
onDelete,
|
|
357
|
+
canDelete
|
|
358
|
+
}) {
|
|
359
|
+
const [isEditing, setIsEditing] = (0, import_react2.useState)(false);
|
|
360
|
+
const [editName, setEditName] = (0, import_react2.useState)(drawing.name);
|
|
361
|
+
const inputRef = (0, import_react2.useRef)(null);
|
|
362
|
+
(0, import_react2.useEffect)(() => {
|
|
363
|
+
if (isEditing && inputRef.current) {
|
|
364
|
+
inputRef.current.focus();
|
|
365
|
+
inputRef.current.select();
|
|
366
|
+
}
|
|
367
|
+
}, [isEditing]);
|
|
368
|
+
const handleSubmit = () => {
|
|
369
|
+
const trimmed = editName.trim();
|
|
370
|
+
if (trimmed && trimmed !== drawing.name) {
|
|
371
|
+
onRename(trimmed);
|
|
372
|
+
} else {
|
|
373
|
+
setEditName(drawing.name);
|
|
374
|
+
}
|
|
375
|
+
setIsEditing(false);
|
|
376
|
+
};
|
|
377
|
+
const handleKeyDown = (e) => {
|
|
378
|
+
if (e.key === "Enter") {
|
|
379
|
+
handleSubmit();
|
|
380
|
+
} else if (e.key === "Escape") {
|
|
381
|
+
setEditName(drawing.name);
|
|
382
|
+
setIsEditing(false);
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
const handleDoubleClick = (e) => {
|
|
386
|
+
e.stopPropagation();
|
|
387
|
+
setIsEditing(true);
|
|
388
|
+
};
|
|
389
|
+
const handleDeleteClick = (e) => {
|
|
390
|
+
e.stopPropagation();
|
|
391
|
+
if (window.confirm(`Ta bort "${drawing.name}"?`)) {
|
|
392
|
+
onDelete();
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
const formatDate = (timestamp) => {
|
|
396
|
+
const date = new Date(timestamp);
|
|
397
|
+
return date.toLocaleDateString("sv-SE", {
|
|
398
|
+
month: "short",
|
|
399
|
+
day: "numeric",
|
|
400
|
+
hour: "2-digit",
|
|
401
|
+
minute: "2-digit"
|
|
402
|
+
});
|
|
403
|
+
};
|
|
404
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
405
|
+
"li",
|
|
406
|
+
{
|
|
407
|
+
className: `${DrawingList_default.item} ${isActive ? DrawingList_default.active : ""}`,
|
|
408
|
+
onClick: onSelect,
|
|
409
|
+
children: [
|
|
410
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: DrawingList_default.itemContent, children: isEditing ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
411
|
+
"input",
|
|
412
|
+
{
|
|
413
|
+
ref: inputRef,
|
|
414
|
+
type: "text",
|
|
415
|
+
value: editName,
|
|
416
|
+
onChange: (e) => setEditName(e.target.value),
|
|
417
|
+
onBlur: handleSubmit,
|
|
418
|
+
onKeyDown: handleKeyDown,
|
|
419
|
+
className: DrawingList_default.editInput,
|
|
420
|
+
onClick: (e) => e.stopPropagation()
|
|
421
|
+
}
|
|
422
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
423
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
424
|
+
"span",
|
|
425
|
+
{
|
|
426
|
+
className: DrawingList_default.name,
|
|
427
|
+
onDoubleClick: handleDoubleClick,
|
|
428
|
+
title: "Dubbelklicka f\xF6r att byta namn",
|
|
429
|
+
children: drawing.name
|
|
430
|
+
}
|
|
431
|
+
),
|
|
432
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: DrawingList_default.date, children: formatDate(drawing.updatedAt) })
|
|
433
|
+
] }) }),
|
|
434
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: DrawingList_default.actions, children: [
|
|
435
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
436
|
+
"button",
|
|
437
|
+
{
|
|
438
|
+
className: DrawingList_default.actionButton,
|
|
439
|
+
onClick: handleDoubleClick,
|
|
440
|
+
title: "Byt namn (F2)",
|
|
441
|
+
children: "\u270F\uFE0F"
|
|
442
|
+
}
|
|
443
|
+
),
|
|
444
|
+
canDelete && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
445
|
+
"button",
|
|
446
|
+
{
|
|
447
|
+
className: DrawingList_default.actionButton,
|
|
448
|
+
onClick: handleDeleteClick,
|
|
449
|
+
title: "Ta bort",
|
|
450
|
+
children: "\u{1F5D1}\uFE0F"
|
|
451
|
+
}
|
|
452
|
+
)
|
|
453
|
+
] })
|
|
454
|
+
]
|
|
455
|
+
}
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// src/ui/DrawingList/DrawingList.tsx
|
|
460
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
461
|
+
function DrawingList() {
|
|
462
|
+
const { drawings, activeDrawing, switchDrawing, renameDrawing, removeDrawing } = useWorkspace();
|
|
463
|
+
if (drawings.length === 0) {
|
|
464
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: DrawingList_default.empty, children: "Inga ritningar" });
|
|
465
|
+
}
|
|
466
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { className: DrawingList_default.list, children: drawings.map((drawing) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
467
|
+
DrawingListItem,
|
|
468
|
+
{
|
|
469
|
+
drawing,
|
|
470
|
+
isActive: drawing.id === activeDrawing?.id,
|
|
471
|
+
onSelect: () => switchDrawing(drawing.id),
|
|
472
|
+
onRename: (name) => renameDrawing(drawing.id, name),
|
|
473
|
+
onDelete: () => removeDrawing(drawing.id),
|
|
474
|
+
canDelete: drawings.length > 1
|
|
475
|
+
},
|
|
476
|
+
drawing.id
|
|
477
|
+
)) });
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// src/ui/Sidebar/Sidebar.module.css
|
|
481
|
+
var Sidebar_default = {};
|
|
482
|
+
|
|
483
|
+
// src/ui/Sidebar/Sidebar.tsx
|
|
484
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
485
|
+
function Sidebar({ isOpen = true, onToggle, width = 250 }) {
|
|
486
|
+
const { createNewDrawing, isLoading } = useWorkspace();
|
|
487
|
+
const [isCreating, setIsCreating] = (0, import_react3.useState)(false);
|
|
488
|
+
const handleCreateNew = async () => {
|
|
489
|
+
setIsCreating(true);
|
|
490
|
+
await createNewDrawing();
|
|
491
|
+
setIsCreating(false);
|
|
492
|
+
};
|
|
493
|
+
if (!isOpen) {
|
|
494
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
495
|
+
"button",
|
|
496
|
+
{
|
|
497
|
+
className: Sidebar_default.toggleButton,
|
|
498
|
+
onClick: onToggle,
|
|
499
|
+
title: "Open sidebar (Ctrl+B)",
|
|
500
|
+
"aria-label": "Open sidebar",
|
|
501
|
+
children: "\u2630"
|
|
502
|
+
}
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("aside", { className: Sidebar_default.sidebar, style: { width }, children: [
|
|
506
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("header", { className: Sidebar_default.header, children: [
|
|
507
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { className: Sidebar_default.title, children: "Ritningar" }),
|
|
508
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
509
|
+
"button",
|
|
510
|
+
{
|
|
511
|
+
className: Sidebar_default.closeButton,
|
|
512
|
+
onClick: onToggle,
|
|
513
|
+
title: "Close sidebar (Ctrl+B)",
|
|
514
|
+
"aria-label": "Close sidebar",
|
|
515
|
+
children: "\u2715"
|
|
516
|
+
}
|
|
517
|
+
)
|
|
518
|
+
] }),
|
|
519
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: Sidebar_default.content, children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: Sidebar_default.loading, children: "Laddar..." }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DrawingList, {}) }),
|
|
520
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("footer", { className: Sidebar_default.footer, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
521
|
+
"button",
|
|
522
|
+
{
|
|
523
|
+
className: Sidebar_default.newButton,
|
|
524
|
+
onClick: handleCreateNew,
|
|
525
|
+
disabled: isCreating,
|
|
526
|
+
children: isCreating ? "Skapar..." : "+ Ny ritning"
|
|
527
|
+
}
|
|
528
|
+
) })
|
|
529
|
+
] });
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// src/integration/useExcalidrawBridge.ts
|
|
533
|
+
var import_react4 = require("react");
|
|
534
|
+
function useExcalidrawBridge({
|
|
535
|
+
excalidrawAPI,
|
|
536
|
+
autoSaveInterval = 2e3
|
|
537
|
+
}) {
|
|
538
|
+
const { activeDrawing, saveCurrentDrawing } = useWorkspace();
|
|
539
|
+
const saveTimeoutRef = (0, import_react4.useRef)(null);
|
|
540
|
+
const lastDrawingIdRef = (0, import_react4.useRef)(null);
|
|
541
|
+
(0, import_react4.useEffect)(() => {
|
|
542
|
+
if (!excalidrawAPI || !activeDrawing) return;
|
|
543
|
+
if (lastDrawingIdRef.current === activeDrawing.id) return;
|
|
544
|
+
lastDrawingIdRef.current = activeDrawing.id;
|
|
545
|
+
excalidrawAPI.updateScene({
|
|
546
|
+
elements: activeDrawing.elements,
|
|
547
|
+
appState: activeDrawing.appState
|
|
548
|
+
});
|
|
549
|
+
}, [excalidrawAPI, activeDrawing]);
|
|
550
|
+
const scheduleSave = (0, import_react4.useCallback)(() => {
|
|
551
|
+
if (!excalidrawAPI) return;
|
|
552
|
+
if (saveTimeoutRef.current) {
|
|
553
|
+
clearTimeout(saveTimeoutRef.current);
|
|
554
|
+
}
|
|
555
|
+
saveTimeoutRef.current = setTimeout(async () => {
|
|
556
|
+
const elements = excalidrawAPI.getSceneElements();
|
|
557
|
+
const appState = excalidrawAPI.getAppState();
|
|
558
|
+
await saveCurrentDrawing(elements, appState);
|
|
559
|
+
}, autoSaveInterval);
|
|
560
|
+
}, [excalidrawAPI, saveCurrentDrawing, autoSaveInterval]);
|
|
561
|
+
(0, import_react4.useEffect)(() => {
|
|
562
|
+
return () => {
|
|
563
|
+
if (saveTimeoutRef.current) {
|
|
564
|
+
clearTimeout(saveTimeoutRef.current);
|
|
565
|
+
}
|
|
566
|
+
};
|
|
567
|
+
}, []);
|
|
568
|
+
return {
|
|
569
|
+
scheduleSave
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// src/WorkspacePlugin.tsx
|
|
574
|
+
var import_react5 = require("react");
|
|
575
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
576
|
+
function WorkspacePluginInner({
|
|
577
|
+
children,
|
|
578
|
+
defaultSidebarOpen = true,
|
|
579
|
+
sidebarWidth = 250
|
|
580
|
+
}) {
|
|
581
|
+
const [sidebarOpen, setSidebarOpen] = (0, import_react5.useState)(defaultSidebarOpen);
|
|
582
|
+
const { activeDrawing } = useWorkspace();
|
|
583
|
+
(0, import_react5.useEffect)(() => {
|
|
584
|
+
const handleKeyDown = (e) => {
|
|
585
|
+
if (e.ctrlKey && e.key === "b") {
|
|
586
|
+
e.preventDefault();
|
|
587
|
+
setSidebarOpen((prev) => !prev);
|
|
588
|
+
}
|
|
589
|
+
if (e.ctrlKey && e.shiftKey && e.key === "N") {
|
|
590
|
+
e.preventDefault();
|
|
591
|
+
}
|
|
592
|
+
};
|
|
593
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
594
|
+
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
595
|
+
}, []);
|
|
596
|
+
const handleToggleSidebar = (0, import_react5.useCallback)(() => {
|
|
597
|
+
setSidebarOpen((prev) => !prev);
|
|
598
|
+
}, []);
|
|
599
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
600
|
+
"div",
|
|
601
|
+
{
|
|
602
|
+
style: {
|
|
603
|
+
display: "flex",
|
|
604
|
+
height: "100%",
|
|
605
|
+
width: "100%"
|
|
606
|
+
},
|
|
607
|
+
children: [
|
|
608
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
609
|
+
Sidebar,
|
|
610
|
+
{
|
|
611
|
+
isOpen: sidebarOpen,
|
|
612
|
+
onToggle: handleToggleSidebar,
|
|
613
|
+
width: sidebarWidth
|
|
614
|
+
}
|
|
615
|
+
),
|
|
616
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
617
|
+
"main",
|
|
618
|
+
{
|
|
619
|
+
style: {
|
|
620
|
+
flex: 1,
|
|
621
|
+
height: "100%",
|
|
622
|
+
overflow: "hidden"
|
|
623
|
+
},
|
|
624
|
+
children
|
|
625
|
+
}
|
|
626
|
+
)
|
|
627
|
+
]
|
|
628
|
+
}
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
function WorkspacePlugin(props) {
|
|
632
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(WorkspaceProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(WorkspacePluginInner, { ...props }) });
|
|
633
|
+
}
|
|
634
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
635
|
+
0 && (module.exports = {
|
|
636
|
+
DrawingList,
|
|
637
|
+
DrawingListItem,
|
|
638
|
+
Sidebar,
|
|
639
|
+
WorkspacePlugin,
|
|
640
|
+
WorkspaceProvider,
|
|
641
|
+
addDrawingToWorkspace,
|
|
642
|
+
closeDB,
|
|
643
|
+
createDrawing,
|
|
644
|
+
deleteDrawing,
|
|
645
|
+
duplicateDrawing,
|
|
646
|
+
getAllDrawings,
|
|
647
|
+
getDB,
|
|
648
|
+
getDrawing,
|
|
649
|
+
getOrCreateDefaultWorkspace,
|
|
650
|
+
getWorkspace,
|
|
651
|
+
removeDrawingFromWorkspace,
|
|
652
|
+
setActiveDrawing,
|
|
653
|
+
updateDrawing,
|
|
654
|
+
updateWorkspace,
|
|
655
|
+
useExcalidrawBridge,
|
|
656
|
+
useWorkspace
|
|
657
|
+
});
|