@@ -10,7 +10,7 @@
|
|||||||
"react-flow-renderer": "file:../",
|
"react-flow-renderer": "file:../",
|
||||||
"react-router-dom": "^6.3.0",
|
"react-router-dom": "^6.3.0",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
"typescript": "^4.6.4"
|
"typescript": "^4.7.4"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-scripts start",
|
"start": "react-scripts start",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { FC } from 'react';
|
import React, { FC } from 'react';
|
||||||
import { ConnectionLineComponentProps } from 'react-flow-renderer';
|
import { ConnectionLineComponentProps } from 'react-flow-renderer';
|
||||||
|
|
||||||
const ConnectionLine: FC<ConnectionLineComponentProps> = ({ sourceX, sourceY, targetX, targetY }) => {
|
const ConnectionLine: FC<ConnectionLineComponentProps> = ({ sourceX, sourceY, targetX, targetY, toX, toY }) => {
|
||||||
return (
|
return (
|
||||||
<g>
|
<g>
|
||||||
<path
|
<path
|
||||||
@@ -11,7 +11,7 @@ const ConnectionLine: FC<ConnectionLineComponentProps> = ({ sourceX, sourceY, ta
|
|||||||
className="animated"
|
className="animated"
|
||||||
d={`M${sourceX},${sourceY} C ${sourceX} ${targetY} ${sourceX} ${targetY} ${targetX},${targetY}`}
|
d={`M${sourceX},${sourceY} C ${sourceX} ${targetY} ${sourceX} ${targetY} ${targetX},${targetY}`}
|
||||||
/>
|
/>
|
||||||
<circle cx={targetX} cy={targetY} fill="#fff" r={3} stroke="#222" strokeWidth={1.5} />
|
<circle cx={toX} cy={toY} fill="#fff" r={3} stroke="#222" strokeWidth={1.5} />
|
||||||
</g>
|
</g>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { useCallback } from 'react';
|
||||||
import ReactFlow, {
|
import ReactFlow, {
|
||||||
Node,
|
Node,
|
||||||
addEdge,
|
addEdge,
|
||||||
@@ -11,13 +12,13 @@ import ReactFlow, {
|
|||||||
|
|
||||||
import ConnectionLine from './ConnectionLine';
|
import ConnectionLine from './ConnectionLine';
|
||||||
|
|
||||||
const initialNodes: Node[] = [{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 } }];
|
const initialNodes: Node[] = [{ id: '1', type: 'default', data: { label: 'Node 1' }, position: { x: 250, y: 5 } }];
|
||||||
const initialEdges: Edge[] = [];
|
const initialEdges: Edge[] = [];
|
||||||
|
|
||||||
const ConnectionLineFlow = () => {
|
const ConnectionLineFlow = () => {
|
||||||
const [nodes, , onNodesChange] = useNodesState(initialNodes);
|
const [nodes, , onNodesChange] = useNodesState(initialNodes);
|
||||||
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
||||||
const onConnect = (params: Connection | Edge) => setEdges((eds) => addEdge(params, eds));
|
const onConnect = useCallback((params: Connection | Edge) => setEdges((eds) => addEdge(params, eds)), [setEdges]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactFlow
|
<ReactFlow
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ const initialNodes: Node[] = [
|
|||||||
position: { x: 225, y: 50 },
|
position: { x: 225, y: 50 },
|
||||||
className: 'light',
|
className: 'light',
|
||||||
parentNode: '5',
|
parentNode: '5',
|
||||||
|
expandParent: true,
|
||||||
},
|
},
|
||||||
{ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 }, className: 'light' },
|
{ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 }, className: 'light' },
|
||||||
{ id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 }, className: 'light' },
|
{ id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 }, className: 'light' },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MouseEvent as ReactMouseEvent, FC } from 'react';
|
import { FC, useCallback, useState } from 'react';
|
||||||
import ReactFlow, {
|
import ReactFlow, {
|
||||||
addEdge,
|
addEdge,
|
||||||
Handle,
|
Handle,
|
||||||
@@ -6,11 +6,11 @@ import ReactFlow, {
|
|||||||
Position,
|
Position,
|
||||||
Node,
|
Node,
|
||||||
Edge,
|
Edge,
|
||||||
OnConnectStartParams,
|
|
||||||
NodeProps,
|
NodeProps,
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
useNodesState,
|
useNodesState,
|
||||||
useEdgesState,
|
useEdgesState,
|
||||||
|
OnConnectStartParams,
|
||||||
} from 'react-flow-renderer';
|
} from 'react-flow-renderer';
|
||||||
|
|
||||||
import './validation.css';
|
import './validation.css';
|
||||||
@@ -23,10 +23,6 @@ const initialNodes: Node[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const isValidConnection = (connection: Connection) => connection.target === 'B';
|
const isValidConnection = (connection: Connection) => connection.target === 'B';
|
||||||
const onConnectStart = (_: ReactMouseEvent, { nodeId, handleType }: OnConnectStartParams) =>
|
|
||||||
console.log('on connect start', { nodeId, handleType });
|
|
||||||
const onConnectStop = (event: MouseEvent) => console.log('on connect stop', event);
|
|
||||||
const onConnectEnd = (event: MouseEvent) => console.log('on connect end', event);
|
|
||||||
|
|
||||||
const CustomInput: FC<NodeProps> = () => (
|
const CustomInput: FC<NodeProps> = () => (
|
||||||
<>
|
<>
|
||||||
@@ -49,13 +45,33 @@ const nodeTypes: NodeTypes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ValidationFlow = () => {
|
const ValidationFlow = () => {
|
||||||
|
const [value, setValue] = useState(0);
|
||||||
const [nodes, , onNodesChange] = useNodesState(initialNodes);
|
const [nodes, , onNodesChange] = useNodesState(initialNodes);
|
||||||
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
||||||
|
|
||||||
const onConnect = (params: Connection | Edge) => {
|
const onConnectStart = useCallback(
|
||||||
console.log('on connect', params);
|
(event: React.MouseEvent, params: OnConnectStartParams) => {
|
||||||
setEdges((eds) => addEdge(params, eds));
|
console.log('on connect start', params, event, value);
|
||||||
};
|
setValue(1);
|
||||||
|
},
|
||||||
|
[value]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onConnect = useCallback(
|
||||||
|
(params: Connection | Edge) => {
|
||||||
|
console.log('on connect', params);
|
||||||
|
setEdges((eds) => addEdge(params, eds));
|
||||||
|
},
|
||||||
|
[setEdges]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onConnectEnd = useCallback(
|
||||||
|
(event: MouseEvent) => {
|
||||||
|
console.log('on connect end', event, value);
|
||||||
|
setValue(0);
|
||||||
|
},
|
||||||
|
[value]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactFlow
|
<ReactFlow
|
||||||
@@ -68,7 +84,6 @@ const ValidationFlow = () => {
|
|||||||
className="validationflow"
|
className="validationflow"
|
||||||
nodeTypes={nodeTypes}
|
nodeTypes={nodeTypes}
|
||||||
onConnectStart={onConnectStart}
|
onConnectStart={onConnectStart}
|
||||||
onConnectStop={onConnectStop}
|
|
||||||
onConnectEnd={onConnectEnd}
|
onConnectEnd={onConnectEnd}
|
||||||
fitView
|
fitView
|
||||||
/>
|
/>
|
||||||
|
|||||||
Generated
+2769
-2924
File diff suppressed because it is too large
Load Diff
+19
-19
@@ -49,7 +49,7 @@
|
|||||||
"css": "postcss src/*.css --dir dist"
|
"css": "postcss src/*.css --dir dist"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.18.0",
|
"@babel/runtime": "^7.18.9",
|
||||||
"classcat": "^5.0.3",
|
"classcat": "^5.0.3",
|
||||||
"d3-drag": "^3.0.0",
|
"d3-drag": "^3.0.0",
|
||||||
"d3-selection": "^3.0.0",
|
"d3-selection": "^3.0.0",
|
||||||
@@ -57,40 +57,40 @@
|
|||||||
"zustand": "^3.7.2"
|
"zustand": "^3.7.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.18.0",
|
"@babel/core": "^7.18.9",
|
||||||
"@babel/plugin-transform-runtime": "^7.18.0",
|
"@babel/plugin-transform-runtime": "^7.18.9",
|
||||||
"@babel/preset-env": "^7.18.0",
|
"@babel/preset-env": "^7.18.9",
|
||||||
"@babel/preset-react": "^7.17.12",
|
"@babel/preset-react": "^7.18.6",
|
||||||
"@babel/preset-typescript": "^7.17.12",
|
"@babel/preset-typescript": "^7.18.6",
|
||||||
"@rollup/plugin-babel": "^5.3.1",
|
"@rollup/plugin-babel": "^5.3.1",
|
||||||
"@rollup/plugin-commonjs": "^22.0.0",
|
"@rollup/plugin-commonjs": "^22.0.1",
|
||||||
"@rollup/plugin-node-resolve": "^13.3.0",
|
"@rollup/plugin-node-resolve": "^13.3.0",
|
||||||
"@rollup/plugin-replace": "^4.0.0",
|
"@rollup/plugin-replace": "^4.0.0",
|
||||||
"@types/d3": "^7.4.0",
|
"@types/d3": "^7.4.0",
|
||||||
"@types/react": "^18.0.9",
|
"@types/react": "^18.0.15",
|
||||||
"@types/react-dom": "^18.0.4",
|
"@types/react-dom": "^18.0.6",
|
||||||
"@types/resize-observer-browser": "^0.1.7",
|
"@types/resize-observer-browser": "^0.1.7",
|
||||||
"autoprefixer": "^10.4.7",
|
"autoprefixer": "^10.4.7",
|
||||||
"babel-preset-react-app": "^10.0.1",
|
"babel-preset-react-app": "^10.0.1",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"cypress": "^9.6.1",
|
"cypress": "^9.6.1",
|
||||||
"postcss": "^8.4.14",
|
"postcss": "^8.4.14",
|
||||||
"postcss-cli": "^9.1.0",
|
"postcss-cli": "^10.0.0",
|
||||||
"postcss-nested": "^5.0.6",
|
"postcss-nested": "^5.0.6",
|
||||||
"prettier": "^2.6.2",
|
"prettier": "^2.7.1",
|
||||||
"react": "^18.1.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.1.0",
|
"react-dom": "^18.2.0",
|
||||||
"release-it": "^15.0.0",
|
"release-it": "^15.1.2",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"rollup": "^2.74.1",
|
"rollup": "^2.77.0",
|
||||||
"rollup-plugin-livereload": "^2.0.5",
|
"rollup-plugin-livereload": "^2.0.5",
|
||||||
"rollup-plugin-postcss": "^4.0.2",
|
"rollup-plugin-postcss": "^4.0.2",
|
||||||
"rollup-plugin-serve": "^1.1.0",
|
"rollup-plugin-serve": "^2.0.0",
|
||||||
"rollup-plugin-terser": "^7.0.2",
|
"rollup-plugin-terser": "^7.0.2",
|
||||||
"rollup-plugin-typescript2": "^0.31.2",
|
"rollup-plugin-typescript2": "^0.32.1",
|
||||||
"rollup-plugin-visualizer": "^5.6.0",
|
"rollup-plugin-visualizer": "^5.7.1",
|
||||||
"start-server-and-test": "^1.14.0",
|
"start-server-and-test": "^1.14.0",
|
||||||
"typescript": "^4.6.4"
|
"typescript": "^4.7.4"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "16 || 17 || 18",
|
"react": "16 || 17 || 18",
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { useStore } from '../../store';
|
|||||||
import { getRectOfNodes } from '../../utils/graph';
|
import { getRectOfNodes } from '../../utils/graph';
|
||||||
import { getBoundsofRects } from '../../utils';
|
import { getBoundsofRects } from '../../utils';
|
||||||
|
|
||||||
import { MiniMapProps, GetMiniMapNodeAttribute, ReactFlowState, Rect } from '../../types';
|
import { MiniMapProps, GetMiniMapNodeAttribute, ReactFlowState } from '../../types';
|
||||||
|
|
||||||
declare const window: any;
|
declare const window: any;
|
||||||
|
|
||||||
@@ -15,9 +15,12 @@ const defaultWidth = 200;
|
|||||||
const defaultHeight = 150;
|
const defaultHeight = 150;
|
||||||
|
|
||||||
const selector = (s: ReactFlowState) => ({
|
const selector = (s: ReactFlowState) => ({
|
||||||
width: s.width,
|
viewBBox: {
|
||||||
height: s.height,
|
x: -s.transform[0] / s.transform[2],
|
||||||
transform: s.transform,
|
y: -s.transform[1] / s.transform[2],
|
||||||
|
width: s.width / s.transform[2],
|
||||||
|
height: s.height / s.transform[2],
|
||||||
|
},
|
||||||
nodes: Array.from(s.nodeInternals.values()),
|
nodes: Array.from(s.nodeInternals.values()),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -33,19 +36,13 @@ const MiniMap = ({
|
|||||||
nodeStrokeWidth = 2,
|
nodeStrokeWidth = 2,
|
||||||
maskColor = 'rgb(240, 242, 243, 0.7)',
|
maskColor = 'rgb(240, 242, 243, 0.7)',
|
||||||
}: MiniMapProps) => {
|
}: MiniMapProps) => {
|
||||||
const { width: containerWidth, height: containerHeight, transform, nodes } = useStore(selector, shallow);
|
const { viewBBox, nodes } = useStore(selector, shallow);
|
||||||
const elementWidth = (style?.width as number) ?? defaultWidth;
|
const elementWidth = (style?.width as number) ?? defaultWidth;
|
||||||
const elementHeight = (style?.height as number) ?? defaultHeight;
|
const elementHeight = (style?.height as number) ?? defaultHeight;
|
||||||
const nodeColorFunc = getAttrFunction(nodeColor);
|
const nodeColorFunc = getAttrFunction(nodeColor);
|
||||||
const nodeStrokeColorFunc = getAttrFunction(nodeStrokeColor);
|
const nodeStrokeColorFunc = getAttrFunction(nodeStrokeColor);
|
||||||
const nodeClassNameFunc = getAttrFunction(nodeClassName);
|
const nodeClassNameFunc = getAttrFunction(nodeClassName);
|
||||||
const viewBB: Rect = {
|
const boundingRect = nodes.length > 0 ? getBoundsofRects(getRectOfNodes(nodes), viewBBox) : viewBBox;
|
||||||
x: -transform[0] / transform[2],
|
|
||||||
y: -transform[1] / transform[2],
|
|
||||||
width: containerWidth / transform[2],
|
|
||||||
height: containerHeight / transform[2],
|
|
||||||
};
|
|
||||||
const boundingRect = nodes.length > 0 ? getBoundsofRects(getRectOfNodes(nodes), viewBB) : viewBB;
|
|
||||||
const scaledWidth = boundingRect.width / elementWidth;
|
const scaledWidth = boundingRect.width / elementWidth;
|
||||||
const scaledHeight = boundingRect.height / elementHeight;
|
const scaledHeight = boundingRect.height / elementHeight;
|
||||||
const viewScale = Math.max(scaledWidth, scaledHeight);
|
const viewScale = Math.max(scaledWidth, scaledHeight);
|
||||||
@@ -89,7 +86,7 @@ const MiniMap = ({
|
|||||||
<path
|
<path
|
||||||
className="react-flow__minimap-mask"
|
className="react-flow__minimap-mask"
|
||||||
d={`M${x - offset},${y - offset}h${width + offset * 2}v${height + offset * 2}h${-width - offset * 2}z
|
d={`M${x - offset},${y - offset}h${width + offset * 2}v${height + offset * 2}h${-width - offset * 2}z
|
||||||
M${viewBB.x},${viewBB.y}h${viewBB.width}v${viewBB.height}h${-viewBB.width}z`}
|
M${viewBBox.x},${viewBBox.y}h${viewBBox.width}v${viewBBox.height}h${-viewBBox.width}z`}
|
||||||
fill={maskColor}
|
fill={maskColor}
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,77 +1,70 @@
|
|||||||
import React, { useRef, CSSProperties } from 'react';
|
import React, { CSSProperties, useCallback } from 'react';
|
||||||
import shallow from 'zustand/shallow';
|
import shallow from 'zustand/shallow';
|
||||||
|
|
||||||
import { useStore } from '../../store';
|
import { useStore } from '../../store';
|
||||||
import { getBezierPath } from '../Edges/BezierEdge';
|
import { getBezierPath } from '../Edges/BezierEdge';
|
||||||
import { getSmoothStepPath } from '../Edges/SmoothStepEdge';
|
import { getSmoothStepPath } from '../Edges/SmoothStepEdge';
|
||||||
import { ConnectionLineType, ConnectionLineComponent, HandleType, Node, ReactFlowState, Position } from '../../types';
|
import { ConnectionLineType, ConnectionLineComponent, HandleType, Position } from '../../types';
|
||||||
import { getSimpleBezierPath } from '../Edges/SimpleBezierEdge';
|
import { getSimpleBezierPath } from '../Edges/SimpleBezierEdge';
|
||||||
import { internalsSymbol } from '../../utils';
|
import { internalsSymbol } from '../../utils';
|
||||||
|
|
||||||
interface ConnectionLineProps {
|
type ConnectionLineProps = {
|
||||||
connectionNodeId: string;
|
connectionNodeId: string;
|
||||||
connectionHandleId: string | null;
|
|
||||||
connectionHandleType: HandleType;
|
connectionHandleType: HandleType;
|
||||||
connectionPositionX: number;
|
|
||||||
connectionPositionY: number;
|
|
||||||
connectionLineType: ConnectionLineType;
|
connectionLineType: ConnectionLineType;
|
||||||
isConnectable: boolean;
|
isConnectable: boolean;
|
||||||
connectionLineStyle?: CSSProperties;
|
connectionLineStyle?: CSSProperties;
|
||||||
CustomConnectionLineComponent?: ConnectionLineComponent;
|
CustomConnectionLineComponent?: ConnectionLineComponent;
|
||||||
}
|
};
|
||||||
|
|
||||||
const selector = (s: ReactFlowState) => ({ nodeInternals: s.nodeInternals, transform: s.transform });
|
const oppositePosition = {
|
||||||
|
[Position.Left]: Position.Right,
|
||||||
|
[Position.Right]: Position.Left,
|
||||||
|
[Position.Top]: Position.Bottom,
|
||||||
|
[Position.Bottom]: Position.Top,
|
||||||
|
};
|
||||||
|
|
||||||
export default ({
|
export default ({
|
||||||
connectionNodeId,
|
connectionNodeId,
|
||||||
connectionHandleId,
|
|
||||||
connectionHandleType,
|
connectionHandleType,
|
||||||
connectionLineStyle,
|
connectionLineStyle,
|
||||||
connectionPositionX,
|
|
||||||
connectionPositionY,
|
|
||||||
connectionLineType = ConnectionLineType.Bezier,
|
connectionLineType = ConnectionLineType.Bezier,
|
||||||
isConnectable,
|
isConnectable,
|
||||||
CustomConnectionLineComponent,
|
CustomConnectionLineComponent,
|
||||||
}: ConnectionLineProps) => {
|
}: ConnectionLineProps) => {
|
||||||
const nodeId = connectionNodeId;
|
const { fromNode, handleId, toX, toY } = useStore(
|
||||||
const handleId = connectionHandleId;
|
useCallback(
|
||||||
|
(s) => ({
|
||||||
|
fromNode: s.nodeInternals.get(connectionNodeId),
|
||||||
|
handleId: s.connectionHandleId,
|
||||||
|
toX: (s.connectionPosition.x - s.transform[0]) / s.transform[2],
|
||||||
|
toY: (s.connectionPosition.y - s.transform[1]) / s.transform[2],
|
||||||
|
}),
|
||||||
|
[connectionNodeId]
|
||||||
|
),
|
||||||
|
shallow
|
||||||
|
);
|
||||||
|
const fromHandleBounds = fromNode?.[internalsSymbol]?.handleBounds;
|
||||||
|
|
||||||
const { nodeInternals, transform } = useStore(selector, shallow);
|
if (!fromNode || !isConnectable || !fromHandleBounds?.[connectionHandleType]) {
|
||||||
const fromNode = useRef<Node | undefined>(nodeInternals.get(nodeId));
|
|
||||||
const fromHandleBounds = fromNode.current?.[internalsSymbol]?.handleBounds;
|
|
||||||
|
|
||||||
if (!fromNode.current || !isConnectable || !fromHandleBounds?.[connectionHandleType]) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleBound = fromHandleBounds[connectionHandleType];
|
const handleBound = fromHandleBounds[connectionHandleType]!;
|
||||||
const fromHandle = handleId ? handleBound?.find((d) => d.id === handleId) : handleBound?.[0];
|
const fromHandle = handleId ? handleBound.find((d) => d.id === handleId) : handleBound[0];
|
||||||
const fromHandleX = fromHandle ? fromHandle.x + fromHandle.width / 2 : (fromNode.current?.width ?? 0) / 2;
|
const fromHandleX = fromHandle ? fromHandle.x + fromHandle.width / 2 : (fromNode?.width ?? 0) / 2;
|
||||||
const fromHandleY = fromHandle ? fromHandle.y + fromHandle.height / 2 : fromNode.current?.height ?? 0;
|
const fromHandleY = fromHandle ? fromHandle.y + fromHandle.height / 2 : fromNode?.height ?? 0;
|
||||||
const fromX = (fromNode.current.positionAbsolute?.x || 0) + fromHandleX;
|
const fromX = (fromNode?.positionAbsolute?.x || 0) + fromHandleX;
|
||||||
const fromY = (fromNode.current.positionAbsolute?.y || 0) + fromHandleY;
|
const fromY = (fromNode?.positionAbsolute?.y || 0) + fromHandleY;
|
||||||
|
|
||||||
const toX = (connectionPositionX - transform[0]) / transform[2];
|
|
||||||
const toY = (connectionPositionY - transform[1]) / transform[2];
|
|
||||||
|
|
||||||
const fromPosition = fromHandle?.position;
|
const fromPosition = fromHandle?.position;
|
||||||
|
|
||||||
let toPosition: Position | undefined;
|
if (!fromPosition) {
|
||||||
switch (fromPosition) {
|
return null;
|
||||||
case Position.Left:
|
|
||||||
toPosition = Position.Right;
|
|
||||||
break;
|
|
||||||
case Position.Right:
|
|
||||||
toPosition = Position.Left;
|
|
||||||
break;
|
|
||||||
case Position.Top:
|
|
||||||
toPosition = Position.Bottom;
|
|
||||||
break;
|
|
||||||
case Position.Bottom:
|
|
||||||
toPosition = Position.Top;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let toPosition: Position = oppositePosition[fromPosition];
|
||||||
|
|
||||||
let sourceX: number,
|
let sourceX: number,
|
||||||
sourceY: number,
|
sourceY: number,
|
||||||
sourcePosition: Position | undefined,
|
sourcePosition: Position | undefined,
|
||||||
@@ -106,19 +99,25 @@ export default ({
|
|||||||
return (
|
return (
|
||||||
<g className="react-flow__connection">
|
<g className="react-flow__connection">
|
||||||
<CustomConnectionLineComponent
|
<CustomConnectionLineComponent
|
||||||
sourceX={sourceX}
|
|
||||||
sourceY={sourceY}
|
|
||||||
sourcePosition={sourcePosition}
|
|
||||||
targetX={targetX}
|
|
||||||
targetY={targetY}
|
|
||||||
targetPosition={targetPosition}
|
|
||||||
connectionLineType={connectionLineType}
|
connectionLineType={connectionLineType}
|
||||||
connectionLineStyle={connectionLineStyle}
|
connectionLineStyle={connectionLineStyle}
|
||||||
fromNode={fromNode.current}
|
fromNode={fromNode}
|
||||||
fromHandle={fromHandle}
|
fromHandle={fromHandle}
|
||||||
// backward compatibility, mark as deprecated?
|
fromX={fromX}
|
||||||
sourceNode={fromNode.current}
|
fromY={fromY}
|
||||||
|
toX={toX}
|
||||||
|
toY={toY}
|
||||||
|
fromPosition={fromPosition}
|
||||||
|
toPosition={toPosition}
|
||||||
|
// remove in v11
|
||||||
|
sourcePosition={sourcePosition}
|
||||||
|
targetPosition={targetPosition}
|
||||||
|
sourceNode={fromNode}
|
||||||
sourceHandle={fromHandle}
|
sourceHandle={fromHandle}
|
||||||
|
targetX={targetX}
|
||||||
|
targetY={targetY}
|
||||||
|
sourceX={sourceX}
|
||||||
|
sourceY={sourceY}
|
||||||
/>
|
/>
|
||||||
</g>
|
</g>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,19 +1,13 @@
|
|||||||
import React, { memo, ComponentType, useState, useMemo } from 'react';
|
import React, { memo, ComponentType, useState, useMemo } from 'react';
|
||||||
import cc from 'classcat';
|
import cc from 'classcat';
|
||||||
import shallow from 'zustand/shallow';
|
|
||||||
|
|
||||||
import { useStore, useStoreApi } from '../../store';
|
import { useStoreApi } from '../../store';
|
||||||
import { EdgeProps, WrapEdgeProps, ReactFlowState, Connection } from '../../types';
|
import { EdgeProps, WrapEdgeProps, Connection } from '../../types';
|
||||||
import { handleMouseDown } from '../../components/Handle/handler';
|
import { handleMouseDown } from '../../components/Handle/handler';
|
||||||
import { EdgeAnchor } from './EdgeAnchor';
|
import { EdgeAnchor } from './EdgeAnchor';
|
||||||
import { getMarkerId } from '../../utils/graph';
|
import { getMarkerId } from '../../utils/graph';
|
||||||
import { getMouseHandler } from './utils';
|
import { getMouseHandler } from './utils';
|
||||||
|
|
||||||
const selector = (s: ReactFlowState) => ({
|
|
||||||
addSelectedEdges: s.addSelectedEdges,
|
|
||||||
connectionMode: s.connectionMode,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default (EdgeComponent: ComponentType<EdgeProps>) => {
|
export default (EdgeComponent: ComponentType<EdgeProps>) => {
|
||||||
const EdgeWrapper = ({
|
const EdgeWrapper = ({
|
||||||
id,
|
id,
|
||||||
@@ -56,11 +50,11 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
|
|||||||
rfId,
|
rfId,
|
||||||
}: WrapEdgeProps): JSX.Element | null => {
|
}: WrapEdgeProps): JSX.Element | null => {
|
||||||
const [updating, setUpdating] = useState<boolean>(false);
|
const [updating, setUpdating] = useState<boolean>(false);
|
||||||
const { addSelectedEdges, connectionMode } = useStore(selector, shallow);
|
|
||||||
const store = useStoreApi();
|
const store = useStoreApi();
|
||||||
|
|
||||||
const onEdgeClick = (event: React.MouseEvent<SVGGElement, MouseEvent>): void => {
|
const onEdgeClick = (event: React.MouseEvent<SVGGElement, MouseEvent>): void => {
|
||||||
const edge = store.getState().edges.find((e) => e.id === id)!;
|
const { edges, addSelectedEdges } = store.getState();
|
||||||
|
const edge = edges.find((e) => e.id === id)!;
|
||||||
|
|
||||||
if (elementsSelectable) {
|
if (elementsSelectable) {
|
||||||
store.setState({ nodesSelectionActive: false });
|
store.setState({ nodesSelectionActive: false });
|
||||||
@@ -86,32 +80,24 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
|
|||||||
|
|
||||||
onEdgeUpdateStart?.(event, edge, handleType);
|
onEdgeUpdateStart?.(event, edge, handleType);
|
||||||
|
|
||||||
const _onEdgeUpdate = onEdgeUpdateEnd
|
const _onEdgeUpdateEnd = onEdgeUpdateEnd
|
||||||
? (evt: MouseEvent): void => onEdgeUpdateEnd(evt, edge, handleType)
|
? (evt: MouseEvent): void => onEdgeUpdateEnd(evt, edge, handleType)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const onConnectEdge = (connection: Connection) => {
|
const onConnectEdge = (connection: Connection) => onEdgeUpdate?.(edge, connection);
|
||||||
const { edges } = store.getState();
|
|
||||||
const edge = edges.find((e) => e.id === id);
|
|
||||||
|
|
||||||
if (edge && onEdgeUpdate) {
|
handleMouseDown({
|
||||||
onEdgeUpdate(edge, connection);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
handleMouseDown(
|
|
||||||
event,
|
event,
|
||||||
handleId,
|
handleId,
|
||||||
nodeId,
|
nodeId,
|
||||||
store.setState,
|
onConnect: onConnectEdge,
|
||||||
onConnectEdge,
|
|
||||||
isTarget,
|
isTarget,
|
||||||
|
getState: store.getState,
|
||||||
|
setState: store.setState,
|
||||||
isValidConnection,
|
isValidConnection,
|
||||||
connectionMode,
|
elementEdgeUpdaterType: handleType,
|
||||||
handleType,
|
onEdgeUpdateEnd: _onEdgeUpdateEnd,
|
||||||
_onEdgeUpdate,
|
});
|
||||||
store.getState
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const onEdgeUpdaterSourceMouseDown = (event: React.MouseEvent<SVGGElement, MouseEvent>): void =>
|
const onEdgeUpdaterSourceMouseDown = (event: React.MouseEvent<SVGGElement, MouseEvent>): void =>
|
||||||
|
|||||||
@@ -1,17 +1,8 @@
|
|||||||
import { MouseEvent as ReactMouseEvent } from 'react';
|
import { MouseEvent as ReactMouseEvent } from 'react';
|
||||||
import { SetState } from 'zustand';
|
import { GetState, SetState } from 'zustand';
|
||||||
|
|
||||||
import { getHostForElement } from '../../utils';
|
import { getHostForElement } from '../../utils';
|
||||||
import {
|
import { OnConnect, ConnectionMode, Connection, HandleType, ReactFlowState } from '../../types';
|
||||||
OnConnect,
|
|
||||||
OnConnectStart,
|
|
||||||
OnConnectStop,
|
|
||||||
OnConnectEnd,
|
|
||||||
ConnectionMode,
|
|
||||||
Connection,
|
|
||||||
HandleType,
|
|
||||||
ReactFlowState,
|
|
||||||
} from '../../types';
|
|
||||||
|
|
||||||
type ValidConnectionFunc = (connection: Connection) => boolean;
|
type ValidConnectionFunc = (connection: Connection) => boolean;
|
||||||
|
|
||||||
@@ -83,21 +74,29 @@ function resetRecentHandle(hoveredHandle: Element): void {
|
|||||||
hoveredHandle?.classList.remove('react-flow__handle-connecting');
|
hoveredHandle?.classList.remove('react-flow__handle-connecting');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleMouseDown(
|
export function handleMouseDown({
|
||||||
event: ReactMouseEvent,
|
event,
|
||||||
handleId: string | null,
|
handleId,
|
||||||
nodeId: string,
|
nodeId,
|
||||||
setState: SetState<ReactFlowState>,
|
onConnect,
|
||||||
onConnect: OnConnect,
|
isTarget,
|
||||||
isTarget: boolean,
|
getState,
|
||||||
isValidConnection: ValidConnectionFunc,
|
setState,
|
||||||
connectionMode: ConnectionMode,
|
isValidConnection,
|
||||||
elementEdgeUpdaterType?: HandleType,
|
elementEdgeUpdaterType,
|
||||||
onEdgeUpdateEnd?: (evt: MouseEvent) => void,
|
onEdgeUpdateEnd,
|
||||||
onConnectStart?: OnConnectStart,
|
}: {
|
||||||
onConnectStop?: OnConnectStop,
|
event: ReactMouseEvent;
|
||||||
onConnectEnd?: OnConnectEnd
|
handleId: string | null;
|
||||||
): void {
|
nodeId: string;
|
||||||
|
onConnect: OnConnect;
|
||||||
|
isTarget: boolean;
|
||||||
|
getState: GetState<ReactFlowState>;
|
||||||
|
setState: SetState<ReactFlowState>;
|
||||||
|
isValidConnection: ValidConnectionFunc;
|
||||||
|
elementEdgeUpdaterType?: HandleType;
|
||||||
|
onEdgeUpdateEnd?: (evt: MouseEvent) => void;
|
||||||
|
}): void {
|
||||||
const reactFlowNode = (event.target as Element).closest('.react-flow');
|
const reactFlowNode = (event.target as Element).closest('.react-flow');
|
||||||
// when react-flow is used inside a shadow root we can't use document
|
// when react-flow is used inside a shadow root we can't use document
|
||||||
const doc = getHostForElement(event.target as HTMLElement);
|
const doc = getHostForElement(event.target as HTMLElement);
|
||||||
@@ -114,6 +113,7 @@ export function handleMouseDown(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { onConnectStart, connectionMode } = getState();
|
||||||
const handleType = elementEdgeUpdaterType ? elementEdgeUpdaterType : elementBelowIsTarget ? 'target' : 'source';
|
const handleType = elementEdgeUpdaterType ? elementEdgeUpdaterType : elementBelowIsTarget ? 'target' : 'source';
|
||||||
const containerBounds = reactFlowNode.getBoundingClientRect();
|
const containerBounds = reactFlowNode.getBoundingClientRect();
|
||||||
let recentHoveredHandle: Element;
|
let recentHoveredHandle: Element;
|
||||||
@@ -160,6 +160,7 @@ export function handleMouseDown(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onMouseUp(event: MouseEvent) {
|
function onMouseUp(event: MouseEvent) {
|
||||||
|
const { onConnectStop, onConnectEnd } = getState();
|
||||||
const { connection, isValid } = checkElementBelowIsValid(
|
const { connection, isValid } = checkElementBelowIsValid(
|
||||||
event,
|
event,
|
||||||
connectionMode,
|
connectionMode,
|
||||||
|
|||||||
@@ -14,17 +14,8 @@ const alwaysValid = () => true;
|
|||||||
export type HandleComponentProps = HandleProps & Omit<HTMLAttributes<HTMLDivElement>, 'id'>;
|
export type HandleComponentProps = HandleProps & Omit<HTMLAttributes<HTMLDivElement>, 'id'>;
|
||||||
|
|
||||||
const selector = (s: ReactFlowState) => ({
|
const selector = (s: ReactFlowState) => ({
|
||||||
onConnectAction: s.onConnect,
|
|
||||||
onConnectStart: s.onConnectStart,
|
|
||||||
onConnectStop: s.onConnectStop,
|
|
||||||
onConnectEnd: s.onConnectEnd,
|
|
||||||
onClickConnectStart: s.onClickConnectStart,
|
|
||||||
onClickConnectStop: s.onClickConnectStop,
|
|
||||||
onClickConnectEnd: s.onClickConnectEnd,
|
|
||||||
connectionMode: s.connectionMode,
|
|
||||||
connectionStartHandle: s.connectionStartHandle,
|
connectionStartHandle: s.connectionStartHandle,
|
||||||
connectOnClick: s.connectOnClick,
|
connectOnClick: s.connectOnClick,
|
||||||
hasDefaultEdges: s.hasDefaultEdges,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
||||||
@@ -45,25 +36,13 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
|||||||
) => {
|
) => {
|
||||||
const store = useStoreApi();
|
const store = useStoreApi();
|
||||||
const nodeId = useContext(NodeIdContext) as string;
|
const nodeId = useContext(NodeIdContext) as string;
|
||||||
const {
|
const { connectionStartHandle, connectOnClick } = useStore(selector, shallow);
|
||||||
onConnectAction,
|
|
||||||
onConnectStart,
|
|
||||||
onConnectStop,
|
|
||||||
onConnectEnd,
|
|
||||||
onClickConnectStart,
|
|
||||||
onClickConnectStop,
|
|
||||||
onClickConnectEnd,
|
|
||||||
connectionMode,
|
|
||||||
connectionStartHandle,
|
|
||||||
connectOnClick,
|
|
||||||
hasDefaultEdges,
|
|
||||||
} = useStore(selector, shallow);
|
|
||||||
|
|
||||||
const handleId = id || null;
|
const handleId = id || null;
|
||||||
const isTarget = type === 'target';
|
const isTarget = type === 'target';
|
||||||
|
|
||||||
const onConnectExtended = (params: Connection) => {
|
const onConnectExtended = (params: Connection) => {
|
||||||
const { defaultEdgeOptions } = store.getState();
|
const { defaultEdgeOptions, onConnect: onConnectAction, hasDefaultEdges } = store.getState();
|
||||||
|
|
||||||
const edgeParams = {
|
const edgeParams = {
|
||||||
...defaultEdgeOptions,
|
...defaultEdgeOptions,
|
||||||
@@ -80,26 +59,22 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
|||||||
|
|
||||||
const onMouseDownHandler = (event: React.MouseEvent<HTMLDivElement>) => {
|
const onMouseDownHandler = (event: React.MouseEvent<HTMLDivElement>) => {
|
||||||
if (event.button === 0) {
|
if (event.button === 0) {
|
||||||
handleMouseDown(
|
handleMouseDown({
|
||||||
event,
|
event,
|
||||||
handleId,
|
handleId,
|
||||||
nodeId,
|
nodeId,
|
||||||
store.setState,
|
onConnect: onConnectExtended,
|
||||||
onConnectExtended,
|
|
||||||
isTarget,
|
isTarget,
|
||||||
|
getState: store.getState,
|
||||||
|
setState: store.setState,
|
||||||
isValidConnection,
|
isValidConnection,
|
||||||
connectionMode,
|
});
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
onConnectStart,
|
|
||||||
onConnectStop,
|
|
||||||
onConnectEnd
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
onMouseDown?.(event);
|
onMouseDown?.(event);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onClick = (event: React.MouseEvent) => {
|
const onClick = (event: React.MouseEvent) => {
|
||||||
|
const { onClickConnectStart, onClickConnectStop, onClickConnectEnd, connectionMode } = store.getState();
|
||||||
if (!connectionStartHandle) {
|
if (!connectionStartHandle) {
|
||||||
onClickConnectStart?.(event, { nodeId, handleId, handleType: type });
|
onClickConnectStart?.(event, { nodeId, handleId, handleType: type });
|
||||||
store.setState({ connectionStartHandle: { nodeId, type, handleId } });
|
store.setState({ connectionStartHandle: { nodeId, type, handleId } });
|
||||||
@@ -128,28 +103,26 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
|||||||
store.setState({ connectionStartHandle: null });
|
store.setState({ connectionStartHandle: null });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClasses = cc([
|
|
||||||
'react-flow__handle',
|
|
||||||
`react-flow__handle-${position}`,
|
|
||||||
'nodrag',
|
|
||||||
className,
|
|
||||||
{
|
|
||||||
source: !isTarget,
|
|
||||||
target: isTarget,
|
|
||||||
connectable: isConnectable,
|
|
||||||
connecting:
|
|
||||||
connectionStartHandle?.nodeId === nodeId &&
|
|
||||||
connectionStartHandle?.handleId === handleId &&
|
|
||||||
connectionStartHandle?.type === type,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-handleid={handleId}
|
data-handleid={handleId}
|
||||||
data-nodeid={nodeId}
|
data-nodeid={nodeId}
|
||||||
data-handlepos={position}
|
data-handlepos={position}
|
||||||
className={handleClasses}
|
className={cc([
|
||||||
|
'react-flow__handle',
|
||||||
|
`react-flow__handle-${position}`,
|
||||||
|
'nodrag',
|
||||||
|
className,
|
||||||
|
{
|
||||||
|
source: !isTarget,
|
||||||
|
target: isTarget,
|
||||||
|
connectable: isConnectable,
|
||||||
|
connecting:
|
||||||
|
connectionStartHandle?.nodeId === nodeId &&
|
||||||
|
connectionStartHandle?.handleId === handleId &&
|
||||||
|
connectionStartHandle?.type === type,
|
||||||
|
},
|
||||||
|
])}
|
||||||
onMouseDown={onMouseDownHandler}
|
onMouseDown={onMouseDownHandler}
|
||||||
onClick={connectOnClick ? onClick : undefined}
|
onClick={connectOnClick ? onClick : undefined}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
import React, { useEffect, useRef, memo, ComponentType, MouseEvent } from 'react';
|
import React, { useEffect, useRef, memo, ComponentType, MouseEvent } from 'react';
|
||||||
import cc from 'classcat';
|
import cc from 'classcat';
|
||||||
|
|
||||||
import { useStore, useStoreApi } from '../../store';
|
import { useStoreApi } from '../../store';
|
||||||
import { Provider } from '../../contexts/NodeIdContext';
|
import { Provider } from '../../contexts/NodeIdContext';
|
||||||
import { NodeProps, WrapNodeProps, ReactFlowState } from '../../types';
|
import { NodeProps, WrapNodeProps } from '../../types';
|
||||||
import useDrag from '../../hooks/useDrag';
|
import useDrag from '../../hooks/useDrag';
|
||||||
import { getMouseHandler, handleNodeClick } from './utils';
|
import { getMouseHandler, handleNodeClick } from './utils';
|
||||||
|
|
||||||
const selector = (s: ReactFlowState) => s.updateNodeDimensions;
|
|
||||||
|
|
||||||
export default (NodeComponent: ComponentType<NodeProps>) => {
|
export default (NodeComponent: ComponentType<NodeProps>) => {
|
||||||
const NodeWrapper = ({
|
const NodeWrapper = ({
|
||||||
id,
|
id,
|
||||||
@@ -23,9 +21,6 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
|||||||
onMouseLeave,
|
onMouseLeave,
|
||||||
onContextMenu,
|
onContextMenu,
|
||||||
onDoubleClick,
|
onDoubleClick,
|
||||||
onDragStart,
|
|
||||||
onDrag,
|
|
||||||
onDragStop,
|
|
||||||
style,
|
style,
|
||||||
className,
|
className,
|
||||||
isDraggable,
|
isDraggable,
|
||||||
@@ -44,7 +39,6 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
|||||||
initialized,
|
initialized,
|
||||||
}: WrapNodeProps) => {
|
}: WrapNodeProps) => {
|
||||||
const store = useStoreApi();
|
const store = useStoreApi();
|
||||||
const updateNodeDimensions = useStore(selector);
|
|
||||||
const nodeRef = useRef<HTMLDivElement>(null);
|
const nodeRef = useRef<HTMLDivElement>(null);
|
||||||
const prevSourcePosition = useRef(sourcePosition);
|
const prevSourcePosition = useRef(sourcePosition);
|
||||||
const prevTargetPosition = useRef(targetPosition);
|
const prevTargetPosition = useRef(targetPosition);
|
||||||
@@ -96,14 +90,11 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
|||||||
if (targetPosChanged) {
|
if (targetPosChanged) {
|
||||||
prevTargetPosition.current = targetPosition;
|
prevTargetPosition.current = targetPosition;
|
||||||
}
|
}
|
||||||
updateNodeDimensions([{ id, nodeElement: nodeRef.current, forceUpdate: true }]);
|
store.getState().updateNodeDimensions([{ id, nodeElement: nodeRef.current, forceUpdate: true }]);
|
||||||
}
|
}
|
||||||
}, [id, type, sourcePosition, targetPosition]);
|
}, [id, type, sourcePosition, targetPosition]);
|
||||||
|
|
||||||
const dragging = useDrag({
|
const dragging = useDrag({
|
||||||
onStart: onDragStart,
|
|
||||||
onDrag: onDrag,
|
|
||||||
onStop: onDragStop,
|
|
||||||
nodeRef,
|
nodeRef,
|
||||||
disabled: hidden || !isDraggable,
|
disabled: hidden || !isDraggable,
|
||||||
noDragClassName,
|
noDragClassName,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* made a selection with on or several nodes
|
* made a selection with on or several nodes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { memo, useCallback, useRef, MouseEvent } from 'react';
|
import React, { memo, useRef, MouseEvent } from 'react';
|
||||||
import cc from 'classcat';
|
import cc from 'classcat';
|
||||||
import shallow from 'zustand/shallow';
|
import shallow from 'zustand/shallow';
|
||||||
|
|
||||||
@@ -13,16 +13,14 @@ import { getRectOfNodes } from '../../utils/graph';
|
|||||||
import useDrag from '../../hooks/useDrag';
|
import useDrag from '../../hooks/useDrag';
|
||||||
|
|
||||||
export interface NodesSelectionProps {
|
export interface NodesSelectionProps {
|
||||||
onSelectionDragStart?: (event: MouseEvent, nodes: Node[]) => void;
|
|
||||||
onSelectionDrag?: (event: MouseEvent, nodes: Node[]) => void;
|
|
||||||
onSelectionDragStop?: (event: MouseEvent, nodes: Node[]) => void;
|
|
||||||
onSelectionContextMenu?: (event: MouseEvent, nodes: Node[]) => void;
|
onSelectionContextMenu?: (event: MouseEvent, nodes: Node[]) => void;
|
||||||
noPanClassName?: string;
|
noPanClassName?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const selector = (s: ReactFlowState) => ({
|
const selector = (s: ReactFlowState) => ({
|
||||||
transform: s.transform,
|
transformString: `translate(${s.transform[0]}px,${s.transform[1]}px) scale(${s.transform[2]})`,
|
||||||
userSelectionActive: s.userSelectionActive,
|
userSelectionActive: s.userSelectionActive,
|
||||||
|
...getRectOfNodes(Array.from(s.nodeInternals.values()).filter((n) => n.selected)),
|
||||||
});
|
});
|
||||||
|
|
||||||
const bboxSelector = (s: ReactFlowState) => {
|
const bboxSelector = (s: ReactFlowState) => {
|
||||||
@@ -30,31 +28,14 @@ const bboxSelector = (s: ReactFlowState) => {
|
|||||||
return getRectOfNodes(selectedNodes);
|
return getRectOfNodes(selectedNodes);
|
||||||
};
|
};
|
||||||
|
|
||||||
function useGetMemoizedHandler(handler?: (event: MouseEvent, nodes: Node[]) => void) {
|
function NodesSelection({ onSelectionContextMenu, noPanClassName }: NodesSelectionProps) {
|
||||||
return useCallback((event: MouseEvent, _: Node, nodes: Node[]) => handler?.(event, nodes), [handler]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function NodesSelection({
|
|
||||||
onSelectionDragStart,
|
|
||||||
onSelectionDrag,
|
|
||||||
onSelectionDragStop,
|
|
||||||
onSelectionContextMenu,
|
|
||||||
noPanClassName,
|
|
||||||
}: NodesSelectionProps) {
|
|
||||||
const store = useStoreApi();
|
const store = useStoreApi();
|
||||||
const { transform, userSelectionActive } = useStore(selector, shallow);
|
const { transformString, userSelectionActive } = useStore(selector, shallow);
|
||||||
const { width, height, x: left, y: top } = useStore(bboxSelector, shallow);
|
const { width, height, x: left, y: top } = useStore(bboxSelector, shallow);
|
||||||
|
|
||||||
const nodeRef = useRef(null);
|
const nodeRef = useRef(null);
|
||||||
|
|
||||||
// it's important that these handlers are memoized to avoid multiple creation of d3 drag handler
|
|
||||||
const onStart = useGetMemoizedHandler(onSelectionDragStart);
|
|
||||||
const onDrag = useGetMemoizedHandler(onSelectionDrag);
|
|
||||||
const onStop = useGetMemoizedHandler(onSelectionDragStop);
|
|
||||||
|
|
||||||
useDrag({
|
useDrag({
|
||||||
onStart,
|
|
||||||
onDrag,
|
|
||||||
onStop,
|
|
||||||
nodeRef,
|
nodeRef,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -73,7 +54,7 @@ function NodesSelection({
|
|||||||
<div
|
<div
|
||||||
className={cc(['react-flow__nodesselection', 'react-flow__container', noPanClassName])}
|
className={cc(['react-flow__nodesselection', 'react-flow__container', noPanClassName])}
|
||||||
style={{
|
style={{
|
||||||
transform: `translate(${transform[0]}px,${transform[1]}px) scale(${transform[2]})`,
|
transform: transformString,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -3,56 +3,46 @@ import { SetState } from 'zustand';
|
|||||||
import shallow from 'zustand/shallow';
|
import shallow from 'zustand/shallow';
|
||||||
|
|
||||||
import { useStore, useStoreApi } from '../../store';
|
import { useStore, useStoreApi } from '../../store';
|
||||||
import {
|
import { Node, Edge, ReactFlowState, CoordinateExtent, ReactFlowProps } from '../../types';
|
||||||
Node,
|
|
||||||
Edge,
|
|
||||||
ReactFlowState,
|
|
||||||
OnConnect,
|
|
||||||
OnConnectStart,
|
|
||||||
OnConnectStop,
|
|
||||||
OnConnectEnd,
|
|
||||||
CoordinateExtent,
|
|
||||||
OnNodesChange,
|
|
||||||
OnEdgesChange,
|
|
||||||
ConnectionMode,
|
|
||||||
SnapGrid,
|
|
||||||
DefaultEdgeOptions,
|
|
||||||
FitViewOptions,
|
|
||||||
OnNodesDelete,
|
|
||||||
OnEdgesDelete,
|
|
||||||
} from '../../types';
|
|
||||||
|
|
||||||
interface StoreUpdaterProps {
|
type StoreUpdaterProps = Pick<
|
||||||
nodes?: Node[];
|
ReactFlowProps,
|
||||||
edges?: Edge[];
|
| 'nodes'
|
||||||
defaultNodes?: Node[];
|
| 'edges'
|
||||||
defaultEdges?: Edge[];
|
| 'defaultNodes'
|
||||||
onConnect?: OnConnect;
|
| 'defaultEdges'
|
||||||
onConnectStart?: OnConnectStart;
|
| 'onConnect'
|
||||||
onConnectStop?: OnConnectStop;
|
| 'onConnectStart'
|
||||||
onConnectEnd?: OnConnectEnd;
|
| 'onConnectStop'
|
||||||
onClickConnectStart?: OnConnectStart;
|
| 'onConnectEnd'
|
||||||
onClickConnectStop?: OnConnectStop;
|
| 'onClickConnectStart'
|
||||||
onClickConnectEnd?: OnConnectEnd;
|
| 'onClickConnectStop'
|
||||||
nodesDraggable?: boolean;
|
| 'onClickConnectEnd'
|
||||||
nodesConnectable?: boolean;
|
| 'nodesDraggable'
|
||||||
minZoom?: number;
|
| 'nodesConnectable'
|
||||||
maxZoom?: number;
|
| 'minZoom'
|
||||||
nodeExtent?: CoordinateExtent;
|
| 'maxZoom'
|
||||||
onNodesChange?: OnNodesChange;
|
| 'nodeExtent'
|
||||||
onEdgesChange?: OnEdgesChange;
|
| 'onNodesChange'
|
||||||
elementsSelectable?: boolean;
|
| 'onEdgesChange'
|
||||||
connectionMode?: ConnectionMode;
|
| 'elementsSelectable'
|
||||||
snapToGrid?: boolean;
|
| 'connectionMode'
|
||||||
snapGrid?: SnapGrid;
|
| 'snapToGrid'
|
||||||
translateExtent?: CoordinateExtent;
|
| 'snapGrid'
|
||||||
connectOnClick: boolean;
|
| 'translateExtent'
|
||||||
defaultEdgeOptions?: DefaultEdgeOptions;
|
| 'connectOnClick'
|
||||||
fitView?: boolean;
|
| 'defaultEdgeOptions'
|
||||||
fitViewOptions?: FitViewOptions;
|
| 'fitView'
|
||||||
onNodesDelete?: OnNodesDelete;
|
| 'fitViewOptions'
|
||||||
onEdgesDelete?: OnEdgesDelete;
|
| 'onNodesDelete'
|
||||||
}
|
| 'onEdgesDelete'
|
||||||
|
| 'onNodeDragStart'
|
||||||
|
| 'onNodeDrag'
|
||||||
|
| 'onNodeDragStop'
|
||||||
|
| 'onSelectionDragStart'
|
||||||
|
| 'onSelectionDrag'
|
||||||
|
| 'onSelectionDragStop'
|
||||||
|
>;
|
||||||
|
|
||||||
const selector = (s: ReactFlowState) => ({
|
const selector = (s: ReactFlowState) => ({
|
||||||
setNodes: s.setNodes,
|
setNodes: s.setNodes,
|
||||||
@@ -112,6 +102,12 @@ const StoreUpdater = ({
|
|||||||
fitViewOptions,
|
fitViewOptions,
|
||||||
onNodesDelete,
|
onNodesDelete,
|
||||||
onEdgesDelete,
|
onEdgesDelete,
|
||||||
|
onNodeDrag,
|
||||||
|
onNodeDragStart,
|
||||||
|
onNodeDragStop,
|
||||||
|
onSelectionDrag,
|
||||||
|
onSelectionDragStart,
|
||||||
|
onSelectionDragStop,
|
||||||
}: StoreUpdaterProps) => {
|
}: StoreUpdaterProps) => {
|
||||||
const {
|
const {
|
||||||
setNodes,
|
setNodes,
|
||||||
@@ -154,6 +150,12 @@ const StoreUpdater = ({
|
|||||||
useDirectStoreUpdater('fitViewOnInitOptions', fitViewOptions, store.setState);
|
useDirectStoreUpdater('fitViewOnInitOptions', fitViewOptions, store.setState);
|
||||||
useDirectStoreUpdater('onNodesDelete', onNodesDelete, store.setState);
|
useDirectStoreUpdater('onNodesDelete', onNodesDelete, store.setState);
|
||||||
useDirectStoreUpdater('onEdgesDelete', onEdgesDelete, store.setState);
|
useDirectStoreUpdater('onEdgesDelete', onEdgesDelete, store.setState);
|
||||||
|
useDirectStoreUpdater('onNodeDrag', onNodeDrag, store.setState);
|
||||||
|
useDirectStoreUpdater('onNodeDragStart', onNodeDragStart, store.setState);
|
||||||
|
useDirectStoreUpdater('onNodeDragStop', onNodeDragStop, store.setState);
|
||||||
|
useDirectStoreUpdater('onSelectionDrag', onSelectionDrag, store.setState);
|
||||||
|
useDirectStoreUpdater('onSelectionDragStart', onSelectionDragStart, store.setState);
|
||||||
|
useDirectStoreUpdater('onSelectionDragStop', onSelectionDragStop, store.setState);
|
||||||
|
|
||||||
useStoreUpdater<Node[]>(nodes, setNodes);
|
useStoreUpdater<Node[]>(nodes, setNodes);
|
||||||
useStoreUpdater<Edge[]>(edges, setEdges);
|
useStoreUpdater<Edge[]>(edges, setEdges);
|
||||||
|
|||||||
@@ -45,9 +45,7 @@ interface EdgeRendererProps {
|
|||||||
|
|
||||||
const selector = (s: ReactFlowState) => ({
|
const selector = (s: ReactFlowState) => ({
|
||||||
connectionNodeId: s.connectionNodeId,
|
connectionNodeId: s.connectionNodeId,
|
||||||
connectionHandleId: s.connectionHandleId,
|
|
||||||
connectionHandleType: s.connectionHandleType,
|
connectionHandleType: s.connectionHandleType,
|
||||||
connectionPosition: s.connectionPosition,
|
|
||||||
nodesConnectable: s.nodesConnectable,
|
nodesConnectable: s.nodesConnectable,
|
||||||
elementsSelectable: s.elementsSelectable,
|
elementsSelectable: s.elementsSelectable,
|
||||||
width: s.width,
|
width: s.width,
|
||||||
@@ -59,9 +57,7 @@ const selector = (s: ReactFlowState) => ({
|
|||||||
const EdgeRenderer = (props: EdgeRendererProps) => {
|
const EdgeRenderer = (props: EdgeRendererProps) => {
|
||||||
const {
|
const {
|
||||||
connectionNodeId,
|
connectionNodeId,
|
||||||
connectionHandleId,
|
|
||||||
connectionHandleType,
|
connectionHandleType,
|
||||||
connectionPosition,
|
|
||||||
nodesConnectable,
|
nodesConnectable,
|
||||||
elementsSelectable,
|
elementsSelectable,
|
||||||
width,
|
width,
|
||||||
@@ -201,10 +197,7 @@ const EdgeRenderer = (props: EdgeRendererProps) => {
|
|||||||
>
|
>
|
||||||
<ConnectionLine
|
<ConnectionLine
|
||||||
connectionNodeId={connectionNodeId!}
|
connectionNodeId={connectionNodeId!}
|
||||||
connectionHandleId={connectionHandleId}
|
|
||||||
connectionHandleType={connectionHandleType!}
|
connectionHandleType={connectionHandleType!}
|
||||||
connectionPositionX={connectionPosition.x}
|
|
||||||
connectionPositionY={connectionPosition.y}
|
|
||||||
connectionLineStyle={connectionLineStyle}
|
connectionLineStyle={connectionLineStyle}
|
||||||
connectionLineType={connectionLineType}
|
connectionLineType={connectionLineType}
|
||||||
isConnectable={nodesConnectable}
|
isConnectable={nodesConnectable}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React, { memo, ReactNode, WheelEvent, MouseEvent } from 'react';
|
import React, { memo, ReactNode, WheelEvent, MouseEvent } from 'react';
|
||||||
import shallow from 'zustand/shallow';
|
|
||||||
|
|
||||||
import { useStore, useStoreApi } from '../../store';
|
import { useStore, useStoreApi } from '../../store';
|
||||||
import useGlobalKeyHandler from '../../hooks/useGlobalKeyHandler';
|
import useGlobalKeyHandler from '../../hooks/useGlobalKeyHandler';
|
||||||
@@ -27,10 +26,7 @@ export type FlowRendererProps = Omit<
|
|||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
const selector = (s: ReactFlowState) => ({
|
const selector = (s: ReactFlowState) => s.nodesSelectionActive;
|
||||||
resetSelectedElements: s.resetSelectedElements,
|
|
||||||
nodesSelectionActive: s.nodesSelectionActive,
|
|
||||||
});
|
|
||||||
|
|
||||||
const FlowRenderer = ({
|
const FlowRenderer = ({
|
||||||
children,
|
children,
|
||||||
@@ -55,27 +51,24 @@ const FlowRenderer = ({
|
|||||||
defaultPosition,
|
defaultPosition,
|
||||||
defaultZoom,
|
defaultZoom,
|
||||||
preventScrolling,
|
preventScrolling,
|
||||||
onSelectionDragStart,
|
|
||||||
onSelectionDrag,
|
|
||||||
onSelectionDragStop,
|
|
||||||
onSelectionContextMenu,
|
onSelectionContextMenu,
|
||||||
noWheelClassName,
|
noWheelClassName,
|
||||||
noPanClassName,
|
noPanClassName,
|
||||||
}: FlowRendererProps) => {
|
}: FlowRendererProps) => {
|
||||||
const store = useStoreApi();
|
const store = useStoreApi();
|
||||||
const { resetSelectedElements, nodesSelectionActive } = useStore(selector, shallow);
|
const nodesSelectionActive = useStore(selector);
|
||||||
const selectionKeyPressed = useKeyPress(selectionKeyCode);
|
const selectionKeyPressed = useKeyPress(selectionKeyCode);
|
||||||
|
|
||||||
useGlobalKeyHandler({ deleteKeyCode, multiSelectionKeyCode });
|
useGlobalKeyHandler({ deleteKeyCode, multiSelectionKeyCode });
|
||||||
|
|
||||||
const onClick = (event: MouseEvent) => {
|
const onClick = (event: MouseEvent) => {
|
||||||
onPaneClick?.(event);
|
onPaneClick?.(event);
|
||||||
resetSelectedElements();
|
store.getState().resetSelectedElements();
|
||||||
|
|
||||||
store.setState({ nodesSelectionActive: false });
|
store.setState({ nodesSelectionActive: false });
|
||||||
};
|
};
|
||||||
const onContextMenu = (event: MouseEvent) => onPaneContextMenu?.(event);
|
|
||||||
const onWheel = (event: WheelEvent) => onPaneScroll?.(event);
|
const onContextMenu = onPaneContextMenu ? (event: MouseEvent) => onPaneContextMenu(event) : undefined;
|
||||||
|
const onWheel = onPaneScroll ? (event: WheelEvent) => onPaneScroll(event) : undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ZoomPane
|
<ZoomPane
|
||||||
@@ -101,13 +94,7 @@ const FlowRenderer = ({
|
|||||||
{children}
|
{children}
|
||||||
<UserSelection selectionKeyPressed={selectionKeyPressed} />
|
<UserSelection selectionKeyPressed={selectionKeyPressed} />
|
||||||
{nodesSelectionActive && (
|
{nodesSelectionActive && (
|
||||||
<NodesSelection
|
<NodesSelection onSelectionContextMenu={onSelectionContextMenu} noPanClassName={noPanClassName} />
|
||||||
onSelectionDragStart={onSelectionDragStart}
|
|
||||||
onSelectionDrag={onSelectionDrag}
|
|
||||||
onSelectionDragStop={onSelectionDragStop}
|
|
||||||
onSelectionContextMenu={onSelectionContextMenu}
|
|
||||||
noPanClassName={noPanClassName}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
className="react-flow__pane react-flow__container"
|
className="react-flow__pane react-flow__container"
|
||||||
|
|||||||
@@ -40,12 +40,6 @@ const GraphView = ({
|
|||||||
onNodeMouseMove,
|
onNodeMouseMove,
|
||||||
onNodeMouseLeave,
|
onNodeMouseLeave,
|
||||||
onNodeContextMenu,
|
onNodeContextMenu,
|
||||||
onNodeDragStart,
|
|
||||||
onNodeDrag,
|
|
||||||
onNodeDragStop,
|
|
||||||
onSelectionDragStart,
|
|
||||||
onSelectionDrag,
|
|
||||||
onSelectionDragStop,
|
|
||||||
onSelectionContextMenu,
|
onSelectionContextMenu,
|
||||||
connectionLineType,
|
connectionLineType,
|
||||||
connectionLineStyle,
|
connectionLineStyle,
|
||||||
@@ -110,9 +104,6 @@ const GraphView = ({
|
|||||||
panOnDrag={panOnDrag}
|
panOnDrag={panOnDrag}
|
||||||
defaultPosition={defaultPosition}
|
defaultPosition={defaultPosition}
|
||||||
defaultZoom={defaultZoom}
|
defaultZoom={defaultZoom}
|
||||||
onSelectionDragStart={onSelectionDragStart}
|
|
||||||
onSelectionDrag={onSelectionDrag}
|
|
||||||
onSelectionDragStop={onSelectionDragStop}
|
|
||||||
onSelectionContextMenu={onSelectionContextMenu}
|
onSelectionContextMenu={onSelectionContextMenu}
|
||||||
preventScrolling={preventScrolling}
|
preventScrolling={preventScrolling}
|
||||||
noDragClassName={noDragClassName}
|
noDragClassName={noDragClassName}
|
||||||
@@ -150,9 +141,6 @@ const GraphView = ({
|
|||||||
onNodeMouseMove={onNodeMouseMove}
|
onNodeMouseMove={onNodeMouseMove}
|
||||||
onNodeMouseLeave={onNodeMouseLeave}
|
onNodeMouseLeave={onNodeMouseLeave}
|
||||||
onNodeContextMenu={onNodeContextMenu}
|
onNodeContextMenu={onNodeContextMenu}
|
||||||
onNodeDragStop={onNodeDragStop}
|
|
||||||
onNodeDrag={onNodeDrag}
|
|
||||||
onNodeDragStart={onNodeDragStart}
|
|
||||||
selectNodesOnDrag={selectNodesOnDrag}
|
selectNodesOnDrag={selectNodesOnDrag}
|
||||||
onlyRenderVisibleElements={onlyRenderVisibleElements}
|
onlyRenderVisibleElements={onlyRenderVisibleElements}
|
||||||
noPanClassName={noPanClassName}
|
noPanClassName={noPanClassName}
|
||||||
|
|||||||
@@ -3,14 +3,7 @@ import shallow from 'zustand/shallow';
|
|||||||
|
|
||||||
import useVisibleNodes from '../../hooks/useVisibleNodes';
|
import useVisibleNodes from '../../hooks/useVisibleNodes';
|
||||||
import { useStore } from '../../store';
|
import { useStore } from '../../store';
|
||||||
import {
|
import { NodeMouseHandler, NodeTypesWrapped, Position, ReactFlowState, WrapNodeProps } from '../../types';
|
||||||
NodeDragHandler,
|
|
||||||
NodeMouseHandler,
|
|
||||||
NodeTypesWrapped,
|
|
||||||
Position,
|
|
||||||
ReactFlowState,
|
|
||||||
WrapNodeProps,
|
|
||||||
} from '../../types';
|
|
||||||
import { internalsSymbol } from '../../utils';
|
import { internalsSymbol } from '../../utils';
|
||||||
|
|
||||||
interface NodeRendererProps {
|
interface NodeRendererProps {
|
||||||
@@ -22,9 +15,6 @@ interface NodeRendererProps {
|
|||||||
onNodeMouseMove?: NodeMouseHandler;
|
onNodeMouseMove?: NodeMouseHandler;
|
||||||
onNodeMouseLeave?: NodeMouseHandler;
|
onNodeMouseLeave?: NodeMouseHandler;
|
||||||
onNodeContextMenu?: NodeMouseHandler;
|
onNodeContextMenu?: NodeMouseHandler;
|
||||||
onNodeDragStart?: NodeDragHandler;
|
|
||||||
onNodeDrag?: NodeDragHandler;
|
|
||||||
onNodeDragStop?: NodeDragHandler;
|
|
||||||
onlyRenderVisibleElements: boolean;
|
onlyRenderVisibleElements: boolean;
|
||||||
noPanClassName: string;
|
noPanClassName: string;
|
||||||
noDragClassName: string;
|
noDragClassName: string;
|
||||||
@@ -109,9 +99,6 @@ const NodeRenderer = (props: NodeRendererProps) => {
|
|||||||
onMouseLeave={props.onNodeMouseLeave}
|
onMouseLeave={props.onNodeMouseLeave}
|
||||||
onContextMenu={props.onNodeContextMenu}
|
onContextMenu={props.onNodeContextMenu}
|
||||||
onDoubleClick={props.onNodeDoubleClick}
|
onDoubleClick={props.onNodeDoubleClick}
|
||||||
onDragStart={props.onNodeDragStart}
|
|
||||||
onDrag={props.onNodeDrag}
|
|
||||||
onDragStop={props.onNodeDragStop}
|
|
||||||
selected={!!node.selected}
|
selected={!!node.selected}
|
||||||
isDraggable={isDraggable}
|
isDraggable={isDraggable}
|
||||||
isSelectable={isSelectable}
|
isSelectable={isSelectable}
|
||||||
|
|||||||
@@ -165,9 +165,6 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
|
|||||||
onNodeMouseLeave={onNodeMouseLeave}
|
onNodeMouseLeave={onNodeMouseLeave}
|
||||||
onNodeContextMenu={onNodeContextMenu}
|
onNodeContextMenu={onNodeContextMenu}
|
||||||
onNodeDoubleClick={onNodeDoubleClick}
|
onNodeDoubleClick={onNodeDoubleClick}
|
||||||
onNodeDragStart={onNodeDragStart}
|
|
||||||
onNodeDrag={onNodeDrag}
|
|
||||||
onNodeDragStop={onNodeDragStop}
|
|
||||||
nodeTypes={nodeTypesWrapped}
|
nodeTypes={nodeTypesWrapped}
|
||||||
edgeTypes={edgeTypesWrapped}
|
edgeTypes={edgeTypesWrapped}
|
||||||
connectionLineType={connectionLineType}
|
connectionLineType={connectionLineType}
|
||||||
@@ -193,9 +190,6 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
|
|||||||
onPaneClick={onPaneClick}
|
onPaneClick={onPaneClick}
|
||||||
onPaneScroll={onPaneScroll}
|
onPaneScroll={onPaneScroll}
|
||||||
onPaneContextMenu={onPaneContextMenu}
|
onPaneContextMenu={onPaneContextMenu}
|
||||||
onSelectionDragStart={onSelectionDragStart}
|
|
||||||
onSelectionDrag={onSelectionDrag}
|
|
||||||
onSelectionDragStop={onSelectionDragStop}
|
|
||||||
onSelectionContextMenu={onSelectionContextMenu}
|
onSelectionContextMenu={onSelectionContextMenu}
|
||||||
onEdgeUpdate={onEdgeUpdate}
|
onEdgeUpdate={onEdgeUpdate}
|
||||||
onEdgeContextMenu={onEdgeContextMenu}
|
onEdgeContextMenu={onEdgeContextMenu}
|
||||||
@@ -243,6 +237,12 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
|
|||||||
fitViewOptions={fitViewOptions}
|
fitViewOptions={fitViewOptions}
|
||||||
onNodesDelete={onNodesDelete}
|
onNodesDelete={onNodesDelete}
|
||||||
onEdgesDelete={onEdgesDelete}
|
onEdgesDelete={onEdgesDelete}
|
||||||
|
onNodeDragStart={onNodeDragStart}
|
||||||
|
onNodeDrag={onNodeDrag}
|
||||||
|
onNodeDragStop={onNodeDragStop}
|
||||||
|
onSelectionDrag={onSelectionDrag}
|
||||||
|
onSelectionDragStart={onSelectionDragStart}
|
||||||
|
onSelectionDragStop={onSelectionDragStop}
|
||||||
/>
|
/>
|
||||||
{onSelectionChange && <SelectionListener onSelectionChange={onSelectionChange} />}
|
{onSelectionChange && <SelectionListener onSelectionChange={onSelectionChange} />}
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import React, { ReactNode } from 'react';
|
|||||||
import { useStore } from '../../store';
|
import { useStore } from '../../store';
|
||||||
import { ReactFlowState } from '../../types';
|
import { ReactFlowState } from '../../types';
|
||||||
|
|
||||||
const selector = (s: ReactFlowState) => s.transform;
|
const selector = (s: ReactFlowState) => `translate(${s.transform[0]}px,${s.transform[1]}px) scale(${s.transform[2]})`;
|
||||||
|
|
||||||
type ViewportProps = {
|
type ViewportProps = {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
@@ -13,10 +13,7 @@ function Viewport({ children }: ViewportProps) {
|
|||||||
const transform = useStore(selector);
|
const transform = useStore(selector);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className="react-flow__viewport react-flow__container" style={{ transform: transform }}>
|
||||||
className="react-flow__viewport react-flow__container"
|
|
||||||
style={{ transform: `translate(${transform[0]}px,${transform[1]}px) scale(${transform[2]})` }}
|
|
||||||
>
|
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
+20
-12
@@ -4,7 +4,7 @@ import { select } from 'd3-selection';
|
|||||||
|
|
||||||
import { useStoreApi } from '../../store';
|
import { useStoreApi } from '../../store';
|
||||||
import { pointToRendererPoint } from '../../utils/graph';
|
import { pointToRendererPoint } from '../../utils/graph';
|
||||||
import { NodeDragItem, NodeDragHandler } from '../../types';
|
import { NodeDragItem, Node, SelectionDragHandler } from '../../types';
|
||||||
import { getDragItems, getEventHandlerParams, hasSelector, updatePosition } from './utils';
|
import { getDragItems, getEventHandlerParams, hasSelector, updatePosition } from './utils';
|
||||||
import { handleNodeClick } from '../../components/Nodes/utils';
|
import { handleNodeClick } from '../../components/Nodes/utils';
|
||||||
|
|
||||||
@@ -13,9 +13,6 @@ export type UseDragData = { dx: number; dy: number };
|
|||||||
|
|
||||||
type UseDragParams = {
|
type UseDragParams = {
|
||||||
nodeRef: RefObject<Element>;
|
nodeRef: RefObject<Element>;
|
||||||
onStart?: NodeDragHandler;
|
|
||||||
onDrag?: NodeDragHandler;
|
|
||||||
onStop?: NodeDragHandler;
|
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
noDragClassName?: string;
|
noDragClassName?: string;
|
||||||
handleSelector?: string;
|
handleSelector?: string;
|
||||||
@@ -24,10 +21,11 @@ type UseDragParams = {
|
|||||||
selectNodesOnDrag?: boolean;
|
selectNodesOnDrag?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function wrapSelectionDragFunc(selectionFunc?: SelectionDragHandler) {
|
||||||
|
return (event: MouseEvent, _: Node, nodes: Node[]) => selectionFunc?.(event, nodes);
|
||||||
|
}
|
||||||
|
|
||||||
function useDrag({
|
function useDrag({
|
||||||
onStart,
|
|
||||||
onDrag,
|
|
||||||
onStop,
|
|
||||||
nodeRef,
|
nodeRef,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
noDragClassName,
|
noDragClassName,
|
||||||
@@ -60,7 +58,15 @@ function useDrag({
|
|||||||
} else {
|
} else {
|
||||||
const dragHandler = drag()
|
const dragHandler = drag()
|
||||||
.on('start', (event: UseDragEvent) => {
|
.on('start', (event: UseDragEvent) => {
|
||||||
const { nodeInternals, multiSelectionActive, unselectNodesAndEdges } = store.getState();
|
const {
|
||||||
|
nodeInternals,
|
||||||
|
multiSelectionActive,
|
||||||
|
unselectNodesAndEdges,
|
||||||
|
onNodeDragStart,
|
||||||
|
onSelectionDragStart,
|
||||||
|
} = store.getState();
|
||||||
|
|
||||||
|
const onStart = nodeId ? onNodeDragStart : wrapSelectionDragFunc(onSelectionDragStart);
|
||||||
|
|
||||||
if (!selectNodesOnDrag && !multiSelectionActive && nodeId) {
|
if (!selectNodesOnDrag && !multiSelectionActive && nodeId) {
|
||||||
if (!nodeInternals.get(nodeId)?.selected) {
|
if (!nodeInternals.get(nodeId)?.selected) {
|
||||||
@@ -90,7 +96,7 @@ function useDrag({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.on('drag', (event: UseDragEvent) => {
|
.on('drag', (event: UseDragEvent) => {
|
||||||
const { updateNodePositions, nodeInternals, nodeExtent } = store.getState();
|
const { updateNodePositions, nodeInternals, nodeExtent, onNodeDrag, onSelectionDrag } = store.getState();
|
||||||
const pointerPos = getPointerPosition(event);
|
const pointerPos = getPointerPosition(event);
|
||||||
|
|
||||||
// skip events without movement
|
// skip events without movement
|
||||||
@@ -100,6 +106,8 @@ function useDrag({
|
|||||||
updatePosition(n, pointerPos, nodeInternals, nodeExtent)
|
updatePosition(n, pointerPos, nodeInternals, nodeExtent)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const onDrag = nodeId ? onNodeDrag : wrapSelectionDragFunc(onSelectionDrag);
|
||||||
|
|
||||||
updateNodePositions(dragItems.current, true, true);
|
updateNodePositions(dragItems.current, true, true);
|
||||||
setDragging(true);
|
setDragging(true);
|
||||||
|
|
||||||
@@ -116,6 +124,9 @@ function useDrag({
|
|||||||
event.on('end', (event) => {
|
event.on('end', (event) => {
|
||||||
setDragging(false);
|
setDragging(false);
|
||||||
if (dragItems.current) {
|
if (dragItems.current) {
|
||||||
|
const { updateNodePositions, nodeInternals, onNodeDragStop, onSelectionDragStop } = store.getState();
|
||||||
|
const onStop = nodeId ? onNodeDragStop : wrapSelectionDragFunc(onSelectionDragStop);
|
||||||
|
|
||||||
updateNodePositions(dragItems.current, false, false);
|
updateNodePositions(dragItems.current, false, false);
|
||||||
|
|
||||||
if (onStop) {
|
if (onStop) {
|
||||||
@@ -147,9 +158,6 @@ function useDrag({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
onStart,
|
|
||||||
onDrag,
|
|
||||||
onStop,
|
|
||||||
nodeRef,
|
nodeRef,
|
||||||
disabled,
|
disabled,
|
||||||
noDragClassName,
|
noDragClassName,
|
||||||
|
|||||||
@@ -8,11 +8,10 @@ import { ReactFlowState } from '../types';
|
|||||||
function useVisibleNodes(onlyRenderVisible: boolean) {
|
function useVisibleNodes(onlyRenderVisible: boolean) {
|
||||||
const nodes = useStore(
|
const nodes = useStore(
|
||||||
useCallback(
|
useCallback(
|
||||||
(s: ReactFlowState) => {
|
(s: ReactFlowState) =>
|
||||||
return onlyRenderVisible
|
onlyRenderVisible
|
||||||
? getNodesInside(s.nodeInternals, { x: 0, y: 0, width: s.width, height: s.height }, s.transform, true)
|
? getNodesInside(s.nodeInternals, { x: 0, y: 0, width: s.width, height: s.height }, s.transform, true)
|
||||||
: Array.from(s.nodeInternals.values());
|
: Array.from(s.nodeInternals.values()),
|
||||||
},
|
|
||||||
[onlyRenderVisible]
|
[onlyRenderVisible]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import {
|
|||||||
OnMoveEnd,
|
OnMoveEnd,
|
||||||
NodeDragHandler,
|
NodeDragHandler,
|
||||||
NodeMouseHandler,
|
NodeMouseHandler,
|
||||||
|
SelectionDragHandler,
|
||||||
} from '.';
|
} from '.';
|
||||||
import { HandleType } from './handles';
|
import { HandleType } from './handles';
|
||||||
|
|
||||||
@@ -73,9 +74,9 @@ export interface ReactFlowProps extends HTMLAttributes<HTMLDivElement> {
|
|||||||
onMoveStart?: OnMoveStart;
|
onMoveStart?: OnMoveStart;
|
||||||
onMoveEnd?: OnMoveEnd;
|
onMoveEnd?: OnMoveEnd;
|
||||||
onSelectionChange?: OnSelectionChangeFunc;
|
onSelectionChange?: OnSelectionChangeFunc;
|
||||||
onSelectionDragStart?: (event: ReactMouseEvent, nodes: Node[]) => void;
|
onSelectionDragStart?: SelectionDragHandler;
|
||||||
onSelectionDrag?: (event: ReactMouseEvent, nodes: Node[]) => void;
|
onSelectionDrag?: SelectionDragHandler;
|
||||||
onSelectionDragStop?: (event: ReactMouseEvent, nodes: Node[]) => void;
|
onSelectionDragStop?: SelectionDragHandler;
|
||||||
onSelectionContextMenu?: (event: ReactMouseEvent, nodes: Node[]) => void;
|
onSelectionContextMenu?: (event: ReactMouseEvent, nodes: Node[]) => void;
|
||||||
onPaneScroll?: (event?: WheelEvent) => void;
|
onPaneScroll?: (event?: WheelEvent) => void;
|
||||||
onPaneClick?: (event: ReactMouseEvent) => void;
|
onPaneClick?: (event: ReactMouseEvent) => void;
|
||||||
|
|||||||
+11
-5
@@ -148,17 +148,23 @@ export enum ConnectionLineType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type ConnectionLineComponentProps = {
|
export type ConnectionLineComponentProps = {
|
||||||
|
connectionLineStyle?: CSSProperties;
|
||||||
|
connectionLineType: ConnectionLineType;
|
||||||
|
fromNode?: Node;
|
||||||
|
fromHandle?: HandleElement;
|
||||||
|
fromX: number;
|
||||||
|
fromY: number;
|
||||||
|
toX: number;
|
||||||
|
toY: number;
|
||||||
|
fromPosition: Position;
|
||||||
|
toPosition: Position;
|
||||||
|
// remove in v11
|
||||||
sourceX: number;
|
sourceX: number;
|
||||||
sourceY: number;
|
sourceY: number;
|
||||||
sourcePosition?: Position;
|
sourcePosition?: Position;
|
||||||
targetX: number;
|
targetX: number;
|
||||||
targetY: number;
|
targetY: number;
|
||||||
targetPosition?: Position;
|
targetPosition?: Position;
|
||||||
connectionLineStyle?: CSSProperties;
|
|
||||||
connectionLineType: ConnectionLineType;
|
|
||||||
fromNode?: Node;
|
|
||||||
fromHandle?: HandleElement;
|
|
||||||
// backward compatibility, mark as deprecated?
|
|
||||||
sourceNode?: Node;
|
sourceNode?: Node;
|
||||||
sourceHandle?: HandleElement;
|
sourceHandle?: HandleElement;
|
||||||
};
|
};
|
||||||
|
|||||||
+18
-1
@@ -3,7 +3,16 @@ import { Selection as D3Selection, ZoomBehavior } from 'd3';
|
|||||||
|
|
||||||
import { XYPosition, Rect, Transform, CoordinateExtent } from './utils';
|
import { XYPosition, Rect, Transform, CoordinateExtent } from './utils';
|
||||||
import { NodeChange, EdgeChange } from './changes';
|
import { NodeChange, EdgeChange } from './changes';
|
||||||
import { Node, NodeInternals, NodeDimensionUpdate, NodeProps, WrapNodeProps, NodeDragItem } from './nodes';
|
import {
|
||||||
|
Node,
|
||||||
|
NodeInternals,
|
||||||
|
NodeDimensionUpdate,
|
||||||
|
NodeProps,
|
||||||
|
WrapNodeProps,
|
||||||
|
NodeDragItem,
|
||||||
|
NodeDragHandler,
|
||||||
|
SelectionDragHandler,
|
||||||
|
} from './nodes';
|
||||||
import { Edge, EdgeProps, WrapEdgeProps } from './edges';
|
import { Edge, EdgeProps, WrapEdgeProps } from './edges';
|
||||||
import { HandleType, StartHandle } from './handles';
|
import { HandleType, StartHandle } from './handles';
|
||||||
import { DefaultEdgeOptions } from '.';
|
import { DefaultEdgeOptions } from '.';
|
||||||
@@ -165,6 +174,14 @@ export type ReactFlowStore = {
|
|||||||
|
|
||||||
connectionStartHandle: StartHandle | null;
|
connectionStartHandle: StartHandle | null;
|
||||||
|
|
||||||
|
onNodeDragStart?: NodeDragHandler;
|
||||||
|
onNodeDrag?: NodeDragHandler;
|
||||||
|
onNodeDragStop?: NodeDragHandler;
|
||||||
|
|
||||||
|
onSelectionDragStart?: SelectionDragHandler;
|
||||||
|
onSelectionDrag?: SelectionDragHandler;
|
||||||
|
onSelectionDragStop?: SelectionDragHandler;
|
||||||
|
|
||||||
onConnect?: OnConnect;
|
onConnect?: OnConnect;
|
||||||
onConnectStart?: OnConnectStart;
|
onConnectStart?: OnConnectStart;
|
||||||
onConnectStop?: OnConnectStop;
|
onConnectStop?: OnConnectStop;
|
||||||
|
|||||||
+1
-3
@@ -55,6 +55,7 @@ export interface NodeProps<T = any> {
|
|||||||
|
|
||||||
export type NodeMouseHandler = (event: ReactMouseEvent, node: Node) => void;
|
export type NodeMouseHandler = (event: ReactMouseEvent, node: Node) => void;
|
||||||
export type NodeDragHandler = (event: ReactMouseEvent, node: Node, nodes: Node[]) => void;
|
export type NodeDragHandler = (event: ReactMouseEvent, node: Node, nodes: Node[]) => void;
|
||||||
|
export type SelectionDragHandler = (event: ReactMouseEvent, nodes: Node[]) => void;
|
||||||
|
|
||||||
export interface WrapNodeProps<T = any> {
|
export interface WrapNodeProps<T = any> {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -74,9 +75,6 @@ export interface WrapNodeProps<T = any> {
|
|||||||
onMouseMove?: NodeMouseHandler;
|
onMouseMove?: NodeMouseHandler;
|
||||||
onMouseLeave?: NodeMouseHandler;
|
onMouseLeave?: NodeMouseHandler;
|
||||||
onContextMenu?: NodeMouseHandler;
|
onContextMenu?: NodeMouseHandler;
|
||||||
onDragStart?: NodeDragHandler;
|
|
||||||
onDrag?: NodeDragHandler;
|
|
||||||
onDragStop?: NodeDragHandler;
|
|
||||||
style?: CSSProperties;
|
style?: CSSProperties;
|
||||||
className?: string;
|
className?: string;
|
||||||
sourcePosition: Position;
|
sourcePosition: Position;
|
||||||
|
|||||||
@@ -10,17 +10,14 @@ function handleParentExpand(res: any[], updateItem: any) {
|
|||||||
if (extendWidth > 0 || extendHeight > 0 || updateItem.position.x < 0 || updateItem.position.y < 0) {
|
if (extendWidth > 0 || extendHeight > 0 || updateItem.position.x < 0 || updateItem.position.y < 0) {
|
||||||
parent.style = { ...parent.style } || {};
|
parent.style = { ...parent.style } || {};
|
||||||
|
|
||||||
|
parent.style.width = parent.style.width ?? parent.width;
|
||||||
|
parent.style.height = parent.style.height ?? parent.height;
|
||||||
|
|
||||||
if (extendWidth > 0) {
|
if (extendWidth > 0) {
|
||||||
if (!parent.style.width) {
|
|
||||||
parent.style.width = parent.width;
|
|
||||||
}
|
|
||||||
parent.style.width += extendWidth;
|
parent.style.width += extendWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (extendHeight > 0) {
|
if (extendHeight > 0) {
|
||||||
if (!parent.style.height) {
|
|
||||||
parent.style.height = parent.height;
|
|
||||||
}
|
|
||||||
parent.style.height += extendHeight;
|
parent.style.height += extendHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user