From 860af7caed2eb0a2b49e51d39a1e010c8f134c1d Mon Sep 17 00:00:00 2001 From: moklick Date: Thu, 8 Dec 2022 17:02:55 +0100 Subject: [PATCH 1/2] fix(minimap): use translateExtent on pan --- .../core/src/container/ZoomPane/index.tsx | 20 +++++++++---------- packages/core/src/store/index.ts | 3 +-- packages/minimap/src/MiniMap.tsx | 10 ++++++++-- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/packages/core/src/container/ZoomPane/index.tsx b/packages/core/src/container/ZoomPane/index.tsx index f94bbe58..636ce5f0 100644 --- a/packages/core/src/container/ZoomPane/index.tsx +++ b/packages/core/src/container/ZoomPane/index.tsx @@ -5,13 +5,12 @@ import type { D3ZoomEvent } from 'd3-zoom'; import { select, pointer } from 'd3-selection'; import shallow from 'zustand/shallow'; -import { clamp } from '../../utils'; import useKeyPress from '../../hooks/useKeyPress'; import useResizeHandler from '../../hooks/useResizeHandler'; import { useStore, useStoreApi } from '../../hooks/useStore'; import { containerStyle } from '../../styles'; import type { FlowRendererProps } from '../FlowRenderer'; -import { PanOnScrollMode } from '../../types'; +import { CoordinateExtent, PanOnScrollMode } from '../../types'; import type { Viewport, ReactFlowState } from '../../types'; type ZoomPaneProps = Omit< @@ -71,22 +70,24 @@ const ZoomPane = ({ useEffect(() => { if (zoomPane.current) { + const bbox = zoomPane.current.getBoundingClientRect(); const d3ZoomInstance = zoom().scaleExtent([minZoom, maxZoom]).translateExtent(translateExtent); const selection = select(zoomPane.current as Element).call(d3ZoomInstance); + const updatedTransform = zoomIdentity.translate(defaultViewport.x, defaultViewport.y).scale(defaultViewport.zoom); + const extent: CoordinateExtent = [ + [0, 0], + [bbox.width, bbox.height], + ]; - const clampedX = clamp(defaultViewport.x, translateExtent[0][0], translateExtent[1][0]); - const clampedY = clamp(defaultViewport.y, translateExtent[0][1], translateExtent[1][1]); - const clampedZoom = clamp(defaultViewport.zoom, minZoom, maxZoom); - const updatedTransform = zoomIdentity.translate(clampedX, clampedY).scale(clampedZoom); - - d3ZoomInstance.transform(selection, updatedTransform); + const constrainedTransform = d3ZoomInstance.constrain()(updatedTransform, extent, translateExtent); + d3ZoomInstance.transform(selection, constrainedTransform); store.setState({ d3Zoom: d3ZoomInstance, d3Selection: selection, d3ZoomHandler: selection.on('wheel.zoom'), // we need to pass transform because zoom handler is not registered when we set the initial transform - transform: [clampedX, clampedY, clampedZoom], + transform: [constrainedTransform.x, constrainedTransform.y, constrainedTransform.k], domNode: zoomPane.current.closest('.react-flow') as HTMLDivElement, }); } @@ -156,7 +157,6 @@ const ZoomPane = ({ } else if (!selectionKeyPressed) { d3Zoom.on('zoom', (event: D3ZoomEvent) => { const { onViewportChange } = store.getState(); - store.setState({ transform: [event.transform.x, event.transform.y, event.transform.k] }); if (onMove || onViewportChange) { diff --git a/packages/core/src/store/index.ts b/packages/core/src/store/index.ts index bdb02137..51acb34d 100644 --- a/packages/core/src/store/index.ts +++ b/packages/core/src/store/index.ts @@ -209,8 +209,7 @@ const createRFStore = () => set({ maxZoom }); }, setTranslateExtent: (translateExtent: CoordinateExtent) => { - const { d3Zoom } = get(); - d3Zoom?.translateExtent(translateExtent); + get().d3Zoom?.translateExtent(translateExtent); set({ translateExtent }); }, diff --git a/packages/minimap/src/MiniMap.tsx b/packages/minimap/src/MiniMap.tsx index b1d4c555..c9634fd6 100644 --- a/packages/minimap/src/MiniMap.tsx +++ b/packages/minimap/src/MiniMap.tsx @@ -14,6 +14,7 @@ import { getBoundsOfRects, useStoreApi, getNodePositionWithOrigin, + CoordinateExtent, } from '@reactflow/core'; import type { ReactFlowState, Rect } from '@reactflow/core'; @@ -109,7 +110,7 @@ function MiniMap({ }; const panHandler = (event: D3ZoomEvent) => { - const { transform, d3Selection, d3Zoom } = store.getState(); + const { transform, d3Selection, d3Zoom, translateExtent, width, height } = store.getState(); if (event.sourceEvent.type !== 'mousemove' || !d3Selection || !d3Zoom) { return; @@ -120,10 +121,15 @@ function MiniMap({ x: transform[0] - event.sourceEvent.movementX * viewScaleRef.current * Math.max(1, transform[2]), y: transform[1] - event.sourceEvent.movementY * viewScaleRef.current * Math.max(1, transform[2]), }; + const extent: CoordinateExtent = [ + [0, 0], + [width, height], + ]; const nextTransform = zoomIdentity.translate(position.x, position.y).scale(transform[2]); + const constrainedTransform = d3Zoom.constrain()(nextTransform, extent, translateExtent); - d3Zoom.transform(d3Selection, nextTransform); + d3Zoom.transform(d3Selection, constrainedTransform); }; const zoomAndPanHandler = zoom() From 4244bae25a36cb4904dc1fbba26e1c4d5d463cb9 Mon Sep 17 00:00:00 2001 From: moklick Date: Thu, 8 Dec 2022 17:04:18 +0100 Subject: [PATCH 2/2] chore(changeset): add --- .changeset/purple-mails-kneel.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/purple-mails-kneel.md diff --git a/.changeset/purple-mails-kneel.md b/.changeset/purple-mails-kneel.md new file mode 100644 index 00000000..f2da8f5a --- /dev/null +++ b/.changeset/purple-mails-kneel.md @@ -0,0 +1,6 @@ +--- +'@reactflow/core': patch +'@reactflow/minimap': patch +--- + +Use translateExtent correctly