Merge branch 'main' of github.com:wbkd/react-flow
This commit is contained in:
@@ -29,7 +29,7 @@ const UseZoomPanHelperFlow = () => {
|
|||||||
const [elements, setElements] = useState<Elements>(initialElements);
|
const [elements, setElements] = useState<Elements>(initialElements);
|
||||||
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
|
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
|
||||||
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
|
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
|
||||||
const { project } = useZoomPanHelper();
|
const { project, setCenter, zoomIn, zoomOut } = useZoomPanHelper();
|
||||||
|
|
||||||
const onPaneClick = useCallback(
|
const onPaneClick = useCallback(
|
||||||
(evt) => {
|
(evt) => {
|
||||||
@@ -48,8 +48,26 @@ const UseZoomPanHelperFlow = () => {
|
|||||||
[project]
|
[project]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const onElementClick = useCallback(
|
||||||
|
(_, element) => {
|
||||||
|
const { x, y } = element.position;
|
||||||
|
setCenter(x, y, 1);
|
||||||
|
},
|
||||||
|
[setCenter]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactFlow elements={elements} onElementsRemove={onElementsRemove} onConnect={onConnect} onPaneClick={onPaneClick}>
|
<ReactFlow
|
||||||
|
elements={elements}
|
||||||
|
onElementClick={onElementClick}
|
||||||
|
onElementsRemove={onElementsRemove}
|
||||||
|
onConnect={onConnect}
|
||||||
|
onPaneClick={onPaneClick}
|
||||||
|
>
|
||||||
|
<div style={{ position: 'absolute', left: 0, top: 0, zIndex: 100 }}>
|
||||||
|
<button onClick={() => zoomIn(1200)}>zoomIn</button>
|
||||||
|
<button onClick={() => zoomOut(0)}>zoomOut</button>
|
||||||
|
</div>
|
||||||
<Background />
|
<Background />
|
||||||
<MiniMap />
|
<MiniMap />
|
||||||
</ReactFlow>
|
</ReactFlow>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { zoomIdentity } from 'd3-zoom';
|
import { zoomIdentity } from 'd3-zoom';
|
||||||
|
import { Selection as D3Selection } from 'd3';
|
||||||
|
|
||||||
import { useStoreState, useStore } from '../store/hooks';
|
import { useStoreState, useStore } from '../store/hooks';
|
||||||
import { getRectOfNodes, pointToRendererPoint, getTransformForBounds } from '../utils/graph';
|
import { getRectOfNodes, pointToRendererPoint, getTransformForBounds } from '../utils/graph';
|
||||||
@@ -19,6 +20,10 @@ const initialZoomPanHelper: ZoomPanHelperFunctions = {
|
|||||||
initialized: false,
|
initialized: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getTransition = (selection: D3Selection<Element, unknown, null, undefined>, duration: number = 0) => {
|
||||||
|
return selection.transition().duration(duration);
|
||||||
|
};
|
||||||
|
|
||||||
const useZoomPanHelper = (): ZoomPanHelperFunctions => {
|
const useZoomPanHelper = (): ZoomPanHelperFunctions => {
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
const d3Zoom = useStoreState((s) => s.d3Zoom);
|
const d3Zoom = useStoreState((s) => s.d3Zoom);
|
||||||
@@ -27,15 +32,19 @@ const useZoomPanHelper = (): ZoomPanHelperFunctions => {
|
|||||||
const zoomPanHelperFunctions = useMemo<ZoomPanHelperFunctions>(() => {
|
const zoomPanHelperFunctions = useMemo<ZoomPanHelperFunctions>(() => {
|
||||||
if (d3Selection && d3Zoom) {
|
if (d3Selection && d3Zoom) {
|
||||||
return {
|
return {
|
||||||
zoomIn: () => d3Zoom.scaleBy(d3Selection, 1.2),
|
zoomIn: (duration?: number) => d3Zoom.scaleBy(getTransition(d3Selection, duration), 1.2),
|
||||||
zoomOut: () => d3Zoom.scaleBy(d3Selection, 1 / 1.2),
|
zoomOut: (duration?: number) => d3Zoom.scaleBy(getTransition(d3Selection, duration), 1 / 1.2),
|
||||||
zoomTo: (zoomLevel: number) => d3Zoom.scaleTo(d3Selection, zoomLevel),
|
zoomTo: (zoomLevel: number, duration?: number) =>
|
||||||
|
d3Zoom.scaleTo(getTransition(d3Selection, duration), zoomLevel),
|
||||||
transform: (transform: FlowTransform) => {
|
transform: (transform: FlowTransform) => {
|
||||||
const nextTransform = zoomIdentity.translate(transform.x, transform.y).scale(transform.zoom);
|
const nextTransform = zoomIdentity.translate(transform.x, transform.y).scale(transform.zoom);
|
||||||
|
|
||||||
d3Zoom.transform(d3Selection, nextTransform);
|
d3Zoom.transform(getTransition(d3Selection), nextTransform);
|
||||||
},
|
},
|
||||||
fitView: (options: FitViewParams = { padding: DEFAULT_PADDING, includeHiddenNodes: false }) => {
|
fitView: (
|
||||||
|
options: FitViewParams = { padding: DEFAULT_PADDING, includeHiddenNodes: false },
|
||||||
|
duration?: number
|
||||||
|
) => {
|
||||||
const { nodes, width, height, minZoom, maxZoom } = store.getState();
|
const { nodes, width, height, minZoom, maxZoom } = store.getState();
|
||||||
|
|
||||||
if (!nodes.length) {
|
if (!nodes.length) {
|
||||||
@@ -53,24 +62,23 @@ const useZoomPanHelper = (): ZoomPanHelperFunctions => {
|
|||||||
);
|
);
|
||||||
const transform = zoomIdentity.translate(x, y).scale(zoom);
|
const transform = zoomIdentity.translate(x, y).scale(zoom);
|
||||||
|
|
||||||
d3Zoom.transform(d3Selection, transform);
|
d3Zoom.transform(getTransition(d3Selection, duration), transform);
|
||||||
},
|
},
|
||||||
setCenter: (x: number, y: number, zoom?: number) => {
|
setCenter: (x: number, y: number, zoom?: number, duration?: number) => {
|
||||||
const { width, height, maxZoom } = store.getState();
|
const { width, height, maxZoom } = store.getState();
|
||||||
|
|
||||||
const nextZoom = typeof zoom !== 'undefined' ? zoom : maxZoom;
|
const nextZoom = typeof zoom !== 'undefined' ? zoom : maxZoom;
|
||||||
const centerX = width / 2 - x * nextZoom;
|
const centerX = width / 2 - x * nextZoom;
|
||||||
const centerY = height / 2 - y * nextZoom;
|
const centerY = height / 2 - y * nextZoom;
|
||||||
const transform = zoomIdentity.translate(centerX, centerY).scale(nextZoom);
|
const transform = zoomIdentity.translate(centerX, centerY).scale(nextZoom);
|
||||||
|
d3Zoom.transform(getTransition(d3Selection, duration), transform);
|
||||||
d3Zoom.transform(d3Selection, transform);
|
|
||||||
},
|
},
|
||||||
fitBounds: (bounds: Rect, padding = DEFAULT_PADDING) => {
|
fitBounds: (bounds: Rect, padding = DEFAULT_PADDING, duration?: number) => {
|
||||||
const { width, height, minZoom, maxZoom } = store.getState();
|
const { width, height, minZoom, maxZoom } = store.getState();
|
||||||
const [x, y, zoom] = getTransformForBounds(bounds, width, height, minZoom, maxZoom, padding);
|
const [x, y, zoom] = getTransformForBounds(bounds, width, height, minZoom, maxZoom, padding);
|
||||||
const transform = zoomIdentity.translate(x, y).scale(zoom);
|
const transform = zoomIdentity.translate(x, y).scale(zoom);
|
||||||
|
|
||||||
d3Zoom.transform(d3Selection, transform);
|
d3Zoom.transform(getTransition(d3Selection, duration), transform);
|
||||||
},
|
},
|
||||||
project: (position: XYPosition) => {
|
project: (position: XYPosition) => {
|
||||||
const { transform, snapToGrid, snapGrid } = store.getState();
|
const { transform, snapToGrid, snapGrid } = store.getState();
|
||||||
|
|||||||
+7
-7
@@ -262,7 +262,7 @@ export type FlowExportObject<T = any> = {
|
|||||||
zoom: number;
|
zoom: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FitViewFunc = (fitViewOptions?: FitViewParams) => void;
|
export type FitViewFunc = (fitViewOptions?: FitViewParams, duration?: number) => void;
|
||||||
export type ProjectFunc = (position: XYPosition) => XYPosition;
|
export type ProjectFunc = (position: XYPosition) => XYPosition;
|
||||||
export type ToObjectFunc<T = any> = () => FlowExportObject<T>;
|
export type ToObjectFunc<T = any> = () => FlowExportObject<T>;
|
||||||
|
|
||||||
@@ -372,13 +372,13 @@ export enum PanOnScrollMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ZoomPanHelperFunctions {
|
export interface ZoomPanHelperFunctions {
|
||||||
zoomIn: () => void;
|
zoomIn: (duration?: number) => void;
|
||||||
zoomOut: () => void;
|
zoomOut: (duration?: number) => void;
|
||||||
zoomTo: (zoomLevel: number) => void;
|
zoomTo: (zoomLevel: number, duration?: number) => void;
|
||||||
transform: (transform: FlowTransform) => void;
|
transform: (transform: FlowTransform, duration?: number) => void;
|
||||||
fitView: FitViewFunc;
|
fitView: FitViewFunc;
|
||||||
setCenter: (x: number, y: number, zoom?: number) => void;
|
setCenter: (x: number, y: number, zoom?: number, duration?: number) => void;
|
||||||
fitBounds: (bounds: Rect, padding?: number) => void;
|
fitBounds: (bounds: Rect, padding?: number, duration?: number) => void;
|
||||||
project: (position: XYPosition) => XYPosition;
|
project: (position: XYPosition) => XYPosition;
|
||||||
initialized: boolean;
|
initialized: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user