From db1249889090ba6283dd92783776bf500f36d9e4 Mon Sep 17 00:00:00 2001 From: YurenHao0426 Date: Sat, 14 Feb 2026 05:07:03 +0000 Subject: Fix file drag-drop: allow dragging items out of folders and to empty area - Drop on file moves item to file's parent directory - Drop on empty area moves item to project root - Container gets flex-1 height so empty droppable space exists - Mark internal drags with dataTransfer to distinguish from OS file uploads Co-Authored-By: Claude Opus 4.6 --- frontend/src/components/LeftSidebar.tsx | 38 +++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/frontend/src/components/LeftSidebar.tsx b/frontend/src/components/LeftSidebar.tsx index 441b7e0..d802fde 100644 --- a/frontend/src/components/LeftSidebar.tsx +++ b/frontend/src/components/LeftSidebar.tsx @@ -399,25 +399,31 @@ const LeftSidebar: React.FC = ({ isOpen, onToggle }) => { } }; - // Drag move blueprint into folder + // Drag move blueprint into/out of folder const onItemDragStart = (e: React.DragEvent, item: FSItem) => { setDragItem(item); e.dataTransfer.effectAllowed = 'move'; + e.dataTransfer.setData('text/plain', item.path); // mark as internal drag }; const onItemDragOver = (e: React.DragEvent, item: FSItem) => { - if (item.type === 'folder') { - e.preventDefault(); - e.dataTransfer.dropEffect = 'move'; - } + if (!dragItem || dragItem.path === item.path) return; + e.preventDefault(); + e.dataTransfer.dropEffect = 'move'; }; const onItemDrop = async (e: React.DragEvent, target: FSItem) => { e.preventDefault(); - if (!dragItem || target.type !== 'folder') return; - const newPath = joinPath(target.path, dragItem.name); + e.stopPropagation(); + if (!dragItem) return; + // Drop on folder → move into it; drop on file → move to file's parent + const destFolder = target.type === 'folder' + ? target.path + : target.path.split('/').slice(0, -1).join('/') || '.'; + const newPath = joinPath(destFolder, dragItem.name); if (newPath === dragItem.path) return; await renameProjectItem(dragItem.path, undefined, newPath); setDragItem(null); }; + const onItemDragEnd = () => { setDragItem(null); }; const toggleFolder = (path: string) => { setExpanded(prev => { @@ -462,6 +468,7 @@ const LeftSidebar: React.FC = ({ isOpen, onToggle }) => { onDragStart={(e) => onItemDragStart(e, item)} onDragOver={(e) => onItemDragOver(e, item)} onDrop={(e) => onItemDrop(e, item)} + onDragEnd={onItemDragEnd} >
{item.type === 'folder' ? ( @@ -869,8 +876,8 @@ const LeftSidebar: React.FC = ({ isOpen, onToggle }) => { {/* Content Area */}
{activeTab === 'project' && ( -
= ({ isOpen, onToggle }) => { />
{ if (activeTab === 'project') { openContextMenu(e); } }} - onDragOver={(e) => { - if (e.dataTransfer.types.includes('Files')) { e.preventDefault(); } + onDragOver={(e) => { + if (e.dataTransfer.types.includes('Files') || e.dataTransfer.types.includes('text/plain')) { e.preventDefault(); } }} onDrop={async (e) => { if (e.dataTransfer.files?.length) { e.preventDefault(); await handleUploadFiles(e.dataTransfer.files, currentFolder); + } else if (dragItem) { + // Internal drag: move item to root + e.preventDefault(); + const newPath = joinPath('.', dragItem.name); + if (newPath !== dragItem.path) { + await renameProjectItem(dragItem.path, undefined, newPath); + } + setDragItem(null); } }} > -- cgit v1.2.3