import React from 'react'; import { DndContext, DragOverlay, PointerSensor, closestCenter, useSensor, useSensors, useDraggable, useDroppable, type DragEndEvent, } from '@dnd-kit/core'; import { Icon } from "@/components/icon/Icon"; export const DraggableSessionRow: React.FC<{ sessionId: string; sessionDirectory: string | null; sessionTitle: string; children: React.ReactNode; }> = ({ sessionId, sessionDirectory, sessionTitle, children }) => { const { attributes, listeners, setNodeRef, isDragging } = useDraggable({ id: `session-drag:${sessionId}`, data: { type: 'session', sessionId, sessionDirectory, sessionTitle }, }); const handlePointerDown = React.useCallback( (e: React.PointerEvent) => { e.stopPropagation(); if (listeners?.onPointerDown) { (listeners.onPointerDown as (event: React.PointerEvent) => void)(e); } }, [listeners], ); return (
{children}
); }; export const DroppableFolderWrapper: React.FC<{ folderId: string; children: ( droppableRef: (node: HTMLElement | null) => void, isOver: boolean, ) => React.ReactNode; }> = ({ folderId, children }) => { const { setNodeRef, isOver } = useDroppable({ id: `folder-drop:${folderId}`, data: { type: 'folder', folderId }, }); return <>{children(setNodeRef, isOver)}; }; export const SessionFolderDndScope: React.FC<{ scopeKey: string | null; hasFolders: boolean; onSessionDroppedOnFolder: (sessionId: string, folderId: string) => void; children: React.ReactNode; }> = ({ scopeKey, hasFolders, onSessionDroppedOnFolder, children }) => { const sensors = useSensors( useSensor(PointerSensor, { activationConstraint: { distance: 8 } }), ); const [activeDragId, setActiveDragId] = React.useState(null); const [activeDragTitle, setActiveDragTitle] = React.useState('Session'); const [activeDragWidth, setActiveDragWidth] = React.useState(null); const [activeDragHeight, setActiveDragHeight] = React.useState(null); if (!scopeKey) { return <>{children}; } const handleDragEnd = (event: DragEndEvent) => { setActiveDragId(null); setActiveDragWidth(null); setActiveDragHeight(null); const { active, over } = event; if (!over) return; const activeData = active.data.current as { type?: string; sessionId?: string } | undefined; const overData = over.data.current as { type?: string; folderId?: string } | undefined; if (activeData?.type === 'session' && activeData.sessionId && overData?.type === 'folder' && overData.folderId) { onSessionDroppedOnFolder(activeData.sessionId, overData.folderId); } }; return ( { const data = event.active.data.current as { type?: string; sessionId?: string; sessionTitle?: string } | undefined; if (data?.type === 'session' && data.sessionId) { setActiveDragId(data.sessionId); setActiveDragTitle(data.sessionTitle ?? 'Session'); const width = event.active.rect.current.initial?.width; const height = event.active.rect.current.initial?.height; setActiveDragWidth(typeof width === 'number' ? width : null); setActiveDragHeight(typeof height === 'number' ? height : null); } }} onDragCancel={() => { setActiveDragId(null); setActiveDragWidth(null); setActiveDragHeight(null); }} onDragEnd={handleDragEnd} > {children} {activeDragId && hasFolders ? (
{activeDragTitle}
) : null}
); };