Merge pull request #2367 from wbkd/feat/hooks
Feat: create useOnViewportChange and useOnSelectionChange hooks
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
name: '🐛 Bug report'
|
name: '🐛 Bug Report'
|
||||||
description: Create a report to help us improve React Flow
|
description: Create a report to help us improve React Flow
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
name: 🛠 Feature Request
|
||||||
|
description: Suggest a new feature or idea for React Flow
|
||||||
|
labels: feature request
|
||||||
|
body:
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Please describe the feature that you want to propose
|
||||||
|
description: A clear and concise description of what you want to happen.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
+8
-1
@@ -9,5 +9,12 @@ dist
|
|||||||
stats.html
|
stats.html
|
||||||
.eslintcache
|
.eslintcache
|
||||||
.idea
|
.idea
|
||||||
.yarn
|
|
||||||
.log
|
.log
|
||||||
|
|
||||||
|
.yarn/*
|
||||||
|
!.yarn/patches
|
||||||
|
!.yarn/releases
|
||||||
|
!.yarn/plugins
|
||||||
|
!.yarn/sdks
|
||||||
|
!.yarn/versions
|
||||||
|
.pnp.*
|
||||||
File diff suppressed because one or more lines are too long
+1
-1
@@ -2,4 +2,4 @@ nodeLinker: node-modules
|
|||||||
|
|
||||||
plugins:
|
plugins:
|
||||||
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
|
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
|
||||||
spec: '@yarnpkg/plugin-workspace-tools'
|
spec: "@yarnpkg/plugin-workspace-tools"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { memo, FC, CSSProperties } from 'react';
|
import React, { memo, FC, CSSProperties, useCallback } from 'react';
|
||||||
import { Handle, Position, NodeProps, Connection, Edge } from '@react-flow/bundle';
|
import { Handle, Position, NodeProps, Connection, Edge, useOnViewportChange, Viewport } from '@react-flow/bundle';
|
||||||
|
|
||||||
const targetHandleStyle: CSSProperties = { background: '#555' };
|
const targetHandleStyle: CSSProperties = { background: '#555' };
|
||||||
const sourceHandleStyleA: CSSProperties = { ...targetHandleStyle, top: 10 };
|
const sourceHandleStyleA: CSSProperties = { ...targetHandleStyle, top: 10 };
|
||||||
@@ -12,6 +12,16 @@ const sourceHandleStyleB: CSSProperties = {
|
|||||||
const onConnect = (params: Connection | Edge) => console.log('handle onConnect', params);
|
const onConnect = (params: Connection | Edge) => console.log('handle onConnect', params);
|
||||||
|
|
||||||
const ColorSelectorNode: FC<NodeProps> = ({ data, isConnectable }) => {
|
const ColorSelectorNode: FC<NodeProps> = ({ data, isConnectable }) => {
|
||||||
|
const onStart = useCallback((viewport: Viewport) => console.log('onStart', viewport), []);
|
||||||
|
const onChange = useCallback((viewport: Viewport) => console.log('onChange', viewport), []);
|
||||||
|
const onEnd = useCallback((viewport: Viewport) => console.log('onEnd', viewport), []);
|
||||||
|
|
||||||
|
useOnViewportChange({
|
||||||
|
onStart,
|
||||||
|
onChange,
|
||||||
|
onEnd,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Handle type="target" position={Position.Left} style={targetHandleStyle} onConnect={onConnect} />
|
<Handle type="target" position={Position.Left} style={targetHandleStyle} onConnect={onConnect} />
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ const initBgColor = '#1A192B';
|
|||||||
|
|
||||||
const connectionLineStyle = { stroke: '#fff' };
|
const connectionLineStyle = { stroke: '#fff' };
|
||||||
const snapGrid: SnapGrid = [16, 16];
|
const snapGrid: SnapGrid = [16, 16];
|
||||||
const defaultViewport = { x: 0, y: 0, zoom: 1.5 };
|
|
||||||
|
|
||||||
const nodeTypes = {
|
const nodeTypes = {
|
||||||
selectorNode: ColorSelectorNode,
|
selectorNode: ColorSelectorNode,
|
||||||
@@ -137,8 +136,9 @@ const CustomNodeFlow = () => {
|
|||||||
connectionLineStyle={connectionLineStyle}
|
connectionLineStyle={connectionLineStyle}
|
||||||
snapToGrid={true}
|
snapToGrid={true}
|
||||||
snapGrid={snapGrid}
|
snapGrid={snapGrid}
|
||||||
defaultViewport={defaultViewport}
|
|
||||||
fitView
|
fitView
|
||||||
|
minZoom={0.3}
|
||||||
|
maxZoom={2}
|
||||||
>
|
>
|
||||||
<MiniMap
|
<MiniMap
|
||||||
nodeStrokeColor={(n: Node): string => {
|
nodeStrokeColor={(n: Node): string => {
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ const UseZoomPanHelperFlow = () => {
|
|||||||
const logNodes = useCallback(() => {
|
const logNodes = useCallback(() => {
|
||||||
console.log('nodes', getNodes());
|
console.log('nodes', getNodes());
|
||||||
console.log('edges', getEdges());
|
console.log('edges', getEdges());
|
||||||
}, [getNodes]);
|
}, [getNodes, getEdges]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
addEdges({ id: 'e3-4', source: '3', target: '4' });
|
addEdges({ id: 'e3-4', source: '3', target: '4' });
|
||||||
|
|||||||
@@ -1,30 +1,33 @@
|
|||||||
import React, { memo, FC, useMemo, CSSProperties } from 'react';
|
import React, { useState, memo, FC, useMemo, CSSProperties } from 'react';
|
||||||
import { Handle, Position, NodeProps } from '@react-flow/bundle';
|
import { Handle, Position, NodeProps, useUpdateNodeInternals } from '@react-flow/bundle';
|
||||||
|
|
||||||
const nodeStyles: CSSProperties = { padding: 10, border: '1px solid #ddd' };
|
const nodeStyles: CSSProperties = { padding: 10, border: '1px solid #ddd' };
|
||||||
|
|
||||||
const CustomNode: FC<NodeProps> = ({ data }) => {
|
const CustomNode: FC<NodeProps> = ({ id }) => {
|
||||||
|
const [handleCount, setHandleCount] = useState(1);
|
||||||
|
const updateNodeInternals = useUpdateNodeInternals();
|
||||||
|
|
||||||
const handles = useMemo(
|
const handles = useMemo(
|
||||||
() =>
|
() =>
|
||||||
Array.from({ length: data.handleCount }, (x, i) => {
|
Array.from({ length: handleCount }, (x, i) => {
|
||||||
const handleId = `handle-${i}`;
|
const handleId = `handle-${i}`;
|
||||||
return (
|
return <Handle key={handleId} type="source" position={Position.Right} id={handleId} style={{ top: 10 * i }} />;
|
||||||
<Handle
|
|
||||||
key={handleId}
|
|
||||||
type="source"
|
|
||||||
position={Position.Right}
|
|
||||||
id={handleId}
|
|
||||||
style={{ top: 10 * i + data.handlePosition * 10 }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}),
|
}),
|
||||||
[data.handleCount, data.handlePosition]
|
[handleCount]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={nodeStyles}>
|
<div style={nodeStyles}>
|
||||||
<Handle type="target" position={Position.Left} />
|
<Handle type="target" position={Position.Left} />
|
||||||
<div>output handle count: {data.handleCount}</div>
|
<div>output handle count: {handleCount}</div>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setHandleCount((c) => c + 1);
|
||||||
|
updateNodeInternals(id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
add handle
|
||||||
|
</button>
|
||||||
{handles}
|
{handles}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback, CSSProperties, MouseEvent } from 'react';
|
import { useCallback, MouseEvent } from 'react';
|
||||||
import {
|
import {
|
||||||
ReactFlow,
|
ReactFlow,
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
Node,
|
Node,
|
||||||
Connection,
|
Connection,
|
||||||
Edge,
|
Edge,
|
||||||
useUpdateNodeInternals,
|
|
||||||
Position,
|
Position,
|
||||||
useNodesState,
|
useNodesState,
|
||||||
useEdgesState,
|
useEdgesState,
|
||||||
@@ -16,28 +15,15 @@ import {
|
|||||||
|
|
||||||
import CustomNode from './CustomNode';
|
import CustomNode from './CustomNode';
|
||||||
|
|
||||||
const initialHandleCount = 1;
|
|
||||||
|
|
||||||
const initialNodes: Node[] = [
|
const initialNodes: Node[] = [
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'custom',
|
type: 'custom',
|
||||||
data: {
|
data: { label: 'Node 1' },
|
||||||
label: 'Node 1',
|
|
||||||
handleCount: initialHandleCount,
|
|
||||||
handlePosition: 0,
|
|
||||||
},
|
|
||||||
position: { x: 250, y: 5 },
|
position: { x: 250, y: 5 },
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const buttonWrapperStyles: CSSProperties = {
|
|
||||||
position: 'absolute',
|
|
||||||
right: 10,
|
|
||||||
top: 10,
|
|
||||||
zIndex: 10,
|
|
||||||
};
|
|
||||||
|
|
||||||
const nodeTypes: NodeTypes = {
|
const nodeTypes: NodeTypes = {
|
||||||
custom: CustomNode,
|
custom: CustomNode,
|
||||||
};
|
};
|
||||||
@@ -50,7 +36,6 @@ const UpdateNodeInternalsFlow = () => {
|
|||||||
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
||||||
const onConnect = useCallback((params: Edge | Connection) => setEdges((els) => addEdge(params, els)), [setEdges]);
|
const onConnect = useCallback((params: Edge | Connection) => setEdges((els) => addEdge(params, els)), [setEdges]);
|
||||||
|
|
||||||
const updateNodeInternals = useUpdateNodeInternals();
|
|
||||||
const { project } = useReactFlow();
|
const { project } = useReactFlow();
|
||||||
|
|
||||||
const onPaneClick = useCallback(
|
const onPaneClick = useCallback(
|
||||||
@@ -67,36 +52,6 @@ const UpdateNodeInternalsFlow = () => {
|
|||||||
[project, setNodes]
|
[project, setNodes]
|
||||||
);
|
);
|
||||||
|
|
||||||
const toggleHandleCount = useCallback(() => {
|
|
||||||
setNodes((nds) =>
|
|
||||||
nds.map((node) => {
|
|
||||||
return {
|
|
||||||
...node,
|
|
||||||
data: {
|
|
||||||
...node.data,
|
|
||||||
handleCount: node.data?.handleCount === 1 ? 2 : 1,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}, [setNodes]);
|
|
||||||
|
|
||||||
const toggleHandlePosition = useCallback(() => {
|
|
||||||
setNodes((nds) =>
|
|
||||||
nds.map((node) => {
|
|
||||||
return {
|
|
||||||
...node,
|
|
||||||
data: {
|
|
||||||
...node.data,
|
|
||||||
handlePosition: node.data?.handlePosition === 0 ? 1 : 0,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}, [setNodes]);
|
|
||||||
|
|
||||||
const updateNode = useCallback(() => updateNodeInternals('1'), [updateNodeInternals]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactFlow
|
<ReactFlow
|
||||||
nodes={nodes}
|
nodes={nodes}
|
||||||
@@ -106,13 +61,7 @@ const UpdateNodeInternalsFlow = () => {
|
|||||||
nodeTypes={nodeTypes}
|
nodeTypes={nodeTypes}
|
||||||
onConnect={onConnect}
|
onConnect={onConnect}
|
||||||
onPaneClick={onPaneClick}
|
onPaneClick={onPaneClick}
|
||||||
>
|
/>
|
||||||
<div style={buttonWrapperStyles}>
|
|
||||||
<button onClick={toggleHandleCount}>toggle handle count</button>
|
|
||||||
<button onClick={toggleHandlePosition}>toggle handle position</button>
|
|
||||||
<button onClick={updateNode}>update node internals</button>
|
|
||||||
</div>
|
|
||||||
</ReactFlow>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { memo, useEffect } from 'react';
|
|||||||
import shallow from 'zustand/shallow';
|
import shallow from 'zustand/shallow';
|
||||||
|
|
||||||
import { ReactFlowState, OnSelectionChangeFunc, Node, Edge } from '../../types';
|
import { ReactFlowState, OnSelectionChangeFunc, Node, Edge } from '../../types';
|
||||||
import { useStore } from '../../hooks/useStore';
|
import { useStore, useStoreApi } from '../../hooks/useStore';
|
||||||
|
|
||||||
interface SelectionListenerProps {
|
interface SelectionListenerProps {
|
||||||
onSelectionChange: OnSelectionChangeFunc;
|
onSelectionChange: OnSelectionChangeFunc;
|
||||||
@@ -28,10 +28,14 @@ function areEqual(objA: SelectorSlice, objB: SelectorSlice) {
|
|||||||
// This is just a helper component for calling the onSelectionChange listener.
|
// This is just a helper component for calling the onSelectionChange listener.
|
||||||
// @TODO: Now that we have the onNodesChange and on EdgesChange listeners, do we still need this component?
|
// @TODO: Now that we have the onNodesChange and on EdgesChange listeners, do we still need this component?
|
||||||
function SelectionListener({ onSelectionChange }: SelectionListenerProps) {
|
function SelectionListener({ onSelectionChange }: SelectionListenerProps) {
|
||||||
|
const store = useStoreApi();
|
||||||
const { selectedNodes, selectedEdges } = useStore(selector, areEqual);
|
const { selectedNodes, selectedEdges } = useStore(selector, areEqual);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onSelectionChange({ nodes: selectedNodes, edges: selectedEdges });
|
const params = { nodes: selectedNodes, edges: selectedEdges };
|
||||||
|
|
||||||
|
onSelectionChange(params);
|
||||||
|
store.getState().onSelectionChange?.(params);
|
||||||
}, [selectedNodes, selectedEdges]);
|
}, [selectedNodes, selectedEdges]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -155,10 +155,15 @@ const ZoomPane = ({
|
|||||||
d3Zoom.on('zoom', null);
|
d3Zoom.on('zoom', null);
|
||||||
} else if (!selectionKeyPressed) {
|
} else if (!selectionKeyPressed) {
|
||||||
d3Zoom.on('zoom', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
d3Zoom.on('zoom', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||||
|
const { onViewportChange } = store.getState();
|
||||||
|
|
||||||
store.setState({ transform: [event.transform.x, event.transform.y, event.transform.k] });
|
store.setState({ transform: [event.transform.x, event.transform.y, event.transform.k] });
|
||||||
if (onMove) {
|
|
||||||
|
if (onMove || onViewportChange) {
|
||||||
const flowTransform = eventToFlowTransform(event.transform);
|
const flowTransform = eventToFlowTransform(event.transform);
|
||||||
onMove(event.sourceEvent as MouseEvent | TouchEvent, flowTransform);
|
|
||||||
|
onViewportChange?.(flowTransform);
|
||||||
|
onMove?.(event.sourceEvent as MouseEvent | TouchEvent, flowTransform);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -168,15 +173,19 @@ const ZoomPane = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (d3Zoom) {
|
if (d3Zoom) {
|
||||||
d3Zoom.on('start', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
d3Zoom.on('start', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||||
|
const { onViewportChangeStart } = store.getState();
|
||||||
isZoomingOrPanning.current = true;
|
isZoomingOrPanning.current = true;
|
||||||
|
|
||||||
if (event.sourceEvent?.type === 'mousedown') {
|
if (event.sourceEvent?.type === 'mousedown') {
|
||||||
store.setState({ paneDragging: true });
|
store.setState({ paneDragging: true });
|
||||||
}
|
}
|
||||||
if (onMoveStart) {
|
|
||||||
|
if (onMoveStart || onViewportChangeStart) {
|
||||||
const flowTransform = eventToFlowTransform(event.transform);
|
const flowTransform = eventToFlowTransform(event.transform);
|
||||||
prevTransform.current = flowTransform;
|
prevTransform.current = flowTransform;
|
||||||
|
|
||||||
onMoveStart(event.sourceEvent as MouseEvent | TouchEvent, flowTransform);
|
onViewportChangeStart?.(flowTransform);
|
||||||
|
onMoveStart?.(event.sourceEvent as MouseEvent | TouchEvent, flowTransform);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -185,14 +194,17 @@ const ZoomPane = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (d3Zoom) {
|
if (d3Zoom) {
|
||||||
d3Zoom.on('end', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
d3Zoom.on('end', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||||
|
const { onViewportChangeEnd } = store.getState();
|
||||||
|
|
||||||
isZoomingOrPanning.current = false;
|
isZoomingOrPanning.current = false;
|
||||||
store.setState({ paneDragging: false });
|
store.setState({ paneDragging: false });
|
||||||
|
|
||||||
if (onMoveEnd && viewChanged(prevTransform.current, event.transform)) {
|
if ((onMoveEnd || onViewportChangeEnd) && viewChanged(prevTransform.current, event.transform)) {
|
||||||
const flowTransform = eventToFlowTransform(event.transform);
|
const flowTransform = eventToFlowTransform(event.transform);
|
||||||
prevTransform.current = flowTransform;
|
prevTransform.current = flowTransform;
|
||||||
|
|
||||||
onMoveEnd(event.sourceEvent as MouseEvent | TouchEvent, flowTransform);
|
onViewportChangeEnd?.(flowTransform);
|
||||||
|
onMoveEnd?.(event.sourceEvent as MouseEvent | TouchEvent, flowTransform);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
|
import { useStoreApi } from './useStore';
|
||||||
|
import type { OnSelectionChangeFunc } from '../types';
|
||||||
|
|
||||||
|
export type UseOnSelectionChangeOptions = {
|
||||||
|
onChange?: OnSelectionChangeFunc;
|
||||||
|
};
|
||||||
|
|
||||||
|
function useOnSelectionChange({ onChange }: UseOnSelectionChangeOptions) {
|
||||||
|
const store = useStoreApi();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
store.setState({ onSelectionChange: onChange });
|
||||||
|
}, [onChange]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useOnSelectionChange;
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
|
import { useStoreApi } from './useStore';
|
||||||
|
import type { OnViewportChange } from '../types';
|
||||||
|
|
||||||
|
export type UseOnViewportChangeOptions = {
|
||||||
|
onStart?: OnViewportChange;
|
||||||
|
onChange?: OnViewportChange;
|
||||||
|
onEnd?: OnViewportChange;
|
||||||
|
};
|
||||||
|
|
||||||
|
function useOnViewportChange({ onStart, onChange, onEnd }: UseOnViewportChangeOptions) {
|
||||||
|
const store = useStoreApi();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
store.setState({ onViewportChangeStart: onStart });
|
||||||
|
}, [onStart]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log('change onchange');
|
||||||
|
store.setState({ onViewportChange: onChange });
|
||||||
|
}, [onChange]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
store.setState({ onViewportChangeEnd: onEnd });
|
||||||
|
}, [onEnd]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useOnViewportChange;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
import useViewportHelper from './useViewportHelper';
|
import useViewportHelper from './useViewportHelper';
|
||||||
import { useStoreApi } from '../hooks/useStore';
|
import { useStoreApi } from '../hooks/useStore';
|
||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
|
|
||||||
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
||||||
export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlowInstance<NodeData, EdgeData> {
|
export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlowInstance<NodeData, EdgeData> {
|
||||||
const { initialized: viewportInitialized, ...viewportHelperFunctions } = useViewportHelper();
|
const viewportHelper = useViewportHelper();
|
||||||
const store = useStoreApi();
|
const store = useStoreApi();
|
||||||
|
|
||||||
const getNodes = useCallback<Instance.GetNodes<NodeData>>(() => {
|
const getNodes = useCallback<Instance.GetNodes<NodeData>>(() => {
|
||||||
@@ -111,17 +111,18 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return {
|
return useMemo(() => {
|
||||||
...viewportHelperFunctions,
|
return {
|
||||||
viewportInitialized,
|
...viewportHelper,
|
||||||
getNodes,
|
getNodes,
|
||||||
getNode,
|
getNode,
|
||||||
getEdges,
|
getEdges,
|
||||||
getEdge,
|
getEdge,
|
||||||
setNodes,
|
setNodes,
|
||||||
setEdges,
|
setEdges,
|
||||||
addNodes,
|
addNodes,
|
||||||
addEdges,
|
addEdges,
|
||||||
toObject,
|
toObject,
|
||||||
};
|
};
|
||||||
|
}, [viewportHelper, getNodes, getNode, getEdges, getEdge, setNodes, setEdges, addNodes, addEdges, toObject]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,15 +10,10 @@ function useUpdateNodeInternals(): UpdateNodeInternals {
|
|||||||
const updateNodeDimensions = useStore(selector);
|
const updateNodeDimensions = useStore(selector);
|
||||||
|
|
||||||
return useCallback<UpdateNodeInternals>((id: string) => {
|
return useCallback<UpdateNodeInternals>((id: string) => {
|
||||||
const { domNode } = store.getState();
|
const nodeElement = store.getState().domNode?.querySelector(`.react-flow__node[data-id="${id}"]`) as HTMLDivElement;
|
||||||
if (!domNode) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const nodeElement = domNode.querySelector(`.react-flow__node[data-id="${id}"]`) as HTMLDivElement;
|
|
||||||
|
|
||||||
if (nodeElement) {
|
if (nodeElement) {
|
||||||
updateNodeDimensions([{ id, nodeElement, forceUpdate: true }]);
|
requestAnimationFrame(() => updateNodeDimensions([{ id, nodeElement, forceUpdate: true }]));
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const initialViewportHelper: ViewportHelperFunctions = {
|
|||||||
setCenter: noop,
|
setCenter: noop,
|
||||||
fitBounds: noop,
|
fitBounds: noop,
|
||||||
project: (position: XYPosition) => position,
|
project: (position: XYPosition) => position,
|
||||||
initialized: false,
|
viewportInitialized: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const selector = (s: ReactFlowState) => ({
|
const selector = (s: ReactFlowState) => ({
|
||||||
@@ -72,7 +72,7 @@ const useViewportHelper = (): ViewportHelperFunctions => {
|
|||||||
const { transform, snapToGrid, snapGrid } = store.getState();
|
const { transform, snapToGrid, snapGrid } = store.getState();
|
||||||
return pointToRendererPoint(position, transform, snapToGrid, snapGrid);
|
return pointToRendererPoint(position, transform, snapToGrid, snapGrid);
|
||||||
},
|
},
|
||||||
initialized: true,
|
viewportInitialized: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,5 +40,6 @@ export { default as useViewport } from './hooks/useViewport';
|
|||||||
export { default as useKeyPress } from './hooks/useKeyPress';
|
export { default as useKeyPress } from './hooks/useKeyPress';
|
||||||
export * from './hooks/useNodesEdgesState';
|
export * from './hooks/useNodesEdgesState';
|
||||||
export { useStore, useStoreApi } from './hooks/useStore';
|
export { useStore, useStoreApi } from './hooks/useStore';
|
||||||
|
export { default as useOnViewportChange } from './hooks/useOnViewportChange';
|
||||||
|
|
||||||
export * from './types';
|
export * from './types';
|
||||||
|
|||||||
@@ -44,6 +44,7 @@
|
|||||||
.react-flow__connection-path {
|
.react-flow__connection-path {
|
||||||
stroke: #b1b1b7;
|
stroke: #b1b1b7;
|
||||||
stroke-width: 1;
|
stroke-width: 1;
|
||||||
|
fill: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.react-flow__edge {
|
.react-flow__edge {
|
||||||
|
|||||||
@@ -112,6 +112,8 @@ export type UnselectNodesAndEdgesParams = {
|
|||||||
edges?: Edge[];
|
edges?: Edge[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type OnViewportChange = (viewport: Viewport) => void;
|
||||||
|
|
||||||
export interface ViewportHelperFunctions {
|
export interface ViewportHelperFunctions {
|
||||||
zoomIn: ZoomInOut;
|
zoomIn: ZoomInOut;
|
||||||
zoomOut: ZoomInOut;
|
zoomOut: ZoomInOut;
|
||||||
@@ -123,7 +125,7 @@ export interface ViewportHelperFunctions {
|
|||||||
setCenter: SetCenter;
|
setCenter: SetCenter;
|
||||||
fitBounds: FitBounds;
|
fitBounds: FitBounds;
|
||||||
project: Project;
|
project: Project;
|
||||||
initialized: boolean;
|
viewportInitialized: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ReactFlowStore = {
|
export type ReactFlowStore = {
|
||||||
@@ -193,6 +195,13 @@ export type ReactFlowStore = {
|
|||||||
|
|
||||||
onNodesDelete?: OnNodesDelete;
|
onNodesDelete?: OnNodesDelete;
|
||||||
onEdgesDelete?: OnEdgesDelete;
|
onEdgesDelete?: OnEdgesDelete;
|
||||||
|
|
||||||
|
// event handlers
|
||||||
|
onViewportChangeStart?: OnViewportChange;
|
||||||
|
onViewportChange?: OnViewportChange;
|
||||||
|
onViewportChangeEnd?: OnViewportChange;
|
||||||
|
|
||||||
|
onSelectionChange?: OnSelectionChangeFunc;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ReactFlowActions = {
|
export type ReactFlowActions = {
|
||||||
|
|||||||
Reference in New Issue
Block a user