Fix FileTree drag and drop

This commit is contained in:
2025-11-04 18:19:54 +01:00
parent 76ab168c6e
commit 2045d36211

View File

@@ -6,7 +6,7 @@ import {
IconFolderOpen, IconFolderOpen,
IconUpload, IconUpload,
} from '@tabler/icons-react'; } from '@tabler/icons-react';
import { Tooltip, Text, Box } from '@mantine/core'; import { Text, Box } from '@mantine/core';
import useResizeObserver from '@react-hook/resize-observer'; import useResizeObserver from '@react-hook/resize-observer';
import { useFileOperations } from '../../hooks/useFileOperations'; import { useFileOperations } from '../../hooks/useFileOperations';
import type { FileNode } from '@/types/models'; import type { FileNode } from '@/types/models';
@@ -53,13 +53,12 @@ function Node({
style, style,
dragHandle, dragHandle,
onNodeClick, onNodeClick,
...rest
}: { }: {
node: NodeApi<FileNode>; node: NodeApi<FileNode>;
style: React.CSSProperties; style: React.CSSProperties;
dragHandle?: React.Ref<HTMLDivElement>; dragHandle?: React.Ref<HTMLDivElement>;
onNodeClick?: (node: NodeApi<FileNode>) => void; onNodeClick?: (node: NodeApi<FileNode>) => void;
} & Record<string, unknown>) { }) {
const handleClick = () => { const handleClick = () => {
if (node.isInternal) { if (node.isInternal) {
node.toggle(); node.toggle();
@@ -69,7 +68,6 @@ function Node({
}; };
return ( return (
<Tooltip label={node.data.name} openDelay={500}>
<div <div
ref={dragHandle} // This enables dragging for the node ref={dragHandle} // This enables dragging for the node
style={{ style={{
@@ -82,9 +80,14 @@ function Node({
overflow: 'hidden', overflow: 'hidden',
// Add visual feedback when being dragged // Add visual feedback when being dragged
opacity: node.state?.isDragging ? 0.5 : 1, opacity: node.state?.isDragging ? 0.5 : 1,
// Highlight when this node will receive the drop
backgroundColor: node.state?.willReceiveDrop
? 'rgba(0, 123, 255, 0.2)'
: 'transparent',
borderRadius: '4px',
}} }}
onClick={handleClick} onClick={handleClick}
{...rest} title={node.data.name}
> >
<FileIcon node={node} /> <FileIcon node={node} />
<span <span
@@ -99,7 +102,6 @@ function Node({
{node.data.name} {node.data.name}
</span> </span>
</div> </div>
</Tooltip>
); );
} }
@@ -205,16 +207,17 @@ export const FileTree: React.FC<FileTreeProps> = ({
// External file drag and drop handlers // External file drag and drop handlers
const handleDragEnter = useCallback((e: React.DragEvent) => { const handleDragEnter = useCallback((e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
// Check if drag contains files (not internal tree nodes) // Check if drag contains files (not internal tree nodes)
if (e.dataTransfer.types.includes('Files')) { if (e.dataTransfer.types.includes('Files')) {
e.preventDefault();
e.stopPropagation();
setIsDragOver(true); setIsDragOver(true);
} }
}, []); }, []);
const handleDragLeave = useCallback((e: React.DragEvent) => { const handleDragLeave = useCallback((e: React.DragEvent) => {
// Only handle if it's an external file drag
if (e.dataTransfer.types.includes('Files')) {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
@@ -222,24 +225,28 @@ export const FileTree: React.FC<FileTreeProps> = ({
if (e.currentTarget === e.target) { if (e.currentTarget === e.target) {
setIsDragOver(false); setIsDragOver(false);
} }
}
}, []); }, []);
const handleDragOver = useCallback((e: React.DragEvent) => { const handleDragOver = useCallback((e: React.DragEvent) => {
// Only handle external file drags
if (e.dataTransfer.types.includes('Files')) {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
// Set the drop effect to indicate this is a valid drop target // Set the drop effect to indicate this is a valid drop target
e.dataTransfer.dropEffect = 'copy'; e.dataTransfer.dropEffect = 'copy';
}
}, []); }, []);
const handleDrop = useCallback( const handleDrop = useCallback(
(e: React.DragEvent) => { (e: React.DragEvent) => {
const { files } = e.dataTransfer;
// Only handle if it's an external file drop
if (files && files.length > 0) {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
setIsDragOver(false); setIsDragOver(false);
const { files } = e.dataTransfer;
if (files && files.length > 0) {
const uploadFiles = async () => { const uploadFiles = async () => {
try { try {
const success = await handleUpload(files); const success = await handleUpload(files);
@@ -305,7 +312,10 @@ export const FileTree: React.FC<FileTreeProps> = ({
height={size.height} height={size.height}
indent={24} indent={24}
rowHeight={28} rowHeight={28}
idAccessor="id"
onMove={handleTreeMove} onMove={handleTreeMove}
disableDrag={() => false}
disableDrop={() => false}
onActivate={(node) => { onActivate={(node) => {
const fileNode = node.data; const fileNode = node.data;
if (!node.isInternal) { if (!node.isInternal) {