From f675451384f565f89dc4ec240313707800c6a039 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Fri, 27 Mar 2026 19:24:09 +0100 Subject: [PATCH] fix: use non-passive wheel listener to prevent page scroll during diagram zoom React's onWheel is passive by default, so preventDefault() doesn't stop page scrolling. Attach native wheel listener with { passive: false } via useEffect instead. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../ProcessDiagram/ProcessDiagram.tsx | 2 +- .../components/ProcessDiagram/useZoomPan.ts | 22 ++++++++++++------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/ui/src/components/ProcessDiagram/ProcessDiagram.tsx b/ui/src/components/ProcessDiagram/ProcessDiagram.tsx index 5fcbb043..f3cadaac 100644 --- a/ui/src/components/ProcessDiagram/ProcessDiagram.tsx +++ b/ui/src/components/ProcessDiagram/ProcessDiagram.tsx @@ -193,8 +193,8 @@ export function ProcessDiagram({ )} (null); + const svgRef = useRef(null); const clampScale = (s: number) => Math.min(MAX_SCALE, Math.max(MIN_SCALE, s)); /** Returns the CSS transform string for the content element. */ const transform = `translate(${state.translateX}px, ${state.translateY}px) scale(${state.scale})`; - const onWheel = useCallback( - (e: React.WheelEvent) => { + // Attach wheel listener with { passive: false } so preventDefault() stops page scroll. + // React's onWheel is passive by default and cannot prevent scrolling. + useEffect(() => { + const svg = svgRef.current; + if (!svg) return; + const handler = (e: WheelEvent) => { e.preventDefault(); const direction = e.deltaY < 0 ? 1 : -1; const factor = 1 + direction * ZOOM_STEP; - const rect = (e.currentTarget as SVGSVGElement).getBoundingClientRect(); + const rect = svg.getBoundingClientRect(); const cursorX = e.clientX - rect.left; const cursorY = e.clientY - rect.top; @@ -45,9 +50,10 @@ export function useZoomPan() { translateY: cursorY - scaleRatio * (cursorY - prev.translateY), }; }); - }, - [], - ); + }; + svg.addEventListener('wheel', handler, { passive: false }); + return () => svg.removeEventListener('wheel', handler); + }, []); const onPointerDown = useCallback( (e: React.PointerEvent) => { @@ -158,10 +164,10 @@ export function useZoomPan() { return { state, containerRef, + svgRef, transform, panTo, resetView, - onWheel, onPointerDown, onPointerMove, onPointerUp,