From d7a3172a60db32ec30d5d8507b063e134280b00e Mon Sep 17 00:00:00 2001 From: peterkogo Date: Mon, 25 Aug 2025 19:12:33 +0200 Subject: [PATCH 1/5] prevent pinch zooming while making a new conection --- packages/react/src/container/ZoomPane/index.tsx | 5 ++++- packages/svelte/src/lib/actions/zoom/index.ts | 1 + packages/svelte/src/lib/container/Zoom/Zoom.svelte | 3 ++- packages/system/src/types/panzoom.ts | 1 + packages/system/src/xypanzoom/XYPanZoom.ts | 2 ++ packages/system/src/xypanzoom/filter.ts | 7 +++++++ 6 files changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/react/src/container/ZoomPane/index.tsx b/packages/react/src/container/ZoomPane/index.tsx index 61af78e6..bce2b2b7 100644 --- a/packages/react/src/container/ZoomPane/index.tsx +++ b/packages/react/src/container/ZoomPane/index.tsx @@ -25,6 +25,7 @@ type ZoomPaneProps = Omit< const selector = (s: ReactFlowState) => ({ userSelectionActive: s.userSelectionActive, lib: s.lib, + connectionInProgress: s.connection.inProgress, }); export function ZoomPane({ @@ -51,7 +52,7 @@ export function ZoomPane({ }: ZoomPaneProps) { const store = useStoreApi(); const zoomPane = useRef(null); - const { userSelectionActive, lib } = useStore(selector, shallow); + const { userSelectionActive, lib, connectionInProgress } = useStore(selector, shallow); const zoomActivationKeyPressed = useKeyPress(zoomActivationKeyCode); const panZoom = useRef(); @@ -126,6 +127,7 @@ export function ZoomPane({ noWheelClassName, lib, onTransformChange, + connectionInProgress, }); }, [ onPaneContextMenu, @@ -143,6 +145,7 @@ export function ZoomPane({ noWheelClassName, lib, onTransformChange, + connectionInProgress, ]); return ( diff --git a/packages/svelte/src/lib/actions/zoom/index.ts b/packages/svelte/src/lib/actions/zoom/index.ts index 34ead520..27d7328f 100644 --- a/packages/svelte/src/lib/actions/zoom/index.ts +++ b/packages/svelte/src/lib/actions/zoom/index.ts @@ -37,6 +37,7 @@ type ZoomParams = { paneClickDistance: number; onTransformChange: (transform: Transform) => void; onDraggingChange: (dragging: boolean) => void; + connectionInProgress: boolean; }; export default function zoom(domNode: Element, params: ZoomParams) { diff --git a/packages/svelte/src/lib/container/Zoom/Zoom.svelte b/packages/svelte/src/lib/container/Zoom/Zoom.svelte index 42a644a6..9c159c99 100644 --- a/packages/svelte/src/lib/container/Zoom/Zoom.svelte +++ b/packages/svelte/src/lib/container/Zoom/Zoom.svelte @@ -70,7 +70,8 @@ paneClickDistance, onTransformChange: (transform: Transform) => { store.viewport = { x: transform[0], y: transform[1], zoom: transform[2] }; - } + }, + connectionInProgress: store.connection.inProgress }} > {@render children()} diff --git a/packages/system/src/types/panzoom.ts b/packages/system/src/types/panzoom.ts index ae968fc8..7fa54e97 100644 --- a/packages/system/src/types/panzoom.ts +++ b/packages/system/src/types/panzoom.ts @@ -42,6 +42,7 @@ export type PanZoomUpdateOptions = { zoomActivationKeyPressed: boolean; lib: string; onTransformChange: OnTransformChange; + connectionInProgress: boolean; }; export type PanZoomInstance = { diff --git a/packages/system/src/xypanzoom/XYPanZoom.ts b/packages/system/src/xypanzoom/XYPanZoom.ts index 09d28dc2..85b8ba1c 100644 --- a/packages/system/src/xypanzoom/XYPanZoom.ts +++ b/packages/system/src/xypanzoom/XYPanZoom.ts @@ -108,6 +108,7 @@ export function XYPanZoom({ zoomActivationKeyPressed, lib, onTransformChange, + connectionInProgress, }: PanZoomUpdateOptions) { if (userSelectionActive && !zoomPanValues.isZoomingOrPanning) { destroy(); @@ -178,6 +179,7 @@ export function XYPanZoom({ noPanClassName, noWheelClassName, lib, + connectionInProgress, }); d3ZoomInstance.filter(filter); diff --git a/packages/system/src/xypanzoom/filter.ts b/packages/system/src/xypanzoom/filter.ts index 57f92486..8446f20c 100644 --- a/packages/system/src/xypanzoom/filter.ts +++ b/packages/system/src/xypanzoom/filter.ts @@ -12,6 +12,7 @@ export type FilterParams = { noWheelClassName: string; noPanClassName: string; lib: string; + connectionInProgress: boolean; }; export function createFilter({ @@ -25,6 +26,7 @@ export function createFilter({ noWheelClassName, noPanClassName, lib, + connectionInProgress, }: FilterParams) { return (event: any): boolean => { const zoomScroll = zoomActivationKeyPressed || zoomOnScroll; @@ -48,6 +50,11 @@ export function createFilter({ return false; } + // we want to disable pinch-zooming while making a connection + if (connectionInProgress) { + return false; + } + // if the target element is inside an element with the nowheel class, we prevent zooming if (isWrappedWithClass(event, noWheelClassName) && event.type === 'wheel') { return false; From 61778080c73f136870bfdccdb2d003490e857f7f Mon Sep 17 00:00:00 2001 From: peterkogo Date: Tue, 26 Aug 2025 11:21:07 +0200 Subject: [PATCH 2/5] prevent multitouch aborting connection --- packages/system/src/xyhandle/XYHandle.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/system/src/xyhandle/XYHandle.ts b/packages/system/src/xyhandle/XYHandle.ts index aa71d49d..82de2f8a 100644 --- a/packages/system/src/xyhandle/XYHandle.ts +++ b/packages/system/src/xyhandle/XYHandle.ts @@ -207,6 +207,11 @@ function onPointerDown( } function onPointerUp(event: MouseEvent | TouchEvent) { + // Prevent multi-touch aborting connection + if ('touches' in event && event.touches.length > 0) { + return; + } + if (connectionStarted) { if ((closestHandle || resultHandleDomNode) && connection && isValid) { onConnect?.(connection); From f869808e3dfd3d289db034d21d505d62f84cdde3 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Tue, 26 Aug 2025 11:24:04 +0200 Subject: [PATCH 3/5] add(changeset) --- .changeset/chilled-tigers-compare.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/chilled-tigers-compare.md diff --git a/.changeset/chilled-tigers-compare.md b/.changeset/chilled-tigers-compare.md new file mode 100644 index 00000000..74a4f7e5 --- /dev/null +++ b/.changeset/chilled-tigers-compare.md @@ -0,0 +1,7 @@ +--- +'@xyflow/react': patch +'@xyflow/svelte': patch +'@xyflow/system': patch +--- + +Prevent multi-touch events while making a new connection From 61bb5da5283338eafd022d06c47e500257042480 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Tue, 26 Aug 2025 16:52:38 +0200 Subject: [PATCH 4/5] allow wheel events when connection is being made --- packages/system/src/xypanzoom/filter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/system/src/xypanzoom/filter.ts b/packages/system/src/xypanzoom/filter.ts index 8446f20c..70375f4c 100644 --- a/packages/system/src/xypanzoom/filter.ts +++ b/packages/system/src/xypanzoom/filter.ts @@ -51,7 +51,7 @@ export function createFilter({ } // we want to disable pinch-zooming while making a connection - if (connectionInProgress) { + if (connectionInProgress && !(event.type === 'wheel')) { return false; } From 12d9e4ace48d244a94bc7fdf55dc9fd1a65b78b4 Mon Sep 17 00:00:00 2001 From: Moritz Klack Date: Tue, 26 Aug 2025 18:31:27 +0200 Subject: [PATCH 5/5] Refactor event type checks for xypanzoom filter --- packages/system/src/xypanzoom/filter.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/system/src/xypanzoom/filter.ts b/packages/system/src/xypanzoom/filter.ts index 70375f4c..3822f7aa 100644 --- a/packages/system/src/xypanzoom/filter.ts +++ b/packages/system/src/xypanzoom/filter.ts @@ -31,6 +31,7 @@ export function createFilter({ return (event: any): boolean => { const zoomScroll = zoomActivationKeyPressed || zoomOnScroll; const pinchZoom = zoomOnPinch && event.ctrlKey; + const isWheelEvent = event.type === 'wheel'; if ( event.button === 1 && @@ -51,24 +52,24 @@ export function createFilter({ } // we want to disable pinch-zooming while making a connection - if (connectionInProgress && !(event.type === 'wheel')) { + if (connectionInProgress && !isWheelEvent) { return false; } // if the target element is inside an element with the nowheel class, we prevent zooming - if (isWrappedWithClass(event, noWheelClassName) && event.type === 'wheel') { + if (isWrappedWithClass(event, noWheelClassName) && isWheelEvent) { return false; } // if the target element is inside an element with the nopan class, we prevent panning if ( isWrappedWithClass(event, noPanClassName) && - (event.type !== 'wheel' || (panOnScroll && event.type === 'wheel' && !zoomActivationKeyPressed)) + (!isWheelEvent || (panOnScroll && isWheelEvent && !zoomActivationKeyPressed)) ) { return false; } - if (!zoomOnPinch && event.ctrlKey && event.type === 'wheel') { + if (!zoomOnPinch && event.ctrlKey && isWheelEvent) { return false; } @@ -78,7 +79,7 @@ export function createFilter({ } // when there is no scroll handling enabled, we prevent all wheel events - if (!zoomScroll && !panOnScroll && !pinchZoom && event.type === 'wheel') { + if (!zoomScroll && !panOnScroll && !pinchZoom && isWheelEvent) { return false; } @@ -97,6 +98,6 @@ export function createFilter({ (Array.isArray(panOnDrag) && panOnDrag.includes(event.button)) || !event.button || event.button <= 1; // default filter for d3-zoom - return (!event.ctrlKey || event.type === 'wheel') && buttonAllowed; + return (!event.ctrlKey || isWheelEvent) && buttonAllowed; }; }