dify / web /app /components /workflow /custom-edge.tsx
Severian's picture
initial commit
a8b3f00
raw
history blame
2.89 kB
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)