| import { | |
| memo, | |
| useCallback, | |
| useState, | |
| } from 'react' | |
| import { intersection } from 'lodash-es' | |
| import type { EdgeProps } from 'reactflow' | |
| import { | |
| BaseEdge, | |
| EdgeLabelRenderer, | |
| Position, | |
| getBezierPath, | |
| } from 'reactflow' | |
| import { | |
| useAvailableBlocks, | |
| useNodesInteractions, | |
| } from './hooks' | |
| import BlockSelector from './block-selector' | |
| import type { | |
| Edge, | |
| OnSelectBlock, | |
| } from './types' | |
| import { ITERATION_CHILDREN_Z_INDEX } from './constants' | |
| import cn from '@/utils/classnames' | |
| const CustomEdge = ({ | |
| id, | |
| data, | |
| source, | |
| sourceHandleId, | |
| target, | |
| targetHandleId, | |
| sourceX, | |
| sourceY, | |
| targetX, | |
| targetY, | |
| selected, | |
| }: EdgeProps) => { | |
| const [ | |
| edgePath, | |
| labelX, | |
| labelY, | |
| ] = getBezierPath({ | |
| sourceX: sourceX - 8, | |
| sourceY, | |
| sourcePosition: Position.Right, | |
| targetX: targetX + 8, | |
| targetY, | |
| targetPosition: Position.Left, | |
| curvature: 0.16, | |
| }) | |
| const [open, setOpen] = useState(false) | |
| const { handleNodeAdd } = useNodesInteractions() | |
| const { availablePrevBlocks } = useAvailableBlocks((data as Edge['data'])!.targetType, (data as Edge['data'])?.isInIteration) | |
| const { availableNextBlocks } = useAvailableBlocks((data as Edge['data'])!.sourceType, (data as Edge['data'])?.isInIteration) | |
| const handleOpenChange = useCallback((v: boolean) => { | |
| setOpen(v) | |
| }, []) | |
| const handleInsert = useCallback<OnSelectBlock>((nodeType, toolDefaultValue) => { | |
| handleNodeAdd( | |
| { | |
| nodeType, | |
| toolDefaultValue, | |
| }, | |
| { | |
| prevNodeId: source, | |
| prevNodeSourceHandle: sourceHandleId || 'source', | |
| nextNodeId: target, | |
| nextNodeTargetHandle: targetHandleId || 'target', | |
| }, | |
| ) | |
| }, [handleNodeAdd, source, sourceHandleId, target, targetHandleId]) | |
| return ( | |
| <> | |
| <BaseEdge | |
| id={id} | |
| path={edgePath} | |
| style={{ | |
| stroke: (selected || data?._connectedNodeIsHovering || data?._run) ? '#2970FF' : '#D0D5DD', | |
| strokeWidth: 2, | |
| }} | |
| /> | |
| <EdgeLabelRenderer> | |
| <div | |
| className={cn( | |
| 'nopan nodrag hover:scale-125', | |
| data?._hovering ? 'block' : 'hidden', | |
| open && '!block', | |
| data.isInIteration && `z-[${ITERATION_CHILDREN_Z_INDEX}]`, | |
| )} | |
| style={{ | |
| position: 'absolute', | |
| transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`, | |
| pointerEvents: 'all', | |
| }} | |
| > | |
| <BlockSelector | |
| open={open} | |
| onOpenChange={handleOpenChange} | |
| asChild | |
| onSelect={handleInsert} | |
| availableBlocksTypes={intersection(availablePrevBlocks, availableNextBlocks)} | |
| triggerClassName={() => 'hover:scale-150 transition-all'} | |
| /> | |
| </div> | |
| </EdgeLabelRenderer> | |
| </> | |
| ) | |
| } | |
| export default memo(CustomEdge) | |