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 */}
{inputs?.length > 0 && (
{inputs?.map((item, index) => (
))}
)}
{parameters?.length > 0 && (
)}
{renderOutput(node)}
);
};
export default CanvasNode;