rita-workspace 0.5.48 → 0.5.53
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 +49 -4
- package/dist/index.d.mts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +206 -34
- package/dist/index.mjs +205 -34
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,51 @@
|
|
|
1
|
-
# Rita Workspace
|
|
1
|
+
# Excalidraw/Rita Workspace
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/rita-workspace)
|
|
4
|
+
[](https://www.npmjs.com/package/rita-workspace)
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
npm install rita-workspace
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Multi-drawing workspace feature for Excalidraw/Rita (Excalidraw fork based on B310-digital/excalidraw).
|
|
11
|
+
|
|
12
|
+
<img width="504" height="574" alt="image" src="https://github.com/user-attachments/assets/ada58a93-a664-4ab2-80e2-55354ae3a544" />
|
|
13
|
+
|
|
14
|
+
## Quick Start
|
|
15
|
+
|
|
16
|
+
Add to your main app file (e.g. `App.tsx`). For full integration (debounced save, conflict handling, toggle state), see [INTEGRATION.md](./docs/INTEGRATION.md).
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
import { WorkspaceProvider, useWorkspace, DrawingsDialog } from "rita-workspace";
|
|
20
|
+
|
|
21
|
+
function App() {
|
|
22
|
+
const [dialogOpen, setDialogOpen] = useState(false);
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<WorkspaceProvider lang="en">
|
|
26
|
+
<button onClick={() => setDialogOpen(true)}>My Drawings</button>
|
|
27
|
+
<DrawingsDialog
|
|
28
|
+
open={dialogOpen}
|
|
29
|
+
onClose={() => setDialogOpen(false)}
|
|
30
|
+
/>
|
|
31
|
+
<ExcalidrawWithWorkspace />
|
|
32
|
+
</WorkspaceProvider>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function ExcalidrawWithWorkspace() {
|
|
37
|
+
const { activeDrawing, saveCurrentDrawing } = useWorkspace();
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<Excalidraw
|
|
41
|
+
initialData={activeDrawing}
|
|
42
|
+
onChange={(elements, appState, files) => {
|
|
43
|
+
saveCurrentDrawing(elements, appState, files);
|
|
44
|
+
}}
|
|
45
|
+
/>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
```
|
|
4
49
|
|
|
5
50
|
## Features
|
|
6
51
|
|
|
@@ -10,7 +55,7 @@ Multi-drawing workspace feature for Rita (Excalidraw fork based on B310-digital/
|
|
|
10
55
|
- **Multi-tab conflict detection** - Prevents data loss when same drawing is open in multiple tabs
|
|
11
56
|
- **F5 preserves write ownership** - TAB_ID and openedAt persist in sessionStorage across page refresh
|
|
12
57
|
- **Cross-tab refresh** - Creating/renaming/deleting drawings in one tab auto-refreshes other tabs via BroadcastChannel
|
|
13
|
-
- **Workspace toggle** -
|
|
58
|
+
- **Workspace toggle** - Can be enabled/disabled per browser tab
|
|
14
59
|
- **Export/Import** - Export workspace as JSON, export all drawings as individual .excalidraw files, import .excalidraw files
|
|
15
60
|
- **i18n support** - Swedish and English with automatic Excalidraw language sync
|
|
16
61
|
- **Optimized loading** - DB pre-warming and parallel initialization
|
|
@@ -111,7 +156,7 @@ if (isDrawingOpenedEarlierInOtherTab(drawingId)) {
|
|
|
111
156
|
- **localStorage** (`rita-workspace-tabs`) — persistent tab registry, backup for BroadcastChannel
|
|
112
157
|
- **Stale tab cleanup** — on mount, pings other tabs via BroadcastChannel and removes entries that don't respond
|
|
113
158
|
|
|
114
|
-
## Workspace Toggle
|
|
159
|
+
## Workspace Toggle
|
|
115
160
|
|
|
116
161
|
The workspace can be enabled/disabled per browser tab using `sessionStorage`:
|
|
117
162
|
|
package/dist/index.d.mts
CHANGED
|
@@ -17,6 +17,8 @@ interface Drawing {
|
|
|
17
17
|
interface Folder {
|
|
18
18
|
id: string;
|
|
19
19
|
name: string;
|
|
20
|
+
/** Manual sort position. Lower = earlier in list. Falls back to createdAt when unset. */
|
|
21
|
+
position?: number;
|
|
20
22
|
createdAt: number;
|
|
21
23
|
updatedAt: number;
|
|
22
24
|
}
|
|
@@ -77,6 +79,11 @@ declare function createFolder(name: string): Promise<Folder>;
|
|
|
77
79
|
declare function getFolder(id: string): Promise<Folder | undefined>;
|
|
78
80
|
declare function getAllFolders(): Promise<Folder[]>;
|
|
79
81
|
declare function renameFolder(id: string, name: string): Promise<Folder | undefined>;
|
|
82
|
+
/**
|
|
83
|
+
* Reassigns 'position' field on the given ordered folder ids (0..n-1).
|
|
84
|
+
* Folders not in `orderedIds` are left untouched.
|
|
85
|
+
*/
|
|
86
|
+
declare function reorderFolders(orderedIds: string[]): Promise<void>;
|
|
80
87
|
declare function deleteFolder(id: string): Promise<void>;
|
|
81
88
|
|
|
82
89
|
declare function getOrCreateDefaultWorkspace(): Promise<Workspace>;
|
|
@@ -160,6 +167,8 @@ interface WorkspaceContextValue {
|
|
|
160
167
|
/** Re-numbers `position` for the given drawing IDs in order. Use the full ordered slice
|
|
161
168
|
* the user reordered (e.g. all root drawings, or all drawings in a folder). */
|
|
162
169
|
reorderDrawings: (orderedIds: string[]) => Promise<void>;
|
|
170
|
+
/** Assigns position to the listed folder ids in given order. Pass full folder list. */
|
|
171
|
+
reorderFolders: (orderedIds: string[]) => Promise<void>;
|
|
163
172
|
saveCurrentDrawing: (expectedDrawingId: string, elements: unknown[], appState: Record<string, unknown>, files?: Record<string, unknown>) => Promise<void>;
|
|
164
173
|
saveDrawingById: (id: string, elements: unknown[], appState: Record<string, unknown>, files?: Record<string, unknown>) => Promise<void>;
|
|
165
174
|
refreshDrawings: () => Promise<void>;
|
|
@@ -401,4 +410,4 @@ interface WorkspacePluginProps {
|
|
|
401
410
|
*/
|
|
402
411
|
declare function WorkspacePlugin(props: WorkspacePluginProps): react_jsx_runtime.JSX.Element;
|
|
403
412
|
|
|
404
|
-
export { type Drawing, DrawingList, DrawingListItem, type DrawingMeta, DrawingsDialog, type DrawingsDialogProps, type ExcalidrawImperativeAPI, type Folder, Sidebar, type SupportedLanguage, type Translations, type Workspace, WorkspaceBridge, type WorkspaceBridgeProps, type WorkspaceContextValue, WorkspaceMenuItems, type WorkspaceMenuItemsProps, WorkspacePlugin, WorkspaceProvider, addDrawingToWorkspace, closeDB, createDrawing, createFolder, deleteDrawing, deleteFolder, duplicateDrawing, getAllDrawings, getAllDrawingsMeta, getAllFolders, getDB, getDrawing, getFolder, getOrCreateDefaultWorkspace, getTranslations, getWorkspace, isDrawingOpenedEarlierInOtherTab, isLanguageSupported, moveDrawingToFolder, removeDrawingFromWorkspace, renameFolder, reorderDrawings, setActiveDrawing, updateDrawing, updateWorkspace, useExcalidrawBridge, useWorkspace, useWorkspaceLang, warmDB };
|
|
413
|
+
export { type Drawing, DrawingList, DrawingListItem, type DrawingMeta, DrawingsDialog, type DrawingsDialogProps, type ExcalidrawImperativeAPI, type Folder, Sidebar, type SupportedLanguage, type Translations, type Workspace, WorkspaceBridge, type WorkspaceBridgeProps, type WorkspaceContextValue, WorkspaceMenuItems, type WorkspaceMenuItemsProps, WorkspacePlugin, WorkspaceProvider, addDrawingToWorkspace, closeDB, createDrawing, createFolder, deleteDrawing, deleteFolder, duplicateDrawing, getAllDrawings, getAllDrawingsMeta, getAllFolders, getDB, getDrawing, getFolder, getOrCreateDefaultWorkspace, getTranslations, getWorkspace, isDrawingOpenedEarlierInOtherTab, isLanguageSupported, moveDrawingToFolder, removeDrawingFromWorkspace, renameFolder, reorderDrawings, reorderFolders, setActiveDrawing, updateDrawing, updateWorkspace, useExcalidrawBridge, useWorkspace, useWorkspaceLang, warmDB };
|
package/dist/index.d.ts
CHANGED
|
@@ -17,6 +17,8 @@ interface Drawing {
|
|
|
17
17
|
interface Folder {
|
|
18
18
|
id: string;
|
|
19
19
|
name: string;
|
|
20
|
+
/** Manual sort position. Lower = earlier in list. Falls back to createdAt when unset. */
|
|
21
|
+
position?: number;
|
|
20
22
|
createdAt: number;
|
|
21
23
|
updatedAt: number;
|
|
22
24
|
}
|
|
@@ -77,6 +79,11 @@ declare function createFolder(name: string): Promise<Folder>;
|
|
|
77
79
|
declare function getFolder(id: string): Promise<Folder | undefined>;
|
|
78
80
|
declare function getAllFolders(): Promise<Folder[]>;
|
|
79
81
|
declare function renameFolder(id: string, name: string): Promise<Folder | undefined>;
|
|
82
|
+
/**
|
|
83
|
+
* Reassigns 'position' field on the given ordered folder ids (0..n-1).
|
|
84
|
+
* Folders not in `orderedIds` are left untouched.
|
|
85
|
+
*/
|
|
86
|
+
declare function reorderFolders(orderedIds: string[]): Promise<void>;
|
|
80
87
|
declare function deleteFolder(id: string): Promise<void>;
|
|
81
88
|
|
|
82
89
|
declare function getOrCreateDefaultWorkspace(): Promise<Workspace>;
|
|
@@ -160,6 +167,8 @@ interface WorkspaceContextValue {
|
|
|
160
167
|
/** Re-numbers `position` for the given drawing IDs in order. Use the full ordered slice
|
|
161
168
|
* the user reordered (e.g. all root drawings, or all drawings in a folder). */
|
|
162
169
|
reorderDrawings: (orderedIds: string[]) => Promise<void>;
|
|
170
|
+
/** Assigns position to the listed folder ids in given order. Pass full folder list. */
|
|
171
|
+
reorderFolders: (orderedIds: string[]) => Promise<void>;
|
|
163
172
|
saveCurrentDrawing: (expectedDrawingId: string, elements: unknown[], appState: Record<string, unknown>, files?: Record<string, unknown>) => Promise<void>;
|
|
164
173
|
saveDrawingById: (id: string, elements: unknown[], appState: Record<string, unknown>, files?: Record<string, unknown>) => Promise<void>;
|
|
165
174
|
refreshDrawings: () => Promise<void>;
|
|
@@ -401,4 +410,4 @@ interface WorkspacePluginProps {
|
|
|
401
410
|
*/
|
|
402
411
|
declare function WorkspacePlugin(props: WorkspacePluginProps): react_jsx_runtime.JSX.Element;
|
|
403
412
|
|
|
404
|
-
export { type Drawing, DrawingList, DrawingListItem, type DrawingMeta, DrawingsDialog, type DrawingsDialogProps, type ExcalidrawImperativeAPI, type Folder, Sidebar, type SupportedLanguage, type Translations, type Workspace, WorkspaceBridge, type WorkspaceBridgeProps, type WorkspaceContextValue, WorkspaceMenuItems, type WorkspaceMenuItemsProps, WorkspacePlugin, WorkspaceProvider, addDrawingToWorkspace, closeDB, createDrawing, createFolder, deleteDrawing, deleteFolder, duplicateDrawing, getAllDrawings, getAllDrawingsMeta, getAllFolders, getDB, getDrawing, getFolder, getOrCreateDefaultWorkspace, getTranslations, getWorkspace, isDrawingOpenedEarlierInOtherTab, isLanguageSupported, moveDrawingToFolder, removeDrawingFromWorkspace, renameFolder, reorderDrawings, setActiveDrawing, updateDrawing, updateWorkspace, useExcalidrawBridge, useWorkspace, useWorkspaceLang, warmDB };
|
|
413
|
+
export { type Drawing, DrawingList, DrawingListItem, type DrawingMeta, DrawingsDialog, type DrawingsDialogProps, type ExcalidrawImperativeAPI, type Folder, Sidebar, type SupportedLanguage, type Translations, type Workspace, WorkspaceBridge, type WorkspaceBridgeProps, type WorkspaceContextValue, WorkspaceMenuItems, type WorkspaceMenuItemsProps, WorkspacePlugin, WorkspaceProvider, addDrawingToWorkspace, closeDB, createDrawing, createFolder, deleteDrawing, deleteFolder, duplicateDrawing, getAllDrawings, getAllDrawingsMeta, getAllFolders, getDB, getDrawing, getFolder, getOrCreateDefaultWorkspace, getTranslations, getWorkspace, isDrawingOpenedEarlierInOtherTab, isLanguageSupported, moveDrawingToFolder, removeDrawingFromWorkspace, renameFolder, reorderDrawings, reorderFolders, setActiveDrawing, updateDrawing, updateWorkspace, useExcalidrawBridge, useWorkspace, useWorkspaceLang, warmDB };
|
package/dist/index.js
CHANGED
|
@@ -60,6 +60,7 @@ __export(index_exports, {
|
|
|
60
60
|
removeDrawingFromWorkspace: () => removeDrawingFromWorkspace,
|
|
61
61
|
renameFolder: () => renameFolder,
|
|
62
62
|
reorderDrawings: () => reorderDrawings,
|
|
63
|
+
reorderFolders: () => reorderFolders,
|
|
63
64
|
setActiveDrawing: () => setActiveDrawing,
|
|
64
65
|
updateDrawing: () => updateDrawing,
|
|
65
66
|
updateWorkspace: () => updateWorkspace,
|
|
@@ -224,6 +225,17 @@ async function renameFolder(id, name) {
|
|
|
224
225
|
await db.put("folders", updated);
|
|
225
226
|
return updated;
|
|
226
227
|
}
|
|
228
|
+
async function reorderFolders(orderedIds) {
|
|
229
|
+
const db = await getDB();
|
|
230
|
+
const tx = db.transaction("folders", "readwrite");
|
|
231
|
+
const store = tx.objectStore("folders");
|
|
232
|
+
for (let i = 0; i < orderedIds.length; i++) {
|
|
233
|
+
const existing = await store.get(orderedIds[i]);
|
|
234
|
+
if (!existing) continue;
|
|
235
|
+
await store.put({ ...existing, position: i });
|
|
236
|
+
}
|
|
237
|
+
await tx.done;
|
|
238
|
+
}
|
|
227
239
|
async function deleteFolder(id) {
|
|
228
240
|
const db = await getDB();
|
|
229
241
|
const allDrawings = await db.getAll("drawings");
|
|
@@ -1309,6 +1321,19 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
1309
1321
|
refreshDrawingsRef.current();
|
|
1310
1322
|
}
|
|
1311
1323
|
}, []);
|
|
1324
|
+
const reorderFolders2 = (0, import_react.useCallback)(async (orderedIds) => {
|
|
1325
|
+
const positionMap = new Map(orderedIds.map((id, idx) => [id, idx]));
|
|
1326
|
+
setFolders((prev) => prev.map(
|
|
1327
|
+
(f) => positionMap.has(f.id) ? { ...f, position: positionMap.get(f.id) } : f
|
|
1328
|
+
));
|
|
1329
|
+
try {
|
|
1330
|
+
await reorderFolders(orderedIds);
|
|
1331
|
+
broadcastWorkspaceChange();
|
|
1332
|
+
} catch (err) {
|
|
1333
|
+
setError(err instanceof Error ? err.message : "Failed to reorder folders");
|
|
1334
|
+
refreshDrawingsRef.current();
|
|
1335
|
+
}
|
|
1336
|
+
}, []);
|
|
1312
1337
|
const value = {
|
|
1313
1338
|
workspace,
|
|
1314
1339
|
drawings,
|
|
@@ -1329,6 +1354,7 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
1329
1354
|
deleteFolder: deleteFolder2,
|
|
1330
1355
|
moveDrawingToFolder: moveDrawingToFolder2,
|
|
1331
1356
|
reorderDrawings: reorderDrawings2,
|
|
1357
|
+
reorderFolders: reorderFolders2,
|
|
1332
1358
|
saveCurrentDrawing,
|
|
1333
1359
|
saveDrawingById,
|
|
1334
1360
|
refreshDrawings,
|
|
@@ -1698,6 +1724,7 @@ var DrawingsDialog = ({
|
|
|
1698
1724
|
deleteFolder: deleteFolder2,
|
|
1699
1725
|
moveDrawingToFolder: moveDrawingToFolder2,
|
|
1700
1726
|
reorderDrawings: reorderDrawings2,
|
|
1727
|
+
reorderFolders: reorderFolders2,
|
|
1701
1728
|
exportWorkspace,
|
|
1702
1729
|
importWorkspace,
|
|
1703
1730
|
exportDrawingAsExcalidraw,
|
|
@@ -1715,7 +1742,17 @@ var DrawingsDialog = ({
|
|
|
1715
1742
|
const [switchingId, setSwitchingId] = (0, import_react5.useState)(null);
|
|
1716
1743
|
const [selectedId, setSelectedId] = (0, import_react5.useState)(null);
|
|
1717
1744
|
const [hoveredId, setHoveredId] = (0, import_react5.useState)(null);
|
|
1718
|
-
const [expandedFolders, setExpandedFolders] = (0, import_react5.useState)(
|
|
1745
|
+
const [expandedFolders, setExpandedFolders] = (0, import_react5.useState)(() => {
|
|
1746
|
+
try {
|
|
1747
|
+
if (localStorage.getItem("rita-workspace-remember-folder-state") === "false") {
|
|
1748
|
+
return /* @__PURE__ */ new Set();
|
|
1749
|
+
}
|
|
1750
|
+
const raw = localStorage.getItem("rita-workspace-expanded-folders");
|
|
1751
|
+
if (raw) return new Set(JSON.parse(raw));
|
|
1752
|
+
} catch {
|
|
1753
|
+
}
|
|
1754
|
+
return /* @__PURE__ */ new Set();
|
|
1755
|
+
});
|
|
1719
1756
|
const [creatingFolder, setCreatingFolder] = (0, import_react5.useState)(false);
|
|
1720
1757
|
const [newFolderName, setNewFolderName] = (0, import_react5.useState)("");
|
|
1721
1758
|
const [editingFolderId, setEditingFolderId] = (0, import_react5.useState)(null);
|
|
@@ -1745,10 +1782,35 @@ var DrawingsDialog = ({
|
|
|
1745
1782
|
return next;
|
|
1746
1783
|
});
|
|
1747
1784
|
}, []);
|
|
1785
|
+
const [rememberFolderState, setRememberFolderState] = (0, import_react5.useState)(() => {
|
|
1786
|
+
try {
|
|
1787
|
+
return localStorage.getItem("rita-workspace-remember-folder-state") !== "false";
|
|
1788
|
+
} catch {
|
|
1789
|
+
return true;
|
|
1790
|
+
}
|
|
1791
|
+
});
|
|
1792
|
+
const handleToggleRememberFolderState = (0, import_react5.useCallback)(() => {
|
|
1793
|
+
setRememberFolderState((prev) => {
|
|
1794
|
+
const next = !prev;
|
|
1795
|
+
try {
|
|
1796
|
+
if (next) {
|
|
1797
|
+
localStorage.removeItem("rita-workspace-remember-folder-state");
|
|
1798
|
+
} else {
|
|
1799
|
+
localStorage.setItem("rita-workspace-remember-folder-state", "false");
|
|
1800
|
+
localStorage.removeItem("rita-workspace-expanded-folders");
|
|
1801
|
+
}
|
|
1802
|
+
} catch {
|
|
1803
|
+
}
|
|
1804
|
+
return next;
|
|
1805
|
+
});
|
|
1806
|
+
}, []);
|
|
1748
1807
|
const [draggingDrawingId, setDraggingDrawingId] = (0, import_react5.useState)(null);
|
|
1749
1808
|
const [dropTargetFolderId, setDropTargetFolderId] = (0, import_react5.useState)(null);
|
|
1750
1809
|
const [dropTargetDrawingId, setDropTargetDrawingId] = (0, import_react5.useState)(null);
|
|
1751
1810
|
const [dropTargetPosition, setDropTargetPosition] = (0, import_react5.useState)(null);
|
|
1811
|
+
const [draggingFolderId, setDraggingFolderId] = (0, import_react5.useState)(null);
|
|
1812
|
+
const [folderDropTargetId, setFolderDropTargetId] = (0, import_react5.useState)(null);
|
|
1813
|
+
const [folderDropPosition, setFolderDropPosition] = (0, import_react5.useState)(null);
|
|
1752
1814
|
const [position, setPosition] = (0, import_react5.useState)(null);
|
|
1753
1815
|
const dragRef = (0, import_react5.useRef)(null);
|
|
1754
1816
|
const dialogRef = (0, import_react5.useRef)(null);
|
|
@@ -1769,6 +1831,18 @@ var DrawingsDialog = ({
|
|
|
1769
1831
|
}
|
|
1770
1832
|
prevOpenRef.current = open;
|
|
1771
1833
|
}, [open, refreshDrawings]);
|
|
1834
|
+
(0, import_react5.useEffect)(() => {
|
|
1835
|
+
if (!open) return;
|
|
1836
|
+
try {
|
|
1837
|
+
if (localStorage.getItem("rita-workspace-remember-folder-state") === "false") {
|
|
1838
|
+
setExpandedFolders(/* @__PURE__ */ new Set());
|
|
1839
|
+
return;
|
|
1840
|
+
}
|
|
1841
|
+
const raw = localStorage.getItem("rita-workspace-expanded-folders");
|
|
1842
|
+
setExpandedFolders(raw ? new Set(JSON.parse(raw)) : /* @__PURE__ */ new Set());
|
|
1843
|
+
} catch {
|
|
1844
|
+
}
|
|
1845
|
+
}, [open]);
|
|
1772
1846
|
(0, import_react5.useEffect)(() => {
|
|
1773
1847
|
if (creatingFolder && newFolderInputRef.current) {
|
|
1774
1848
|
newFolderInputRef.current.focus();
|
|
@@ -1891,6 +1965,24 @@ var DrawingsDialog = ({
|
|
|
1891
1965
|
].map((d) => d.id);
|
|
1892
1966
|
reorderDrawings2(ordered);
|
|
1893
1967
|
}, [drawings, reorderDrawings2, moveDrawingToFolder2]);
|
|
1968
|
+
const handleFolderReorderDrop = (0, import_react5.useCallback)((draggedFolderId, targetFolderId, pos) => {
|
|
1969
|
+
if (draggedFolderId === targetFolderId) return;
|
|
1970
|
+
const sorted = [...folders].sort(
|
|
1971
|
+
(a, b) => (a.position ?? a.createdAt) - (b.position ?? b.createdAt)
|
|
1972
|
+
);
|
|
1973
|
+
const withoutDragged = sorted.filter((f) => f.id !== draggedFolderId);
|
|
1974
|
+
const targetIdx = withoutDragged.findIndex((f) => f.id === targetFolderId);
|
|
1975
|
+
if (targetIdx === -1) return;
|
|
1976
|
+
const insertIdx = pos === "before" ? targetIdx : targetIdx + 1;
|
|
1977
|
+
const dragged = folders.find((f) => f.id === draggedFolderId);
|
|
1978
|
+
if (!dragged) return;
|
|
1979
|
+
const ordered = [
|
|
1980
|
+
...withoutDragged.slice(0, insertIdx),
|
|
1981
|
+
dragged,
|
|
1982
|
+
...withoutDragged.slice(insertIdx)
|
|
1983
|
+
].map((f) => f.id);
|
|
1984
|
+
reorderFolders2(ordered);
|
|
1985
|
+
}, [folders, reorderFolders2]);
|
|
1894
1986
|
const toggleFolder = (0, import_react5.useCallback)((folderId) => {
|
|
1895
1987
|
setExpandedFolders((prev) => {
|
|
1896
1988
|
const next = new Set(prev);
|
|
@@ -1902,6 +1994,16 @@ var DrawingsDialog = ({
|
|
|
1902
1994
|
return next;
|
|
1903
1995
|
});
|
|
1904
1996
|
}, []);
|
|
1997
|
+
(0, import_react5.useEffect)(() => {
|
|
1998
|
+
if (!rememberFolderState) return;
|
|
1999
|
+
try {
|
|
2000
|
+
localStorage.setItem(
|
|
2001
|
+
"rita-workspace-expanded-folders",
|
|
2002
|
+
JSON.stringify(Array.from(expandedFolders))
|
|
2003
|
+
);
|
|
2004
|
+
} catch {
|
|
2005
|
+
}
|
|
2006
|
+
}, [expandedFolders, rememberFolderState]);
|
|
1905
2007
|
const getLocale = () => {
|
|
1906
2008
|
if (!effectiveLang) return "en-US";
|
|
1907
2009
|
const baseLang = effectiveLang.split("-")[0].toLowerCase();
|
|
@@ -1940,7 +2042,10 @@ var DrawingsDialog = ({
|
|
|
1940
2042
|
for (const folder of folders) {
|
|
1941
2043
|
byFolder[folder.id] = sorted.filter((d) => d.folderId === folder.id);
|
|
1942
2044
|
}
|
|
1943
|
-
const
|
|
2045
|
+
const sortedFolders = [...folders].sort(
|
|
2046
|
+
(a, b) => (a.position ?? a.createdAt) - (b.position ?? b.createdAt)
|
|
2047
|
+
);
|
|
2048
|
+
const foldersFiltered = query ? sortedFolders.filter((f) => f.name.toLowerCase().includes(query) || (byFolder[f.id] || []).length > 0) : sortedFolders;
|
|
1944
2049
|
return { rootDrawings: root, drawingsByFolder: byFolder, filteredFolders: foldersFiltered };
|
|
1945
2050
|
}, [drawings, folders, searchQuery]);
|
|
1946
2051
|
if (!open) return null;
|
|
@@ -2257,10 +2362,11 @@ var DrawingsDialog = ({
|
|
|
2257
2362
|
const renderFolderGroup = (folder) => {
|
|
2258
2363
|
const folderDrawings = drawingsByFolder[folder.id] || [];
|
|
2259
2364
|
const isExpanded = expandedFolders.has(folder.id);
|
|
2365
|
+
const isFolderDropTarget = folderDropTargetId === folder.id;
|
|
2260
2366
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
2261
2367
|
"div",
|
|
2262
2368
|
{
|
|
2263
|
-
style: { marginBottom: "4px" },
|
|
2369
|
+
style: { marginBottom: "4px", position: "relative" },
|
|
2264
2370
|
onDragOver: (e) => {
|
|
2265
2371
|
if (!draggingDrawingId) return;
|
|
2266
2372
|
e.preventDefault();
|
|
@@ -2285,6 +2391,37 @@ var DrawingsDialog = ({
|
|
|
2285
2391
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
2286
2392
|
"div",
|
|
2287
2393
|
{
|
|
2394
|
+
draggable: !editingFolderId && !confirmDeleteFolderId,
|
|
2395
|
+
onDragStart: (e) => {
|
|
2396
|
+
if (draggingDrawingId) return;
|
|
2397
|
+
setDraggingFolderId(folder.id);
|
|
2398
|
+
e.dataTransfer.effectAllowed = "move";
|
|
2399
|
+
},
|
|
2400
|
+
onDragEnd: () => {
|
|
2401
|
+
setDraggingFolderId(null);
|
|
2402
|
+
setFolderDropTargetId(null);
|
|
2403
|
+
setFolderDropPosition(null);
|
|
2404
|
+
},
|
|
2405
|
+
onDragOver: (e) => {
|
|
2406
|
+
if (!draggingFolderId || draggingFolderId === folder.id) return;
|
|
2407
|
+
e.preventDefault();
|
|
2408
|
+
e.stopPropagation();
|
|
2409
|
+
e.dataTransfer.dropEffect = "move";
|
|
2410
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
2411
|
+
const isAbove = e.clientY < rect.top + rect.height / 2;
|
|
2412
|
+
setFolderDropTargetId(folder.id);
|
|
2413
|
+
setFolderDropPosition(isAbove ? "before" : "after");
|
|
2414
|
+
},
|
|
2415
|
+
onDrop: (e) => {
|
|
2416
|
+
if (draggingFolderId && draggingFolderId !== folder.id && folderDropPosition) {
|
|
2417
|
+
e.preventDefault();
|
|
2418
|
+
e.stopPropagation();
|
|
2419
|
+
handleFolderReorderDrop(draggingFolderId, folder.id, folderDropPosition);
|
|
2420
|
+
setDraggingFolderId(null);
|
|
2421
|
+
setFolderDropTargetId(null);
|
|
2422
|
+
setFolderDropPosition(null);
|
|
2423
|
+
}
|
|
2424
|
+
},
|
|
2288
2425
|
onClick: () => {
|
|
2289
2426
|
toggleFolder(folder.id);
|
|
2290
2427
|
setSelectedId(null);
|
|
@@ -2295,9 +2432,11 @@ var DrawingsDialog = ({
|
|
|
2295
2432
|
alignItems: "center",
|
|
2296
2433
|
gap: "8px",
|
|
2297
2434
|
borderRadius: "8px",
|
|
2298
|
-
cursor: "pointer",
|
|
2435
|
+
cursor: draggingFolderId === folder.id ? "grabbing" : "pointer",
|
|
2436
|
+
opacity: draggingFolderId === folder.id ? 0.5 : 1,
|
|
2299
2437
|
backgroundColor: dropTargetFolderId === folder.id ? "var(--color-primary-light, rgba(108, 99, 255, 0.2))" : "var(--color-surface-mid, rgba(0, 0, 0, 0.03))",
|
|
2300
2438
|
border: dropTargetFolderId === folder.id ? "2px dashed var(--color-primary, #6c63ff)" : "2px solid transparent",
|
|
2439
|
+
boxShadow: isFolderDropTarget && folderDropPosition === "before" ? "inset 0 3px 0 0 var(--color-primary, #6c63ff)" : isFolderDropTarget && folderDropPosition === "after" ? "inset 0 -3px 0 0 var(--color-primary, #6c63ff)" : "none",
|
|
2301
2440
|
transition: "background-color 0.15s, border-color 0.15s"
|
|
2302
2441
|
},
|
|
2303
2442
|
children: [
|
|
@@ -2674,36 +2813,68 @@ var DrawingsDialog = ({
|
|
|
2674
2813
|
}
|
|
2675
2814
|
)
|
|
2676
2815
|
] }),
|
|
2677
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2816
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { padding: "0 20px 16px", display: "flex", flexDirection: "column", gap: "8px" }, children: [
|
|
2817
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
2818
|
+
"label",
|
|
2819
|
+
{
|
|
2820
|
+
style: {
|
|
2821
|
+
display: "flex",
|
|
2822
|
+
alignItems: "center",
|
|
2823
|
+
gap: "10px",
|
|
2824
|
+
padding: "10px 12px",
|
|
2825
|
+
border: "1px solid var(--default-border-color, #e0e0e0)",
|
|
2826
|
+
borderRadius: "8px",
|
|
2827
|
+
cursor: "pointer",
|
|
2828
|
+
userSelect: "none"
|
|
2829
|
+
},
|
|
2830
|
+
children: [
|
|
2831
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2832
|
+
"input",
|
|
2833
|
+
{
|
|
2834
|
+
type: "checkbox",
|
|
2835
|
+
checked: autoStart,
|
|
2836
|
+
onChange: handleToggleAutoStart,
|
|
2837
|
+
style: { width: "16px", height: "16px", cursor: "pointer", flexShrink: 0 }
|
|
2838
|
+
}
|
|
2839
|
+
),
|
|
2840
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
|
|
2841
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: "14px", fontWeight: 500 }, children: t.autoStartLabel }),
|
|
2842
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: "12px", color: "var(--text-secondary-color, #888)", marginTop: "2px" }, children: t.autoStartDesc })
|
|
2843
|
+
] })
|
|
2844
|
+
]
|
|
2845
|
+
}
|
|
2846
|
+
),
|
|
2847
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
2848
|
+
"label",
|
|
2849
|
+
{
|
|
2850
|
+
style: {
|
|
2851
|
+
display: "flex",
|
|
2852
|
+
alignItems: "center",
|
|
2853
|
+
gap: "10px",
|
|
2854
|
+
padding: "10px 12px",
|
|
2855
|
+
border: "1px solid var(--default-border-color, #e0e0e0)",
|
|
2856
|
+
borderRadius: "8px",
|
|
2857
|
+
cursor: "pointer",
|
|
2858
|
+
userSelect: "none"
|
|
2859
|
+
},
|
|
2860
|
+
children: [
|
|
2861
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2862
|
+
"input",
|
|
2863
|
+
{
|
|
2864
|
+
type: "checkbox",
|
|
2865
|
+
checked: rememberFolderState,
|
|
2866
|
+
onChange: handleToggleRememberFolderState,
|
|
2867
|
+
style: { width: "16px", height: "16px", cursor: "pointer", flexShrink: 0 }
|
|
2868
|
+
}
|
|
2869
|
+
),
|
|
2870
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
|
|
2871
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: "14px", fontWeight: 500 }, children: "Kom ih\xE5g utf\xE4llda mappar" }),
|
|
2872
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: "12px", color: "var(--text-secondary-color, #888)", marginTop: "2px" }, children: "Beh\xE5ller vilka mappar som \xE4r utf\xE4llda mellan sessioner. Om av: alla mappar startar minimerade." })
|
|
2873
|
+
] })
|
|
2874
|
+
]
|
|
2875
|
+
}
|
|
2876
|
+
)
|
|
2877
|
+
] })
|
|
2707
2878
|
]
|
|
2708
2879
|
}
|
|
2709
2880
|
)
|
|
@@ -3041,6 +3212,7 @@ function WorkspacePlugin(props) {
|
|
|
3041
3212
|
removeDrawingFromWorkspace,
|
|
3042
3213
|
renameFolder,
|
|
3043
3214
|
reorderDrawings,
|
|
3215
|
+
reorderFolders,
|
|
3044
3216
|
setActiveDrawing,
|
|
3045
3217
|
updateDrawing,
|
|
3046
3218
|
updateWorkspace,
|
package/dist/index.mjs
CHANGED
|
@@ -152,6 +152,17 @@ async function renameFolder(id, name) {
|
|
|
152
152
|
await db.put("folders", updated);
|
|
153
153
|
return updated;
|
|
154
154
|
}
|
|
155
|
+
async function reorderFolders(orderedIds) {
|
|
156
|
+
const db = await getDB();
|
|
157
|
+
const tx = db.transaction("folders", "readwrite");
|
|
158
|
+
const store = tx.objectStore("folders");
|
|
159
|
+
for (let i = 0; i < orderedIds.length; i++) {
|
|
160
|
+
const existing = await store.get(orderedIds[i]);
|
|
161
|
+
if (!existing) continue;
|
|
162
|
+
await store.put({ ...existing, position: i });
|
|
163
|
+
}
|
|
164
|
+
await tx.done;
|
|
165
|
+
}
|
|
155
166
|
async function deleteFolder(id) {
|
|
156
167
|
const db = await getDB();
|
|
157
168
|
const allDrawings = await db.getAll("drawings");
|
|
@@ -1237,6 +1248,19 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
1237
1248
|
refreshDrawingsRef.current();
|
|
1238
1249
|
}
|
|
1239
1250
|
}, []);
|
|
1251
|
+
const reorderFolders2 = useCallback(async (orderedIds) => {
|
|
1252
|
+
const positionMap = new Map(orderedIds.map((id, idx) => [id, idx]));
|
|
1253
|
+
setFolders((prev) => prev.map(
|
|
1254
|
+
(f) => positionMap.has(f.id) ? { ...f, position: positionMap.get(f.id) } : f
|
|
1255
|
+
));
|
|
1256
|
+
try {
|
|
1257
|
+
await reorderFolders(orderedIds);
|
|
1258
|
+
broadcastWorkspaceChange();
|
|
1259
|
+
} catch (err) {
|
|
1260
|
+
setError(err instanceof Error ? err.message : "Failed to reorder folders");
|
|
1261
|
+
refreshDrawingsRef.current();
|
|
1262
|
+
}
|
|
1263
|
+
}, []);
|
|
1240
1264
|
const value = {
|
|
1241
1265
|
workspace,
|
|
1242
1266
|
drawings,
|
|
@@ -1257,6 +1281,7 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
1257
1281
|
deleteFolder: deleteFolder2,
|
|
1258
1282
|
moveDrawingToFolder: moveDrawingToFolder2,
|
|
1259
1283
|
reorderDrawings: reorderDrawings2,
|
|
1284
|
+
reorderFolders: reorderFolders2,
|
|
1260
1285
|
saveCurrentDrawing,
|
|
1261
1286
|
saveDrawingById,
|
|
1262
1287
|
refreshDrawings,
|
|
@@ -1626,6 +1651,7 @@ var DrawingsDialog = ({
|
|
|
1626
1651
|
deleteFolder: deleteFolder2,
|
|
1627
1652
|
moveDrawingToFolder: moveDrawingToFolder2,
|
|
1628
1653
|
reorderDrawings: reorderDrawings2,
|
|
1654
|
+
reorderFolders: reorderFolders2,
|
|
1629
1655
|
exportWorkspace,
|
|
1630
1656
|
importWorkspace,
|
|
1631
1657
|
exportDrawingAsExcalidraw,
|
|
@@ -1643,7 +1669,17 @@ var DrawingsDialog = ({
|
|
|
1643
1669
|
const [switchingId, setSwitchingId] = useState4(null);
|
|
1644
1670
|
const [selectedId, setSelectedId] = useState4(null);
|
|
1645
1671
|
const [hoveredId, setHoveredId] = useState4(null);
|
|
1646
|
-
const [expandedFolders, setExpandedFolders] = useState4(
|
|
1672
|
+
const [expandedFolders, setExpandedFolders] = useState4(() => {
|
|
1673
|
+
try {
|
|
1674
|
+
if (localStorage.getItem("rita-workspace-remember-folder-state") === "false") {
|
|
1675
|
+
return /* @__PURE__ */ new Set();
|
|
1676
|
+
}
|
|
1677
|
+
const raw = localStorage.getItem("rita-workspace-expanded-folders");
|
|
1678
|
+
if (raw) return new Set(JSON.parse(raw));
|
|
1679
|
+
} catch {
|
|
1680
|
+
}
|
|
1681
|
+
return /* @__PURE__ */ new Set();
|
|
1682
|
+
});
|
|
1647
1683
|
const [creatingFolder, setCreatingFolder] = useState4(false);
|
|
1648
1684
|
const [newFolderName, setNewFolderName] = useState4("");
|
|
1649
1685
|
const [editingFolderId, setEditingFolderId] = useState4(null);
|
|
@@ -1673,10 +1709,35 @@ var DrawingsDialog = ({
|
|
|
1673
1709
|
return next;
|
|
1674
1710
|
});
|
|
1675
1711
|
}, []);
|
|
1712
|
+
const [rememberFolderState, setRememberFolderState] = useState4(() => {
|
|
1713
|
+
try {
|
|
1714
|
+
return localStorage.getItem("rita-workspace-remember-folder-state") !== "false";
|
|
1715
|
+
} catch {
|
|
1716
|
+
return true;
|
|
1717
|
+
}
|
|
1718
|
+
});
|
|
1719
|
+
const handleToggleRememberFolderState = useCallback2(() => {
|
|
1720
|
+
setRememberFolderState((prev) => {
|
|
1721
|
+
const next = !prev;
|
|
1722
|
+
try {
|
|
1723
|
+
if (next) {
|
|
1724
|
+
localStorage.removeItem("rita-workspace-remember-folder-state");
|
|
1725
|
+
} else {
|
|
1726
|
+
localStorage.setItem("rita-workspace-remember-folder-state", "false");
|
|
1727
|
+
localStorage.removeItem("rita-workspace-expanded-folders");
|
|
1728
|
+
}
|
|
1729
|
+
} catch {
|
|
1730
|
+
}
|
|
1731
|
+
return next;
|
|
1732
|
+
});
|
|
1733
|
+
}, []);
|
|
1676
1734
|
const [draggingDrawingId, setDraggingDrawingId] = useState4(null);
|
|
1677
1735
|
const [dropTargetFolderId, setDropTargetFolderId] = useState4(null);
|
|
1678
1736
|
const [dropTargetDrawingId, setDropTargetDrawingId] = useState4(null);
|
|
1679
1737
|
const [dropTargetPosition, setDropTargetPosition] = useState4(null);
|
|
1738
|
+
const [draggingFolderId, setDraggingFolderId] = useState4(null);
|
|
1739
|
+
const [folderDropTargetId, setFolderDropTargetId] = useState4(null);
|
|
1740
|
+
const [folderDropPosition, setFolderDropPosition] = useState4(null);
|
|
1680
1741
|
const [position, setPosition] = useState4(null);
|
|
1681
1742
|
const dragRef = useRef3(null);
|
|
1682
1743
|
const dialogRef = useRef3(null);
|
|
@@ -1697,6 +1758,18 @@ var DrawingsDialog = ({
|
|
|
1697
1758
|
}
|
|
1698
1759
|
prevOpenRef.current = open;
|
|
1699
1760
|
}, [open, refreshDrawings]);
|
|
1761
|
+
useEffect3(() => {
|
|
1762
|
+
if (!open) return;
|
|
1763
|
+
try {
|
|
1764
|
+
if (localStorage.getItem("rita-workspace-remember-folder-state") === "false") {
|
|
1765
|
+
setExpandedFolders(/* @__PURE__ */ new Set());
|
|
1766
|
+
return;
|
|
1767
|
+
}
|
|
1768
|
+
const raw = localStorage.getItem("rita-workspace-expanded-folders");
|
|
1769
|
+
setExpandedFolders(raw ? new Set(JSON.parse(raw)) : /* @__PURE__ */ new Set());
|
|
1770
|
+
} catch {
|
|
1771
|
+
}
|
|
1772
|
+
}, [open]);
|
|
1700
1773
|
useEffect3(() => {
|
|
1701
1774
|
if (creatingFolder && newFolderInputRef.current) {
|
|
1702
1775
|
newFolderInputRef.current.focus();
|
|
@@ -1819,6 +1892,24 @@ var DrawingsDialog = ({
|
|
|
1819
1892
|
].map((d) => d.id);
|
|
1820
1893
|
reorderDrawings2(ordered);
|
|
1821
1894
|
}, [drawings, reorderDrawings2, moveDrawingToFolder2]);
|
|
1895
|
+
const handleFolderReorderDrop = useCallback2((draggedFolderId, targetFolderId, pos) => {
|
|
1896
|
+
if (draggedFolderId === targetFolderId) return;
|
|
1897
|
+
const sorted = [...folders].sort(
|
|
1898
|
+
(a, b) => (a.position ?? a.createdAt) - (b.position ?? b.createdAt)
|
|
1899
|
+
);
|
|
1900
|
+
const withoutDragged = sorted.filter((f) => f.id !== draggedFolderId);
|
|
1901
|
+
const targetIdx = withoutDragged.findIndex((f) => f.id === targetFolderId);
|
|
1902
|
+
if (targetIdx === -1) return;
|
|
1903
|
+
const insertIdx = pos === "before" ? targetIdx : targetIdx + 1;
|
|
1904
|
+
const dragged = folders.find((f) => f.id === draggedFolderId);
|
|
1905
|
+
if (!dragged) return;
|
|
1906
|
+
const ordered = [
|
|
1907
|
+
...withoutDragged.slice(0, insertIdx),
|
|
1908
|
+
dragged,
|
|
1909
|
+
...withoutDragged.slice(insertIdx)
|
|
1910
|
+
].map((f) => f.id);
|
|
1911
|
+
reorderFolders2(ordered);
|
|
1912
|
+
}, [folders, reorderFolders2]);
|
|
1822
1913
|
const toggleFolder = useCallback2((folderId) => {
|
|
1823
1914
|
setExpandedFolders((prev) => {
|
|
1824
1915
|
const next = new Set(prev);
|
|
@@ -1830,6 +1921,16 @@ var DrawingsDialog = ({
|
|
|
1830
1921
|
return next;
|
|
1831
1922
|
});
|
|
1832
1923
|
}, []);
|
|
1924
|
+
useEffect3(() => {
|
|
1925
|
+
if (!rememberFolderState) return;
|
|
1926
|
+
try {
|
|
1927
|
+
localStorage.setItem(
|
|
1928
|
+
"rita-workspace-expanded-folders",
|
|
1929
|
+
JSON.stringify(Array.from(expandedFolders))
|
|
1930
|
+
);
|
|
1931
|
+
} catch {
|
|
1932
|
+
}
|
|
1933
|
+
}, [expandedFolders, rememberFolderState]);
|
|
1833
1934
|
const getLocale = () => {
|
|
1834
1935
|
if (!effectiveLang) return "en-US";
|
|
1835
1936
|
const baseLang = effectiveLang.split("-")[0].toLowerCase();
|
|
@@ -1868,7 +1969,10 @@ var DrawingsDialog = ({
|
|
|
1868
1969
|
for (const folder of folders) {
|
|
1869
1970
|
byFolder[folder.id] = sorted.filter((d) => d.folderId === folder.id);
|
|
1870
1971
|
}
|
|
1871
|
-
const
|
|
1972
|
+
const sortedFolders = [...folders].sort(
|
|
1973
|
+
(a, b) => (a.position ?? a.createdAt) - (b.position ?? b.createdAt)
|
|
1974
|
+
);
|
|
1975
|
+
const foldersFiltered = query ? sortedFolders.filter((f) => f.name.toLowerCase().includes(query) || (byFolder[f.id] || []).length > 0) : sortedFolders;
|
|
1872
1976
|
return { rootDrawings: root, drawingsByFolder: byFolder, filteredFolders: foldersFiltered };
|
|
1873
1977
|
}, [drawings, folders, searchQuery]);
|
|
1874
1978
|
if (!open) return null;
|
|
@@ -2185,10 +2289,11 @@ var DrawingsDialog = ({
|
|
|
2185
2289
|
const renderFolderGroup = (folder) => {
|
|
2186
2290
|
const folderDrawings = drawingsByFolder[folder.id] || [];
|
|
2187
2291
|
const isExpanded = expandedFolders.has(folder.id);
|
|
2292
|
+
const isFolderDropTarget = folderDropTargetId === folder.id;
|
|
2188
2293
|
return /* @__PURE__ */ jsxs4(
|
|
2189
2294
|
"div",
|
|
2190
2295
|
{
|
|
2191
|
-
style: { marginBottom: "4px" },
|
|
2296
|
+
style: { marginBottom: "4px", position: "relative" },
|
|
2192
2297
|
onDragOver: (e) => {
|
|
2193
2298
|
if (!draggingDrawingId) return;
|
|
2194
2299
|
e.preventDefault();
|
|
@@ -2213,6 +2318,37 @@ var DrawingsDialog = ({
|
|
|
2213
2318
|
/* @__PURE__ */ jsxs4(
|
|
2214
2319
|
"div",
|
|
2215
2320
|
{
|
|
2321
|
+
draggable: !editingFolderId && !confirmDeleteFolderId,
|
|
2322
|
+
onDragStart: (e) => {
|
|
2323
|
+
if (draggingDrawingId) return;
|
|
2324
|
+
setDraggingFolderId(folder.id);
|
|
2325
|
+
e.dataTransfer.effectAllowed = "move";
|
|
2326
|
+
},
|
|
2327
|
+
onDragEnd: () => {
|
|
2328
|
+
setDraggingFolderId(null);
|
|
2329
|
+
setFolderDropTargetId(null);
|
|
2330
|
+
setFolderDropPosition(null);
|
|
2331
|
+
},
|
|
2332
|
+
onDragOver: (e) => {
|
|
2333
|
+
if (!draggingFolderId || draggingFolderId === folder.id) return;
|
|
2334
|
+
e.preventDefault();
|
|
2335
|
+
e.stopPropagation();
|
|
2336
|
+
e.dataTransfer.dropEffect = "move";
|
|
2337
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
2338
|
+
const isAbove = e.clientY < rect.top + rect.height / 2;
|
|
2339
|
+
setFolderDropTargetId(folder.id);
|
|
2340
|
+
setFolderDropPosition(isAbove ? "before" : "after");
|
|
2341
|
+
},
|
|
2342
|
+
onDrop: (e) => {
|
|
2343
|
+
if (draggingFolderId && draggingFolderId !== folder.id && folderDropPosition) {
|
|
2344
|
+
e.preventDefault();
|
|
2345
|
+
e.stopPropagation();
|
|
2346
|
+
handleFolderReorderDrop(draggingFolderId, folder.id, folderDropPosition);
|
|
2347
|
+
setDraggingFolderId(null);
|
|
2348
|
+
setFolderDropTargetId(null);
|
|
2349
|
+
setFolderDropPosition(null);
|
|
2350
|
+
}
|
|
2351
|
+
},
|
|
2216
2352
|
onClick: () => {
|
|
2217
2353
|
toggleFolder(folder.id);
|
|
2218
2354
|
setSelectedId(null);
|
|
@@ -2223,9 +2359,11 @@ var DrawingsDialog = ({
|
|
|
2223
2359
|
alignItems: "center",
|
|
2224
2360
|
gap: "8px",
|
|
2225
2361
|
borderRadius: "8px",
|
|
2226
|
-
cursor: "pointer",
|
|
2362
|
+
cursor: draggingFolderId === folder.id ? "grabbing" : "pointer",
|
|
2363
|
+
opacity: draggingFolderId === folder.id ? 0.5 : 1,
|
|
2227
2364
|
backgroundColor: dropTargetFolderId === folder.id ? "var(--color-primary-light, rgba(108, 99, 255, 0.2))" : "var(--color-surface-mid, rgba(0, 0, 0, 0.03))",
|
|
2228
2365
|
border: dropTargetFolderId === folder.id ? "2px dashed var(--color-primary, #6c63ff)" : "2px solid transparent",
|
|
2366
|
+
boxShadow: isFolderDropTarget && folderDropPosition === "before" ? "inset 0 3px 0 0 var(--color-primary, #6c63ff)" : isFolderDropTarget && folderDropPosition === "after" ? "inset 0 -3px 0 0 var(--color-primary, #6c63ff)" : "none",
|
|
2229
2367
|
transition: "background-color 0.15s, border-color 0.15s"
|
|
2230
2368
|
},
|
|
2231
2369
|
children: [
|
|
@@ -2602,36 +2740,68 @@ var DrawingsDialog = ({
|
|
|
2602
2740
|
}
|
|
2603
2741
|
)
|
|
2604
2742
|
] }),
|
|
2605
|
-
/* @__PURE__ */
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
/* @__PURE__ */
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2743
|
+
/* @__PURE__ */ jsxs4("div", { style: { padding: "0 20px 16px", display: "flex", flexDirection: "column", gap: "8px" }, children: [
|
|
2744
|
+
/* @__PURE__ */ jsxs4(
|
|
2745
|
+
"label",
|
|
2746
|
+
{
|
|
2747
|
+
style: {
|
|
2748
|
+
display: "flex",
|
|
2749
|
+
alignItems: "center",
|
|
2750
|
+
gap: "10px",
|
|
2751
|
+
padding: "10px 12px",
|
|
2752
|
+
border: "1px solid var(--default-border-color, #e0e0e0)",
|
|
2753
|
+
borderRadius: "8px",
|
|
2754
|
+
cursor: "pointer",
|
|
2755
|
+
userSelect: "none"
|
|
2756
|
+
},
|
|
2757
|
+
children: [
|
|
2758
|
+
/* @__PURE__ */ jsx6(
|
|
2759
|
+
"input",
|
|
2760
|
+
{
|
|
2761
|
+
type: "checkbox",
|
|
2762
|
+
checked: autoStart,
|
|
2763
|
+
onChange: handleToggleAutoStart,
|
|
2764
|
+
style: { width: "16px", height: "16px", cursor: "pointer", flexShrink: 0 }
|
|
2765
|
+
}
|
|
2766
|
+
),
|
|
2767
|
+
/* @__PURE__ */ jsxs4("div", { style: { flex: 1, minWidth: 0 }, children: [
|
|
2768
|
+
/* @__PURE__ */ jsx6("div", { style: { fontSize: "14px", fontWeight: 500 }, children: t.autoStartLabel }),
|
|
2769
|
+
/* @__PURE__ */ jsx6("div", { style: { fontSize: "12px", color: "var(--text-secondary-color, #888)", marginTop: "2px" }, children: t.autoStartDesc })
|
|
2770
|
+
] })
|
|
2771
|
+
]
|
|
2772
|
+
}
|
|
2773
|
+
),
|
|
2774
|
+
/* @__PURE__ */ jsxs4(
|
|
2775
|
+
"label",
|
|
2776
|
+
{
|
|
2777
|
+
style: {
|
|
2778
|
+
display: "flex",
|
|
2779
|
+
alignItems: "center",
|
|
2780
|
+
gap: "10px",
|
|
2781
|
+
padding: "10px 12px",
|
|
2782
|
+
border: "1px solid var(--default-border-color, #e0e0e0)",
|
|
2783
|
+
borderRadius: "8px",
|
|
2784
|
+
cursor: "pointer",
|
|
2785
|
+
userSelect: "none"
|
|
2786
|
+
},
|
|
2787
|
+
children: [
|
|
2788
|
+
/* @__PURE__ */ jsx6(
|
|
2789
|
+
"input",
|
|
2790
|
+
{
|
|
2791
|
+
type: "checkbox",
|
|
2792
|
+
checked: rememberFolderState,
|
|
2793
|
+
onChange: handleToggleRememberFolderState,
|
|
2794
|
+
style: { width: "16px", height: "16px", cursor: "pointer", flexShrink: 0 }
|
|
2795
|
+
}
|
|
2796
|
+
),
|
|
2797
|
+
/* @__PURE__ */ jsxs4("div", { style: { flex: 1, minWidth: 0 }, children: [
|
|
2798
|
+
/* @__PURE__ */ jsx6("div", { style: { fontSize: "14px", fontWeight: 500 }, children: "Kom ih\xE5g utf\xE4llda mappar" }),
|
|
2799
|
+
/* @__PURE__ */ jsx6("div", { style: { fontSize: "12px", color: "var(--text-secondary-color, #888)", marginTop: "2px" }, children: "Beh\xE5ller vilka mappar som \xE4r utf\xE4llda mellan sessioner. Om av: alla mappar startar minimerade." })
|
|
2800
|
+
] })
|
|
2801
|
+
]
|
|
2802
|
+
}
|
|
2803
|
+
)
|
|
2804
|
+
] })
|
|
2635
2805
|
]
|
|
2636
2806
|
}
|
|
2637
2807
|
)
|
|
@@ -2968,6 +3138,7 @@ export {
|
|
|
2968
3138
|
removeDrawingFromWorkspace,
|
|
2969
3139
|
renameFolder,
|
|
2970
3140
|
reorderDrawings,
|
|
3141
|
+
reorderFolders,
|
|
2971
3142
|
setActiveDrawing,
|
|
2972
3143
|
updateDrawing,
|
|
2973
3144
|
updateWorkspace,
|