refactor(connectionline): separate edge renderer and connectionline
This commit is contained in:
@@ -48,8 +48,6 @@ const initialEdges: Edge[] = [
|
|||||||
{ id: 'e1-3', source: '1', target: '3' },
|
{ id: 'e1-3', source: '1', target: '3' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const nodeOrigin: NodeOrigin = [0.5, 0.5];
|
|
||||||
|
|
||||||
const defaultEdgeOptions = {};
|
const defaultEdgeOptions = {};
|
||||||
|
|
||||||
const BasicFlow = () => {
|
const BasicFlow = () => {
|
||||||
@@ -96,7 +94,6 @@ const BasicFlow = () => {
|
|||||||
selectNodesOnDrag={false}
|
selectNodesOnDrag={false}
|
||||||
elevateEdgesOnSelect
|
elevateEdgesOnSelect
|
||||||
elevateNodesOnSelect={false}
|
elevateNodesOnSelect={false}
|
||||||
// nodeOrigin={nodeOrigin}
|
|
||||||
>
|
>
|
||||||
<Background variant={BackgroundVariant.Dots} />
|
<Background variant={BackgroundVariant.Dots} />
|
||||||
<MiniMap />
|
<MiniMap />
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import React, { FC } from 'react';
|
|
||||||
import { ConnectionLineComponentProps } from 'reactflow';
|
import { ConnectionLineComponentProps } from 'reactflow';
|
||||||
|
|
||||||
const ConnectionLine: FC<ConnectionLineComponentProps> = ({ fromX, fromY, toX, toY }) => {
|
function ConnectionLine({ fromX, fromY, toX, toY }: ConnectionLineComponentProps) {
|
||||||
return (
|
return (
|
||||||
<g>
|
<>
|
||||||
<path
|
<path
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="#222"
|
stroke="#222"
|
||||||
@@ -12,8 +11,8 @@ const ConnectionLine: FC<ConnectionLineComponentProps> = ({ fromX, fromY, toX, t
|
|||||||
d={`M${fromX},${fromY} C ${fromX} ${toY} ${fromX} ${toY} ${toX},${toY}`}
|
d={`M${fromX},${fromY} C ${fromX} ${toY} ${fromX} ${toY} ${toX},${toY}`}
|
||||||
/>
|
/>
|
||||||
<circle cx={toX} cy={toY} fill="#fff" r={3} stroke="#222" strokeWidth={1.5} />
|
<circle cx={toX} cy={toY} fill="#fff" r={3} stroke="#222" strokeWidth={1.5} />
|
||||||
</g>
|
</>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export default ConnectionLine;
|
export default ConnectionLine;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { MouseEvent, useCallback } from 'react';
|
import { MouseEvent, useCallback } from 'react';
|
||||||
import ReactFlow, { addEdge, Node, Connection, Edge, useNodesState, useEdgesState, NodeOrigin } from 'reactflow';
|
import ReactFlow, { addEdge, Node, Connection, Edge, useNodesState, useEdgesState } from 'reactflow';
|
||||||
|
|
||||||
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
|
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
|
||||||
const onNodeClick = (_: MouseEvent, node: Node) => console.log('click', node);
|
const onNodeClick = (_: MouseEvent, node: Node) => console.log('click', node);
|
||||||
@@ -100,7 +100,6 @@ const BasicFlow = () => {
|
|||||||
onNodeClick={onNodeClick}
|
onNodeClick={onNodeClick}
|
||||||
onConnect={onConnect}
|
onConnect={onConnect}
|
||||||
onNodeDragStop={onNodeDragStop}
|
onNodeDragStop={onNodeDragStop}
|
||||||
disableKeyboardA11y
|
|
||||||
onNodeDrag={onNodeDrag}
|
onNodeDrag={onNodeDrag}
|
||||||
>
|
>
|
||||||
<div style={{ position: 'absolute', right: 10, top: 10, zIndex: 4 }}>
|
<div style={{ position: 'absolute', right: 10, top: 10, zIndex: 4 }}>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { MouseEvent, useCallback } from 'react';
|
import { MouseEvent, useCallback } from 'react';
|
||||||
import ReactFlow, {
|
import ReactFlow, {
|
||||||
useReactFlow,
|
useReactFlow,
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
@@ -184,6 +184,7 @@ const UpdateNodeInternalsFlow = () => {
|
|||||||
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
||||||
|
|
||||||
const onConnect = (params: Edge | Connection) => setEdges((els) => addEdge(params, els));
|
const onConnect = (params: Edge | Connection) => setEdges((els) => addEdge(params, els));
|
||||||
|
|
||||||
const { project } = useReactFlow();
|
const { project } = useReactFlow();
|
||||||
const onEdgeUpdate = (oldEdge: Edge, newConnection: Connection) =>
|
const onEdgeUpdate = (oldEdge: Edge, newConnection: Connection) =>
|
||||||
setEdges((els) => updateEdge(oldEdge, newConnection, els));
|
setEdges((els) => updateEdge(oldEdge, newConnection, els));
|
||||||
|
|||||||
@@ -6,16 +6,15 @@ import { getBezierPath } from '../Edges/BezierEdge';
|
|||||||
import { getSmoothStepPath } from '../Edges/SmoothStepEdge';
|
import { getSmoothStepPath } from '../Edges/SmoothStepEdge';
|
||||||
import { getSimpleBezierPath } from '../Edges/SimpleBezierEdge';
|
import { getSimpleBezierPath } from '../Edges/SimpleBezierEdge';
|
||||||
import { internalsSymbol } from '../../utils';
|
import { internalsSymbol } from '../../utils';
|
||||||
import type { ConnectionLineComponent, HandleType, ReactFlowStore } from '../../types';
|
import type { ConnectionLineComponent, HandleType, ReactFlowState, ReactFlowStore } from '../../types';
|
||||||
import { Position, ConnectionLineType, ConnectionMode } from '../../types';
|
import { Position, ConnectionLineType, ConnectionMode } from '../../types';
|
||||||
|
|
||||||
type ConnectionLineProps = {
|
type ConnectionLineProps = {
|
||||||
connectionNodeId: string;
|
nodeId: string;
|
||||||
connectionHandleType: HandleType;
|
handleType: HandleType;
|
||||||
connectionLineType: ConnectionLineType;
|
type: ConnectionLineType;
|
||||||
isConnectable: boolean;
|
style?: CSSProperties;
|
||||||
connectionLineStyle?: CSSProperties;
|
CustomComponent?: ConnectionLineComponent;
|
||||||
CustomConnectionLineComponent?: ConnectionLineComponent;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const oppositePosition = {
|
const oppositePosition = {
|
||||||
@@ -26,69 +25,62 @@ const oppositePosition = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ConnectionLine = ({
|
const ConnectionLine = ({
|
||||||
connectionNodeId,
|
nodeId,
|
||||||
connectionHandleType,
|
handleType,
|
||||||
connectionLineStyle,
|
style,
|
||||||
connectionLineType = ConnectionLineType.Bezier,
|
type = ConnectionLineType.Bezier,
|
||||||
isConnectable,
|
CustomComponent,
|
||||||
CustomConnectionLineComponent,
|
|
||||||
}: ConnectionLineProps) => {
|
}: ConnectionLineProps) => {
|
||||||
const { fromNode, handleId, toX, toY, connectionMode } = useStore(
|
const { fromNode, handleId, toX, toY, connectionMode } = useStore(
|
||||||
useCallback(
|
useCallback(
|
||||||
(s: ReactFlowStore) => ({
|
(s: ReactFlowStore) => ({
|
||||||
fromNode: s.nodeInternals.get(connectionNodeId),
|
fromNode: s.nodeInternals.get(nodeId),
|
||||||
handleId: s.connectionHandleId,
|
handleId: s.connectionHandleId,
|
||||||
toX: (s.connectionPosition.x - s.transform[0]) / s.transform[2],
|
toX: (s.connectionPosition.x - s.transform[0]) / s.transform[2],
|
||||||
toY: (s.connectionPosition.y - s.transform[1]) / s.transform[2],
|
toY: (s.connectionPosition.y - s.transform[1]) / s.transform[2],
|
||||||
connectionMode: s.connectionMode,
|
connectionMode: s.connectionMode,
|
||||||
}),
|
}),
|
||||||
[connectionNodeId]
|
[nodeId]
|
||||||
),
|
),
|
||||||
shallow
|
shallow
|
||||||
);
|
);
|
||||||
const fromHandleBounds = fromNode?.[internalsSymbol]?.handleBounds;
|
const fromHandleBounds = fromNode?.[internalsSymbol]?.handleBounds;
|
||||||
let handleBounds = fromHandleBounds?.[connectionHandleType];
|
let handleBounds = fromHandleBounds?.[handleType];
|
||||||
|
|
||||||
if (connectionMode === ConnectionMode.Loose) {
|
if (connectionMode === ConnectionMode.Loose) {
|
||||||
handleBounds = handleBounds
|
handleBounds = handleBounds ? handleBounds : fromHandleBounds?.[handleType === 'source' ? 'target' : 'source'];
|
||||||
? handleBounds
|
|
||||||
: fromHandleBounds?.[connectionHandleType === 'source' ? 'target' : 'source'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fromNode || !isConnectable || !handleBounds) {
|
if (!fromNode || !handleBounds) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fromHandle = handleId ? handleBounds.find((d) => d.id === handleId) : handleBounds[0];
|
const fromHandle = handleId ? handleBounds.find((d) => d.id === handleId) : handleBounds[0];
|
||||||
const fromHandleX = fromHandle ? fromHandle.x + fromHandle.width / 2 : (fromNode?.width ?? 0) / 2;
|
const fromHandleX = fromHandle ? fromHandle.x + fromHandle.width / 2 : (fromNode.width ?? 0) / 2;
|
||||||
const fromHandleY = fromHandle ? fromHandle.y + fromHandle.height / 2 : fromNode?.height ?? 0;
|
const fromHandleY = fromHandle ? fromHandle.y + fromHandle.height / 2 : fromNode.height ?? 0;
|
||||||
const fromX = (fromNode?.positionAbsolute?.x || 0) + fromHandleX;
|
const fromX = (fromNode.positionAbsolute?.x ?? 0) + fromHandleX;
|
||||||
const fromY = (fromNode?.positionAbsolute?.y || 0) + fromHandleY;
|
const fromY = (fromNode.positionAbsolute?.y ?? 0) + fromHandleY;
|
||||||
|
|
||||||
const fromPosition = fromHandle?.position;
|
const fromPosition = fromHandle?.position;
|
||||||
|
const toPosition = fromPosition ? oppositePosition[fromPosition] : null;
|
||||||
|
|
||||||
if (!fromPosition) {
|
if (!fromPosition || !toPosition) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const toPosition: Position = oppositePosition[fromPosition];
|
if (CustomComponent) {
|
||||||
|
|
||||||
if (CustomConnectionLineComponent) {
|
|
||||||
return (
|
return (
|
||||||
<g className="react-flow__connection">
|
<CustomComponent
|
||||||
<CustomConnectionLineComponent
|
connectionLineType={type}
|
||||||
connectionLineType={connectionLineType}
|
connectionLineStyle={style}
|
||||||
connectionLineStyle={connectionLineStyle}
|
fromNode={fromNode}
|
||||||
fromNode={fromNode}
|
fromHandle={fromHandle}
|
||||||
fromHandle={fromHandle}
|
fromX={fromX}
|
||||||
fromX={fromX}
|
fromY={fromY}
|
||||||
fromY={fromY}
|
toX={toX}
|
||||||
toX={toX}
|
toY={toY}
|
||||||
toY={toY}
|
fromPosition={fromPosition}
|
||||||
fromPosition={fromPosition}
|
toPosition={toPosition}
|
||||||
toPosition={toPosition}
|
/>
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,29 +95,62 @@ const ConnectionLine = ({
|
|||||||
targetPosition: toPosition,
|
targetPosition: toPosition,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (connectionLineType === ConnectionLineType.Bezier) {
|
if (type === ConnectionLineType.Bezier) {
|
||||||
// we assume the destination position is opposite to the source position
|
// we assume the destination position is opposite to the source position
|
||||||
[dAttr] = getBezierPath(pathParams);
|
[dAttr] = getBezierPath(pathParams);
|
||||||
} else if (connectionLineType === ConnectionLineType.Step) {
|
} else if (type === ConnectionLineType.Step) {
|
||||||
[dAttr] = getSmoothStepPath({
|
[dAttr] = getSmoothStepPath({
|
||||||
...pathParams,
|
...pathParams,
|
||||||
borderRadius: 0,
|
borderRadius: 0,
|
||||||
});
|
});
|
||||||
} else if (connectionLineType === ConnectionLineType.SmoothStep) {
|
} else if (type === ConnectionLineType.SmoothStep) {
|
||||||
[dAttr] = getSmoothStepPath(pathParams);
|
[dAttr] = getSmoothStepPath(pathParams);
|
||||||
} else if (connectionLineType === ConnectionLineType.SimpleBezier) {
|
} else if (type === ConnectionLineType.SimpleBezier) {
|
||||||
[dAttr] = getSimpleBezierPath(pathParams);
|
[dAttr] = getSimpleBezierPath(pathParams);
|
||||||
} else {
|
} else {
|
||||||
dAttr = `M${fromX},${fromY} ${toX},${toY}`;
|
dAttr = `M${fromX},${fromY} ${toX},${toY}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <path d={dAttr} fill="none" className="react-flow__connection-path" style={style} />;
|
||||||
<g className="react-flow__connection">
|
|
||||||
<path d={dAttr} fill="none" className="react-flow__connection-path" style={connectionLineStyle} />
|
|
||||||
</g>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ConnectionLine.displayName = 'ConnectionLine';
|
ConnectionLine.displayName = 'ConnectionLine';
|
||||||
|
|
||||||
export default ConnectionLine;
|
type ConnectionLineWrapperProps = {
|
||||||
|
type: ConnectionLineType;
|
||||||
|
component?: ConnectionLineComponent;
|
||||||
|
containerStyle?: CSSProperties;
|
||||||
|
style?: CSSProperties;
|
||||||
|
};
|
||||||
|
|
||||||
|
const selector = (s: ReactFlowState) => ({
|
||||||
|
nodeId: s.connectionNodeId,
|
||||||
|
handleType: s.connectionHandleType,
|
||||||
|
nodesConnectable: s.nodesConnectable,
|
||||||
|
width: s.width,
|
||||||
|
height: s.height,
|
||||||
|
});
|
||||||
|
|
||||||
|
function ConnectionLineWrapper({ containerStyle, style, type, component }: ConnectionLineWrapperProps) {
|
||||||
|
const { nodeId, handleType, nodesConnectable, width, height } = useStore(selector, shallow);
|
||||||
|
const isValid = !!(nodeId && handleType && width && nodesConnectable);
|
||||||
|
|
||||||
|
if (!isValid) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
style={containerStyle}
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
className="react-flow__edges react-flow__connectionline react-flow__container"
|
||||||
|
>
|
||||||
|
<g className="react-flow__connection">
|
||||||
|
<ConnectionLine nodeId={nodeId} handleType={handleType} style={style} type={type} CustomComponent={component} />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ConnectionLineWrapper;
|
||||||
|
|||||||
@@ -1,27 +1,19 @@
|
|||||||
import { memo } from 'react';
|
import { memo, ReactNode } from 'react';
|
||||||
import { shallow } from 'zustand/shallow';
|
import { shallow } from 'zustand/shallow';
|
||||||
import cc from 'classcat';
|
import cc from 'classcat';
|
||||||
|
|
||||||
import { useStore } from '../../hooks/useStore';
|
import { useStore } from '../../hooks/useStore';
|
||||||
import useVisibleEdges from '../../hooks/useVisibleEdges';
|
import useVisibleEdges from '../../hooks/useVisibleEdges';
|
||||||
import ConnectionLine from '../../components/ConnectionLine/index';
|
|
||||||
import MarkerDefinitions from './MarkerDefinitions';
|
import MarkerDefinitions from './MarkerDefinitions';
|
||||||
import { getEdgePositions, getHandle, getNodeData } from './utils';
|
import { getEdgePositions, getHandle, getNodeData } from './utils';
|
||||||
|
|
||||||
import { GraphViewProps } from '../GraphView';
|
import { GraphViewProps } from '../GraphView';
|
||||||
import { devWarn } from '../../utils';
|
|
||||||
import { ConnectionMode, Position } from '../../types';
|
import { ConnectionMode, Position } from '../../types';
|
||||||
import type { Edge, ReactFlowState } from '../../types';
|
import type { Edge, ReactFlowState } from '../../types';
|
||||||
|
|
||||||
type EdgeRendererProps = Pick<
|
type EdgeRendererProps = Pick<
|
||||||
GraphViewProps,
|
GraphViewProps,
|
||||||
| 'edgeTypes'
|
| 'edgeTypes'
|
||||||
| 'connectionLineType'
|
|
||||||
| 'connectionLineType'
|
|
||||||
| 'connectionLineStyle'
|
|
||||||
| 'connectionLineComponent'
|
|
||||||
| 'connectionLineContainerStyle'
|
|
||||||
| 'connectionLineContainerStyle'
|
|
||||||
| 'onEdgeClick'
|
| 'onEdgeClick'
|
||||||
| 'onEdgeDoubleClick'
|
| 'onEdgeDoubleClick'
|
||||||
| 'defaultMarkerColor'
|
| 'defaultMarkerColor'
|
||||||
@@ -40,11 +32,10 @@ type EdgeRendererProps = Pick<
|
|||||||
| 'disableKeyboardA11y'
|
| 'disableKeyboardA11y'
|
||||||
> & {
|
> & {
|
||||||
elevateEdgesOnSelect: boolean;
|
elevateEdgesOnSelect: boolean;
|
||||||
|
children: ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
const selector = (s: ReactFlowState) => ({
|
const selector = (s: ReactFlowState) => ({
|
||||||
connectionNodeId: s.connectionNodeId,
|
|
||||||
connectionHandleType: s.connectionHandleType,
|
|
||||||
nodesConnectable: s.nodesConnectable,
|
nodesConnectable: s.nodesConnectable,
|
||||||
edgesFocusable: s.edgesFocusable,
|
edgesFocusable: s.edgesFocusable,
|
||||||
elementsSelectable: s.elementsSelectable,
|
elementsSelectable: s.elementsSelectable,
|
||||||
@@ -52,35 +43,38 @@ const selector = (s: ReactFlowState) => ({
|
|||||||
height: s.height,
|
height: s.height,
|
||||||
connectionMode: s.connectionMode,
|
connectionMode: s.connectionMode,
|
||||||
nodeInternals: s.nodeInternals,
|
nodeInternals: s.nodeInternals,
|
||||||
|
onError: s.onError,
|
||||||
});
|
});
|
||||||
|
|
||||||
const EdgeRenderer = (props: EdgeRendererProps) => {
|
const EdgeRenderer = ({
|
||||||
const {
|
defaultMarkerColor,
|
||||||
connectionNodeId,
|
onlyRenderVisibleElements,
|
||||||
connectionHandleType,
|
elevateEdgesOnSelect,
|
||||||
nodesConnectable,
|
rfId,
|
||||||
edgesFocusable,
|
edgeTypes,
|
||||||
elementsSelectable,
|
noPanClassName,
|
||||||
width,
|
onEdgeUpdate,
|
||||||
height,
|
onEdgeContextMenu,
|
||||||
connectionMode,
|
onEdgeMouseEnter,
|
||||||
nodeInternals,
|
onEdgeMouseMove,
|
||||||
} = useStore(selector, shallow);
|
onEdgeMouseLeave,
|
||||||
const edgeTree = useVisibleEdges(props.onlyRenderVisibleElements, nodeInternals, props.elevateEdgesOnSelect);
|
onEdgeClick,
|
||||||
|
edgeUpdaterRadius,
|
||||||
|
onEdgeDoubleClick,
|
||||||
|
onEdgeUpdateStart,
|
||||||
|
onEdgeUpdateEnd,
|
||||||
|
children,
|
||||||
|
}: EdgeRendererProps) => {
|
||||||
|
const { edgesFocusable, elementsSelectable, width, height, connectionMode, nodeInternals, onError } = useStore(
|
||||||
|
selector,
|
||||||
|
shallow
|
||||||
|
);
|
||||||
|
const edgeTree = useVisibleEdges(onlyRenderVisibleElements, nodeInternals, elevateEdgesOnSelect);
|
||||||
|
|
||||||
if (!width) {
|
if (!width) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
|
||||||
connectionLineType,
|
|
||||||
defaultMarkerColor,
|
|
||||||
connectionLineStyle,
|
|
||||||
connectionLineComponent,
|
|
||||||
connectionLineContainerStyle,
|
|
||||||
} = props;
|
|
||||||
const renderConnectionLine = connectionNodeId && connectionHandleType;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{edgeTree.map(({ level, edges, isMaxLevel }) => (
|
{edgeTree.map(({ level, edges, isMaxLevel }) => (
|
||||||
@@ -91,11 +85,11 @@ const EdgeRenderer = (props: EdgeRendererProps) => {
|
|||||||
height={height}
|
height={height}
|
||||||
className="react-flow__edges react-flow__container"
|
className="react-flow__edges react-flow__container"
|
||||||
>
|
>
|
||||||
{isMaxLevel && <MarkerDefinitions defaultColor={defaultMarkerColor} rfId={props.rfId} />}
|
{isMaxLevel && <MarkerDefinitions defaultColor={defaultMarkerColor} rfId={rfId} />}
|
||||||
<g>
|
<g>
|
||||||
{edges.map((edge: Edge) => {
|
{edges.map((edge: Edge) => {
|
||||||
const [sourceNodeRect, sourceHandleBounds, sourceIsValid] = getNodeData(nodeInternals.get(edge.source)!);
|
const [sourceNodeRect, sourceHandleBounds, sourceIsValid] = getNodeData(nodeInternals.get(edge.source));
|
||||||
const [targetNodeRect, targetHandleBounds, targetIsValid] = getNodeData(nodeInternals.get(edge.target)!);
|
const [targetNodeRect, targetHandleBounds, targetIsValid] = getNodeData(nodeInternals.get(edge.target));
|
||||||
|
|
||||||
if (!sourceIsValid || !targetIsValid) {
|
if (!sourceIsValid || !targetIsValid) {
|
||||||
return null;
|
return null;
|
||||||
@@ -103,31 +97,29 @@ const EdgeRenderer = (props: EdgeRendererProps) => {
|
|||||||
|
|
||||||
let edgeType = edge.type || 'default';
|
let edgeType = edge.type || 'default';
|
||||||
|
|
||||||
if (!props.edgeTypes[edgeType]) {
|
if (!edgeTypes[edgeType]) {
|
||||||
devWarn(
|
onError?.('003', `Edge type "${edgeType}" not found. Using fallback type "default".`);
|
||||||
`Edge type "${edgeType}" not found. Using fallback type "default". Help: https://reactflow.dev/error#300`
|
|
||||||
);
|
|
||||||
|
|
||||||
edgeType = 'default';
|
edgeType = 'default';
|
||||||
}
|
}
|
||||||
|
|
||||||
const EdgeComponent = props.edgeTypes[edgeType] || props.edgeTypes.default;
|
const EdgeComponent = edgeTypes[edgeType] || edgeTypes.default;
|
||||||
// when connection type is loose we can define all handles as sources
|
// when connection type is loose we can define all handles as sources and connect source -> source
|
||||||
const targetNodeHandles =
|
const targetNodeHandles =
|
||||||
connectionMode === ConnectionMode.Strict
|
connectionMode === ConnectionMode.Strict
|
||||||
? targetHandleBounds!.target
|
? targetHandleBounds!.target
|
||||||
: (targetHandleBounds!.target ?? []).concat(targetHandleBounds!.source ?? []);
|
: (targetHandleBounds!.target ?? []).concat(targetHandleBounds!.source ?? []);
|
||||||
const sourceHandle = getHandle(sourceHandleBounds!.source!, edge.sourceHandle || null);
|
const sourceHandle = getHandle(sourceHandleBounds!.source!, edge.sourceHandle);
|
||||||
const targetHandle = getHandle(targetNodeHandles!, edge.targetHandle || null);
|
const targetHandle = getHandle(targetNodeHandles!, edge.targetHandle);
|
||||||
const sourcePosition = sourceHandle?.position || Position.Bottom;
|
const sourcePosition = sourceHandle?.position || Position.Bottom;
|
||||||
const targetPosition = targetHandle?.position || Position.Top;
|
const targetPosition = targetHandle?.position || Position.Top;
|
||||||
const isFocusable = !!(edge.focusable || (edgesFocusable && typeof edge.focusable === 'undefined'));
|
const isFocusable = !!(edge.focusable || (edgesFocusable && typeof edge.focusable === 'undefined'));
|
||||||
|
|
||||||
if (!sourceHandle || !targetHandle) {
|
if (!sourceHandle || !targetHandle) {
|
||||||
devWarn(
|
onError?.(
|
||||||
`Couldn't create edge for ${!sourceHandle ? 'source' : 'target'} handle id: ${
|
'008',
|
||||||
|
`Couldn't create edge for ${!sourceHandle ? 'source' : 'target'} handle id: "${
|
||||||
!sourceHandle ? edge.sourceHandle : edge.targetHandle
|
!sourceHandle ? edge.sourceHandle : edge.targetHandle
|
||||||
}; edge id: ${edge.id}. Help: https://reactflow.dev/error#800`
|
}", edge id: ${edge.id}.`
|
||||||
);
|
);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -146,7 +138,7 @@ const EdgeRenderer = (props: EdgeRendererProps) => {
|
|||||||
<EdgeComponent
|
<EdgeComponent
|
||||||
key={edge.id}
|
key={edge.id}
|
||||||
id={edge.id}
|
id={edge.id}
|
||||||
className={cc([edge.className, props.noPanClassName])}
|
className={cc([edge.className, noPanClassName])}
|
||||||
type={edgeType}
|
type={edgeType}
|
||||||
data={edge.data}
|
data={edge.data}
|
||||||
selected={!!edge.selected}
|
selected={!!edge.selected}
|
||||||
@@ -172,17 +164,17 @@ const EdgeRenderer = (props: EdgeRendererProps) => {
|
|||||||
sourcePosition={sourcePosition}
|
sourcePosition={sourcePosition}
|
||||||
targetPosition={targetPosition}
|
targetPosition={targetPosition}
|
||||||
elementsSelectable={elementsSelectable}
|
elementsSelectable={elementsSelectable}
|
||||||
onEdgeUpdate={props.onEdgeUpdate}
|
onEdgeUpdate={onEdgeUpdate}
|
||||||
onContextMenu={props.onEdgeContextMenu}
|
onContextMenu={onEdgeContextMenu}
|
||||||
onMouseEnter={props.onEdgeMouseEnter}
|
onMouseEnter={onEdgeMouseEnter}
|
||||||
onMouseMove={props.onEdgeMouseMove}
|
onMouseMove={onEdgeMouseMove}
|
||||||
onMouseLeave={props.onEdgeMouseLeave}
|
onMouseLeave={onEdgeMouseLeave}
|
||||||
onClick={props.onEdgeClick}
|
onClick={onEdgeClick}
|
||||||
edgeUpdaterRadius={props.edgeUpdaterRadius}
|
edgeUpdaterRadius={edgeUpdaterRadius}
|
||||||
onEdgeDoubleClick={props.onEdgeDoubleClick}
|
onEdgeDoubleClick={onEdgeDoubleClick}
|
||||||
onEdgeUpdateStart={props.onEdgeUpdateStart}
|
onEdgeUpdateStart={onEdgeUpdateStart}
|
||||||
onEdgeUpdateEnd={props.onEdgeUpdateEnd}
|
onEdgeUpdateEnd={onEdgeUpdateEnd}
|
||||||
rfId={props.rfId}
|
rfId={rfId}
|
||||||
ariaLabel={edge.ariaLabel}
|
ariaLabel={edge.ariaLabel}
|
||||||
isFocusable={isFocusable}
|
isFocusable={isFocusable}
|
||||||
pathOptions={'pathOptions' in edge ? edge.pathOptions : undefined}
|
pathOptions={'pathOptions' in edge ? edge.pathOptions : undefined}
|
||||||
@@ -193,23 +185,7 @@ const EdgeRenderer = (props: EdgeRendererProps) => {
|
|||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
))}
|
))}
|
||||||
{renderConnectionLine && (
|
{children}
|
||||||
<svg
|
|
||||||
style={connectionLineContainerStyle}
|
|
||||||
width={width}
|
|
||||||
height={height}
|
|
||||||
className="react-flow__edges react-flow__connectionline react-flow__container"
|
|
||||||
>
|
|
||||||
<ConnectionLine
|
|
||||||
connectionNodeId={connectionNodeId!}
|
|
||||||
connectionHandleType={connectionHandleType!}
|
|
||||||
connectionLineStyle={connectionLineStyle}
|
|
||||||
connectionLineType={connectionLineType}
|
|
||||||
isConnectable={nodesConnectable}
|
|
||||||
CustomConnectionLineComponent={connectionLineComponent}
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -72,21 +72,18 @@ export function getHandlePosition(position: Position, nodeRect: Rect, handle: Ha
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getHandle(bounds: HandleElement[], handleId: string | null): HandleElement | null {
|
export function getHandle(bounds: HandleElement[], handleId?: string | null): HandleElement | null {
|
||||||
if (!bounds) {
|
if (!bounds) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// there is no handleId when there are no multiple handles/ handles with ids
|
if (handleId) {
|
||||||
// so we just pick the first one
|
return bounds.find((d) => d.id === handleId)!;
|
||||||
let handle: HandleElement | null = null;
|
} else if (bounds.length === 1) {
|
||||||
if (bounds.length === 1 || !handleId) {
|
return bounds[0];
|
||||||
handle = bounds[0];
|
|
||||||
} else if (handleId) {
|
|
||||||
handle = bounds.find((d) => d.id === handleId)!;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return typeof handle === 'undefined' ? null : handle;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EdgePositions {
|
interface EdgePositions {
|
||||||
@@ -167,16 +164,15 @@ export function isEdgeVisible({
|
|||||||
return overlappingArea > 0;
|
return overlappingArea > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getNodeData(node: Node): [Rect, NodeHandleBounds | null, boolean] {
|
export function getNodeData(node?: Node): [Rect, NodeHandleBounds | null, boolean] {
|
||||||
const handleBounds = node?.[internalsSymbol]?.handleBounds || null;
|
const handleBounds = node?.[internalsSymbol]?.handleBounds || null;
|
||||||
|
|
||||||
const isInvalid =
|
const isValid =
|
||||||
!node ||
|
handleBounds &&
|
||||||
!handleBounds ||
|
node?.width &&
|
||||||
!node.width ||
|
node?.height &&
|
||||||
!node.height ||
|
typeof node?.positionAbsolute?.x !== 'undefined' &&
|
||||||
typeof node.positionAbsolute?.x === 'undefined' ||
|
typeof node?.positionAbsolute?.y !== 'undefined';
|
||||||
typeof node.positionAbsolute?.y === 'undefined';
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@@ -186,6 +182,6 @@ export function getNodeData(node: Node): [Rect, NodeHandleBounds | null, boolean
|
|||||||
height: node?.height || 0,
|
height: node?.height || 0,
|
||||||
},
|
},
|
||||||
handleBounds,
|
handleBounds,
|
||||||
!isInvalid,
|
!!isValid,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user