diff --git a/ui/src/components/ProcessDiagram/ProcessDiagram.tsx b/ui/src/components/ProcessDiagram/ProcessDiagram.tsx index 382c22fb..08cd07e1 100644 --- a/ui/src/components/ProcessDiagram/ProcessDiagram.tsx +++ b/ui/src/components/ProcessDiagram/ProcessDiagram.tsx @@ -152,8 +152,12 @@ export function ProcessDiagram({ ); const handleNodeClick = useCallback( - (nodeId: string) => { onNodeSelect?.(nodeId); }, - [onNodeSelect], + (nodeId: string) => { + // Suppress click if the pointer gesture was a drag (pan) + if (zoom.didPan.current) return; + onNodeSelect?.(nodeId); + }, + [onNodeSelect, zoom.didPan], ); const handleNodeDoubleClick = useCallback( diff --git a/ui/src/components/ProcessDiagram/useZoomPan.ts b/ui/src/components/ProcessDiagram/useZoomPan.ts index dcdb9dc5..555e0436 100644 --- a/ui/src/components/ProcessDiagram/useZoomPan.ts +++ b/ui/src/components/ProcessDiagram/useZoomPan.ts @@ -18,6 +18,7 @@ export function useZoomPan() { translateY: 0, }); const isPanning = useRef(false); + const didPan = useRef(false); const panStart = useRef({ x: 0, y: 0 }); const containerRef = useRef(null); const svgRef = useRef(null); @@ -57,8 +58,11 @@ export function useZoomPan() { const onPointerDown = useCallback( (e: React.PointerEvent) => { - if ((e.target as Element).closest('[data-node-id]')) return; + // Always allow drag-to-pan, even over nodes. Click vs drag is + // distinguished by didPan: if the pointer moved, it's a pan and + // node click handlers should be suppressed. isPanning.current = true; + didPan.current = false; panStart.current = { x: e.clientX - state.translateX, y: e.clientY - state.translateY }; (e.currentTarget as SVGSVGElement).setPointerCapture(e.pointerId); }, @@ -68,6 +72,7 @@ export function useZoomPan() { const onPointerMove = useCallback( (e: React.PointerEvent) => { if (!isPanning.current) return; + didPan.current = true; setState(prev => ({ ...prev, translateX: e.clientX - panStart.current.x, @@ -166,6 +171,8 @@ export function useZoomPan() { containerRef, svgRef, transform, + /** True if the last pointer gesture was a drag (not a click). Check in click handlers. */ + didPan, panTo, resetView, onPointerDown,