import type { MouseEvent } from 'react' import { memo, useCallback, useEffect, useState, } from 'react' import { Handle, Position, } from 'reactflow' import { BlockEnum } from '../../../types' import type { Node } from '../../../types' import BlockSelector from '../../../block-selector' import type { ToolDefaultValue } from '../../../block-selector/types' import { useNodesExtraData, useNodesInteractions, } from '../../../hooks' import { useStore } from '../../../store' type NodeHandleProps = { handleId: string handleClassName?: string nodeSelectorClassName?: string } & Pick export const NodeTargetHandle = memo(({ id, data, handleId, handleClassName, nodeSelectorClassName, }: NodeHandleProps) => { const [open, setOpen] = useState(false) const { handleNodeAdd } = useNodesInteractions() const nodesExtraData = useNodesExtraData() const connected = data._connectedTargetHandleIds?.includes(handleId) const availablePrevNodes = nodesExtraData[data.type].availablePrevNodes const isConnectable = !!availablePrevNodes.length const handleOpenChange = useCallback((v: boolean) => { setOpen(v) }, []) const handleHandleClick = useCallback((e: MouseEvent) => { e.stopPropagation() if (!connected) setOpen(v => !v) }, [connected]) const handleSelect = useCallback((type: BlockEnum, toolDefaultValue?: ToolDefaultValue) => { handleNodeAdd( { nodeType: type, toolDefaultValue, }, { nextNodeId: id, nextNodeTargetHandle: handleId, }, ) }, [handleNodeAdd, id, handleId]) return ( <> { !connected && isConnectable && !data._isInvalidConnection && ( ` hidden absolute left-0 top-0 pointer-events-none ${nodeSelectorClassName} group-hover:!flex ${data.selected && '!flex'} ${open && '!flex'} `} availableBlocksTypes={availablePrevNodes} /> ) } ) }) NodeTargetHandle.displayName = 'NodeTargetHandle' export const NodeSourceHandle = memo(({ id, data, handleId, handleClassName, nodeSelectorClassName, }: NodeHandleProps) => { const notInitialWorkflow = useStore(s => s.notInitialWorkflow) const [open, setOpen] = useState(false) const { handleNodeAdd } = useNodesInteractions() const nodesExtraData = useNodesExtraData() const availableNextNodes = nodesExtraData[data.type].availableNextNodes const isConnectable = !!availableNextNodes.length const connected = data._connectedSourceHandleIds?.includes(handleId) const handleOpenChange = useCallback((v: boolean) => { setOpen(v) }, []) const handleHandleClick = useCallback((e: MouseEvent) => { e.stopPropagation() if (!connected) setOpen(v => !v) }, [connected]) const handleSelect = useCallback((type: BlockEnum, toolDefaultValue?: ToolDefaultValue) => { handleNodeAdd( { nodeType: type, toolDefaultValue, }, { prevNodeId: id, prevNodeSourceHandle: handleId, }, ) }, [handleNodeAdd, id, handleId]) useEffect(() => { if (notInitialWorkflow && data.type === BlockEnum.Start) setOpen(true) }, [notInitialWorkflow, data.type]) return ( <> { !connected && isConnectable && !data._isInvalidConnection && ( ` hidden absolute top-0 left-0 pointer-events-none ${nodeSelectorClassName} group-hover:!flex ${data.selected && '!flex'} ${open && '!flex'} `} availableBlocksTypes={availableNextNodes} /> ) } ) }) NodeSourceHandle.displayName = 'NodeSourceHandle'