fix: allow drag-to-pan over diagram nodes and compounds
All checks were successful
CI / cleanup-branch (push) Has been skipped
CI / build (push) Successful in 58s
CI / docker (push) Successful in 55s
CI / deploy-feature (push) Has been skipped
CI / deploy (push) Successful in 37s

Previously onPointerDown bailed out when the target was inside a node
(data-node-id), blocking pan entirely over nodes and compound groups.
Now panning always starts, and a didPan ref distinguishes drag from
click — node click handlers skip selection when the user was dragging.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-03-27 23:21:05 +01:00
parent 41111b082c
commit 004574d442
2 changed files with 14 additions and 3 deletions

View File

@@ -152,8 +152,12 @@ export function ProcessDiagram({
); );
const handleNodeClick = useCallback( const handleNodeClick = useCallback(
(nodeId: string) => { onNodeSelect?.(nodeId); }, (nodeId: string) => {
[onNodeSelect], // Suppress click if the pointer gesture was a drag (pan)
if (zoom.didPan.current) return;
onNodeSelect?.(nodeId);
},
[onNodeSelect, zoom.didPan],
); );
const handleNodeDoubleClick = useCallback( const handleNodeDoubleClick = useCallback(

View File

@@ -18,6 +18,7 @@ export function useZoomPan() {
translateY: 0, translateY: 0,
}); });
const isPanning = useRef(false); const isPanning = useRef(false);
const didPan = useRef(false);
const panStart = useRef({ x: 0, y: 0 }); const panStart = useRef({ x: 0, y: 0 });
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const svgRef = useRef<SVGSVGElement>(null); const svgRef = useRef<SVGSVGElement>(null);
@@ -57,8 +58,11 @@ export function useZoomPan() {
const onPointerDown = useCallback( const onPointerDown = useCallback(
(e: React.PointerEvent<SVGSVGElement>) => { (e: React.PointerEvent<SVGSVGElement>) => {
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; isPanning.current = true;
didPan.current = false;
panStart.current = { x: e.clientX - state.translateX, y: e.clientY - state.translateY }; panStart.current = { x: e.clientX - state.translateX, y: e.clientY - state.translateY };
(e.currentTarget as SVGSVGElement).setPointerCapture(e.pointerId); (e.currentTarget as SVGSVGElement).setPointerCapture(e.pointerId);
}, },
@@ -68,6 +72,7 @@ export function useZoomPan() {
const onPointerMove = useCallback( const onPointerMove = useCallback(
(e: React.PointerEvent<SVGSVGElement>) => { (e: React.PointerEvent<SVGSVGElement>) => {
if (!isPanning.current) return; if (!isPanning.current) return;
didPan.current = true;
setState(prev => ({ setState(prev => ({
...prev, ...prev,
translateX: e.clientX - panStart.current.x, translateX: e.clientX - panStart.current.x,
@@ -166,6 +171,8 @@ export function useZoomPan() {
containerRef, containerRef,
svgRef, svgRef,
transform, transform,
/** True if the last pointer gesture was a drag (not a click). Check in click handlers. */
didPan,
panTo, panTo,
resetView, resetView,
onPointerDown, onPointerDown,