From 3b7b6240c3adb967c5fe9e98ed73c05cd60ecd46 Mon Sep 17 00:00:00 2001 From: balibabu Date: Wed, 12 Jun 2024 17:38:41 +0800 Subject: [PATCH] feat: add delete menu to graph node #918 (#1133) ### What problem does this PR solve? feat: add delete menu to graph node #918 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/components/operate-dropdown/index.tsx | 12 +- web/src/pages/flow/canvas/index.tsx | 4 +- web/src/pages/flow/canvas/node/index.less | 6 +- web/src/pages/flow/canvas/node/index.tsx | 30 +++- web/src/pages/flow/constant.tsx | 17 ++- web/src/pages/flow/flow-sider/index.tsx | 19 ++- web/src/pages/flow/hooks.ts | 7 +- web/src/pages/flow/mock.tsx | 10 +- web/src/pages/flow/store.ts | 139 ++++++++++-------- web/src/pages/flow/utils.ts | 2 +- 10 files changed, 149 insertions(+), 97 deletions(-) diff --git a/web/src/components/operate-dropdown/index.tsx b/web/src/components/operate-dropdown/index.tsx index 941456aef..27865fd1b 100644 --- a/web/src/components/operate-dropdown/index.tsx +++ b/web/src/components/operate-dropdown/index.tsx @@ -1,6 +1,5 @@ -import { ReactComponent as MoreIcon } from '@/assets/svg/more.svg'; import { useShowDeleteConfirm } from '@/hooks/commonHooks'; -import { DeleteOutlined } from '@ant-design/icons'; +import { DeleteOutlined, MoreOutlined } from '@ant-design/icons'; import { Dropdown, MenuProps, Space } from 'antd'; import { useTranslation } from 'react-i18next'; @@ -8,12 +7,14 @@ import React from 'react'; import styles from './index.less'; interface IProps { - deleteItem: () => Promise; + deleteItem: () => Promise | void; + iconFontSize?: number; } const OperateDropdown = ({ deleteItem, children, + iconFontSize = 30, }: React.PropsWithChildren) => { const { t } = useTranslation(); const showDeleteConfirm = useShowDeleteConfirm(); @@ -51,7 +52,10 @@ const OperateDropdown = ({ > {children || ( - + )} diff --git a/web/src/pages/flow/canvas/index.tsx b/web/src/pages/flow/canvas/index.tsx index c6855b9b4..fb10a6d84 100644 --- a/web/src/pages/flow/canvas/index.tsx +++ b/web/src/pages/flow/canvas/index.tsx @@ -18,12 +18,12 @@ import { useSelectCanvasData, useShowDrawer, } from '../hooks'; -import { TextUpdaterNode } from './node'; +import { RagNode } from './node'; import ChatDrawer from '../chat/drawer'; import styles from './index.less'; -const nodeTypes = { textUpdater: TextUpdaterNode }; +const nodeTypes = { ragNode: RagNode }; const edgeTypes = { buttonEdge: ButtonEdge, diff --git a/web/src/pages/flow/canvas/node/index.less b/web/src/pages/flow/canvas/node/index.less index 855e9b5e7..092dec440 100644 --- a/web/src/pages/flow/canvas/node/index.less +++ b/web/src/pages/flow/canvas/node/index.less @@ -1,5 +1,6 @@ -.textUpdaterNode { +.ragNode { // height: 50px; + position: relative; box-shadow: -6px 0 12px 0 rgba(179, 177, 177, 0.08), -3px 0 6px -4px rgba(0, 0, 0, 0.12), @@ -13,6 +14,9 @@ color: #777; font-size: 12px; } + .description { + font-size: 10px; + } } .selectedNode { border: 1px solid rgb(59, 118, 244); diff --git a/web/src/pages/flow/canvas/node/index.tsx b/web/src/pages/flow/canvas/node/index.tsx index eb9a2095e..db08b4d5f 100644 --- a/web/src/pages/flow/canvas/node/index.tsx +++ b/web/src/pages/flow/canvas/node/index.tsx @@ -1,19 +1,28 @@ import classNames from 'classnames'; import { Handle, NodeProps, Position } from 'reactflow'; -import { Space } from 'antd'; -import { Operator } from '../../constant'; +import OperateDropdown from '@/components/operate-dropdown'; +import { Flex, Space } from 'antd'; +import { useCallback } from 'react'; +import { Operator, operatorMap } from '../../constant'; import OperatorIcon from '../../operator-icon'; +import useGraphStore from '../../store'; import styles from './index.less'; -export function TextUpdaterNode({ +export function RagNode({ + id, data, isConnectable = true, selected, }: NodeProps<{ label: string }>) { + const deleteNodeById = useGraphStore((store) => store.deleteNodeById); + const deleteNode = useCallback(() => { + deleteNodeById(id); + }, [id, deleteNodeById]); + return (
@@ -37,14 +46,21 @@ export function TextUpdaterNode({ {/* */} -
- + + - {data.label} + {data.label} + + +
+ {operatorMap[data.label as Operator].description}
); diff --git a/web/src/pages/flow/constant.tsx b/web/src/pages/flow/constant.tsx index 5fb0f06ee..8eb817b34 100644 --- a/web/src/pages/flow/constant.tsx +++ b/web/src/pages/flow/constant.tsx @@ -19,18 +19,27 @@ export const operatorIconMap = { [Operator.Begin]: SlidersOutlined, }; -export const componentList = [ +export const operatorMap = { + [Operator.Retrieval]: { + description: 'Retrieval description drjlftglrthjftl', + }, + [Operator.Generate]: { description: 'Generate description' }, + [Operator.Answer]: { description: 'Answer description' }, + [Operator.Begin]: { description: 'Begin description' }, +}; + +export const componentMenuList = [ { name: Operator.Retrieval, - description: '', + description: operatorMap[Operator.Retrieval].description, }, { name: Operator.Generate, - description: '', + description: operatorMap[Operator.Generate].description, }, { name: Operator.Answer, - description: '', + description: operatorMap[Operator.Answer].description, }, ]; diff --git a/web/src/pages/flow/flow-sider/index.tsx b/web/src/pages/flow/flow-sider/index.tsx index 29d7b2593..b5428abf5 100644 --- a/web/src/pages/flow/flow-sider/index.tsx +++ b/web/src/pages/flow/flow-sider/index.tsx @@ -1,13 +1,15 @@ -import { Card, Flex, Layout, Space } from 'antd'; +import { Card, Flex, Layout, Space, Typography } from 'antd'; import classNames from 'classnames'; -import { componentList } from '../constant'; +import { componentMenuList } from '../constant'; import { useHandleDrag } from '../hooks'; import OperatorIcon from '../operator-icon'; import styles from './index.less'; const { Sider } = Layout; +const { Text } = Typography; + interface IProps { setCollapsed: (width: boolean) => void; collapsed: boolean; @@ -25,7 +27,7 @@ const FlowSide = ({ setCollapsed, collapsed }: IProps) => { onCollapse={(value) => setCollapsed(value)} > - {componentList.map((x) => { + {componentMenuList.map((x) => { return ( { - {/* } - shape={'square'} - /> */}
{x.name} -
{x.description}
+ + {x.description} +
diff --git a/web/src/pages/flow/hooks.ts b/web/src/pages/flow/hooks.ts index a0f79d087..68d29dbbe 100644 --- a/web/src/pages/flow/hooks.ts +++ b/web/src/pages/flow/hooks.ts @@ -18,6 +18,7 @@ import { Node, Position, ReactFlowInstance } from 'reactflow'; import { useDebounceEffect } from 'ahooks'; import { humanId } from 'human-id'; import { useParams } from 'umi'; +import { Operator } from './constant'; import useGraphStore, { RFState } from './store'; import { buildDslComponentsByGraph } from './utils'; @@ -79,7 +80,7 @@ export const useHandleDrop = () => { }); const newNode = { id: `${type}:${humanId()}`, - type: 'textUpdater', + type: 'ragNode', position: position || { x: 0, y: 0, @@ -110,7 +111,9 @@ export const useShowDrawer = () => { const handleShow = useCallback( (node: Node) => { setClickedNode(node); - showDrawer(); + if (node.data.label !== Operator.Answer) { + showDrawer(); + } }, [showDrawer], ); diff --git a/web/src/pages/flow/mock.tsx b/web/src/pages/flow/mock.tsx index 5f8ef0335..5b5fda716 100644 --- a/web/src/pages/flow/mock.tsx +++ b/web/src/pages/flow/mock.tsx @@ -5,7 +5,7 @@ export const initialNodes = [ sourcePosition: Position.Left, targetPosition: Position.Right, id: 'node-1', - type: 'textUpdater', + type: 'ragNode', position: { x: 0, y: 0 }, // position: { x: 400, y: 100 }, data: { label: 123 }, @@ -38,7 +38,7 @@ export const dsl = { nodes: [ { id: 'begin', - type: 'textUpdater', + type: 'ragNode', position: { x: 50, y: 200, @@ -51,7 +51,7 @@ export const dsl = { }, // { // id: 'Answer:China', - // type: 'textUpdater', + // type: 'ragNode', // position: { // x: 150, // y: 200, @@ -64,7 +64,7 @@ export const dsl = { // }, // { // id: 'Retrieval:China', - // type: 'textUpdater', + // type: 'ragNode', // position: { // x: 250, // y: 200, @@ -77,7 +77,7 @@ export const dsl = { // }, // { // id: 'Generate:China', - // type: 'textUpdater', + // type: 'ragNode', // position: { // x: 100, // y: 100, diff --git a/web/src/pages/flow/store.ts b/web/src/pages/flow/store.ts index 8f956e551..191eb05a7 100644 --- a/web/src/pages/flow/store.ts +++ b/web/src/pages/flow/store.ts @@ -34,75 +34,88 @@ export type RFState = { addNode: (nodes: Node) => void; deleteEdge: () => void; deleteEdgeById: (id: string) => void; + deleteNodeById: (id: string) => void; findNodeByName: (operatorName: Operator) => Node | undefined; }; // this is our useStore hook that we can use in our components to get parts of the store and call actions const useGraphStore = create()( - devtools((set, get) => ({ - nodes: [] as Node[], - edges: [] as Edge[], - selectedNodeIds: [], - selectedEdgeIds: [], - onNodesChange: (changes: NodeChange[]) => { - set({ - nodes: applyNodeChanges(changes, get().nodes), - }); - }, - onEdgesChange: (changes: EdgeChange[]) => { - set({ - edges: applyEdgeChanges(changes, get().edges), - }); - }, - onConnect: (connection: Connection) => { - set({ - edges: addEdge(connection, get().edges), - }); - }, - onSelectionChange: ({ nodes, edges }: OnSelectionChangeParams) => { - set({ - selectedEdgeIds: edges.map((x) => x.id), - selectedNodeIds: nodes.map((x) => x.id), - }); - }, - setNodes: (nodes: Node[]) => { - set({ nodes }); - }, - setEdges: (edges: Edge[]) => { - set({ edges }); - }, - addNode: (node: Node) => { - set({ nodes: get().nodes.concat(node) }); - }, - deleteEdge: () => { - const { edges, selectedEdgeIds } = get(); - set({ - edges: edges.filter((edge) => - selectedEdgeIds.every((x) => x !== edge.id), - ), - }); - }, - deleteEdgeById: (id: string) => { - const { edges } = get(); - set({ - edges: edges.filter((edge) => edge.id !== id), - }); - }, - findNodeByName: (name: Operator) => { - return get().nodes.find((x) => x.data.label === name); - }, - updateNodeForm: (nodeId: string, values: any) => { - set({ - nodes: get().nodes.map((node) => { - if (node.id === nodeId) { - node.data = { ...node.data, form: values }; - } + devtools( + (set, get) => ({ + nodes: [] as Node[], + edges: [] as Edge[], + selectedNodeIds: [] as string[], + selectedEdgeIds: [] as string[], + onNodesChange: (changes: NodeChange[]) => { + set({ + nodes: applyNodeChanges(changes, get().nodes), + }); + }, + onEdgesChange: (changes: EdgeChange[]) => { + set({ + edges: applyEdgeChanges(changes, get().edges), + }); + }, + onConnect: (connection: Connection) => { + set({ + edges: addEdge(connection, get().edges), + }); + }, + onSelectionChange: ({ nodes, edges }: OnSelectionChangeParams) => { + set({ + selectedEdgeIds: edges.map((x) => x.id), + selectedNodeIds: nodes.map((x) => x.id), + }); + }, + setNodes: (nodes: Node[]) => { + set({ nodes }); + }, + setEdges: (edges: Edge[]) => { + set({ edges }); + }, + addNode: (node: Node) => { + set({ nodes: get().nodes.concat(node) }); + }, + deleteEdge: () => { + const { edges, selectedEdgeIds } = get(); + set({ + edges: edges.filter((edge) => + selectedEdgeIds.every((x) => x !== edge.id), + ), + }); + }, + deleteEdgeById: (id: string) => { + const { edges } = get(); + set({ + edges: edges.filter((edge) => edge.id !== id), + }); + }, + deleteNodeById: (id: string) => { + const { nodes, edges } = get(); + set({ + nodes: nodes.filter((node) => node.id !== id), + edges: edges + .filter((edge) => edge.source !== id) + .filter((edge) => edge.target !== id), + }); + }, + findNodeByName: (name: Operator) => { + return get().nodes.find((x) => x.data.label === name); + }, + updateNodeForm: (nodeId: string, values: any) => { + set({ + nodes: get().nodes.map((node) => { + if (node.id === nodeId) { + node.data = { ...node.data, form: values }; + } - return node; - }), - }); - }, - })), + return node; + }), + }); + }, + }), + { name: 'graph' }, + ), ); export default useGraphStore; diff --git a/web/src/pages/flow/utils.ts b/web/src/pages/flow/utils.ts index 68735e610..576ef48b8 100644 --- a/web/src/pages/flow/utils.ts +++ b/web/src/pages/flow/utils.ts @@ -41,7 +41,7 @@ export const buildNodesAndEdgesFromDSLComponents = (data: DSLComponents) => { const upstream = [...value.upstream]; nodes.push({ id: key, - type: 'textUpdater', + type: 'ragNode', position: { x: 0, y: 0 }, data: { label: value.obj.component_name,