import { memo } from 'react'; import shallow from 'zustand/shallow'; import cc from 'classcat'; import { useStore } from '../../hooks/useStore'; import useVisibleEdges from '../../hooks/useVisibleEdges'; import ConnectionLine from '../../components/ConnectionLine/index'; import MarkerDefinitions from './MarkerDefinitions'; import { getEdgePositions, getHandle, getNodeData } from './utils'; import { GraphViewProps } from '../GraphView'; import { devWarn } from '../../utils'; import { ConnectionMode, Position } from '../../types'; import type { Edge, ReactFlowState } from '../../types'; type EdgeRendererProps = Pick< GraphViewProps, | 'edgeTypes' | 'connectionLineType' | 'connectionLineType' | 'connectionLineStyle' | 'connectionLineComponent' | 'connectionLineContainerStyle' | 'connectionLineContainerStyle' | 'onEdgeClick' | 'onEdgeDoubleClick' | 'defaultMarkerColor' | 'onlyRenderVisibleElements' | 'onEdgeUpdate' | 'onEdgeContextMenu' | 'onEdgeMouseEnter' | 'onEdgeMouseMove' | 'onEdgeMouseLeave' | 'onEdgeUpdateStart' | 'onEdgeUpdateEnd' | 'edgeUpdaterRadius' | 'noPanClassName' | 'elevateEdgesOnSelect' | 'rfId' | 'disableKeyboardA11y' > & { elevateEdgesOnSelect: boolean; }; const selector = (s: ReactFlowState) => ({ connectionNodeId: s.connectionNodeId, connectionHandleType: s.connectionHandleType, nodesConnectable: s.nodesConnectable, edgesFocusable: s.edgesFocusable, elementsSelectable: s.elementsSelectable, width: s.width, height: s.height, connectionMode: s.connectionMode, nodeInternals: s.nodeInternals, }); const EdgeRenderer = (props: EdgeRendererProps) => { const { connectionNodeId, connectionHandleType, nodesConnectable, edgesFocusable, 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.get(edge.source)!); const [targetNodeRect, targetHandleBounds, targetIsValid] = getNodeData(nodeInternals.get(edge.target)!); if (!sourceIsValid || !targetIsValid) { return null; } let edgeType = edge.type || 'default'; if (!props.edgeTypes[edgeType]) { devWarn( `Edge type "${edgeType}" not found. Using fallback type "default". Help: https://reactflow.dev/error#300` ); edgeType = '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; const isFocusable = !!(edge.focusable || (edgesFocusable && typeof edge.focusable === 'undefined')); if (!sourceHandle || !targetHandle) { devWarn( `Couldn't create edge for ${!sourceHandle ? 'source' : 'target'} handle id: ${ !sourceHandle ? edge.sourceHandle : 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);