Merge pull request #4611 from xyflow/fix/dynamic-onviewportchange
fix(viewport): make onViewportChange prop dynamic
This commit is contained in:
@@ -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 />
|
||||||
|
|||||||
@@ -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 = {
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user