### What problem does this PR solve? fix: The name of the copy operator is displayed the same as before ##3265 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.14.0
| @@ -3,25 +3,28 @@ import { CopyOutlined } from '@ant-design/icons'; | |||
| import { Flex, MenuProps } from 'antd'; | |||
| import { useCallback } from 'react'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import { useGetNodeName } from '../../hooks'; | |||
| import useGraphStore from '../../store'; | |||
| interface IProps { | |||
| id: string; | |||
| iconFontColor?: string; | |||
| label: string; | |||
| } | |||
| const NodeDropdown = ({ id, iconFontColor }: IProps) => { | |||
| const NodeDropdown = ({ id, iconFontColor, label }: IProps) => { | |||
| const { t } = useTranslation(); | |||
| const deleteNodeById = useGraphStore((store) => store.deleteNodeById); | |||
| const duplicateNodeById = useGraphStore((store) => store.duplicateNode); | |||
| const getNodeName = useGetNodeName(); | |||
| const deleteNode = useCallback(() => { | |||
| deleteNodeById(id); | |||
| }, [id, deleteNodeById]); | |||
| const duplicateNode = useCallback(() => { | |||
| duplicateNodeById(id); | |||
| }, [id, duplicateNodeById]); | |||
| duplicateNodeById(id, getNodeName(label)); | |||
| }, [duplicateNodeById, id, getNodeName, label]); | |||
| const items: MenuProps['items'] = [ | |||
| { | |||
| @@ -9,13 +9,13 @@ import { NextNodePopover } from './popover'; | |||
| interface IProps { | |||
| id: string; | |||
| label?: string; | |||
| name?: string; | |||
| label: string; | |||
| name: string; | |||
| gap?: number; | |||
| className?: string; | |||
| } | |||
| export function RunStatus({ id, name }: IProps) { | |||
| export function RunStatus({ id, name }: Omit<IProps, 'label'>) { | |||
| const { t } = useTranslate('flow'); | |||
| return ( | |||
| <section className="flex justify-end items-center pb-1 "> | |||
| @@ -44,7 +44,7 @@ const NodeHeader = ({ label, id, name, gap = 4, className }: IProps) => { | |||
| color={operatorMap[label as Operator].color} | |||
| ></OperatorIcon> | |||
| <span className={styles.nodeTitle}>{name}</span> | |||
| <NodeDropdown id={id}></NodeDropdown> | |||
| <NodeDropdown id={id} label={label}></NodeDropdown> | |||
| </Flex> | |||
| </section> | |||
| ); | |||
| @@ -62,7 +62,7 @@ function NoteNode({ data, id }: NodeProps<NodeData>) { | |||
| onChange={handleNameChange} | |||
| className={styles.noteName} | |||
| ></Input> | |||
| <NodeDropdown id={id}></NodeDropdown> | |||
| <NodeDropdown id={id} label={data.label}></NodeDropdown> | |||
| </Flex> | |||
| <Form | |||
| onValuesChange={handleValuesChange} | |||
| @@ -69,6 +69,7 @@ import { ICategorizeForm, IRelevantForm, ISwitchForm } from './interface'; | |||
| import useGraphStore, { RFState } from './store'; | |||
| import { | |||
| buildDslComponentsByGraph, | |||
| generateNodeNamesWithIncreasingIndex, | |||
| generateSwitchHandleText, | |||
| getNodeDragHandle, | |||
| receiveMessageError, | |||
| @@ -159,12 +160,13 @@ export const useHandleDrag = () => { | |||
| return { handleDragStart }; | |||
| }; | |||
| const splitName = (name: string) => { | |||
| const names = name.split('_'); | |||
| const type = names.at(0); | |||
| const index = Number(names.at(-1)); | |||
| export const useGetNodeName = () => { | |||
| const { t } = useTranslation(); | |||
| return { type, index }; | |||
| return (type: string) => { | |||
| const name = t(`flow.${lowerFirst(type)}`); | |||
| return name; | |||
| }; | |||
| }; | |||
| export const useHandleDrop = () => { | |||
| @@ -173,54 +175,13 @@ export const useHandleDrop = () => { | |||
| const [reactFlowInstance, setReactFlowInstance] = | |||
| useState<ReactFlowInstance<any, any>>(); | |||
| const initializeOperatorParams = useInitializeOperatorParams(); | |||
| const { t } = useTranslation(); | |||
| const getNodeName = useGetNodeName(); | |||
| const onDragOver = useCallback((event: React.DragEvent<HTMLDivElement>) => { | |||
| event.preventDefault(); | |||
| event.dataTransfer.dropEffect = 'move'; | |||
| }, []); | |||
| const generateNodeName = useCallback( | |||
| (type: string) => { | |||
| const name = t(`flow.${lowerFirst(type)}`); | |||
| const templateNameList = nodes | |||
| .filter((x) => { | |||
| const temporaryName = x.data.name; | |||
| const { type, index } = splitName(temporaryName); | |||
| return ( | |||
| temporaryName.match(/_/g)?.length === 1 && | |||
| type === name && | |||
| !isNaN(index) | |||
| ); | |||
| }) | |||
| .map((x) => { | |||
| const temporaryName = x.data.name; | |||
| const { index } = splitName(temporaryName); | |||
| return { | |||
| idx: index, | |||
| name: temporaryName, | |||
| }; | |||
| }) | |||
| .sort((a, b) => a.idx - b.idx); | |||
| let index: number = 0; | |||
| for (let i = 0; i < templateNameList.length; i++) { | |||
| const idx = templateNameList[i]?.idx; | |||
| const nextIdx = templateNameList[i + 1]?.idx; | |||
| if (idx + 1 !== nextIdx) { | |||
| index = idx + 1; | |||
| break; | |||
| } | |||
| } | |||
| return `${name}_${index}`; | |||
| }, | |||
| [t, nodes], | |||
| ); | |||
| const onDrop = useCallback( | |||
| (event: React.DragEvent<HTMLDivElement>) => { | |||
| event.preventDefault(); | |||
| @@ -248,7 +209,7 @@ export const useHandleDrop = () => { | |||
| }, | |||
| data: { | |||
| label: `${type}`, | |||
| name: generateNodeName(type), | |||
| name: generateNodeNamesWithIncreasingIndex(getNodeName(type), nodes), | |||
| form: initializeOperatorParams(type as Operator), | |||
| }, | |||
| sourcePosition: Position.Right, | |||
| @@ -258,7 +219,7 @@ export const useHandleDrop = () => { | |||
| addNode(newNode); | |||
| }, | |||
| [reactFlowInstance, addNode, initializeOperatorParams, generateNodeName], | |||
| [reactFlowInstance, getNodeName, nodes, initializeOperatorParams, addNode], | |||
| ); | |||
| return { onDrop, onDragOver, setReactFlowInstance }; | |||
| @@ -23,7 +23,12 @@ import { devtools } from 'zustand/middleware'; | |||
| import { immer } from 'zustand/middleware/immer'; | |||
| import { Operator, SwitchElseTo } from './constant'; | |||
| import { NodeData } from './interface'; | |||
| import { getNodeDragHandle, getOperatorIndex, isEdgeEqual } from './utils'; | |||
| import { | |||
| generateNodeNamesWithIncreasingIndex, | |||
| getNodeDragHandle, | |||
| getOperatorIndex, | |||
| isEdgeEqual, | |||
| } from './utils'; | |||
| export type RFState = { | |||
| nodes: Node<NodeData>[]; | |||
| @@ -54,7 +59,7 @@ export type RFState = { | |||
| target?: string | null, | |||
| ) => void; | |||
| deletePreviousEdgeOfClassificationNode: (connection: Connection) => void; | |||
| duplicateNode: (id: string) => void; | |||
| duplicateNode: (id: string, name: string) => void; | |||
| deleteEdge: () => void; | |||
| deleteEdgeById: (id: string) => void; | |||
| deleteNodeById: (id: string) => void; | |||
| @@ -63,6 +68,7 @@ export type RFState = { | |||
| updateMutableNodeFormItem: (id: string, field: string, value: any) => void; | |||
| getOperatorTypeFromId: (id?: string | null) => string | undefined; | |||
| updateNodeName: (id: string, name: string) => void; | |||
| generateNodeName: (name: string) => string; | |||
| setClickedNodeId: (id?: string) => void; | |||
| }; | |||
| @@ -226,8 +232,8 @@ const useGraphStore = create<RFState>()( | |||
| } | |||
| } | |||
| }, | |||
| duplicateNode: (id: string) => { | |||
| const { getNode, addNode } = get(); | |||
| duplicateNode: (id: string, name: string) => { | |||
| const { getNode, addNode, generateNodeName } = get(); | |||
| const node = getNode(id); | |||
| const position = { | |||
| x: (node?.position?.x || 0) + 30, | |||
| @@ -236,7 +242,7 @@ const useGraphStore = create<RFState>()( | |||
| addNode({ | |||
| ...(node || {}), | |||
| data: node?.data, | |||
| data: { ...(node?.data ?? {}), name: generateNodeName(name) }, | |||
| selected: false, | |||
| dragging: false, | |||
| id: `${node?.data?.label}:${humanId()}`, | |||
| @@ -383,6 +389,11 @@ const useGraphStore = create<RFState>()( | |||
| setClickedNodeId: (id?: string) => { | |||
| set({ clickedNodeId: id }); | |||
| }, | |||
| generateNodeName: (name: string) => { | |||
| const { nodes } = get(); | |||
| return generateNodeNamesWithIncreasingIndex(name, nodes); | |||
| }, | |||
| })), | |||
| { name: 'graph' }, | |||
| ), | |||
| @@ -184,11 +184,12 @@ export const buildNewPositionMap = ( | |||
| const intersectionKeys = intersectionWith( | |||
| previousKeys, | |||
| currentKeys, | |||
| (categoryDataKey, positionMapKey) => categoryDataKey === positionMapKey, | |||
| (categoryDataKey: string, positionMapKey: string) => | |||
| categoryDataKey === positionMapKey, | |||
| ); | |||
| // difference set | |||
| const currentDifferenceKeys = currentKeys.filter( | |||
| (x) => !intersectionKeys.some((y) => y === x), | |||
| (x) => !intersectionKeys.some((y: string) => y === x), | |||
| ); | |||
| const newPositionMap = currentDifferenceKeys.reduce< | |||
| Record<string, IPosition> | |||
| @@ -240,3 +241,51 @@ export const generateSwitchHandleText = (idx: number) => { | |||
| export const getNodeDragHandle = (nodeType?: string) => { | |||
| return nodeType === Operator.Note ? '.note-drag-handle' : undefined; | |||
| }; | |||
| const splitName = (name: string) => { | |||
| const names = name.split('_'); | |||
| const type = names.at(0); | |||
| const index = Number(names.at(-1)); | |||
| return { type, index }; | |||
| }; | |||
| export const generateNodeNamesWithIncreasingIndex = ( | |||
| name: string, | |||
| nodes: Node[], | |||
| ) => { | |||
| const templateNameList = nodes | |||
| .filter((x) => { | |||
| const temporaryName = x.data.name; | |||
| const { type, index } = splitName(temporaryName); | |||
| return ( | |||
| temporaryName.match(/_/g)?.length === 1 && | |||
| type === name && | |||
| !isNaN(index) | |||
| ); | |||
| }) | |||
| .map((x) => { | |||
| const temporaryName = x.data.name; | |||
| const { index } = splitName(temporaryName); | |||
| return { | |||
| idx: index, | |||
| name: temporaryName, | |||
| }; | |||
| }) | |||
| .sort((a, b) => a.idx - b.idx); | |||
| let index: number = 0; | |||
| for (let i = 0; i < templateNameList.length; i++) { | |||
| const idx = templateNameList[i]?.idx; | |||
| const nextIdx = templateNameList[i + 1]?.idx; | |||
| if (idx + 1 !== nextIdx) { | |||
| index = idx + 1; | |||
| break; | |||
| } | |||
| } | |||
| return `${name}_${index}`; | |||
| }; | |||