diff --git a/docs/useDocs/autoLayout.md b/docs/useDocs/autoLayout.md
new file mode 100644
index 0000000..543ae3e
--- /dev/null
+++ b/docs/useDocs/autoLayout.md
@@ -0,0 +1,143 @@
+---
+nav: 使用文档
+group:
+ title: 进阶使用
+ order: 2
+title: 自动布局
+description:
+---
+
+## 自动布局
+
+当你使用 FlowView 组件传入 nodes 和 edges 列表后,FlowView 会自动梳理节点之间的逻辑关系,并给出一个自动布局结果渲染在画布上,比如下面这段关系渲染后的结果如下。
+
+```js
+const nodes = [
+ {
+ id: 'a1',
+ data: {
+ title: '节点1',
+ },
+ },
+ {
+ id: 'a2',
+ data: {
+ title: '节点2',
+ },
+ },
+ {
+ id: 'a3',
+ data: {
+ title: '节点3',
+ },
+ },
+ {
+ id: 'a4',
+ data: {
+ title: '节点4',
+ },
+ },
+ {
+ id: 'a5',
+ data: {
+ title: '节点5',
+ },
+ },
+ {
+ id: 'a6',
+ data: {
+ title: '节点6',
+ },
+ },
+];
+
+const edges = [
+ {
+ source: 'a1',
+ target: 'a2',
+ },
+ {
+ source: 'a1',
+ target: 'a3',
+ },
+ {
+ source: 'a2',
+ target: 'a3',
+ },
+ {
+ source: 'a3',
+ target: 'a6',
+ },
+ {
+ source: 'a2',
+ target: 'a4',
+ },
+ {
+ source: 'a3',
+ target: 'a5',
+ },
+ {
+ source: 'a2',
+ target: 'a6',
+ },
+];
+
+function App() {
+ return (
+
+
+
+ );
+}
+
+export default App;
+```
+
+布局效果如下所示:
+
+
+## 自定义布局
+
+如果希望自己设置坐标,可以给节点添加 position 属性来控制。
+
+```js
+const nodes = [
+ {
+ id: 'a1',
+ position: {
+ x: 100,
+ y: 300,
+ },
+ data: {
+ title: '节点1',
+ },
+ },
+ {
+ id: 'a2',
+ position: {
+ x: 300,
+ y: 600,
+ },
+ data: {
+ title: '节点2',
+ },
+ },
+ ...
+];
+```
+
+
+
+## 关闭自动布局
+
+你也可以在 FlowView 中直接关闭自动布局功能
+
+```js
+
+```
+
+:::info
+当你关掉自动布局能力,并且没有为节点单独设置坐标,那么 FlowView 会将节点的坐标初始化为 1,1。效果上就是所有的节点重合在一起。
+:::
+
+
diff --git a/docs/useDocs/baseInrro.md b/docs/useDocs/baseInrro.md
index 7d29631..7db97f6 100644
--- a/docs/useDocs/baseInrro.md
+++ b/docs/useDocs/baseInrro.md
@@ -7,3 +7,42 @@ title: 根本性概念
order: 1
description:
---
+
+## 术语和定义
+
+### Nodes
+
+ProFlow 中的节点是一个 React 组件。这意味着它可以渲染您喜欢的任何内容。每个节点都有一个 x 和 y 坐标,这告诉它它在视口中的位置。在不设置 type 的情况下,FlowView 组件默认会把组件设置为[Lineage](/components/lineage-node)类型的节点。你也可以自定义节点类型。
+
+
+
+### Custom Nodes
+
+[自定义节点使用说明](/components/customNodeDoc)
+
+
+### Handles
+
+`Handle` 可以翻译为 “**句柄**” 或者 “**端口**”,是边缘连接到节点的位置。`Handle`可以放置在任何地方。
+
+可以用以下方式式引入 `Handle` 与 `Position`,来自定义 `Handle` 在节点中的位置。
+
+```ts
+import { Handle, type Position } from '@ant-design/pro-flow';
+```
+
+
+
+### Edges
+
+一条 Edge 连接两个节点。每个边缘都需要一个`source` 和 一个`target`。 ProFlow 内置了五种边缘类型:
+
+- 'straight'
+- 'step'
+ -'smoothstep'
+ -'bezier'
+- 'radius' 。
+
+FlowView 把 `smoothstep` 设置为默认类型。 你也可以自定义边缘类型。
+
+
diff --git a/docs/useDocs/customNodeDoc.md b/docs/useDocs/customNodeDoc.md
new file mode 100644
index 0000000..12b45ec
--- /dev/null
+++ b/docs/useDocs/customNodeDoc.md
@@ -0,0 +1,161 @@
+---
+nav: 使用文档
+group:
+ title: 进阶使用
+ order: 2
+title: 自定义节点
+description:
+---
+
+## 自定义节点
+
+ProFlow 的一个强大功能是能够添加自定义节点。在自定义节点中,您可以渲染所需的所有内容。例如,您可以定义多个源句柄和目标句柄,并呈现表单输入或图表。在本节中,我们将实现一个带有输入字段的节点,该输入字段更新应用程序另一部分中的一些文本。
+
+## 实现自定义节点
+
+让我们开始实现:声明一个自定义节点`TextUpdaterNode`组件,它的结构由 3 个 Handle 组件和一块内容区组成。其中一个 Handle 的位置在顶部,两个在底部。
+
+```js
+import { useCallback } from 'react';
+import { Handle, Position } from '@ant-design/pro-flow';
+
+const handleStyle = { left: 10 };
+
+function TextUpdaterNode({ data }) {
+ const onChange = useCallback((evt) => {
+ console.log(evt.target.value);
+ }, []);
+
+ return (
+ <>
+
+
+
+
+
+
+
+ >
+ );
+}
+```
+
+## 添加节点类型
+
+你可以将新的节点类型添加到 prop 中传递给 FlowView。
+
+:::warning
+**在组件外部定义或缓存`nodeTypes`非常重要。** 否则 React 会在每次渲染时创建一个新的对象,这会导致性能问题和错误。
+:::
+
+```js
+const nodeTypes = useMemo(() => ({ textUpdater: TextUpdaterNode }) ,[]);
+
+return
+```
+
+定义新节点类型后,可以在 nodes 列表中使用这个类型了。
+
+```js
+const nodes = [
+ {
+ id: 'n1',
+ type: 'textUpdater',
+ data: { value: 123 },
+ },
+];
+```
+
+然后你就可以得到如下的自定义节点效果。
+
+
+## 使用多个 Handle
+
+当一个节点拥有多个 Handle 时,在连接时只传递节点 Id 是不够的,还需要传递特定的 HandleId。
+
+```js
+const initialEdge = [
+ { id: 'edge-1', source: 'n1', sourceHandle: 'a', target: 'n2' },
+ { id: 'edge-2', source: 'n1', sourceHandle: 'b', target: 'n3' },
+];
+```
+
+效果如下:
+
+
+## 添加节点交互
+
+在自定义节点的 data 中,FlowView 还会透传 2 个属性 selectType 与 zoom。你可以通过这两个属性来给节点配置一些交互。
+
+```js
+import {
+ FlowView,
+ FlowViewProvider,
+ Handle,
+ Position,
+ SelectType,
+ useFlowViewer,
+} from '@ant-design/pro-flow';
+
+const edges = [
+ { id: 'edge-1', source: 'n1', target: 'n2', sourceHandle: 'a' },
+ { id: 'edge-2', source: 'n1', target: 'n3', sourceHandle: 'b' },
+];
+
+const CustomNode: FC<{
+ data: {
+ title: string,
+ selectType: SelectType,
+ },
+}> = ({ data }) => {
+ console.log(data);
+ const onChange = useCallback((evt) => {
+ console.log(evt.target.value);
+ }, []);
+
+ return (
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const nodeTypes = { customNode: CustomNode };
+
+function App() {
+ const flowViewer = useFlowViewer();
+
+ return (
+
+ {
+ flowViewer?.selectNode(n.id, SelectType.SELECT);
+ }}
+ onPaneClick={() => {
+ flowViewer?.selectNodes(['n1', 'n2', 'n3'], SelectType.DEFAULT);
+ }}
+ nodes={nodes}
+ edges={edges}
+ nodeTypes={nodeTypes}
+ miniMap={false}
+ />
+
+ );
+}
+function ProApp() {
+ return (
+
+
+
+ );
+}
+```
+
+效果如下:
+
diff --git a/docs/useDocs/demos/CoreEdge.tsx b/docs/useDocs/demos/CoreEdge.tsx
new file mode 100644
index 0000000..6ca5afe
--- /dev/null
+++ b/docs/useDocs/demos/CoreEdge.tsx
@@ -0,0 +1,108 @@
+import styled from 'styled-components';
+import { FlowView } from '../../../src/index';
+
+const Container = styled.div`
+ width: 800px;
+ height: 300px;
+`;
+
+const nodes = [
+ {
+ id: 'a1',
+ data: {
+ title: '节点1',
+ },
+ },
+ {
+ id: 'a2',
+ data: {
+ title: '节点2',
+ },
+ },
+ {
+ id: 'a3',
+ data: {
+ title: '节点3',
+ },
+ },
+ {
+ id: 'a4',
+ data: {
+ title: '节点4',
+ },
+ },
+ {
+ id: 'a5',
+ data: {
+ title: '节点5',
+ },
+ },
+ {
+ id: 'a6',
+ data: {
+ title: '节点6',
+ },
+ },
+];
+
+const edges = [
+ {
+ source: 'a1',
+ target: 'a2',
+ type: 'straight',
+ label: 'straight',
+ },
+ {
+ source: 'a1',
+ target: 'a3',
+ type: 'step',
+ label: 'step',
+ },
+ {
+ source: 'a2',
+ target: 'a3',
+ type: 'straight',
+ label: 'straight',
+ },
+ {
+ source: 'a3',
+ target: 'a6',
+ type: 'bezier',
+ label: 'bezier',
+ },
+ {
+ source: 'a2',
+ target: 'a4',
+ type: 'smmothstep',
+ label: 'smmothstep',
+ },
+ {
+ source: 'a3',
+ target: 'a5',
+ type: 'bezier',
+ label: 'bezier',
+ },
+ {
+ source: 'a2',
+ target: 'a6',
+ type: 'radius',
+ label: 'radius',
+ },
+];
+
+function App() {
+ return (
+
+ {
+ console.log(node);
+ }}
+ nodes={nodes}
+ edges={edges}
+ miniMap={false}
+ />
+
+ );
+}
+
+export default App;
diff --git a/docs/useDocs/demos/CoreHandle.tsx b/docs/useDocs/demos/CoreHandle.tsx
new file mode 100644
index 0000000..b4da5d5
--- /dev/null
+++ b/docs/useDocs/demos/CoreHandle.tsx
@@ -0,0 +1,71 @@
+import { FC } from 'react';
+import styled from 'styled-components';
+import { FlowView, Handle, Position } from '../../../src/index';
+
+const Wrap = styled.div`
+ width: 200px;
+ height: 83px;
+ background-color: white;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: relative;
+`;
+
+const Container = styled.div`
+ width: 800px;
+ height: 300px;
+`;
+
+const CustomNode: FC<{
+ data: {
+ title: string;
+ };
+}> = (props) => {
+ const { data } = props;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const nodes = [
+ {
+ id: 'b1',
+ type: 'customNode',
+ data: {
+ title: '一堆 Handle',
+ },
+ },
+];
+
+const nodeTypes = { customNode: CustomNode };
+
+function App() {
+ return (
+
+ {
+ console.log(node);
+ }}
+ nodes={nodes}
+ edges={[]}
+ nodeTypes={nodeTypes}
+ miniMap={false}
+ autoLayout={false}
+ />
+
+ );
+}
+
+export default App;
diff --git a/docs/useDocs/demos/CoreNode.tsx b/docs/useDocs/demos/CoreNode.tsx
new file mode 100644
index 0000000..0e95a9c
--- /dev/null
+++ b/docs/useDocs/demos/CoreNode.tsx
@@ -0,0 +1,31 @@
+/**
+ * compact: true
+ */
+import { FlowView } from '@ant-design/pro-flow';
+import styled from 'styled-components';
+
+const nodes = [
+ {
+ id: 'a1',
+ data: {
+ title: 'XXX_API_b3',
+ logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original',
+ description: 'XXX_XXX_XXX_API',
+ },
+ },
+];
+
+function App() {
+ return (
+
+
+
+ );
+}
+
+export default App;
+
+const Container = styled.div`
+ width: 100%;
+ height: 300px;
+`;
diff --git a/docs/useDocs/demos/CustomerNode.tsx b/docs/useDocs/demos/CustomerNode.tsx
new file mode 100644
index 0000000..e4fade6
--- /dev/null
+++ b/docs/useDocs/demos/CustomerNode.tsx
@@ -0,0 +1,61 @@
+import { FC, useCallback } from 'react';
+import styled from 'styled-components';
+import { FlowView, Handle, Position } from '../../../src/index';
+
+const Wrap = styled.div`
+ width: 200px;
+ height: 83px;
+ background-color: white;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+`;
+
+const Container = styled.div`
+ width: 800px;
+ height: 300px;
+`;
+
+const CustomNode: FC<{
+ data: {
+ title: string;
+ };
+}> = ({ data }) => {
+ const onChange = useCallback((evt) => {
+ console.log(evt.target.value);
+ }, []);
+
+ return (
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const nodes = [
+ {
+ id: 'b1',
+ type: 'customNode',
+ data: {
+ title: 'Text',
+ },
+ },
+];
+
+const nodeTypes = { customNode: CustomNode };
+
+function App() {
+ return (
+
+
+
+ );
+}
+
+export default App;
diff --git a/docs/useDocs/demos/autoLayoutDemo1.tsx b/docs/useDocs/demos/autoLayoutDemo1.tsx
new file mode 100644
index 0000000..96e22ac
--- /dev/null
+++ b/docs/useDocs/demos/autoLayoutDemo1.tsx
@@ -0,0 +1,87 @@
+import styled from 'styled-components';
+import { FlowView } from '../../../src/index';
+
+const Container = styled.div`
+ width: 800px;
+ height: 300px;
+`;
+
+const nodes = [
+ {
+ id: 'a1',
+ data: {
+ title: '节点1',
+ },
+ },
+ {
+ id: 'a2',
+ data: {
+ title: '节点2',
+ },
+ },
+ {
+ id: 'a3',
+ data: {
+ title: '节点3',
+ },
+ },
+ {
+ id: 'a4',
+ data: {
+ title: '节点4',
+ },
+ },
+ {
+ id: 'a5',
+ data: {
+ title: '节点5',
+ },
+ },
+ {
+ id: 'a6',
+ data: {
+ title: '节点6',
+ },
+ },
+];
+
+const edges = [
+ {
+ source: 'a1',
+ target: 'a2',
+ },
+ {
+ source: 'a1',
+ target: 'a3',
+ },
+ {
+ source: 'a2',
+ target: 'a3',
+ },
+ {
+ source: 'a3',
+ target: 'a6',
+ },
+ {
+ source: 'a2',
+ target: 'a4',
+ },
+ {
+ source: 'a3',
+ target: 'a5',
+ },
+ {
+ source: 'a2',
+ target: 'a6',
+ },
+];
+
+function App() {
+ return (
+
+
+
+ );
+}
+
+export default App;
diff --git a/docs/useDocs/demos/autoLayoutDemo2.tsx b/docs/useDocs/demos/autoLayoutDemo2.tsx
new file mode 100644
index 0000000..6e612c6
--- /dev/null
+++ b/docs/useDocs/demos/autoLayoutDemo2.tsx
@@ -0,0 +1,95 @@
+import styled from 'styled-components';
+import { FlowView } from '../../../src/index';
+
+const Container = styled.div`
+ width: 800px;
+ height: 300px;
+`;
+
+const nodes = [
+ {
+ id: 'a1',
+ position: {
+ x: 100,
+ y: 300,
+ },
+ data: {
+ title: '节点1',
+ },
+ },
+ {
+ id: 'a2',
+ position: {
+ x: 300,
+ y: 600,
+ },
+ data: {
+ title: '节点2',
+ },
+ },
+ {
+ id: 'a3',
+ data: {
+ title: '节点3',
+ },
+ },
+ {
+ id: 'a4',
+ data: {
+ title: '节点4',
+ },
+ },
+ {
+ id: 'a5',
+ data: {
+ title: '节点5',
+ },
+ },
+ {
+ id: 'a6',
+ data: {
+ title: '节点6',
+ },
+ },
+];
+
+const edges = [
+ {
+ source: 'a1',
+ target: 'a2',
+ },
+ {
+ source: 'a1',
+ target: 'a3',
+ },
+ {
+ source: 'a2',
+ target: 'a3',
+ },
+ {
+ source: 'a3',
+ target: 'a6',
+ },
+ {
+ source: 'a2',
+ target: 'a4',
+ },
+ {
+ source: 'a3',
+ target: 'a5',
+ },
+ {
+ source: 'a2',
+ target: 'a6',
+ },
+];
+
+function App() {
+ return (
+
+
+
+ );
+}
+
+export default App;
diff --git a/docs/useDocs/demos/autoLayoutDemo3.tsx b/docs/useDocs/demos/autoLayoutDemo3.tsx
new file mode 100644
index 0000000..0d14be1
--- /dev/null
+++ b/docs/useDocs/demos/autoLayoutDemo3.tsx
@@ -0,0 +1,87 @@
+import styled from 'styled-components';
+import { FlowView } from '../../../src/index';
+
+const Container = styled.div`
+ width: 800px;
+ height: 300px;
+`;
+
+const nodes = [
+ {
+ id: 'a1',
+ data: {
+ title: '节点1',
+ },
+ },
+ {
+ id: 'a2',
+ data: {
+ title: '节点2',
+ },
+ },
+ {
+ id: 'a3',
+ data: {
+ title: '节点3',
+ },
+ },
+ {
+ id: 'a4',
+ data: {
+ title: '节点4',
+ },
+ },
+ {
+ id: 'a5',
+ data: {
+ title: '节点5',
+ },
+ },
+ {
+ id: 'a6',
+ data: {
+ title: '节点6',
+ },
+ },
+];
+
+const edges = [
+ {
+ source: 'a1',
+ target: 'a2',
+ },
+ {
+ source: 'a1',
+ target: 'a3',
+ },
+ {
+ source: 'a2',
+ target: 'a3',
+ },
+ {
+ source: 'a3',
+ target: 'a6',
+ },
+ {
+ source: 'a2',
+ target: 'a4',
+ },
+ {
+ source: 'a3',
+ target: 'a5',
+ },
+ {
+ source: 'a2',
+ target: 'a6',
+ },
+];
+
+function App() {
+ return (
+
+
+
+ );
+}
+
+export default App;
diff --git a/docs/useDocs/demos/index.less b/docs/useDocs/demos/index.less
new file mode 100644
index 0000000..05ed87f
--- /dev/null
+++ b/docs/useDocs/demos/index.less
@@ -0,0 +1,205 @@
+.pipeNodeWrap {
+ width: 260px;
+ min-height: 100px;
+ background-color: #f6f8fa;
+ padding: 16px;
+ box-sizing: border-box;
+ border-radius: 8px;
+
+ .handle {
+ top: 0;
+ }
+
+ .stepTitle {
+ overflow: hidden;
+ color: #8c8c8c;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ }
+ .pipeNode {
+ margin-top: 10px;
+ width: 232px;
+ box-sizing: border-box;
+ border: 1px solid rgba(0, 0, 0, 0.08);
+ border-radius: 8px;
+
+ .mainBox {
+ width: 100%;
+ padding: 12px;
+ height: 70px;
+ background-color: white;
+ display: flex;
+ border-bottom: none;
+ border-radius: 8px;
+ box-sizing: border-box;
+ .logo {
+ img {
+ width: 16px;
+ height: 16px;
+ margin-top: 4px;
+ }
+ }
+ .wrap {
+ margin-left: 8px;
+ display: flex;
+ flex-direction: column;
+ .title {
+ color: #000;
+ font-weight: 500;
+ font-size: 14px;
+ line-height: 22px;
+ white-space: nowrap;
+ }
+ .des {
+ margin-top: 8px;
+ color: #00000073;
+ font-size: 12px;
+ }
+ }
+ }
+ }
+
+ .children {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding-bottom: 10px;
+
+ .childrenBox {
+ width: 200px;
+ padding: 12px;
+ height: 70px;
+ background-color: white;
+ display: flex;
+ border: 1px solid rgba(0, 0, 0, 0.08);
+ border-radius: 8px;
+ box-sizing: border-box;
+ margin-top: 10px;
+
+ .logo {
+ img {
+ width: 16px;
+ height: 16px;
+ margin-top: 4px;
+ }
+ }
+ .wrap {
+ margin-left: 8px;
+ display: flex;
+ flex-direction: column;
+ .title {
+ color: #000;
+ font-weight: 500;
+ font-size: 14px;
+ line-height: 22px;
+ white-space: nowrap;
+ }
+ .des {
+ margin-top: 8px;
+ color: #00000073;
+ font-size: 12px;
+ }
+ }
+ }
+ }
+}
+.container {
+ width: 100%;
+ height: 600px;
+}
+
+.techUIpipeNodeWrap {
+ width: 260px;
+ min-height: 100px;
+ background-color: #f6f8fa;
+ padding: 12px 6px;
+ box-sizing: border-box;
+ border-radius: 8px;
+
+ .pipeNode {
+ width: 100%;
+ border-radius: 4px;
+ background-color: white;
+ padding-bottom: 12px;
+
+ .mainBox {
+ width: 100%;
+ padding: 12px;
+ height: 45px;
+ background-color: white;
+ display: flex;
+ border-bottom: none;
+ border-radius: 8px;
+ box-sizing: border-box;
+ position: relative;
+ .logo {
+ img {
+ width: 16px;
+ height: 16px;
+ margin-top: 4px;
+ }
+ }
+ .title {
+ margin-left: 8px;
+ color: #000;
+ font-weight: 500;
+ font-size: 14px;
+ line-height: 22px;
+ white-space: nowrap;
+ }
+ .subLogo {
+ position: absolute;
+ right: 6px;
+ top: 8px;
+ img {
+ width: 80px;
+ height: 25px;
+ }
+ }
+ }
+
+ .children {
+ padding-bottom: 8px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+
+ .childrenBox {
+ width: 220px;
+ height: 30px;
+ margin-top: 3px;
+ border: 1px solid #f1f1f1;
+ box-sizing: border-box;
+ padding-left: 5px;
+ display: flex;
+ align-items: center;
+ border-radius: 3px;
+ position: relative;
+
+ .logo {
+ display: flex;
+ align-items: center;
+ img {
+ width: 16px;
+ height: 16px;
+ }
+ }
+ .title {
+ margin-left: 8px;
+ color: #000;
+ font-weight: 500;
+ font-size: 14px;
+ line-height: 22px;
+ white-space: nowrap;
+ }
+ }
+ }
+ }
+
+ .des {
+ margin-left: 16px;
+
+ color: #00000073;
+ font-size: 10px;
+ }
+}
diff --git a/docs/useDocs/demos/multiHandle.tsx b/docs/useDocs/demos/multiHandle.tsx
new file mode 100644
index 0000000..8202929
--- /dev/null
+++ b/docs/useDocs/demos/multiHandle.tsx
@@ -0,0 +1,119 @@
+import { FC, useCallback } from 'react';
+import styled from 'styled-components';
+import {
+ FlowView,
+ FlowViewProvider,
+ Handle,
+ Position,
+ SelectType,
+ useFlowViewer,
+} from '../../../src/index';
+
+const Wrap = styled.div`
+ width: 200px;
+ height: 83px;
+ background-color: white;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ &.default {
+ border: none;
+ }
+
+ &.select {
+ border: 1px solid red;
+ }
+`;
+
+const Container = styled.div`
+ width: 800px;
+ height: 300px;
+`;
+
+const CustomNode: FC<{
+ data: {
+ title: string;
+ selectType: SelectType;
+ };
+}> = ({ data }) => {
+ console.log(data);
+ const onChange = useCallback((evt) => {
+ console.log(evt.target.value);
+ }, []);
+
+ return (
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const nodes = [
+ {
+ id: 'n1',
+ type: 'customNode',
+ data: {
+ title: 'Text',
+ },
+ },
+ {
+ id: 'n2',
+ data: {
+ title: 'n2 node',
+ },
+ },
+ {
+ id: 'n3',
+ data: {
+ title: 'n3 node',
+ },
+ },
+];
+
+const edges = [
+ { id: 'edge-1', source: 'n1', target: 'n2', sourceHandle: 'a' },
+ { id: 'edge-2', source: 'n1', target: 'n3', sourceHandle: 'b' },
+];
+
+const nodeTypes = { customNode: CustomNode };
+
+function App() {
+ const flowViewer = useFlowViewer();
+
+ console.log(flowViewer);
+
+ return (
+
+ {
+ console.log(n);
+ flowViewer?.selectNode(n.id, SelectType.SELECT);
+ }}
+ onPaneClick={() => {
+ flowViewer?.selectNodes(['n1', 'n2', 'n3'], SelectType.DEFAULT);
+ }}
+ nodes={nodes}
+ edges={edges}
+ nodeTypes={nodeTypes}
+ miniMap={false}
+ />
+
+ );
+}
+
+function ProApp() {
+ return (
+
+
+
+ );
+}
+
+export default ProApp;
diff --git a/docs/useDocs/demos/pipelineDemo.tsx b/docs/useDocs/demos/pipelineDemo.tsx
new file mode 100644
index 0000000..592c157
--- /dev/null
+++ b/docs/useDocs/demos/pipelineDemo.tsx
@@ -0,0 +1,318 @@
+/**
+ * compact: true
+ */
+import {
+ Background,
+ FlowView,
+ FlowViewProvider,
+ Handle,
+ Position,
+ useFlowViewer,
+} from '@ant-design/pro-flow';
+import { FC, useCallback } from 'react';
+import { SelectType } from '../../../src';
+import './index.less';
+
+interface PipeNodeChild {
+ title: string;
+ des: string;
+ logo: string;
+}
+
+interface PipeNode {
+ stepTitle: string;
+ title: string;
+ des: string;
+ logo: string;
+ needSwitch?: boolean;
+ open?: boolean;
+ children?: PipeNodeChild[];
+ selectType?: SelectType;
+}
+
+const nodeWidth = 170;
+const nodeHeight = 500;
+
+const PipeNode: FC<{
+ data: PipeNode;
+}> = ({ data }) => {
+ const {
+ stepTitle,
+ title,
+ des,
+ logo,
+ needSwitch = false,
+ open = false,
+ children = [],
+ selectType,
+ } = data;
+
+ return (
+
+
+
{stepTitle}
+
+
+
+
+
+
+ {needSwitch && (
+
+ )}
+
+ {children.length > 0 && (
+
+ {children.map((item, index) => (
+
+
+
+
+
+
{item.title}
+
{item.des}
+
+
+ ))}
+
+ )}
+
+
+
+ );
+};
+
+const nodeTypes = { pipeNode: PipeNode };
+
+const nodes = [
+ {
+ id: 'a1',
+ type: 'pipeNode',
+ width: nodeWidth,
+ height: nodeHeight,
+ data: {
+ stepTitle: '阶段 1: 部署平台 tnpmregistry@...',
+ title: 'tnpmregistry@DEFAULT ...',
+ logo: 'https://mdn.alipayobjects.com/huamei_d2ejos/afts/img/A*sko9RoPu-HgAAAAAAAAAAAAADvl6AQ/original',
+ des: '34秒',
+ needSwitch: true,
+ open: true,
+ children: [
+ {
+ title: '参数初始化',
+ logo: 'https://mdn.alipayobjects.com/huamei_d2ejos/afts/img/A*sko9RoPu-HgAAAAAAAAAAAAADvl6AQ/original',
+ des: '1秒',
+ },
+ {
+ title: 'NPM 组件初始化',
+ logo: 'https://mdn.alipayobjects.com/huamei_d2ejos/afts/img/A*sko9RoPu-HgAAAAAAAAAAAAADvl6AQ/original',
+ des: '30秒',
+ },
+ {
+ title: '同步成员(仅子组件生...',
+ logo: 'https://mdn.alipayobjects.com/huamei_d2ejos/afts/img/A*sko9RoPu-HgAAAAAAAAAAAAADvl6AQ/original',
+ des: '0秒',
+ },
+ {
+ title: '注册部署平台',
+ logo: 'https://mdn.alipayobjects.com/huamei_d2ejos/afts/img/A*sko9RoPu-HgAAAAAAAAAAAAADvl6AQ/original',
+ des: '0秒',
+ },
+ ],
+ },
+ },
+ {
+ id: 'a2',
+ type: 'pipeNode',
+ width: nodeWidth,
+ height: nodeHeight,
+ data: {
+ stepTitle: '阶段 2: 部署平台 hitu@DEFAULT ...',
+ title: 'hitu@DEFAULT 初始化',
+ logo: 'https://mdn.alipayobjects.com/huamei_d2ejos/afts/img/A*sko9RoPu-HgAAAAAAAAAAAAADvl6AQ/original',
+ des: '2秒',
+ needSwitch: true,
+ open: true,
+ children: [
+ {
+ title: '初始化海图组件',
+ logo: 'https://mdn.alipayobjects.com/huamei_d2ejos/afts/img/A*sko9RoPu-HgAAAAAAAAAAAAADvl6AQ/original',
+ des: '1秒',
+ },
+ {
+ title: '注册部署平台',
+ logo: 'https://mdn.alipayobjects.com/huamei_d2ejos/afts/img/A*sko9RoPu-HgAAAAAAAAAAAAADvl6AQ/original',
+ des: '0秒',
+ },
+ ],
+ },
+ },
+ {
+ id: 'a3',
+ type: 'pipeNode',
+ width: nodeWidth,
+ height: nodeHeight,
+ data: {
+ stepTitle: '阶段 3: 三方关联初始化',
+ title: '监控初始化',
+ logo: 'https://mdn.alipayobjects.com/huamei_d2ejos/afts/img/A*sko9RoPu-HgAAAAAAAAAAAAADvl6AQ/original',
+ des: '1秒',
+ },
+ },
+ {
+ id: 'a4',
+ type: 'pipeNode',
+ width: nodeWidth,
+ height: nodeHeight,
+ data: {
+ stepTitle: '阶段 4: 准备组件仓库初始化',
+ title: '重命名 name',
+ logo: 'https://mdn.alipayobjects.com/huamei_d2ejos/afts/img/A*sko9RoPu-HgAAAAAAAAAAAAADvl6AQ/original',
+ des: '1秒',
+ },
+ },
+ {
+ id: 'a5',
+ type: 'pipeNode',
+ width: nodeWidth,
+ height: nodeHeight,
+ data: {
+ stepTitle: '阶段 5: 代码初始化',
+ title: '仓库初始化',
+ logo: 'https://mdn.alipayobjects.com/huamei_d2ejos/afts/img/A*7gzVQJ63mfEAAAAAAAAAAAAADvl6AQ/original',
+ des: '6秒',
+ needSwitch: true,
+ open: true,
+ children: [
+ {
+ title: '创建仓库',
+ logo: 'https://mdn.alipayobjects.com/huamei_d2ejos/afts/img/A*sko9RoPu-HgAAAAAAAAAAAAADvl6AQ/original',
+ des: '3秒',
+ },
+ {
+ title: '接触分支保护',
+ logo: 'https://mdn.alipayobjects.com/huamei_d2ejos/afts/img/A*sko9RoPu-HgAAAAAAAAAAAAADvl6AQ/original',
+ des: '1秒',
+ },
+ {
+ title: '初始化代码模板',
+ logo: 'https://mdn.alipayobjects.com/huamei_d2ejos/afts/img/A*7gzVQJ63mfEAAAAAAAAAAAAADvl6AQ/original',
+ des: '1秒',
+ },
+ {
+ title: '创建代码基线',
+ logo: 'https://mdn.alipayobjects.com/huamei_d2ejos/afts/img/A*Em_PQoTrMDgAAAAAAAAAAAAADvl6AQ/original',
+ des: '未开始',
+ },
+ ],
+ },
+ },
+ {
+ id: 'a6',
+ type: 'pipeNode',
+ width: nodeWidth,
+ height: nodeHeight,
+ data: {
+ stepTitle: '阶段 6: 仓库权限同步',
+ title: '同步至埋点平台',
+ logo: 'https://mdn.alipayobjects.com/huamei_d2ejos/afts/img/A*Em_PQoTrMDgAAAAAAAAAAAAADvl6AQ/original',
+ des: '未开始',
+ },
+ },
+];
+
+const edges = [
+ {
+ id: 'e1',
+ source: 'a1',
+ target: 'a2',
+ animated: true,
+ },
+ {
+ id: 'e2',
+ source: 'a2',
+ target: 'a3',
+ animated: true,
+ },
+ {
+ id: 'e3',
+ source: 'a3',
+ target: 'a4',
+ animated: true,
+ },
+ {
+ id: 'e4',
+ source: 'a4',
+ target: 'a5',
+ animated: true,
+ },
+ {
+ id: 'e5',
+ source: 'a5',
+ target: 'a6',
+ animated: true,
+ },
+];
+
+function App() {
+ const flowViewer = useFlowViewer();
+
+ const handleClick = useCallback(
+ (e, n) => {
+ flowViewer?.zoomToNode(n.id, 1000);
+ },
+ [flowViewer],
+ );
+
+ const handlePaneClick = useCallback(() => {
+ // flowViewer?.zoomToNode(n.id, 1000);
+ }, [flowViewer]);
+
+ return (
+
+
+
+
+
+ );
+}
+
+function ProApp() {
+ return (
+
+
+
+ );
+}
+
+export default ProApp;
diff --git a/docs/useDocs/demos/selectFlow.tsx b/docs/useDocs/demos/selectFlow.tsx
index a359b43..6aded35 100644
--- a/docs/useDocs/demos/selectFlow.tsx
+++ b/docs/useDocs/demos/selectFlow.tsx
@@ -48,19 +48,19 @@ const initEdges = [
];
function App() {
- const { updateSelectNode, updateSelectEdges, updateSelectNodes } = useFlowViewer();
+ const { selectNode, selectEdges, selectNodes } = useFlowViewer();
return (
{
- updateSelectNodes!(['a1', 'a2', 'a3'], SelectType.SUB_SELECT);
- updateSelectNode!(node.id, SelectType.SELECT);
- updateSelectEdges!(['a1-a2', 'a1-a3'], SelectType.SUB_SELECT);
+ selectNodes(['a1', 'a2', 'a3'], SelectType.SUB_SELECT);
+ selectNode(node.id, SelectType.SELECT);
+ selectEdges(['a1-a2', 'a1-a3'], SelectType.SUB_SELECT);
}}
onPaneClick={() => {
- updateSelectNodes!(['a1', 'a2', 'a3'], SelectType.DEFAULT);
- updateSelectEdges!(['a1-a2', 'a1-a3'], SelectType.DEFAULT);
+ selectNodes(['a1', 'a2', 'a3'], SelectType.DEFAULT);
+ selectEdges(['a1-a2', 'a1-a3'], SelectType.DEFAULT);
}}
nodes={initNodes}
edges={initEdges}
diff --git a/docs/useDocs/demos/useFlowViewer.tsx b/docs/useDocs/demos/useFlowViewer.tsx
new file mode 100644
index 0000000..8257375
--- /dev/null
+++ b/docs/useDocs/demos/useFlowViewer.tsx
@@ -0,0 +1,113 @@
+import { Button } from 'antd';
+import styled from 'styled-components';
+import {
+ EdgeType,
+ FlowPanel,
+ FlowView,
+ FlowViewProvider,
+ SelectType,
+ useFlowViewer,
+} from '../../../src/index';
+
+const initNodes = [
+ {
+ id: 'a1',
+ data: {
+ title: 'a1 节点',
+ logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original',
+ describe: 'XXX_XXX_XXX_API',
+ },
+ },
+ {
+ id: 'a2',
+ data: {
+ title: 'XXX_API_b4',
+ logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original',
+ describe: 'XXX_XXX_XXX_API',
+ },
+ },
+ {
+ id: 'a3',
+ data: {
+ title: 'XXX_API_b4',
+ logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original',
+ describe: 'XXX_XXX_XXX_API',
+ },
+ },
+];
+const initEdges = [
+ {
+ id: 'a1-a2',
+ source: 'a1',
+ target: 'a2',
+ },
+ {
+ id: 'a1-a3',
+ source: 'a1',
+ target: 'a3',
+ type: EdgeType.radius,
+ },
+];
+
+function App() {
+ const { selectNode, selectEdges, selectNodes, zoomToNode } = useFlowViewer();
+
+ return (
+
+ {
+ selectNodes(['a1', 'a2', 'a3'], SelectType.SUB_SELECT);
+ selectNode(node.id, SelectType.SELECT);
+ selectEdges(['a1-a2', 'a1-a3'], SelectType.SUB_SELECT);
+ }}
+ onPaneClick={() => {
+ selectNodes(['a1', 'a2', 'a3'], SelectType.DEFAULT);
+ selectEdges(['a1-a2', 'a1-a3'], SelectType.DEFAULT);
+ }}
+ nodes={initNodes}
+ edges={initEdges}
+ >
+
+
+
+
+
+
+
+
+
+ );
+}
+
+function ProApp() {
+ return (
+
+
+
+ );
+}
+
+export default ProApp;
+
+const Container = styled.div`
+ width: 800px;
+ height: 500px;
+`;
diff --git a/docs/useDocs/intro.md b/docs/useDocs/intro.md
index 3071289..dc31311 100644
--- a/docs/useDocs/intro.md
+++ b/docs/useDocs/intro.md
@@ -3,7 +3,7 @@ nav: 使用文档
group:
title: 基础介绍
order: 1
-title: 为什么选择 ProFlow ?
+title: ProFlow 特性
description:
---
diff --git a/docs/useDocs/layoud.md b/docs/useDocs/layoud.md
deleted file mode 100644
index 6f829bd..0000000
--- a/docs/useDocs/layoud.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-nav: 使用文档
-group:
- title: 进阶使用
- order: 3
-title: 布局
-description: 自动布局 、 手动布局
----
-
-正在加急编写中...
diff --git a/docs/useDocs/pipleLineDemo.md b/docs/useDocs/pipleLineDemo.md
new file mode 100644
index 0000000..2e03487
--- /dev/null
+++ b/docs/useDocs/pipleLineDemo.md
@@ -0,0 +1,10 @@
+---
+nav: 使用文档
+group:
+ title: 案例展示
+ order: 10
+title: 雨燕 PipeLine
+description:
+---
+
+
diff --git a/docs/useFlowEditor.md b/docs/useDocs/useFlowEditor.md
similarity index 97%
rename from docs/useFlowEditor.md
rename to docs/useDocs/useFlowEditor.md
index 00bbabf..bcff950 100644
--- a/docs/useFlowEditor.md
+++ b/docs/useDocs/useFlowEditor.md
@@ -1,4 +1,11 @@
-# useFlowEditor
+---
+nav: 使用文档
+group:
+ title: 进阶使用
+ order: 3
+title: useFlowEditor
+description:
+---
```ts
import { useFlowEditor } from '@ant-design/pro-flow';
diff --git a/docs/useDocs/useFlowViewer.md b/docs/useDocs/useFlowViewer.md
new file mode 100644
index 0000000..ac313e3
--- /dev/null
+++ b/docs/useDocs/useFlowViewer.md
@@ -0,0 +1,211 @@
+---
+nav: 使用文档
+group:
+ title: 进阶使用
+ order: 2
+title: useFlowViewer
+description:
+---
+
+## 使用方式
+
+使用`FlowViewProvider`来包裹你的组件,即可使用`FlowView`提供的 hook 能力
+
+```js
+import { FlowViewProvider, useFlowViewer } from '@ant-design/pro-flow';
+
+function App() {
+ const viewer = useFlowViewer();
+
+ return (
+
+
+
+ );
+}
+
+function ProApp() {
+ return (
+
+
+
+ );
+}
+```
+
+## 功能展示
+
+
+
+## 节点选中
+
+提供了一组公共节点选中方法,包括:
+
+1. selectNode: 选中节点;
+2. selectNodes: 批量选中节点;
+3. selectEdge: 选中边缘;
+4. selectEdges: 批量选中边缘;
+
+这些方法可以用于在前端为一个或多个节点(边缘)标记选中状态。
+
+### selectNode
+
+```js
+selectNode: (nodeId: string, selectType: SelectType) => void;
+```
+
+#### 参数
+
+- `nodeId` - 节点 Id
+ - 类型: `string`
+- `selectType` - 选中样式
+ - 类型: [SelectType](/components/flow-view#selecttype)
+
+#### 返回值
+
+- 类型: `void`
+
+### selectNodes
+
+```js
+selectNodes: (nodeIds: string[], selectType: SelectType) => void;
+```
+
+#### 参数
+
+- `nodeIds` - 节点 Id 的数组
+ - 类型: `string[]`
+- `selectType` - 选中样式
+
+ - 类型: [SelectType](/components/flow-view#selecttype)
+
+### selectEdge
+
+```js
+selectEdge: (edgeId: string, selectType: SelectType) => void;
+```
+
+#### 参数
+
+- `edgeId` - 边 Id
+ - 类型: `string`
+- `selectType` - 选中样式
+
+ - 类型: [SelectType](/components/flow-view#selecttype)
+
+### selectEdges
+
+```js
+selectEdges: (edgeIds: string[], selectType: SelectType) => void;
+```
+
+#### 参数
+
+- `edgeIds` - 边 Id 的数组
+ - 类型: `string[]`
+- `selectType` - 选中样式
+ - 类型: [SelectType](/components/flow-view#selecttype)
+
+#### 返回值
+
+- 类型: `void`
+
+## 画布聚焦
+
+1. zoomTo: 画布缩放;
+2. zoomToNode: 快速聚焦到某个节点;
+3. getViewport: 获取当前窗口坐标及缩放等级;
+4. setViewport: 设置窗口坐标及缩放等级;
+
+### selectNode
+
+```js
+zoomTo: (zoomNumber: number, duration?: number) => void;
+```
+
+#### 参数
+
+- `zoomNumber` - zoom 等级
+ - 类型: `number`
+- `duration` - 持续时间
+ - 类型: `number`
+ - 可选参数
+
+#### 返回值
+
+- 类型: `void`
+
+### selectNode
+
+```js
+zoomToNode: (nodeId: string, duration?: number) => void;
+```
+
+#### 参数
+
+- `nodeId` - 节点 Id
+ - 类型: `string`
+- `duration` - 持续时间
+ - 类型: `number`
+ - 可选参数
+
+#### 返回值
+
+- 类型: `void`
+
+### getViewport
+
+```js
+getViewport: () => Viewport;
+```
+
+#### 参数
+
+- 无
+
+#### 返回值
+
+- 类型: `Viewport`
+
+```js
+type Viewport = {
+ x: number,
+ y: number,
+ zoom: number,
+};
+```
+
+### setViewport
+
+```js
+setViewport: (viewport: Viewport, duration?: number) => void;
+```
+
+#### 参数
+
+- `viewport` - 视窗
+ - 类型: `Viewport`
+- `duration` - 持续时间
+ - 类型: `number`
+ - 可选参数
+
+#### 返回值
+
+- 类型: `void`
+
+## 小地图
+
+1. setMiniMapPosition: 设置 MiniMap 在窗口的坐标位置
+
+### setMiniMapPosition
+
+```js
+setMiniMapPosition: (x: number, y: number) => void;
+```
+
+#### 参数
+
+- `x` - x 轴坐标
+ - 类型: `number`
+- `y` - y 轴坐标
+ - 类型: `number`
diff --git a/docs/useDocs/zoom.md b/docs/useDocs/zoom.md
deleted file mode 100644
index 06c3822..0000000
--- a/docs/useDocs/zoom.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-nav: 使用文档
-group:
- title: 进阶使用
- order: 3
-title: 缩放
-description:
----
-
-正在加急编写中...
diff --git a/src/Background/demos/index.tsx b/src/Background/demos/index.tsx
index 32f2cde..acbfd77 100644
--- a/src/Background/demos/index.tsx
+++ b/src/Background/demos/index.tsx
@@ -1,8 +1,7 @@
-import { FlowView } from '@/index';
+import { Background, BackgroundVariant, FlowPanel, FlowView } from '@ant-design/pro-flow';
+import { Button } from 'antd';
import { createStyles } from 'antd-style';
import { memo, useState } from 'react';
-import { Panel } from 'reactflow';
-import Background, { BackgroundVariant } from '..';
const useStyles = createStyles(({ css }) => ({
container: css`
@@ -18,18 +17,12 @@ const BackgroundDemo = memo(() => {
return (
-
+
variant:
-
-
-
-
+
+
+
+
diff --git a/src/FlowEditor/demos/index.tsx b/src/FlowEditor/demos/index.tsx
index 06fb080..7c049ed 100644
--- a/src/FlowEditor/demos/index.tsx
+++ b/src/FlowEditor/demos/index.tsx
@@ -5,7 +5,7 @@ import { Handle, Position } from 'reactflow';
import styled from 'styled-components';
const StringNode = styled.div`
- width: 120px;
+ width: 150px;
height: 30px;
text-align: center;
background-color: white;
diff --git a/src/FlowEditor/index.md b/src/FlowEditor/index.md
index f4a6845..922cf92 100644
--- a/src/FlowEditor/index.md
+++ b/src/FlowEditor/index.md
@@ -7,3 +7,7 @@ description:
## default
+
+## API
+
+FlowEditor 为数据驱动解决方案,需搭配 [useFlowEditor](/use-docs/use-flow-editor) 使用。
diff --git a/src/FlowPanel/demos/index.tsx b/src/FlowPanel/demos/index.tsx
index e626fb9..f5e5254 100644
--- a/src/FlowPanel/demos/index.tsx
+++ b/src/FlowPanel/demos/index.tsx
@@ -1,7 +1,6 @@
-import { FlowView } from '@/index';
+import { FlowPanel, FlowView } from '@ant-design/pro-flow';
import { createStyles } from 'antd-style';
import { memo } from 'react';
-import FlowPanel from '..';
const useStyles = createStyles(({ css }) => ({
container: css`
diff --git a/src/FlowView/components/DefaultNode.tsx b/src/FlowView/components/DefaultNode.tsx
index 1dce259..a9d9293 100644
--- a/src/FlowView/components/DefaultNode.tsx
+++ b/src/FlowView/components/DefaultNode.tsx
@@ -2,9 +2,11 @@ import { DefaultNodeData } from '@/constants';
import { FC } from 'react';
import { useStyles } from '../styles';
-const DefaultNode: FC = (props) => {
+const DefaultNode: FC<{
+ data: DefaultNodeData;
+}> = ({ data }) => {
const { styles, cx } = useStyles();
- const { className, children } = props;
+ const { className, children } = data;
return {children}
;
};
diff --git a/src/FlowView/constants.tsx b/src/FlowView/constants.tsx
index c100eb4..6534c1b 100644
--- a/src/FlowView/constants.tsx
+++ b/src/FlowView/constants.tsx
@@ -29,7 +29,7 @@ export interface NodeMapItem = ({ data }) => {
+ const onChange = useCallback((evt: { target: { value: any } }) => {
+ console.log(evt.target.value);
+ }, []);
+
+ return (
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default CustomNode;
diff --git a/src/FlowView/demos/ProFlowDemo.tsx b/src/FlowView/demos/ProFlowDemo.tsx
index d4393c5..62c22c2 100644
--- a/src/FlowView/demos/ProFlowDemo.tsx
+++ b/src/FlowView/demos/ProFlowDemo.tsx
@@ -1,10 +1,16 @@
-import { EdgeType, FlowViewEdge, FlowViewNode, SelectType } from '@/index';
-import { FlowView } from '@ant-design/pro-flow';
+import {
+ EdgeType,
+ FlowViewEdge,
+ FlowViewNode,
+ FlowViewProvider,
+ SelectType,
+} from '@ant-design/pro-flow';
import { Progress } from 'antd';
import { createStyles } from 'antd-style';
import React, { useState } from 'react';
import styled from 'styled-components';
-import { FlowViewProvider } from '../provider/FlowViewProvider';
+import { FlowView } from '../../index';
+import CustomNode from './CustomerNode';
const useStyles = createStyles(({ css }) => ({
container: css`
@@ -53,17 +59,19 @@ const nodes: FlowViewNode[] = [
{
id: 'a1',
label: '123',
- type: 'default',
data: {
- children: default node, 123123
,
+ title: 'XXX_API_ddddddddddddddddddddddddddddddddddddddddddddddddddddddb1',
+ logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original',
+ description: 'XXX_XXX_XXX_API',
},
},
{
id: 'b1',
+ label: 'label',
data: {
title: 'XXX_API_ddddddddddddddddddddddddddddddddddddddddddddddddddddddb1',
logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original',
- describe: 'XXX_XXX_XXX_API',
+ description: 'XXX_XXX_XXX_API',
titleSlot: {
type: 'left',
value: (
@@ -84,7 +92,7 @@ const nodes: FlowViewNode[] = [
data: {
title: 'XXX_APIddddddddddddddddddddddddddddddddddddddddddddddddddd_b2',
logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original',
- describe: 'XXX_XXX_XXX_API',
+ description: 'XXX_XXX_XXX_API',
titleSlot: {
type: 'right',
value: ,
@@ -94,9 +102,9 @@ const nodes: FlowViewNode[] = [
{
id: 'b3',
data: {
- title: 'XXX_API_b3',
+ title: 'XXX_APIddddddddddddddddddddddddddddddddddddddddddddddddddd_b2',
logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original',
- describe: 'XXX_XXX_XXX_API',
+ description: 'XXX_XXX_XXX_API',
},
},
{
@@ -104,7 +112,7 @@ const nodes: FlowViewNode[] = [
data: {
title: 'XXX_API_b4',
logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original',
- describe: 'XXX_XXX_XXX_API',
+ description: 'XXX_XXX_XXX_API',
},
},
{
@@ -112,7 +120,7 @@ const nodes: FlowViewNode[] = [
data: {
title: 'XXXX产品',
logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*ezaYT4wYRBwAAAAAAAAAAAAADvuvAQ/original',
- describe: '2031030213014',
+ description: '2031030213014',
},
},
{
@@ -185,25 +193,21 @@ const edges: FlowViewEdge[] = [
id: 'a1-b1',
source: 'a1',
target: 'b1',
- type: EdgeType.radius,
},
{
id: 'a1-b2',
source: 'a1',
target: 'b2',
- type: EdgeType.radius,
},
{
id: 'a1-b3',
source: 'a1',
target: 'b3',
- type: EdgeType.radius,
},
{
id: 'a1-b4',
source: 'a1',
target: 'b4',
- type: EdgeType.radius,
},
{
@@ -244,6 +248,7 @@ const ProFlowDemo = () => {
const { styles } = useStyles();
const handleHighlight = (node: FlowViewNode) => {
+ console.log(node);
_nodes.forEach((_node) => {
if (_node.id === node.id) {
_node.select = SelectType.SELECT;
@@ -263,8 +268,18 @@ const ProFlowDemo = () => {
};
const handleUnHighlight = () => {
- setNodes(nodes);
- setEdges(edges);
+ setNodes(
+ _nodes.map((_node) => {
+ _node.select = SelectType.DEFAULT;
+ return _node;
+ }),
+ );
+ setEdges(
+ edges.map((edge) => {
+ edge.select = SelectType.DEFAULT;
+ return edge;
+ }),
+ );
};
return (
@@ -274,6 +289,7 @@ const ProFlowDemo = () => {
onPaneClick={handleUnHighlight}
nodes={_nodes}
edges={_edges}
+ nodeTypes={{ textCustomNode: CustomNode }}
>
);
diff --git a/src/FlowView/helper.tsx b/src/FlowView/helper.tsx
index aa85f12..9892f45 100644
--- a/src/FlowView/helper.tsx
+++ b/src/FlowView/helper.tsx
@@ -1,18 +1,7 @@
-import LineageNodeGroup from '@/LineageGroupNode';
-import LineageNode from '@/LineageNode';
-import {
- DefaultNodeData,
- EdgeType,
- FlowViewEdge,
- FlowViewNode,
- LineageGroupNodeData,
- LineageNodeData,
- NodeHandler,
-} from '@/constants';
+import { FlowViewEdge, FlowViewNode } from '@/constants';
import Dagre from '@dagrejs/dagre';
import { cx } from 'antd-style';
import { Edge, Node, Position } from 'reactflow';
-import DefaultNode from './components/DefaultNode';
import {
EDGE_DANGER,
EDGE_SELECT,
@@ -27,35 +16,28 @@ import {
SelectType,
} from './constants';
-// 这里的type是指节点的连接点在哪里,input是在左边,output是在右边,default是左右两边都有
-function getTypeFromEdge(node: NodeMapItem) {
- if (node.left?.length && node.right?.length) {
- return 'default';
- }
- if (node.left?.length) {
- return 'output';
- }
- if (node.right?.length) {
- return 'input';
- }
- return 'default';
-}
-
export function convertMappingFrom(nodes: FlowViewNode[], edges: FlowViewEdge[], zoom: number) {
const mapping: NodeMapping = {};
+
nodes.forEach((node) => {
- const { type = 'lineage', position = { x: NaN, y: NaN } } = node;
+ const {
+ width,
+ height,
+ select = SelectType.DEFAULT,
+ type = 'lineage',
+ position = { x: NaN, y: NaN },
+ } = node;
mapping[node.id] = {
id: node.id,
- // width: width ? width : node.group ? 355 : 322,
- // height: height ? height : node.group ? 1100 : 85,
data: node.data,
- select: node.select,
+ select,
flowNodeType: type,
right: [],
left: [],
position,
+ width,
+ height,
zoom,
label: node.label,
};
@@ -70,7 +52,23 @@ export function convertMappingFrom(nodes: FlowViewNode[], edges: FlowViewEdge[],
return mapping;
}
-export function setNodePosition(nodes: Node[], edges: Edge[]) {
+export function setNodePosition(nodes: Node[], edges: Edge[], autoLayout: boolean) {
+ if (!autoLayout) {
+ return {
+ _nodes: nodes.map((node) => {
+ const { x: _x, y: _y } = node.position;
+ return {
+ ...node,
+ position: {
+ x: isNaN(_x) ? 1 : _x,
+ y: isNaN(_y) ? 1 : _y,
+ },
+ };
+ }) as unknown as Node[],
+ _edges: edges,
+ };
+ }
+
const g = new Dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({}));
g.setGraph({
@@ -86,9 +84,7 @@ export function setNodePosition(nodes: Node[], edges: Edge[]) {
return {
_nodes: nodes.map((node) => {
const { x, y } = g.node(node.id);
- console.log(x, y);
const { x: _x, y: _y } = node.position;
- console.log(_x, _y);
return {
...node,
position: {
@@ -101,7 +97,7 @@ export function setNodePosition(nodes: Node[], edges: Edge[]) {
};
}
-function sortEdges(edges: Edge[]) {
+export function sortEdges(edges: Edge[]) {
const highEdges: Edge[] = edges.filter((item) => {
return item.className?.includes('edgeSelected') || item.className?.includes('edgeSubSelected');
});
@@ -159,13 +155,26 @@ function getEdgeClsFromSelectType(select: SelectType) {
export function getRenderEdges(edges: FlowViewEdge[]) {
return edges.map((edge) => {
- const { source, target, select = SelectType.DEFAULT, type } = edge;
+ const {
+ source,
+ target,
+ select = SelectType.DEFAULT,
+ type = 'smoothstep',
+ label,
+ animated,
+ sourceHandle,
+ targetHandle,
+ } = edge;
return {
id: `${source}-${target}`,
source,
target,
- type: type === EdgeType.default ? 'smoothstep' : 'radiusEdge',
+ sourceHandle,
+ targetHandle,
+ type,
+ animated,
+ label,
className: getEdgeClsFromSelectType(select),
};
});
@@ -177,69 +186,126 @@ export function getRenderEdges(edges: FlowViewEdge[]) {
// })
}
-const NodeComponentHandler: NodeHandler = {
- default: (node: NodeMapItem) => ,
- lineage: (node: NodeMapItem) => {
- const { select = SelectType.DEFAULT } = node;
-
- return (
-
- );
- },
- lineageGroup: (node: NodeMapItem) => {
- const { select = SelectType.DEFAULT } = node;
-
- return (
-
- );
- },
+// const NodeComponentHandler: NodeHandler = {
+// default: (node: NodeMapItem) => ,
+// lineage: (node: NodeMapItem) => {
+// const { select = SelectType.DEFAULT } = node;
+
+// return (
+//
+// );
+// },
+// lineageGroup: (node: NodeMapItem) => {
+// const { select = SelectType.DEFAULT } = node;
+
+// return (
+//
+// );
+// },
+// };
+
+const getWidthAndHeight = (node: NodeMapItem) => {
+ if (['lineage', 'default'].includes(node.flowNodeType!)) {
+ return {
+ width: 320,
+ height: 83,
+ };
+ } else if (node.flowNodeType === 'lineageGroup') {
+ return {
+ width: 355,
+ height: 1100,
+ };
+ } else {
+ return {
+ width: node.width || 1,
+ height: node.height || 1,
+ };
+ }
+};
+
+const getHandleType = (node: NodeMapItem) => {
+ if (node.left?.length === 0 && node.right?.length === 0) {
+ return 'none';
+ } else if (node.left?.length === 0) {
+ return 'input';
+ } else if (node.right?.length === 0) {
+ return 'output';
+ } else {
+ return 'both';
+ }
+};
+
+// 只有pro flow节点才有的额外属性
+const getProFlowNodeData = (node: NodeMapItem) => {
+ if (['lineage'].includes(node.flowNodeType!)) {
+ return {
+ ...node.data,
+ selectType: node.select,
+ label: node.label,
+ zoom: node.zoom,
+ handleType: getHandleType(node),
+ };
+ } else if (node.flowNodeType === 'lineageGroup') {
+ return {
+ data: node.data,
+ selectType: node.select,
+ label: node.label,
+ zoom: node.zoom,
+ handleType: getHandleType(node),
+ };
+ } else {
+ return {
+ ...node.data,
+ selectType: node.select,
+ zoom: node.zoom,
+ };
+ }
};
export const getRenderData = (
mapping: NodeMapping,
edges: FlowViewEdge[],
+ autoLayout: boolean,
): {
nodes: Node[];
edges: Edge[];
} => {
const renderNodes: Node[] = [];
const renderEdges: Edge[] = getRenderEdges(edges);
- // const { styles, cx } = useStyles();
Object.keys(mapping).forEach((id) => {
const node = mapping[id];
const { flowNodeType } = node;
+ const { width, height } = getWidthAndHeight(node);
renderNodes.push({
sourcePosition: Position.Right,
targetPosition: Position.Left,
id: node.id!,
position: node.position!,
- type: getTypeFromEdge(node),
- width: node.group ? 355 : 320,
- height: node.group ? 1100 : 83,
+ type: flowNodeType,
+ width: width,
+ height: height,
className: cx(INIT_NODE),
- data: {
- label: NodeComponentHandler[flowNodeType!](node),
- },
+ data: getProFlowNodeData(node),
});
});
- const { _nodes, _edges } = setNodePosition(renderNodes, renderEdges);
+ const { _nodes, _edges } = setNodePosition(renderNodes, renderEdges, autoLayout);
return {
nodes: _nodes,
diff --git a/src/FlowView/hooks/useFlowView.ts b/src/FlowView/hooks/useFlowView.ts
index 9026bcb..e23e748 100644
--- a/src/FlowView/hooks/useFlowView.ts
+++ b/src/FlowView/hooks/useFlowView.ts
@@ -1,4 +1,5 @@
import { useContext } from 'react';
+import { Viewport } from 'reactflow';
import { FlowViewContext } from '../provider/provider';
export const useFlowView = () => {
@@ -22,13 +23,60 @@ export const useMiniMap = () => {
};
export const useFlowViewer = () => {
- const { updateSelectNode, updateSelectEdge, updateSelectEdges, updateSelectNodes } =
- useContext(FlowViewContext);
-
- return {
+ const {
updateSelectNode,
updateSelectEdge,
updateSelectEdges,
updateSelectNodes,
+ setMiniMapPosition: setPosition,
+ reactFlowInstance,
+ } = useContext(FlowViewContext);
+
+ const getNode = (nodeId: string) => {
+ return reactFlowInstance?.getNode(nodeId);
+ };
+
+ const getNodes = () => {
+ return reactFlowInstance?.getNodes();
+ };
+
+ const zoomTo = (zoomNumber: number, duration?: number) => {
+ reactFlowInstance?.zoomTo(zoomNumber, { duration });
+ };
+
+ const zoomToNode = (nodeId: string, duration?: number) => {
+ const node = getNode(nodeId);
+ if (node) {
+ reactFlowInstance?.fitView({
+ nodes: [{ id: nodeId }],
+ duration,
+ });
+ }
+ };
+
+ const setMiniMapPosition = (x: number, y: number) => {
+ setPosition!({ x, y });
+ };
+
+ const getViewport = () => {
+ return reactFlowInstance?.getViewport!();
+ };
+
+ const setViewport = (viewport: Viewport, duration?: number) => {
+ return reactFlowInstance?.setViewport!(viewport, { duration });
+ };
+
+ return {
+ selectNode: updateSelectNode!,
+ selectEdge: updateSelectEdge!,
+ selectEdges: updateSelectEdges!,
+ selectNodes: updateSelectNodes!,
+ getNode,
+ getNodes,
+ zoomTo,
+ getViewport,
+ setViewport,
+ zoomToNode,
+ setMiniMapPosition,
};
};
diff --git a/src/FlowView/index.md b/src/FlowView/index.md
index 82a11f0..127e6e1 100644
--- a/src/FlowView/index.md
+++ b/src/FlowView/index.md
@@ -19,20 +19,21 @@ description:
| className | `string` | 边数据 | - | - |
| style | `CSSProperties` | 节点数据 | - | - |
| miniMap | `boolean` | 边数据 | - | - |
+| autoLayout | `boolean` | 自动布局 | true | - |
| background | `boolean` | 节点数据 | - | - |
| children | `React.ReactNode` | 边数据 | - | - |
### FlowViewNode
-| 属性名 | 类型 | 描述 | 默认值 | 必选 |
-| ------ | ---------------------------------------------- | ------------ | ------ | ---- |
-| id | `string` | 边数据 | - | - |
-| select | `SelectType` | 节点数据 | - | - |
-| data | `NodeTypeDataMap[T]` | 边数据 | - | - |
-| type | `T = 'default' \| 'lineage' \| 'lineageGroup'` | 节点类型() | - | - |
-| label | `string` | 边数据 | - | - |
-| width | `number` | 节点数据 | - | - |
-| height | `number` | 边数据 | - | - |
+| 属性名 | 类型 | 描述 | 默认值 | 必选 |
+| ------ | ---------------------------------------------------------- | -------- | ------ | ---- |
+| id | `string` | 边数据 | - | - |
+| select | `SelectType` | 节点数据 | - | - |
+| data | `NodeTypeDataMap[T]` | 边数据 | - | - |
+| type | `T = 'default' \| 'lineage' \| 'lineageGroup' \| 'string'` | 节点类型 | - | - |
+| label | `string` | 边数据 | - | - |
+| width | `number` | 节点数据 | - | - |
+| height | `number` | 边数据 | - | - |
#### NodeTypeDataMap[T]
diff --git a/src/FlowView/index.tsx b/src/FlowView/index.tsx
index 152add6..bdc9ab0 100644
--- a/src/FlowView/index.tsx
+++ b/src/FlowView/index.tsx
@@ -1,14 +1,17 @@
+import LineageNodeGroup from '@/LineageGroupNode';
+import LineageNode from '@/LineageNode';
import React, {
createContext,
useCallback,
useContext,
useEffect,
+ useMemo,
type MouseEvent as ReactMouseEvent,
} from 'react';
-import ReactFlow, { Edge, Node } from 'reactflow';
+import ReactFlow, { BackgroundVariant, Edge, Node, useViewport } from 'reactflow';
import 'reactflow/dist/style.css';
-import Background, { BackgroundVariant } from '../Background';
-import { FlowViewProps, ProFlowController, RadiusEdge } from '../index';
+import { Background, FlowViewProps, ProFlowController, RadiusEdge } from '../index';
+import DefaultNode from './components/DefaultNode';
import { FlowViewContext } from './provider/provider';
import { useStyles } from './styles';
@@ -24,9 +27,12 @@ const FlowView: React.FC> = (props) => {
onEdgeClick = initFn,
nodes = [],
edges = [],
+ nodeTypes = {},
+ edgeTypes = {},
miniMap = true,
children,
background = true,
+ autoLayout = true,
} = props;
const {
miniMapPosition,
@@ -35,10 +41,25 @@ const FlowView: React.FC> = (props) => {
edges: renderEdges,
} = useContext(FlowViewContext);
const { styles, cx } = useStyles();
+ const nodeTypesMemo = useMemo(() => {
+ return {
+ ...nodeTypes,
+ lineage: LineageNode,
+ lineageGroup: LineageNodeGroup,
+ default: DefaultNode,
+ };
+ }, []);
+ const edgeTypesMemo = useMemo(() => {
+ return {
+ ...edgeTypes,
+ radius: RadiusEdge,
+ };
+ }, []);
+ const { zoom } = useViewport();
useEffect(() => {
- flowDataAdapter!(nodes, edges);
- }, [nodes, edges]);
+ flowDataAdapter!(nodes, edges, zoom, autoLayout);
+ }, [nodes, edges, zoom]);
const handleNodeDragStart = useCallback(
(event: ReactMouseEvent, node: Node, nodes: Node[]) => {
@@ -71,12 +92,11 @@ const FlowView: React.FC> = (props) => {
(event: ReactMouseEvent, edge: Edge) => {
// TODO: 应当把事件中的 node 转换为 FlowViewNode 透出给用户
// const {node} = transformNode(node);
- handleEdgeClick(event, edge);
+ onEdgeClick(event, edge);
},
[onEdgeClick],
);
- // TODO: 要把loading状态包掉,要把空状态包掉。
return (
> = (props) => {
onEdgeClick={handleEdgeClick}
nodes={renderNodes}
edges={renderEdges}
- edgeTypes={{
- radiusEdge: RadiusEdge,
- }}
+ nodeTypes={nodeTypesMemo}
+ edgeTypes={edgeTypesMemo}
panOnScroll
fitView
minZoom={MIN_ZOOM}
diff --git a/src/FlowView/provider/FlowViewProvider.tsx b/src/FlowView/provider/FlowViewProvider.tsx
index 706f921..ad63cbc 100644
--- a/src/FlowView/provider/FlowViewProvider.tsx
+++ b/src/FlowView/provider/FlowViewProvider.tsx
@@ -1,6 +1,6 @@
import { FlowViewEdge, FlowViewNode, MiniMapPosition, NodeTypeDataMap } from '@/constants';
import { FC, ReactNode, useCallback, useEffect, useState } from 'react';
-import { Edge, Node, ReactFlowProvider, useReactFlow, useViewport } from 'reactflow';
+import { Edge, Node, ReactFlowProvider, useReactFlow } from 'reactflow';
import { NodeMapping, SelectType } from '../constants';
import { convertMappingFrom, getRenderData } from '../helper';
import { FlowViewContext } from './provider';
@@ -13,20 +13,26 @@ const ProviderInner: FC<{ children: ReactNode }> = ({ children }) => {
const [edges, setEdges] = useState([]);
const [initEdges, setInitEdges] = useState(undefined);
const [mapping, setMapping] = useState({});
- const { zoom } = useViewport();
+ const [autoLayout, setAutoLayout] = useState(true);
const convertRenderData = useCallback(() => {
- const { nodes: _nodes, edges: _edges } = getRenderData(mapping, initEdges!);
+ const { nodes: _nodes, edges: _edges } = getRenderData(mapping, initEdges!, autoLayout);
setNodes(_nodes);
setEdges(_edges);
- }, [initEdges]);
+ }, [mapping, initEdges, autoLayout]);
const flowDataAdapter = useCallback(
- (initNodes: FlowViewNode[], initEdges: FlowViewEdge[]) => {
+ (
+ initNodes: FlowViewNode[],
+ initEdges: FlowViewEdge[],
+ zoom: number,
+ autoLayout: boolean,
+ ) => {
if (initNodes.length === 0) return;
setMapping(convertMappingFrom(initNodes!, initEdges!, zoom));
setInitEdges(initEdges);
+ setAutoLayout(autoLayout);
},
[],
);
@@ -86,6 +92,7 @@ const ProviderInner: FC<{ children: ReactNode }> = ({ children }) => {
value={{
nodes,
edges,
+ // dargePosition,
flowDataAdapter,
isUseProvider: true,
reactFlowInstance,
diff --git a/src/FlowView/provider/provider.ts b/src/FlowView/provider/provider.ts
index 604b9ff..e97ca00 100644
--- a/src/FlowView/provider/provider.ts
+++ b/src/FlowView/provider/provider.ts
@@ -4,7 +4,12 @@ import { Edge, Node, ReactFlowInstance } from 'reactflow';
import { NodeMapping, SelectType } from '../constants';
interface FlowViewContextProps {
- flowDataAdapter?: (nodes: FlowViewNode[], edges: FlowViewEdge[]) => void;
+ flowDataAdapter?: (
+ nodes: FlowViewNode[],
+ edges: FlowViewEdge[],
+ zoom: number,
+ autoLayout: boolean,
+ ) => void;
nodes?: Node[];
edges?: Edge[];
mapping?: NodeMapping;
diff --git a/src/FlowView/styles.tsx b/src/FlowView/styles.tsx
index d513251..fe7eb81 100644
--- a/src/FlowView/styles.tsx
+++ b/src/FlowView/styles.tsx
@@ -21,8 +21,6 @@ export const useStyles = createStyles(({ css, cx }) => ({
.${INIT_NODE} {
padding: 0;
box-sizing: border-box;
- width: 320px;
- height: 83px;
border: none;
border-radius: 8px;
cursor: pointer;
@@ -75,8 +73,6 @@ export const useStyles = createStyles(({ css, cx }) => ({
`,
nodeWrap: cx(
css`
- width: 320px;
- height: 85px;
display: flex;
z-index: 10 !important;
position: absolute;
diff --git a/src/LineageGroupNode/demos/index.tsx b/src/LineageGroupNode/demos/index.tsx
index a6cab64..916c979 100644
--- a/src/LineageGroupNode/demos/index.tsx
+++ b/src/LineageGroupNode/demos/index.tsx
@@ -1,5 +1,4 @@
-import { FlowViewEdge, FlowViewNode, SelectType } from '@/index';
-import { FlowView } from '@ant-design/pro-flow';
+import { FlowView, FlowViewEdge, FlowViewNode, SelectType } from '@/index';
import { createStyles } from 'antd-style';
import { useState } from 'react';
diff --git a/src/LineageGroupNode/index.tsx b/src/LineageGroupNode/index.tsx
index 8a3f981..68d059a 100644
--- a/src/LineageGroupNode/index.tsx
+++ b/src/LineageGroupNode/index.tsx
@@ -4,14 +4,22 @@ import { LineageGroupNodeData, LineageNodeData } from '@/constants';
import { getClsFromSelectType } from '@/utils';
import { cx } from 'antd-style';
import React from 'react';
+import { Handle, Position } from 'reactflow';
+import styled from 'styled-components';
import { useStyles } from './styles';
+const Wrap = styled.div`
+ width: 357px;
+ height: 632px;
+ position: relative;
+`;
export interface LineageNodeGroupProps {
id?: string;
zoom?: number;
label?: string;
- select?: SelectType;
+ selectType?: SelectType;
data: LineageGroupNodeData[];
+ handleType?: 'input' | 'output' | 'none' | ' both';
}
const convertMappingNode = (nodeList: LineageGroupNodeData[]): NodeMapItem[] => {
@@ -35,49 +43,61 @@ const GroupItem = (node: NodeMapItem) => {
);
};
-const LineageNodeGroup: React.FC = ({
- data,
- select = SelectType.SELECT,
- zoom = 1,
- label,
-}) => {
+const LineageNodeGroup: React.FC<{
+ data: LineageNodeGroupProps;
+}> = ({ data }) => {
const { styles } = useStyles();
+ const {
+ handleType = 'both',
+ selectType = SelectType.SELECT,
+ zoom = 1,
+ label,
+ data: _data,
+ } = data;
- if ((data as LineageGroupNodeData[]).length < 7) {
+ if ((_data as LineageGroupNodeData[]).length < 7) {
return 数组长度必须大于等于7!
;
}
- const nodeList = convertMappingNode(data as LineageGroupNodeData[]);
+ const nodeList = convertMappingNode(_data as LineageGroupNodeData[]);
return (
- <>
- {label && (
-
- {zoom <= 0.1 ? `${String(label).substring(0, 3)}...` : label}
-
- )}
-
- {nodeList!.map((_node, index) => {
- const data = _node.data as LineageNodeData;
- _node.position = {
- x: 0,
- y: 100 * index,
- };
- _node.title = data.title;
- _node.logo = data.logo;
- _node.des = data.describe;
- return GroupItem(_node);
- })}
-
-
- 查看更多
-
+
+ {handleType === 'output' || handleType === 'both' ? (
+
+ ) : null}
+
+ {label && (
+
+ {zoom <= 0.1 ? `${String(label).substring(0, 3)}...` : label}
+
+ )}
+
+ {nodeList!.map((_node, index) => {
+ const data = _node.data as LineageNodeData;
+ _node.position = {
+ x: 0,
+ y: 100 * index,
+ };
+ _node.title = data.title;
+ _node.logo = data.logo;
+ _node.des = data.describe;
+ return GroupItem(_node);
+ })}
+
+
+ 查看更多
+
+
- >
+ {handleType === 'input' || handleType === 'both' ? (
+
+ ) : null}
+
);
};
diff --git a/src/LineageGroupNode/styles.ts b/src/LineageGroupNode/styles.ts
index d78e204..ab400e5 100644
--- a/src/LineageGroupNode/styles.ts
+++ b/src/LineageGroupNode/styles.ts
@@ -40,17 +40,16 @@ export const useStyles = createStyles(({ css }) => ({
line-height: 32px;
bottom: 16px;
left: 112px;
+ text-align: center;
img {
width: 14px;
height: 14px;
margin-left: 4px;
- transform: translateY(-1px);
+ transform: translateY(2px);
}
`,
groupItemWrap: css`
- width: 320px;
- height: 83px;
display: flex;
border-radius: 12px;
box-sizing: border-box;
diff --git a/src/LineageNode/demos/DataViewList.tsx b/src/LineageNode/demos/DataViewList.tsx
index 82e64d8..4d87a38 100644
--- a/src/LineageNode/demos/DataViewList.tsx
+++ b/src/LineageNode/demos/DataViewList.tsx
@@ -1,6 +1,6 @@
import { createStyles } from 'antd-style';
import { memo } from 'react';
-import BloodNode from '..';
+import LineageNode from '..';
const useStyles = createStyles(({ css }) => ({
container: css`
@@ -51,12 +51,14 @@ const NodeList = memo(() => {
{nodeList.map((item) => {
return (
-
);
})}
diff --git a/src/LineageNode/demos/index.tsx b/src/LineageNode/demos/index.tsx
index ebd4f81..b4eb77e 100644
--- a/src/LineageNode/demos/index.tsx
+++ b/src/LineageNode/demos/index.tsx
@@ -1,5 +1,4 @@
-import { FlowViewEdge, FlowViewNode, SelectType } from '@/index';
-import { FlowView } from '@ant-design/pro-flow';
+import { FlowView, FlowViewEdge, FlowViewNode, SelectType } from '@ant-design/pro-flow';
import { Progress } from 'antd';
import { createStyles } from 'antd-style';
import React, { useState } from 'react';
diff --git a/src/LineageNode/index.tsx b/src/LineageNode/index.tsx
index 5462a82..a79a33e 100644
--- a/src/LineageNode/index.tsx
+++ b/src/LineageNode/index.tsx
@@ -1,9 +1,16 @@
import { SelectType } from '@/FlowView/constants';
import { getClsFromSelectType } from '@/utils';
import React from 'react';
+import { Handle, Position } from 'reactflow';
import styled from 'styled-components';
import { useStyles } from './styles';
+const Wrap = styled.div`
+ width: 320px;
+ height: 83px;
+ position: relative;
+`;
+
interface BloodNodeProps {
logo: string;
title?: string;
@@ -19,6 +26,7 @@ interface BloodNodeProps {
type: 'left' | 'right';
value: React.ReactNode;
};
+ handleType?: 'input' | 'output' | 'none' | ' both';
}
const zoomNum = (num: number, zoom: number, limitMax?: boolean) => {
@@ -52,46 +60,58 @@ const TitleSlotRight = styled.div`
top: 9px;
`;
-const BloodNode: React.FC
> = ({
- title,
- logo,
- description,
- className,
- selectType = SelectType.SELECT,
- zoom = 1,
- label,
- titleSlot,
-}) => {
+const LineageNode: React.FC<{
+ data: BloodNodeProps;
+}> = ({ data }) => {
const { styles, cx } = useStyles();
+ const {
+ title,
+ logo,
+ description,
+ className,
+ selectType = SelectType.SELECT,
+ zoom = 1,
+ label,
+ titleSlot,
+ handleType = 'both',
+ } = data;
return (
- <>
- {label && (
-
- {zoom <= 0.1 ? `${String(label).substring(0, 3)}...` : label}
-
- )}
-
-
-
-
-
{title}
- {!!titleSlot && !!titleSlot.value && titleSlot.type === 'left' && (
-
{titleSlot.value}
- )}
- {!!titleSlot && !!titleSlot.value && titleSlot.type === 'right' && (
-
-
- {titleSlot.value}
-
- )}
-
+
+ {handleType === 'output' || handleType === 'both' ? (
+
+ ) : null}
+
+ {label && (
+
+ {zoom <= 0.1 ? `${String(label).substring(0, 3)}...` : label}
+
+ )}
+
+
+
+
+
{title}
+ {!!titleSlot && !!titleSlot.value && titleSlot.type === 'left' && (
+
{titleSlot.value}
+ )}
+ {!!titleSlot && !!titleSlot.value && titleSlot.type === 'right' && (
+
+
+ {titleSlot.value}
+
+ )}
+
-
{description}
+
{description}
+
- >
+ {handleType === 'input' || handleType === 'both' ? (
+
+ ) : null}
+
);
};
-export default BloodNode;
+export default LineageNode;
diff --git a/src/LineageNode/styles.ts b/src/LineageNode/styles.ts
index f6315af..6c203b6 100644
--- a/src/LineageNode/styles.ts
+++ b/src/LineageNode/styles.ts
@@ -4,8 +4,6 @@ export const useStyles = createStyles(({ css, cx, prefixCls }) => ({
nodeWrap: cx(
`${prefixCls}-xx`,
css`
- width: 320px;
- height: 85px;
display: flex;
z-index: 10 !important;
position: absolute;
diff --git a/src/ProFlowController/demos/FlowControllerDemo.tsx b/src/ProFlowController/demos/FlowControllerDemo.tsx
index 1f671c0..de7fc2a 100644
--- a/src/ProFlowController/demos/FlowControllerDemo.tsx
+++ b/src/ProFlowController/demos/FlowControllerDemo.tsx
@@ -1,4 +1,4 @@
-import { FlowView, ProFlowController } from '@/index';
+import { FlowView, ProFlowController } from '@ant-design/pro-flow';
import { createStyles } from 'antd-style';
import { memo } from 'react';
@@ -13,7 +13,21 @@ const FlowControllerDemo = memo(() => {
const { styles } = useStyles();
return (
diff --git a/src/constants.tsx b/src/constants.tsx
index 85c5874..23c2493 100644
--- a/src/constants.tsx
+++ b/src/constants.tsx
@@ -1,5 +1,5 @@
import React, { type CSSProperties, type MouseEvent as ReactMouseEvent } from 'react';
-import { Node } from 'reactflow';
+import { Edge, EdgeProps, Node, NodeProps } from 'reactflow';
import { NodeMapItem } from './FlowView/constants';
export enum SelectType {
@@ -53,8 +53,8 @@ export type DefaultNodeType
= T extends FlowNodeType ? T : 'lineage';
export interface FlowViewNode> {
id: string;
select?: SelectType;
- data: NodeTypeDataMap[T];
- type?: T;
+ data: NodeTypeDataMap[T] | any;
+ type?: T | string;
label?: string;
width?: number;
height?: number;
@@ -68,7 +68,11 @@ export interface FlowViewEdge {
id: string;
source: string;
target: string;
+ sourceHandle?: string;
+ targetHandle?: string;
+ animated?: boolean;
select?: SelectType;
+ label?: string;
type?: EdgeType;
}
@@ -76,14 +80,16 @@ export interface FlowViewProps {
onNodeDragStart?: (event: ReactMouseEvent, node: Node, nodes: Node[]) => void;
onPaneClick?: (event: ReactMouseEvent) => void;
onNodeClick?: (event: ReactMouseEvent, node: Node) => void;
- onEdgeClick?: (event: ReactMouseEvent) => void;
+ onEdgeClick?: (event: ReactMouseEvent, edge: Edge) => void;
nodes: FlowViewNode[];
edges: FlowViewEdge[];
- className?: string;
+ nodeTypes?: Record>;
+ edgeTypes?: Record>;
style?: CSSProperties;
miniMap?: boolean;
background?: boolean;
children?: React.ReactNode;
+ autoLayout?: boolean;
}
export interface MiniMapPosition {
diff --git a/src/index.ts b/src/index.ts
index e40787c..c47e990 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,10 +1,14 @@
+import { Handle, Position } from 'reactflow';
+
export * from './Background';
+export { default as Background } from './Background';
export { default as BasicNode } from './BasicNode';
export { default as CanvasLoading } from './CanvasLoading';
export * from './ControlInput';
export { default as EditableText } from './EditableText';
export * from './FlowEditor';
export * from './FlowPanel';
+export { default as FlowPanel } from './FlowPanel';
export { FlowStoreProvider, type FlowEditorStoreProviderProps } from './FlowStoreProvider';
export { default as FlowView } from './FlowView/FlowView';
export * from './FlowView/hooks/useFlowView';
@@ -16,3 +20,4 @@ export * from './ProFlowController';
export { default as ProFlowController } from './ProFlowController';
export { default as RadiusEdge } from './RadiusEdge';
export * from './constants';
+export { Handle, Position };