Merge pull request #4611 from xyflow/fix/dynamic-onviewportchange

fix(viewport): make  onViewportChange prop dynamic
This commit is contained in:
Moritz Klack
2024-09-02 15:07:13 +02:00
committed by GitHub
7 changed files with 42 additions and 20 deletions
+7
View File
@@ -0,0 +1,7 @@
---
'@xyflow/react': patch
'@xyflow/svelte': patch
'@xyflow/system': patch
---
make onViewportChange a dynamic prop
@@ -1,4 +1,4 @@
import { MouseEvent, useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
import { import {
ReactFlow, ReactFlow,
addEdge, addEdge,
@@ -52,13 +52,17 @@ const initEdges: Edge[] = [
]; ];
const Flow = () => { const Flow = () => {
const [nodes, setNodes, onNodesChange] = useNodesState(initNodes); const [nodes, _, onNodesChange] = useNodesState(initNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initEdges); const [edges, setEdges, onEdgesChange] = useEdgesState(initEdges);
const [viewport, setViewport] = useState<Viewport>({ x: 0, y: 0, zoom: 1 }); const [viewport, setViewport] = useState<Viewport>({ x: 0, y: 0, zoom: 1 });
const [viewport2, setViewport2] = useState<Viewport>({ x: 100, y: 100, zoom: 1.5 });
const [currentViewport, setCurrentViewport] = useState(0);
const { fitView } = useReactFlow(); const { fitView } = useReactFlow();
const onConnect = useCallback((params: Connection | Edge) => setEdges((eds) => addEdge(params, eds)), [setEdges]); const onConnect = useCallback((params: Connection | Edge) => setEdges((eds) => addEdge(params, eds)), [setEdges]);
const setter = currentViewport === 0 ? setViewport : setViewport2;
return ( return (
<ReactFlow <ReactFlow
nodes={nodes} nodes={nodes}
@@ -66,12 +70,13 @@ const Flow = () => {
onNodesChange={onNodesChange} onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange} onEdgesChange={onEdgesChange}
onConnect={onConnect} onConnect={onConnect}
viewport={viewport} viewport={currentViewport === 0 ? viewport : viewport2}
onViewportChange={setViewport} onViewportChange={setter}
> >
<Panel position="top-left"> <Panel position="top-left">
<button onClick={() => setViewport((vp) => ({ ...vp, y: vp.y + 10 }))}>update viewport</button> <button onClick={() => setter((vp) => ({ ...vp, y: vp.y + 10 }))}>update viewport</button>
<button onClick={() => fitView()}>fitView</button> <button onClick={() => fitView()}>fitView</button>
<button onClick={() => setCurrentViewport(currentViewport === 0 ? 1 : 0)}>toggle viewport</button>
</Panel> </Panel>
<MiniMap /> <MiniMap />
@@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import { useEffect, useRef } from 'react'; import { useCallback, useEffect, useRef } from 'react';
import { shallow } from 'zustand/shallow'; import { shallow } from 'zustand/shallow';
import { XYPanZoom, PanOnScrollMode, type Transform, type PanZoomInstance } from '@xyflow/system'; import { XYPanZoom, PanOnScrollMode, type Transform, type PanZoomInstance } from '@xyflow/system';
@@ -57,6 +57,17 @@ export function ZoomPane({
useResizeHandler(zoomPane); useResizeHandler(zoomPane);
const onTransformChange = useCallback(
(transform: Transform) => {
onViewportChange?.({ x: transform[0], y: transform[1], zoom: transform[2] });
if (!isControlledViewport) {
store.setState({ transform });
}
},
[onViewportChange, isControlledViewport]
);
useEffect(() => { useEffect(() => {
if (zoomPane.current) { if (zoomPane.current) {
panZoom.current = XYPanZoom({ panZoom.current = XYPanZoom({
@@ -66,13 +77,6 @@ export function ZoomPane({
translateExtent, translateExtent,
viewport: defaultViewport, viewport: defaultViewport,
paneClickDistance, paneClickDistance,
onTransformChange: (transform: Transform) => {
onViewportChange?.({ x: transform[0], y: transform[1], zoom: transform[2] });
if (!isControlledViewport) {
store.setState({ transform });
}
},
onDraggingChange: (paneDragging: boolean) => store.setState({ paneDragging }), onDraggingChange: (paneDragging: boolean) => store.setState({ paneDragging }),
onPanZoomStart: (event, vp) => { onPanZoomStart: (event, vp) => {
const { onViewportChangeStart, onMoveStart } = store.getState(); const { onViewportChangeStart, onMoveStart } = store.getState();
@@ -121,6 +125,7 @@ export function ZoomPane({
userSelectionActive, userSelectionActive,
noWheelClassName, noWheelClassName,
lib, lib,
onTransformChange,
}); });
}, [ }, [
onPaneContextMenu, onPaneContextMenu,
@@ -137,6 +142,7 @@ export function ZoomPane({
userSelectionActive, userSelectionActive,
noWheelClassName, noWheelClassName,
lib, lib,
onTransformChange,
]); ]);
return ( return (
@@ -5,6 +5,7 @@ import {
type CoordinateExtent, type CoordinateExtent,
type OnPanZoom, type OnPanZoom,
type PanZoomInstance, type PanZoomInstance,
type Transform,
type Viewport type Viewport
} from '@xyflow/system'; } from '@xyflow/system';
@@ -36,6 +37,7 @@ type ZoomParams = {
userSelectionActive: boolean; userSelectionActive: boolean;
lib: string; lib: string;
paneClickDistance: number; paneClickDistance: number;
onTransformChange: (transform: Transform) => void;
}; };
export default function zoom(domNode: Element, params: ZoomParams) { export default function zoom(domNode: Element, params: ZoomParams) {
@@ -57,8 +59,6 @@ export default function zoom(domNode: Element, params: ZoomParams) {
translateExtent, translateExtent,
viewport: initialViewport, viewport: initialViewport,
paneClickDistance, paneClickDistance,
onTransformChange: (transform) =>
viewport.set({ x: transform[0], y: transform[1], zoom: transform[2] }),
onDraggingChange: dragging.set onDraggingChange: dragging.set
}); });
const currentViewport = panZoomInstance.getViewport(); const currentViewport = panZoomInstance.getViewport();
@@ -1,10 +1,10 @@
<script lang="ts"> <script lang="ts">
import { PanOnScrollMode } from '@xyflow/system'; import { onMount } from 'svelte';
import { PanOnScrollMode, type Transform } from '@xyflow/system';
import { useStore } from '$lib/store'; import { useStore } from '$lib/store';
import zoom from '$lib/actions/zoom'; import zoom from '$lib/actions/zoom';
import type { ZoomProps } from './types'; import type { ZoomProps } from './types';
import { onMount } from 'svelte';
type $$Props = ZoomProps; type $$Props = ZoomProps;
@@ -39,6 +39,9 @@
$: _panOnDrag = $panActivationKeyPressed || panOnDrag; $: _panOnDrag = $panActivationKeyPressed || panOnDrag;
$: _panOnScroll = $panActivationKeyPressed || panOnScroll; $: _panOnScroll = $panActivationKeyPressed || panOnScroll;
const onTransformChange = (transform: Transform) =>
viewport.set({ x: transform[0], y: transform[1], zoom: transform[2] });
onMount(() => { onMount(() => {
$viewportInitialized = true; $viewportInitialized = true;
}); });
@@ -70,7 +73,8 @@
userSelectionActive: !!$selectionRect, userSelectionActive: !!$selectionRect,
translateExtent: $translateExtent, translateExtent: $translateExtent,
lib: $lib, lib: $lib,
paneClickDistance paneClickDistance,
onTransformChange
}} }}
> >
<slot /> <slot />
+1 -1
View File
@@ -12,7 +12,6 @@ export type PanZoomParams = {
paneClickDistance: number; paneClickDistance: number;
viewport: Viewport; viewport: Viewport;
translateExtent: CoordinateExtent; translateExtent: CoordinateExtent;
onTransformChange: OnTransformChange;
onDraggingChange: OnDraggingChange; onDraggingChange: OnDraggingChange;
onPanZoomStart?: OnPanZoom; onPanZoomStart?: OnPanZoom;
onPanZoom?: OnPanZoom; onPanZoom?: OnPanZoom;
@@ -40,6 +39,7 @@ export type PanZoomUpdateOptions = {
zoomOnDoubleClick: boolean; zoomOnDoubleClick: boolean;
zoomActivationKeyPressed: boolean; zoomActivationKeyPressed: boolean;
lib: string; lib: string;
onTransformChange: OnTransformChange;
}; };
export type PanZoomInstance = { export type PanZoomInstance = {
+1 -1
View File
@@ -40,7 +40,6 @@ export function XYPanZoom({
onPanZoom, onPanZoom,
onPanZoomStart, onPanZoomStart,
onPanZoomEnd, onPanZoomEnd,
onTransformChange,
onDraggingChange, onDraggingChange,
}: PanZoomParams): PanZoomInstance { }: PanZoomParams): PanZoomInstance {
const zoomPanValues: ZoomPanValues = { const zoomPanValues: ZoomPanValues = {
@@ -105,6 +104,7 @@ export function XYPanZoom({
zoomOnDoubleClick, zoomOnDoubleClick,
zoomActivationKeyPressed, zoomActivationKeyPressed,
lib, lib,
onTransformChange,
}: PanZoomUpdateOptions) { }: PanZoomUpdateOptions) {
if (userSelectionActive && !zoomPanValues.isZoomingOrPanning) { if (userSelectionActive && !zoomPanValues.isZoomingOrPanning) {
destroy(); destroy();