fix: allow drag-to-pan over diagram nodes and compounds
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:
@@ -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(
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user