fix(refboard): drag-drop opening in new tab — use ref for drop target

The drag/drop setup was searching for a <canvas> element to find its
parent, which was fragile and could fail (returning null). When it
failed, document-level dragover/drop prevention was never registered,
so the browser's default behavior (open file in new tab) kicked in.

Fix: pass a direct ref to the outer canvas container div from Editor
to useCanvasSetup. No more DOM querying for the drop target.
This commit is contained in:
Hiren Kangad
2026-03-10 11:20:26 +05:30
parent 9716beee8a
commit 205ce65a8b
2 changed files with 9 additions and 17 deletions
+6 -15
View File
@@ -40,6 +40,7 @@ interface CanvasSetupDeps {
undoRef: React.MutableRefObject<UndoManager | null>; undoRef: React.MutableRefObject<UndoManager | null>;
syncRef: React.MutableRefObject<SyncHandle | null>; syncRef: React.MutableRefObject<SyncHandle | null>;
inboxZoneRef: React.MutableRefObject<InboxZone | null>; inboxZoneRef: React.MutableRefObject<InboxZone | null>;
canvasContainerRef: React.RefObject<HTMLDivElement | null>;
onCanvasChange: (changedIds?: string[]) => void; onCanvasChange: (changedIds?: string[]) => void;
showToast: (msg: string) => void; showToast: (msg: string) => void;
setOnlineUsers: React.Dispatch<React.SetStateAction<OnlineUser[]>>; setOnlineUsers: React.Dispatch<React.SetStateAction<OnlineUser[]>>;
@@ -53,7 +54,7 @@ interface CanvasSetupDeps {
export function useCanvasSetup(deps: CanvasSetupDeps) { export function useCanvasSetup(deps: CanvasSetupDeps) {
const { const {
boardData, resolvedBoardId, user, isPublicView, boardData, resolvedBoardId, user, isPublicView,
canvasRef, selectionRef, undoRef, syncRef, inboxZoneRef, canvasRef, selectionRef, undoRef, syncRef, inboxZoneRef, canvasContainerRef,
onCanvasChange, showToast, setOnlineUsers, setSelectedLayerIds, onCanvasChange, showToast, setOnlineUsers, setSelectedLayerIds,
} = deps; } = deps;
@@ -252,21 +253,11 @@ export function useCanvasSetup(deps: CanvasSetupDeps) {
// Setup drag/drop and paste // Setup drag/drop and paste
if (!isPublicView || user) { if (!isPublicView || user) {
const canvasElements = document.querySelectorAll('canvas'); const dropTarget = canvasContainerRef.current;
let domContainer: HTMLElement | null = null; if (dropTarget) {
for (const c of canvasElements) { dropCleanupRef.current = setupDragDrop(dropTarget, viewport, scene, resolvedBoardId, onCanvasChange);
if (c.parentElement && c.width > 100) {
domContainer = c.parentElement;
break;
}
}
if (domContainer) {
dropCleanupRef.current = setupDragDrop(domContainer, viewport, scene, resolvedBoardId, onCanvasChange);
pasteCleanupRef.current = setupPaste(viewport, scene, resolvedBoardId, onCanvasChange);
} else {
pasteCleanupRef.current = setupPaste(viewport, scene, resolvedBoardId, onCanvasChange);
} }
pasteCleanupRef.current = setupPaste(viewport, scene, resolvedBoardId, onCanvasChange);
} }
}, 200); }, 200);
+3 -2
View File
@@ -59,6 +59,7 @@ export default function Editor({ isPublicView }: EditorProps) {
const toolCleanupRef = useRef<(() => void) | null>(null); const toolCleanupRef = useRef<(() => void) | null>(null);
const inboxZoneRef = useRef<InboxZone | null>(null); const inboxZoneRef = useRef<InboxZone | null>(null);
const clipboardRef = useRef<SceneItem[]>([]); const clipboardRef = useRef<SceneItem[]>([]);
const canvasContainerRef = useRef<HTMLDivElement>(null);
// UI state // UI state
const [activeTool, setActiveTool] = useState<ToolType>(ToolType.SELECT); const [activeTool, setActiveTool] = useState<ToolType>(ToolType.SELECT);
@@ -136,7 +137,7 @@ export default function Editor({ isPublicView }: EditorProps) {
// Canvas setup (selection, undo, sync, socket, drag/drop, paste, inbox) // Canvas setup (selection, undo, sync, socket, drag/drop, paste, inbox)
useCanvasSetup({ useCanvasSetup({
boardData, resolvedBoardId, user, isPublicView, boardData, resolvedBoardId, user, isPublicView,
canvasRef, selectionRef, undoRef, syncRef, inboxZoneRef, canvasRef, selectionRef, undoRef, syncRef, inboxZoneRef, canvasContainerRef,
onCanvasChange, showToast, setOnlineUsers, setSelectedLayerIds, onCanvasChange, showToast, setOnlineUsers, setSelectedLayerIds,
}); });
@@ -473,7 +474,7 @@ export default function Editor({ isPublicView }: EditorProps) {
/>} />}
{/* Canvas */} {/* Canvas */}
<div style={{ flex: 1, position: 'relative', overflow: 'hidden', background: '#1e1e1e' }} onContextMenu={handleContextMenu}> <div ref={canvasContainerRef} style={{ flex: 1, position: 'relative', overflow: 'hidden', background: '#1e1e1e' }} onContextMenu={handleContextMenu}>
{/* Dot grid — behind transparent canvas */} {/* Dot grid — behind transparent canvas */}
{showGrid && (() => { {showGrid && (() => {
const scale = canvasTransform[0] || 1; const scale = canvasTransform[0] || 1;