fix: drag&drop events on folder headers with stopPropagation, separate root drop zone

This commit is contained in:
Agent Zero
2026-07-17 17:39:09 +02:00
parent ede37aced8
commit 5814b1691c
+46 -8
View File
@@ -50,18 +50,39 @@ function TreeItem({
const [expanded, setExpanded] = useState(true); const [expanded, setExpanded] = useState(true);
const [isDragOver, setIsDragOver] = useState(false); const [isDragOver, setIsDragOver] = useState(false);
const isRoot = node.folder === null; const isRoot = node.folder === null;
const folderId = node.folder?.id || null;
// Drop handler for this specific folder - stopPropagation so parent doesn't also fire
const handleDrop = (e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
setIsDragOver(false);
onDropToFolder(folderId);
};
const handleDragOver = (e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
setIsDragOver(true);
};
const handleDragLeave = (e: React.DragEvent) => {
e.stopPropagation();
// Only clear if leaving to a non-child element
setIsDragOver(false);
};
return ( return (
<div <div>
onDragOver={(e) => { e.preventDefault(); setIsDragOver(true); }} {/* Folder header - this is the drop target */}
onDragLeave={() => setIsDragOver(false)}
onDrop={(e) => { e.preventDefault(); setIsDragOver(false); onDropToFolder(node.folder?.id || null); }}
>
{!isRoot && ( {!isRoot && (
<div <div
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
className={clsx( className={clsx(
'group flex items-center gap-1 px-2 py-1.5 text-sm font-medium rounded-md cursor-pointer', 'group flex items-center gap-1 px-2 py-1.5 text-sm font-medium rounded-md cursor-pointer',
isDragOver ? 'bg-primary-100 ring-1 ring-primary-300' : 'text-secondary-700 hover:bg-secondary-100' isDragOver ? 'bg-primary-100 ring-2 ring-primary-400' : 'text-secondary-700 hover:bg-secondary-100'
)} )}
style={{ paddingLeft: depth * 12 + 8 }} style={{ paddingLeft: depth * 12 + 8 }}
onClick={() => setExpanded(!expanded)} onClick={() => setExpanded(!expanded)}
@@ -77,17 +98,19 @@ function TreeItem({
<button onClick={(e) => { e.stopPropagation(); onDeleteFolder(node.folder!.id); }} className="opacity-0 group-hover:opacity-100 text-secondary-400 hover:text-red-600" title="Löschen"></button> <button onClick={(e) => { e.stopPropagation(); onDeleteFolder(node.folder!.id); }} className="opacity-0 group-hover:opacity-100 text-secondary-400 hover:text-red-600" title="Löschen"></button>
</div> </div>
)} )}
{/* Sessions in this folder */}
{(isRoot || expanded) && ( {(isRoot || expanded) && (
<div> <div>
{node.sessions.map((session) => ( {node.sessions.map((session) => (
<div <div
key={session.id} key={session.id}
draggable draggable
onDragStart={() => onDragSession(session.id)} onDragStart={(e) => { e.stopPropagation(); onDragSession(session.id); }}
onClick={() => onSelectSession(session.id)} onClick={() => onSelectSession(session.id)}
className={clsx( className={clsx(
'group flex items-center gap-2 px-3 py-1.5 text-sm cursor-pointer rounded-md', 'group flex items-center gap-2 px-3 py-1.5 text-sm cursor-pointer rounded-md',
'hover:bg-secondary-100 active:cursor-grabbing', 'hover:bg-secondary-100',
activeSessionId === session.id && 'bg-primary-100 text-primary-700' activeSessionId === session.id && 'bg-primary-100 text-primary-700'
)} )}
style={{ paddingLeft: depth * 12 + 24 }} style={{ paddingLeft: depth * 12 + 24 }}
@@ -101,6 +124,21 @@ function TreeItem({
))} ))}
</div> </div>
)} )}
{/* Root drop zone - only for root level, separate from sessions */}
{isRoot && (
<div
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
className={clsx(
'min-h-[8px] rounded-md transition-colors',
isDragOver && 'bg-primary-50 ring-1 ring-primary-300'
)}
/>
)}
{/* Child folders */}
{(isRoot || expanded) && node.children.map((child) => ( {(isRoot || expanded) && node.children.map((child) => (
<TreeItem <TreeItem
key={child.folder!.id} key={child.folder!.id}