import React, { memo, CSSProperties } from 'react'; import shallow from 'zustand/shallow'; import cc from 'classcat'; import { useStore } from '../../store'; import ConnectionLine from '../../components/ConnectionLine/index'; import MarkerDefinitions from './MarkerDefinitions'; import { getEdgePositions, getHandle, getNodeData } from './utils'; import useVisibleEdges from '../../hooks/useVisibleEdges'; import { Position, Edge, ConnectionLineType, ConnectionLineComponent, ConnectionMode, OnEdgeUpdateFunc, HandleType, ReactFlowState, EdgeTypesWrapped, } from '../../types'; interface EdgeRendererProps { edgeTypes: EdgeTypesWrapped; connectionLineType: ConnectionLineType; connectionLineStyle?: CSSProperties; connectionLineComponent?: ConnectionLineComponent; connectionLineContainerStyle?: CSSProperties; onEdgeClick?: (event: React.MouseEvent, node: Edge) => void; onEdgeDoubleClick?: (event: React.MouseEvent, edge: Edge) => void; defaultMarkerColor: string; onlyRenderVisibleElements: boolean; onEdgeUpdate?: OnEdgeUpdateFunc; onEdgeContextMenu?: (event: React.MouseEvent, edge: Edge) => void; onEdgeMouseEnter?: (event: React.MouseEvent, edge: Edge) => void; onEdgeMouseMove?: (event: React.MouseEvent, edge: Edge) => void; onEdgeMouseLeave?: (event: React.MouseEvent, edge: Edge) => void; onEdgeUpdateStart?: (event: React.MouseEvent, edge: Edge, handleType: HandleType) => void; onEdgeUpdateEnd?: (event: MouseEvent, edge: Edge, handleType: HandleType) => void; edgeUpdaterRadius?: number; noPanClassName?: string; elevateEdgesOnSelect: boolean; rfId: string; disableKeyboardA11y: boolean; } const selector = (s: ReactFlowState) => ({ connectionNodeId: s.connectionNodeId, connectionHandleType: s.connectionHandleType, nodesConnectable: s.nodesConnectable, elementsSelectable: s.elementsSelectable, width: s.width, height: s.height, connectionMode: s.connectionMode, nodeInternals: s.nodeInternals, }); const EdgeRenderer = (props: EdgeRendererProps) => { const { connectionNodeId, connectionHandleType, nodesConnectable, elementsSelectable, width, height, connectionMode, nodeInternals, } = useStore(selector, shallow); const edgeTree = useVisibleEdges(props.onlyRenderVisibleElements, nodeInternals, props.elevateEdgesOnSelect); if (!width) { return null; } const { connectionLineType, defaultMarkerColor, connectionLineStyle, connectionLineComponent, connectionLineContainerStyle, } = props; const renderConnectionLine = connectionNodeId && connectionHandleType; return ( <> {edgeTree.map(({ level, edges, isMaxLevel }) => ( {isMaxLevel && } {edges.map((edge: Edge) => { const [sourceNodeRect, sourceHandleBounds, sourceIsValid] = getNodeData(nodeInternals, edge.source); const [targetNodeRect, targetHandleBounds, targetIsValid] = getNodeData(nodeInternals, edge.target); if (!sourceIsValid || !targetIsValid) { return null; } const edgeType = edge.type || 'default'; const EdgeComponent = props.edgeTypes[edgeType] || props.edgeTypes.default; // when connection type is loose we can define all handles as sources const targetNodeHandles = connectionMode === ConnectionMode.Strict ? targetHandleBounds!.target : targetHandleBounds!.target || targetHandleBounds!.source; const sourceHandle = getHandle(sourceHandleBounds!.source!, edge.sourceHandle || null); const targetHandle = getHandle(targetNodeHandles!, edge.targetHandle || null); const sourcePosition = sourceHandle?.position || Position.Bottom; const targetPosition = targetHandle?.position || Position.Top; if (!sourceHandle) { // @ts-ignore if (process.env.NODE_ENV === 'development') { console.warn( `[React Flow]: Couldn't create edge for source handle id: ${edge.sourceHandle}; edge id: ${edge.id}. Help: https://reactflow.dev/error#800` ); } return null; } if (!targetHandle) { // @ts-ignore if (process.env.NODE_ENV === 'development') { console.warn( `[React Flow]: Couldn't create edge for target handle id: ${edge.targetHandle}; edge id: ${edge.id}. Help: https://reactflow.dev/error#800` ); } return null; } const { sourceX, sourceY, targetX, targetY } = getEdgePositions( sourceNodeRect, sourceHandle, sourcePosition, targetNodeRect, targetHandle, targetPosition ); return ( ); })} ))} {renderConnectionLine && ( )} ); }; EdgeRenderer.displayName = 'EdgeRenderer'; export default memo(EdgeRenderer);