From d3dd4c8cc6134c92d27c56af15fe01dbf2ce28c2 Mon Sep 17 00:00:00 2001 From: Sandro Date: Tue, 27 Oct 2026 07:16:20 +0100 Subject: [PATCH] fix(workspace): refine folder row drop target, depth-first tree sorting & auto-expansion --- .../src/features/workspace/WorkspaceView.tsx | 60 ++++++++++++++----- 1 file changed, 45 insertions(+), 15 deletions(-) diff --git a/frontend/src/features/workspace/WorkspaceView.tsx b/frontend/src/features/workspace/WorkspaceView.tsx index c6c88d4..73b183a 100644 --- a/frontend/src/features/workspace/WorkspaceView.tsx +++ b/frontend/src/features/workspace/WorkspaceView.tsx @@ -89,22 +89,20 @@ const FolderTreeItem: React.FC = ({ const hasContent = node.subfolders.length > 0 || node.docs.length > 0; return ( -
{ - e.preventDefault(); - e.stopPropagation(); - setDragOverFolder(node.fullPath); - }} - onDragLeave={(e) => { - e.preventDefault(); - if (dragOverFolder === node.fullPath) setDragOverFolder(null); - }} - onDrop={(e) => handleDropOnFolder(node.fullPath, e)} - className="space-y-0.5" - > +
handleFolderDragStart(e, node.fullPath)} + onDragOver={(e) => { + e.preventDefault(); + e.stopPropagation(); + setDragOverFolder(node.fullPath); + }} + onDragLeave={(e) => { + e.preventDefault(); + if (dragOverFolder === node.fullPath) setDragOverFolder(null); + }} + onDrop={(e) => handleDropOnFolder(node.fullPath, e)} style={{ paddingLeft: `${depth * 12 + 8}px` }} className={`group flex items-center justify-between pr-2 py-1 rounded-btn text-xs cursor-pointer transition-colors ${ isDragOver @@ -464,7 +462,14 @@ export const WorkspaceView: React.FC = ({ } const res = await api.createFolder(clean); setFolders((prev) => (prev.includes(res.path) ? prev : [...prev, res.path].sort())); - setExpandedFolders((prev) => ({ ...prev, [res.path]: true })); + const parts = res.path.split('/'); + let acc = ''; + const expansions: Record = {}; + for (const part of parts) { + acc = acc ? `${acc}/${part}` : part; + expansions[acc] = true; + } + setExpandedFolders((prev) => ({ ...prev, ...expansions })); setNewFolderName(''); setNewFolderIsShared(false); setShowNewFolderModal(false); @@ -526,6 +531,16 @@ export const WorkspaceView: React.FC = ({ setDocs((prev) => prev.map((d) => (d.id === docId ? { ...d, folderPath: updatedDoc.folderPath } : d)) ); + if (targetFolder) { + const parts = targetFolder.split('/'); + let acc = ''; + const expansions: Record = {}; + for (const part of parts) { + acc = acc ? `${acc}/${part}` : part; + expansions[acc] = true; + } + setExpandedFolders((prev) => ({ ...prev, ...expansions })); + } } catch { // ignore } @@ -533,6 +548,16 @@ export const WorkspaceView: React.FC = ({ triggerHaptic(15); try { await api.moveFolder(folderPath, targetFolder); + if (targetFolder) { + const parts = targetFolder.split('/'); + let acc = ''; + const expansions: Record = {}; + for (const part of parts) { + acc = acc ? `${acc}/${part}` : part; + expansions[acc] = true; + } + setExpandedFolders((prev) => ({ ...prev, ...expansions })); + } loadWorkspaceData(); } catch { // ignore @@ -672,7 +697,12 @@ export const WorkspaceView: React.FC = ({ } } - const sortedPaths = Array.from(allPathsSet).sort((a, b) => a.localeCompare(b)); + const sortedPaths = Array.from(allPathsSet).sort((a, b) => { + const depthA = a.split('/').length; + const depthB = b.split('/').length; + if (depthA !== depthB) return depthA - depthB; + return a.localeCompare(b); + }); for (const p of sortedPaths) { const parts = p.split('/');