diff --git a/docs/caseShow/demos/pipelineDemo.tsx b/docs/caseShow/demos/pipelineDemo.tsx index b9624be..b4df109 100644 --- a/docs/caseShow/demos/pipelineDemo.tsx +++ b/docs/caseShow/demos/pipelineDemo.tsx @@ -6,11 +6,12 @@ import { FlowView, FlowViewProvider, Handle, + NodeProps, Position, + SelectType, useFlowViewer, } from '@ant-design/pro-flow'; import { FC, useCallback } from 'react'; -import { SelectType } from '../../../src'; import useStyles from './index.style'; interface PipeNodeChild { @@ -32,9 +33,7 @@ interface PipeNode { const nodeWidth = 170; const nodeHeight = 500; -export const PipeNode: FC<{ - data: PipeNode; -}> = ({ data }) => { +export const PipeNode: FC> = ({ data }) => { const { stepTitle, title, des, logo, needSwitch = false, children = [], selectType } = data; const { styles } = useStyles(); diff --git a/src/BasicGroupNode/demos/data.ts b/src/BasicGroupNode/demos/data.ts index 9f2ebe0..b0a7e0a 100644 --- a/src/BasicGroupNode/demos/data.ts +++ b/src/BasicGroupNode/demos/data.ts @@ -1,6 +1,6 @@ -import { FlowViewNode } from '@ant-design/pro-flow'; +import { BasicGroupNodeData, FlowViewNode } from '@ant-design/pro-flow'; -export const nodes: FlowViewNode[] = [ +export const nodes: FlowViewNode[] = [ { id: 'd1', label: 'group1', @@ -10,7 +10,7 @@ export const nodes: FlowViewNode[] = [ id: 'a5', data: { title: 'XXX数据源', - describe: 'cksadjfnf', + description: 'cksadjfnf', logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*jWDsQ5GTmHMAAAAAAAAAAAAADvuvAQ/original', }, }, @@ -19,7 +19,7 @@ export const nodes: FlowViewNode[] = [ data: { title: 'XXX_API', logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original', - describe: 'XXX_XXX_XXX_API', + description: 'XXX_XXX_XXX_API', }, }, { @@ -27,14 +27,14 @@ export const nodes: FlowViewNode[] = [ data: { title: 'XXXX产品', logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*ezaYT4wYRBwAAAAAAAAAAAAADvuvAQ/original', - describe: '2031030213014', + description: '2031030213014', }, }, { id: 'a8', data: { title: 'XXX数据源', - describe: 'cksadjfnf', + description: 'cksadjfnf', logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*jWDsQ5GTmHMAAAAAAAAAAAAADvuvAQ/original', }, }, @@ -43,7 +43,7 @@ export const nodes: FlowViewNode[] = [ data: { title: 'XXX_API', logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original', - describe: 'XXX_XXX_XXX_API', + description: 'XXX_XXX_XXX_API', }, }, ], @@ -57,7 +57,7 @@ export const nodes: FlowViewNode[] = [ id: 'a5', data: { title: 'XXX数据源', - describe: 'cksadjfnf', + description: 'cksadjfnf', logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*jWDsQ5GTmHMAAAAAAAAAAAAADvuvAQ/original', }, }, @@ -66,7 +66,7 @@ export const nodes: FlowViewNode[] = [ data: { title: 'XXX_API', logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original', - describe: 'XXX_XXX_XXX_API', + description: 'XXX_XXX_XXX_API', }, }, { @@ -74,14 +74,14 @@ export const nodes: FlowViewNode[] = [ data: { title: 'XXXX产品', logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*ezaYT4wYRBwAAAAAAAAAAAAADvuvAQ/original', - describe: '2031030213014', + description: '2031030213014', }, }, { id: 'a8', data: { title: 'XXX数据源', - describe: 'cksadjfnf', + description: 'cksadjfnf', logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*jWDsQ5GTmHMAAAAAAAAAAAAADvuvAQ/original', }, }, @@ -90,7 +90,7 @@ export const nodes: FlowViewNode[] = [ data: { title: 'XXX_API', logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original', - describe: 'XXX_XXX_XXX_API', + description: 'XXX_XXX_XXX_API', }, }, { @@ -98,7 +98,7 @@ export const nodes: FlowViewNode[] = [ data: { title: 'XXXX产品', logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*ezaYT4wYRBwAAAAAAAAAAAAADvuvAQ/original', - describe: '2031030213014', + description: '2031030213014', }, }, { @@ -106,7 +106,7 @@ export const nodes: FlowViewNode[] = [ data: { title: 'XXXX产品', logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*ezaYT4wYRBwAAAAAAAAAAAAADvuvAQ/original', - describe: '2031030213014', + description: '2031030213014', }, }, ], diff --git a/src/BasicGroupNode/index.tsx b/src/BasicGroupNode/index.tsx index b2691b9..22b39b2 100644 --- a/src/BasicGroupNode/index.tsx +++ b/src/BasicGroupNode/index.tsx @@ -92,7 +92,7 @@ const BasicNodeGroup: React.FC<{ }; _node.title = data.title; _node.logo = data.logo; - _node.des = data.describe; + _node.des = data.description; return GroupItem(_node); })}
diff --git a/src/FlowEditor/container/FlowEditor.tsx b/src/FlowEditor/container/FlowEditor.tsx index 4bb9b0b..beae465 100644 --- a/src/FlowEditor/container/FlowEditor.tsx +++ b/src/FlowEditor/container/FlowEditor.tsx @@ -1,7 +1,7 @@ import { createStyles, cx } from 'antd-style'; import isEqual from 'fast-deep-equal'; import { debounce, throttle } from 'lodash-es'; -import { JSXElementConstructor, forwardRef, useCallback, useEffect, useState } from 'react'; +import { JSXElementConstructor, forwardRef, useCallback, useEffect, useMemo } from 'react'; import { Flexbox } from 'react-layout-kit'; import ReactFlow, { Background, @@ -120,12 +120,25 @@ const FlowEditor = forwardRef( ref, ) => { const { theme, styles } = useStyles(); - const [flowInit, setFlowInit] = useState(false); const nodes: Node[] = useStore(flowEditorSelectors.nodeList, isEqual); const edges = useStore(flowEditorSelectors.edgeList, isEqual); const editor = useFlowEditor(); + const nodesInitialized = useNodesInitialized(); + + const flowInit = useMemo(() => { + if (nodesInitialized) { + return true; + } + + if (nodes.length > 0) { + return false; + } else { + return true; + } + }, [nodes, nodesInitialized]); + const [ // onNodesChange, updateEdgesOnConnection, @@ -134,6 +147,7 @@ const FlowEditor = forwardRef( onElementSelectChange, // onEdgesChange, dispatchNodes, + deselectElement, ] = useStore((s) => [ // s.onNodesChange, s.updateEdgesOnConnection, @@ -142,6 +156,7 @@ const FlowEditor = forwardRef( s.onElementSelectChange, // s.onEdgesChange, s.dispatchNodes, + s.deselectElement, ]); const instance = useReactFlow(); @@ -154,8 +169,6 @@ const FlowEditor = forwardRef( onChange: onViewPortChange ? debounce(onViewPortChange, 300) : undefined, }); - const nodesInitialized = useNodesInitialized(); - useEffect(() => { // 先把画布的 viewport 设置好 if (!defaultViewport) { @@ -166,7 +179,6 @@ const FlowEditor = forwardRef( // 然后设定初始化节点的相关状态 if (nodesInitialized) { - setFlowInit(true); onNodesInit?.(editor); } }, [nodesInitialized]); @@ -190,6 +202,7 @@ const FlowEditor = forwardRef( break; case 'remove': + deselectElement(c.id); dispatchNodes({ type: 'deleteNode', id: c.id }); break; case 'select': diff --git a/src/FlowEditor/hooks/useFlowEditor.ts b/src/FlowEditor/hooks/useFlowEditor.ts index a72afba..7e12628 100644 --- a/src/FlowEditor/hooks/useFlowEditor.ts +++ b/src/FlowEditor/hooks/useFlowEditor.ts @@ -1,6 +1,6 @@ import { useMemoizedFn } from 'ahooks'; import { useCallback, useMemo } from 'react'; -import { ReactFlowInstance, useReactFlow } from 'reactflow'; +import { ReactFlowInstance, XYPosition, useReactFlow } from 'reactflow'; import { useStoreApi } from '../store'; import { PublicStoreAction } from '../store/slices'; import { FlattenEdges, FlattenNodes } from '../types'; @@ -10,6 +10,7 @@ export interface FlowEditorInstance extends PublicStoreAction { getFlattenEdges: () => FlattenEdges; getSelectedKeys: () => string[]; reactflow?: ReactFlowInstance; + screenToFlowPosition: (position: XYPosition) => XYPosition; } export const useFlowEditor = (): FlowEditorInstance => { diff --git a/src/FlowEditor/store/slices/edgesSlice.ts b/src/FlowEditor/store/slices/edgesSlice.ts index 56d3ad1..000628e 100644 --- a/src/FlowEditor/store/slices/edgesSlice.ts +++ b/src/FlowEditor/store/slices/edgesSlice.ts @@ -11,6 +11,7 @@ export interface PublicEdgesAction { dispatchEdges: (payload: EdgeDispatch, options?: ActionOptions) => void; addEdges: (edges: Record, options?: ActionOptions) => void; deleteEdge: (id: string) => void; + deleteEdges: (ids: string[]) => void; updateEdge: (id: string, edge: Edge, options?: ActionOptions) => void; updateEdgeData: ( id: string, @@ -83,6 +84,7 @@ export const edgesSlice: StateCreator< changes.forEach((e) => { switch (e.type) { case 'remove': + get().deselectElement(e.id); get().dispatchEdges({ type: 'deleteEdge', id: e.id }); } }); @@ -93,6 +95,13 @@ export const edgesSlice: StateCreator< get().dispatchEdges({ type: 'deleteEdge', id }); }, + deleteEdges: (ids) => { + ids.forEach((id) => { + get().deselectElement(id); + get().dispatchEdges({ type: 'deleteEdge', id }); + }); + }, + updateEdgeData: (id, newData, deepReplace = false, options) => { get().dispatchEdges( { diff --git a/src/FlowEditor/store/slices/generalActionSlice.ts b/src/FlowEditor/store/slices/generalActionSlice.ts index e422108..e1e6d54 100644 --- a/src/FlowEditor/store/slices/generalActionSlice.ts +++ b/src/FlowEditor/store/slices/generalActionSlice.ts @@ -130,8 +130,9 @@ export const generalActionSlice: StateCreator< }, deleteSelection: () => { - const { selectedKeys, flattenEdges, flattenNodes, dispatchNodes, dispatchEdges } = get(); - + const { selectedKeys, flattenEdges, flattenNodes, dispatchNodes, dispatchEdges, deselectAll } = + get(); + deselectAll(); selectedKeys.forEach((id) => { if (flattenNodes[id]) dispatchNodes({ type: 'deleteNode', id }); if (flattenEdges[id]) dispatchEdges({ type: 'deleteEdge', id }); diff --git a/src/FlowEditor/store/slices/nodesSlice.ts b/src/FlowEditor/store/slices/nodesSlice.ts index 6202c14..f7a1497 100644 --- a/src/FlowEditor/store/slices/nodesSlice.ts +++ b/src/FlowEditor/store/slices/nodesSlice.ts @@ -29,11 +29,13 @@ export interface PublicNodesAction { */ deleteNode: (id: string) => void; /** - * 更新节点元数据 - * @param id 要更新的节点 id - * @param key 要更新的元数据键名 - * @param value 要更新的元数据值 - * @param options 节点操作的选项 + * 删除节点元素 + * @param id 要删除的节点 id + */ + deleteNodes: (ids: string[]) => void; + /** + * 批量删除节点 + * @param ids 要批量删除的节点 id */ updateNodeMeta: ( id: string, @@ -154,4 +156,10 @@ export const nodesSlice: StateCreator< get().deselectElement(id); get().dispatchNodes({ type: 'deleteNode', id }); }, + deleteNodes: (ids) => { + ids.forEach((id) => { + get().deselectElement(id); + get().dispatchNodes({ type: 'deleteNode', id }); + }); + }, }); diff --git a/src/FlowView/demos/data.tsx b/src/FlowView/demos/data.tsx index 8e808a7..b78a1aa 100644 --- a/src/FlowView/demos/data.tsx +++ b/src/FlowView/demos/data.tsx @@ -39,7 +39,7 @@ export const nodes: FlowViewNode[] = [ id: 'a1', label: '12345', data: { - title: 'XXX_API_ddddddddddddddddddddddddddddddddddddddddddddddddddddddb1', + title: 'XXX_API_ddddddddddddddddddddddddddddddƒddddddddddddddddddddddddb1', logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original', description: 'XXX_XXX_XXX_API', }, @@ -102,7 +102,7 @@ export const nodes: FlowViewNode[] = [ id: 'a5', data: { title: 'XXX数据源', - describe: 'cksadjfnf', + description: 'cksadjfnf', logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*jWDsQ5GTmHMAAAAAAAAAAAAADvuvAQ/original', }, }, @@ -111,7 +111,7 @@ export const nodes: FlowViewNode[] = [ data: { title: 'XXX_API', logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original', - describe: 'XXX_XXX_XXX_API', + description: 'XXX_XXX_XXX_API', }, }, { @@ -119,14 +119,14 @@ export const nodes: FlowViewNode[] = [ data: { title: 'XXXX产品', logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*ezaYT4wYRBwAAAAAAAAAAAAADvuvAQ/original', - describe: '2031030213014', + description: '2031030213014', }, }, { id: 'a8', data: { title: 'XXX数据源', - describe: 'cksadjfnf', + description: 'cksadjfnf', logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*jWDsQ5GTmHMAAAAAAAAAAAAADvuvAQ/original', }, }, @@ -135,7 +135,7 @@ export const nodes: FlowViewNode[] = [ data: { title: 'XXX_API', logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original', - describe: 'XXX_XXX_XXX_API', + description: 'XXX_XXX_XXX_API', }, }, { @@ -143,7 +143,7 @@ export const nodes: FlowViewNode[] = [ data: { title: 'XXXX产品', logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*ezaYT4wYRBwAAAAAAAAAAAAADvuvAQ/original', - describe: '2031030213014', + description: '2031030213014', }, }, { @@ -151,7 +151,7 @@ export const nodes: FlowViewNode[] = [ data: { title: 'XXXX产品', logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*ezaYT4wYRBwAAAAAAAAAAAAADvuvAQ/original', - describe: '2031030213014', + description: '2031030213014', }, }, ], diff --git a/src/FlowView/provider/FlowViewProvider.tsx b/src/FlowView/provider/FlowViewProvider.tsx index 12b9ba0..86b60ff 100644 --- a/src/FlowView/provider/FlowViewProvider.tsx +++ b/src/FlowView/provider/FlowViewProvider.tsx @@ -1,4 +1,4 @@ -import { FlowViewEdge, FlowViewNode, MiniMapPosition, NodeTypeDataMap } from '@/constants'; +import { FlowViewEdge, FlowViewNode, MiniMapPosition } from '@/constants'; import { FC, ReactNode, useCallback, useEffect, useRef, useState } from 'react'; import { Edge, Node, ReactFlowProvider, useReactFlow } from 'reactflow'; import { LayoutOptions, NodeMapping, SelectType } from '../constants'; @@ -35,7 +35,7 @@ const ProviderInner: FC<{ children: ReactNode }> = ({ children }) => { const flowDataAdapter = useCallback( ( - initNodes: FlowViewNode[], + initNodes: FlowViewNode[], initEdges: FlowViewEdge[], zoom: number, autoLayout: boolean, diff --git a/src/FlowView/provider/provider.ts b/src/FlowView/provider/provider.ts index 3a36f21..55e48cf 100644 --- a/src/FlowView/provider/provider.ts +++ b/src/FlowView/provider/provider.ts @@ -1,4 +1,4 @@ -import { FlowViewEdge, FlowViewNode, MiniMapPosition, NodeTypeDataMap } from '@/constants'; +import { FlowViewEdge, FlowViewNode, MiniMapPosition } from '@/constants'; import { createContext, type ReactNode } from 'react'; import { Edge, Node, ReactFlowInstance } from 'reactflow'; import { NodeMapping, SelectType } from '../constants'; @@ -6,7 +6,7 @@ import { LayoutOptions } from './../constants'; interface FlowViewContextProps { flowDataAdapter?: ( - nodes: FlowViewNode[], + nodes: FlowViewNode[], edges: FlowViewEdge[], zoom: number, autoLayout: boolean, diff --git a/src/constants.tsx b/src/constants.tsx index 65f735d..7ea8db8 100644 --- a/src/constants.tsx +++ b/src/constants.tsx @@ -35,7 +35,7 @@ export interface DefaultNodeData { export interface BasicNodeData { title: string; - describe: string; + description: string; logo: string; titleSlot?: { type: 'left' | 'right'; @@ -48,12 +48,6 @@ export interface BasicGroupNodeData { data: BasicNodeData; } -export interface NodeTypeDataMap { - default: DefaultNodeData; - BasicNode: BasicNodeData; - BasicNodeGroup: BasicGroupNodeData[]; -} - export interface FlowViewNode { id: string; select?: SelectType; diff --git a/src/index.ts b/src/index.ts index eaf6d29..857e122 100644 --- a/src/index.ts +++ b/src/index.ts @@ -31,6 +31,6 @@ export * from './Input'; export * from './MiniMap'; export * from './constants'; -export type { Connection, EdgeChange, EdgeProps, NodeChange } from 'reactflow'; +export type { Connection, EdgeChange, EdgeProps, NodeChange, NodeProps } from 'reactflow'; export type { FlowEditorStoreProviderProps } from './FlowStoreProvider'; export type { ExtraAction } from './NodeField';