import { apiInterceptors, refreshFlowNodeById } from '@/client/api'; import { IFlowNode } from '@/types/flow'; import { getUniqueNodeId, removeIndexFromNodeId } from '@/utils/flow'; import { CopyOutlined, DeleteOutlined, InfoCircleOutlined } from '@ant-design/icons'; import { Form, Popover, Tooltip } from 'antd'; import classNames from 'classnames'; import { cloneDeep } from 'lodash'; import Image from 'next/image'; import { useState } from 'react'; import { useReactFlow } from 'reactflow'; import IconWrapper from '../common/icon-wrapper'; import NodeHandler from './node-handler'; import NodeParamHandler from './node-param-handler'; type CanvasNodeProps = { data: IFlowNode; }; function TypeLabel({ label }: { label: string }) { return
{label}
; } const CanvasNode: React.FC = ({ data }) => { const node = data; const { inputs, outputs, parameters, flow_type: flowType } = node; const [isHovered, setIsHovered] = useState(false); const reactFlow = useReactFlow(); const [form] = Form.useForm(); function onHover() { setIsHovered(true); } function onLeave() { setIsHovered(false); } function copyNode(e: React.MouseEvent) { e.preventDefault(); e.stopPropagation(); const nodes = reactFlow.getNodes(); const originalNode = nodes.find(item => item.id === node.id); if (originalNode) { const newNodeId = getUniqueNodeId(originalNode as IFlowNode, nodes); const cloneNode = cloneDeep(originalNode); const duplicatedNode = { ...cloneNode, id: newNodeId, position: { x: cloneNode.position.x + 400, y: cloneNode.position.y, }, positionAbsolute: { x: cloneNode.positionAbsolute!.x + 400, y: cloneNode.positionAbsolute!.y, }, data: { ...cloneNode.data, id: newNodeId, }, selected: false, }; reactFlow.setNodes(nodes => [...nodes, duplicatedNode]); } } function deleteNode(e: React.MouseEvent) { e.preventDefault(); e.stopPropagation(); reactFlow.setNodes(nodes => nodes.filter(item => item.id !== node.id)); reactFlow.setEdges(edges => edges.filter(edge => edge.source !== node.id && edge.target !== node.id)); } function updateCurrentNodeValue(changedKey: string, changedVal: any) { parameters.forEach(item => { if (item.name === changedKey) { item.value = changedVal; } }); } async function updateDependsNodeValue(changedKey: string, changedVal: any) { const dependParamNodes = parameters.filter(({ ui }) => ui?.refresh_depends?.includes(changedKey)); if (dependParamNodes?.length === 0) return; dependParamNodes.forEach(async item => { const params = { id: removeIndexFromNodeId(data?.id), type_name: data.type_name, type_cls: data.type_cls, flow_type: 'operator' as const, refresh: [ { name: item.name, depends: [ { name: changedKey, value: changedVal, has_value: true, }, ], }, ], }; const [_, res] = await apiInterceptors(refreshFlowNodeById(params)); // update value of the node if (res) { reactFlow.setNodes(nodes => nodes.map(n => { return n.id === node.id ? { ...n, data: { ...n.data, parameters: res.parameters, }, } : n; }), ); } }); } function onParameterValuesChange(changedValues: any) { const [changedKey, changedVal] = Object.entries(changedValues)[0]; updateCurrentNodeValue(changedKey, changedVal); if (changedVal) { updateDependsNodeValue(changedKey, changedVal); } } function renderOutput(data: IFlowNode) { if (flowType === 'operator' && outputs?.length > 0) { return (
{outputs?.map((output, index) => ( ))}
); } else if (flowType === 'resource') { // resource nodes show output default return (
); } return null; } return (

{node.label}

{node.description}

} placement='right' >
} >
{/* icon and label */}

{node.label}

{inputs?.length > 0 && (
{inputs?.map((item, index) => ( ))}
)} {parameters?.length > 0 && (
{parameters?.map((item, index) => ( ))}
)} {renderOutput(node)}
); }; export default CanvasNode;