diff --git a/web/app/components/workflow/block-selector/index.tsx b/web/app/components/workflow/block-selector/index.tsx index dd4b66233e..4b32ac7503 100644 --- a/web/app/components/workflow/block-selector/index.tsx +++ b/web/app/components/workflow/block-selector/index.tsx @@ -24,6 +24,8 @@ import { } from '@/app/components/base/icons/src/vender/line/general' type NodeSelectorProps = { + open?: boolean + onOpenChange?: (open: boolean) => void onSelect: (type: BlockEnum) => void trigger?: (open: boolean) => React.ReactNode placement?: Placement @@ -34,6 +36,8 @@ type NodeSelectorProps = { asChild?: boolean } const NodeSelector: FC = ({ + open: openFromProps, + onOpenChange, onSelect, trigger, placement = 'right', @@ -43,18 +47,25 @@ const NodeSelector: FC = ({ popupClassName, asChild, }) => { - const [open, setOpen] = useState(false) + const [localOpen, setLocalOpen] = useState(false) + const open = openFromProps === undefined ? localOpen : openFromProps + const handleOpenChange = useCallback((newOpen: boolean) => { + setLocalOpen(newOpen) + + if (onOpenChange) + onOpenChange(newOpen) + }, [onOpenChange]) const handleTrigger = useCallback>((e) => { e.stopPropagation() - setOpen(v => !v) - }, []) + handleOpenChange(!open) + }, [open, handleOpenChange]) return ( = ({
{ + const [ + edgePath, + ] = getSimpleBezierPath({ + sourceX: fromX, + sourceY: fromY, + sourcePosition: Position.Right, + targetX: toX, + targetY: toY, + targetPosition: Position.Left, + }) + + return ( + + + + + ) +} + +export default CustomConnectionLine diff --git a/web/app/components/workflow/custom-edge.tsx b/web/app/components/workflow/custom-edge.tsx index f656fb43c4..123d7e05ea 100644 --- a/web/app/components/workflow/custom-edge.tsx +++ b/web/app/components/workflow/custom-edge.tsx @@ -22,10 +22,10 @@ const CustomEdge = ({ labelX, labelY, ] = getSimpleBezierPath({ - sourceX, + sourceX: sourceX - 8, sourceY, sourcePosition: Position.Right, - targetX, + targetX: targetX + 8, targetY, targetPosition: Position.Left, }) diff --git a/web/app/components/workflow/index.tsx b/web/app/components/workflow/index.tsx index fe5a6199a9..dc416de9a5 100644 --- a/web/app/components/workflow/index.tsx +++ b/web/app/components/workflow/index.tsx @@ -13,6 +13,7 @@ import Header from './header' import CustomNode from './nodes' import ZoomInOut from './zoom-in-out' import CustomEdge from './custom-edge' +import CustomConnectionLine from './custom-connection-line' import Panel from './panel' import type { Node } from './types' @@ -69,6 +70,7 @@ const Workflow: FC = memo(({ onEdgeMouseEnter={handleEnterEdge} onEdgeMouseLeave={handleLeaveEdge} multiSelectionKeyCode={null} + connectionLineComponent={CustomConnectionLine} > { + const [open, setOpen] = useState(false) const store = useStoreApi() const incomers = getIncomers({ id } as Node, store.getState().getNodes(), store.getState().edges) + const handleOpenChange = useCallback((v: boolean) => { + setOpen(v) + }, []) + const handleHandleClick = () => { + if (incomers.length === 0 && data.type !== BlockEnum.Start) + handleOpenChange(!open) + } return ( <> @@ -23,26 +35,31 @@ export const NodeTargetHandle = ({ type='target' position={Position.Left} className={` - !top-[17px] !left-0 !w-4 !h-4 !bg-transparent !rounded-none !outline-none !border-none !translate-y-0 z-[1] - after:absolute after:w-0.5 after:h-2 after:-left-0.5 after:top-1 after:bg-primary-500 - ${(data.type === BlockEnum.Start || !incomers.length) && 'opacity-0'} + !top-[17px] !-left-2 !w-4 !h-4 !bg-transparent !rounded-none !outline-none !border-none !translate-y-0 z-[1] + after:absolute after:w-0.5 after:h-2 after:left-1.5 after:top-1 after:bg-primary-500 + ${!incomers.length && 'after:opacity-0'} + ${data.type === BlockEnum.Start && 'opacity-0'} `} isConnectable={data.type !== BlockEnum.Start} - /> - { - incomers.length === 0 && data.type !== BlockEnum.Start && ( - {}} - asChild - placement='left' - triggerClassName={open => ` - hidden absolute -left-2 top-4 - ${data.hovering && '!flex'} - ${open && '!flex'} - `} - /> - ) - } + onClick={handleHandleClick} + > + { + incomers.length === 0 && data.type !== BlockEnum.Start && ( + {}} + asChild + placement='left' + triggerClassName={open => ` + hidden absolute left-0 top-0 pointer-events-none + ${data.hovering && '!flex'} + ${open && '!flex'} + `} + /> + ) + } + ) } @@ -59,9 +76,17 @@ export const NodeSourceHandle = ({ handleClassName, nodeSelectorClassName, }: NodeSourceHandleProps) => { + const [open, setOpen] = useState(false) const store = useStoreApi() const connectedEdges = getConnectedEdges([{ id } as Node], store.getState().edges) const connected = connectedEdges.find(edge => edge.sourceHandle === handleId) + const handleOpenChange = useCallback((v: boolean) => { + setOpen(v) + }, []) + const handleHandleClick = () => { + if (!connected) + handleOpenChange(!open) + } return ( <> @@ -71,25 +96,29 @@ export const NodeSourceHandle = ({ position={Position.Right} className={` !w-4 !h-4 !bg-transparent !rounded-none !outline-none !border-none !translate-y-0 z-[1] - after:absolute after:w-0.5 after:h-2 after:-right-0.5 after:top-1 after:bg-primary-500 - ${!connected && 'opacity-0'} + after:absolute after:w-0.5 after:h-2 after:right-1.5 after:top-1 after:bg-primary-500 + ${!connected && 'after:opacity-0'} ${handleClassName} `} - /> - { - !connected && ( - {}} - asChild - triggerClassName={open => ` - hidden - ${nodeSelectorClassName} - ${data.hovering && '!flex'} - ${open && '!flex'} - `} - /> - ) - } + onClick={handleHandleClick} + > + { + !connected && ( + {}} + asChild + triggerClassName={open => ` + hidden absolute top-0 left-0 pointer-events-none + ${nodeSelectorClassName} + ${data.hovering && '!flex'} + ${open && '!flex'} + `} + /> + ) + } + ) } diff --git a/web/app/components/workflow/nodes/if-else/node.tsx b/web/app/components/workflow/nodes/if-else/node.tsx index 084eda9e8e..259e143326 100644 --- a/web/app/components/workflow/nodes/if-else/node.tsx +++ b/web/app/components/workflow/nodes/if-else/node.tsx @@ -25,8 +25,7 @@ const Node = (props: Pick) => {
{t(`${i18nPrefix}.conditions`)}
@@ -50,8 +49,7 @@ const Node = (props: Pick) => { diff --git a/web/app/components/workflow/nodes/index.tsx b/web/app/components/workflow/nodes/index.tsx index 15cb3a454e..3ba4e05d2e 100644 --- a/web/app/components/workflow/nodes/index.tsx +++ b/web/app/components/workflow/nodes/index.tsx @@ -26,8 +26,7 @@ const CustomNode = memo((props: NodeProps) => { nodeData.type !== BlockEnum.IfElse && ( )