import React , { memo } from 'react' ; import EdgeText from './EdgeText' ; import { getMarkerEnd , getCenter } from './utils' ; import { EdgeSmoothStepProps , Position } from '../../types' ; const bottomLeftCorner = ( x , y , size ) : string => `L ${ x },${ y - size }Q ${ x },${ y } ${ x + size },${ y }` ; const leftBottomCorner = ( x , y , size ) : string => `L ${ x + size },${ y }Q ${ x },${ y } ${ x },${ y - size }` ; const bottomRightCorner = ( x , y , size ) : string => `L ${ x },${ y - size }Q ${ x },${ y } ${ x - size },${ y }` ; const rightBottomCorner = ( x , y , size ) : string => `L ${ x - size },${ y }Q ${ x },${ y } ${ x },${ y - size }` ; const leftTopCorner = ( x , y , size ) : string => `L ${ x + size },${ y }Q ${ x },${ y } ${ x },${ y + size }` ; const topLeftCorner = ( x , y , size ) : string => `L ${ x },${ y + size }Q ${ x },${ y } ${ x + size },${ y }` ; const topRightCorner = ( x , y , size ) : string => `L ${ x },${ y + size }Q ${ x },${ y } ${ x - size },${ y }` ; const rightTopCorner = ( x , y , size ) : string => `L ${ x - size },${ y }Q ${ x },${ y } ${ x },${ y + size }` ; interface GetSmoothStepPathParams { sourceX : number ; sourceY : number ; sourcePosition ? : Position ; targetX : number ; targetY : number ; targetPosition ? : Position ; borderRadius ? : number ; centerX ? : number ; centerY ? : number ; } export function getSmoothStepPath ( { sourceX , sourceY , sourcePosition = Position . Bottom , targetX , targetY , targetPosition = Position . Top , borderRadius = 5 , centerX , centerY , } ) { const [ _centerX , _centerY , offsetX , offsetY ] = getCenter ( { sourceX , sourceY , targetX , targetY } ) ; const cornerWidth = Math . min ( borderRadius , Math . abs ( targetX - sourceX ) ) ; const cornerHeight = Math . min ( borderRadius , Math . abs ( targetY - sourceY ) ) ; const cornerSize = Math . min ( cornerWidth , cornerHeight , offsetX , offsetY ) ; const leftAndRight = [ Position . Left , Position . Right ] ; const cX = typeof centerX !== 'undefined' ? centerX : _centerX ; const cY = typeof centerY !== 'undefined' ? centerY : _centerY ; let firstCornerPath = null ; let secondCornerPath = null ; if ( sourceX <= targetX ) { firstCornerPath = sourceY <= targetY ? bottomLeftCorner ( sourceX , cY , cornerSize ) : topLeftCorner ( sourceX , cY , cornerSize ) ; secondCornerPath = sourceY <= targetY ? rightTopCorner ( targetX , cY , cornerSize ) : rightBottomCorner ( targetX , cY , cornerSize ) ; } else { firstCornerPath = sourceY < targetY ? bottomRightCorner ( sourceX , cY , cornerSize ) : topRightCorner ( sourceX , cY , cornerSize ) ; secondCornerPath = sourceY < targetY ? leftTopCorner ( targetX , cY , cornerSize ) : leftBottomCorner ( targetX , cY , cornerSize ) ; } if ( leftAndRight . includes ( sourcePosition ) && leftAndRight . includes ( targetPosition ) ) { if ( sourceX <= targetX ) { firstCornerPath = sourceY <= targetY ? rightTopCorner ( cX , sourceY , cornerSize ) : rightBottomCorner ( cX , sourceY , cornerSize ) ; secondCornerPath = sourceY <= targetY ? bottomLeftCorner ( cX , targetY , cornerSize ) : topLeftCorner ( cX , targetY , cornerSize ) ; } else if ( ( sourcePosition === Position . Right && targetPosition === Position . Left ) || ( sourcePosition === Position . Left && targetPosition === Position . Right ) || ( sourcePosition === Position . Left && targetPosition === Position . Left ) ) { firstCornerPath = sourceY <= targetY ? leftTopCorner ( cX , sourceY , cornerSize ) : leftBottomCorner ( cX , sourceY , cornerSize ) ; secondCornerPath = sourceY <= targetY ? bottomRightCorner ( cX , targetY , cornerSize ) : topRightCorner ( cX , targetY , cornerSize ) ; } } else if ( leftAndRight . includes ( sourcePosition ) && ! leftAndRight . includes ( targetPosition ) ) { if ( sourceX <= targetX ) { firstCornerPath = sourceY <= targetY ? rightTopCorner ( targetX , sourceY , cornerSize ) : rightBottomCorner ( targetX , sourceY , cornerSize ) ; } else { firstCornerPath = sourceY <= targetY ? leftTopCorner ( targetX , sourceY , cornerSize ) : leftBottomCorner ( targetX , sourceY , cornerSize ) ; } secondCornerPath = '' ; } else if ( ! leftAndRight . includes ( sourcePosition ) && leftAndRight . includes ( targetPosition ) ) { if ( sourceX <= targetX ) { firstCornerPath = sourceY <= targetY ? bottomLeftCorner ( sourceX , targetY , cornerSize ) : topLeftCorner ( sourceX , targetY , cornerSize ) ; } else { firstCornerPath = sourceY <= targetY ? bottomRightCorner ( sourceX , targetY , cornerSize ) : topRightCorner ( sourceX , targetY , cornerSize ) ; } secondCornerPath = '' ; } return `M ${ sourceX },${ sourceY }${ firstCornerPath }${ secondCornerPath }L ${ targetX },${ targetY }` ; } export default memo ( ( { sourceX , sourceY , targetX , targetY , label , labelStyle , labelShowBg , labelBgStyle , labelBgPadding , labelBgBorderRadius , style , sourcePosition = Position . Bottom , targetPosition = Position . Top , arrowHeadType , markerEndId , borderRadius = 5 , } ) => { const [ centerX , centerY ] = getCenter ( { sourceX , sourceY , targetX , targetY , sourcePosition , targetPosition } ) ; const path = getSmoothStepPath ( { sourceX , sourceY , sourcePosition , targetX , targetY , targetPosition , borderRadius , } ) ; const markerEnd = getMarkerEnd ( arrowHeadType , markerEndId ) ; const text = label ? ( < EdgeText x = { centerX } y = { centerY } label = { label } labelStyle = { labelStyle } labelShowBg = { labelShowBg } labelBgStyle = { labelBgStyle } labelBgPadding = { labelBgPadding } labelBgBorderRadius = { labelBgBorderRadius } / > ) : null ; return ( < > < path style = { style } className = "react-flow__edge-path" d = { path } markerEnd = { markerEnd } / > { text } < />); } ) ;