Skip to content

Commit

Permalink
✨ feat: 新增 connect、edges、nodes 的 onChange 回调,修复 FlowEditor Demo bug. (#…
Browse files Browse the repository at this point in the history
…77)

* ✨ feat: connect and edges change callback

* ✨ feat: node change callback

* 🐛 fix: flow editor demo add node

* 🐛 fix: add basic group node export

* 🐛 fix: ci

---------

Co-authored-by: jiangchu <jiangchu.wzy@antgroup.com>
  • Loading branch information
ModestFun and jiangchu committed Jan 10, 2024
1 parent a02eaa4 commit bde06cc
Show file tree
Hide file tree
Showing 10 changed files with 282 additions and 224 deletions.
57 changes: 34 additions & 23 deletions docs/guide/demos/flowEditor/btnGroup.tsx
@@ -1,5 +1,36 @@
export const BtnGroup = (props) => {
const { editor } = props;
import { useFlowEditor } from '@ant-design/pro-flow';
import { useCallback, useEffect, useState } from 'react';

export const BtnGroup = () => {
const editor = useFlowEditor();
const [count, setCount] = useState(0);

const addMockNode = useCallback(() => {
if (editor) {
const id = Math.random();

editor.addNode({
id: `a${id}`,
type: 'StringNode',
position: { x: count * 200, y: 100 },
data: {
title: 'String Node',
handles: {
source: 'a1-source',
target: 'a1-target',
},
},
});

setCount((c) => c + 1);
}
}, [editor, count]);

useEffect(() => {
if (editor) {
addMockNode();
}
}, []);

return (
<>
Expand All @@ -18,27 +49,7 @@ export const BtnGroup = (props) => {
取消全选
</button>
<br />
<button
onClick={() => {
const nodes = editor.getFlattenNodes();
const index = nodes ? Object.keys(nodes).length + 1 : 1;

editor.addNode({
id: `a${index}`,
type: 'StringNode',
position: { x: index * 200, y: 100 },
data: {
title: `String Node ${index}`,
handles: {
source: `a${index}-source`,
target: `a${index}-target`,
},
},
});
}}
>
新增节点
</button>
<button onClick={addMockNode}>新增节点</button>
<button
onClick={() => {
editor.getSelectedKeys().forEach((id) => {
Expand Down
21 changes: 2 additions & 19 deletions docs/guide/demos/flowEditor/proBase.tsx
Expand Up @@ -2,36 +2,19 @@
* compact: true
* defaultShowCode: true
*/
import { FlowEditor, FlowEditorProvider, FlowPanel, useFlowEditor } from '@ant-design/pro-flow';
import { useEffect } from 'react';
import { FlowEditor, FlowEditorProvider, FlowPanel } from '@ant-design/pro-flow';
import { StringRender } from './StringNode';
import { BtnGroup } from './btnGroup';
import useStyles from './css/probase.style';

const ProFlowDemo = () => {
const editor = useFlowEditor();
const { styles } = useStyles();

useEffect(() => {
editor.addNode({
id: 'a1',
type: 'StringNode',
position: { x: 200, y: 100 },
data: {
title: 'String Node',
handles: {
source: 'a1-source',
target: 'a1-target',
},
},
});
}, [editor]);

return (
<div className={styles.container}>
<FlowEditor nodeTypes={{ StringNode: StringRender }} miniMap={false} devtools={true}>
<FlowPanel position="top-center">
<BtnGroup editor={editor} />
<BtnGroup />
</FlowPanel>
</FlowEditor>
</div>
Expand Down
16 changes: 0 additions & 16 deletions src/BasicGroupNode/demos/data.ts
Expand Up @@ -46,22 +46,6 @@ export const nodes: FlowViewNode[] = [
describe: 'XXX_XXX_XXX_API',
},
},
{
id: 'a10',
data: {
title: 'XXXX产品',
logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*ezaYT4wYRBwAAAAAAAAAAAAADvuvAQ/original',
describe: '2031030213014',
},
},
{
id: 'a11',
data: {
title: 'XXXX产品',
logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*ezaYT4wYRBwAAAAAAAAAAAAADvuvAQ/original',
describe: '2031030213014',
},
},
],
},
{
Expand Down
4 changes: 2 additions & 2 deletions src/BasicGroupNode/index.tsx
Expand Up @@ -55,8 +55,8 @@ const BasicNodeGroup: React.FC<{
data: _data,
} = data;

if ((_data as BasicGroupNodeData[]).length < 7) {
return <div className={styles.groupWrap}>数组长度必须大于等于7</div>;
if ((_data as BasicGroupNodeData[]).length < 5) {
return <div className={styles.groupWrap}>数组长度必须大于等于5</div>;
}

const nodeList = convertMappingNode(_data as BasicGroupNodeData[]);
Expand Down
175 changes: 114 additions & 61 deletions src/FlowEditor/container/FlowEditor.tsx
@@ -1,12 +1,13 @@
import { createStyles, cx } from 'antd-style';
import isEqual from 'fast-deep-equal';
import { debounce, throttle } from 'lodash-es';
import { JSXElementConstructor, forwardRef, useEffect, useState } from 'react';
import { JSXElementConstructor, forwardRef, useCallback, useEffect, useState } from 'react';
import { Flexbox } from 'react-layout-kit';
import ReactFlow, {
Background,
BackgroundVariant,
Connection,
Edge,
EdgeChange,
Node,
NodeChange,
Expand Down Expand Up @@ -69,9 +70,20 @@ export interface FlowEditorAppProps {
contextMenuEnabled?: boolean;
onNodesInit?: (editor: FlowEditorInstance) => void;
onNodesInitChange?: (init: boolean) => void;

// nodes 事件
beforeNodesChange?: (changes: NodeChange[]) => boolean;
onNodesChange?: (changes: NodeChange[]) => void;
afterNodesChange?: (changes: NodeChange[]) => void;
// edges 事件
beforeEdgesChange?: (changes: EdgeChange[]) => boolean;
onEdgesChange?: (changes: EdgeChange[]) => void;
afterEdgeChange?: (changes: EdgeChange[]) => void;
// connection 事件
beforeConnect?: (connection: Connection) => boolean;
onConnect?: (connection: Connection) => void;
afterConnect?: (edge: Edge) => void;

style?: React.CSSProperties;
flowProps?: ComponentProps<typeof ReactFlow>;
className?: string;
Expand All @@ -94,8 +106,16 @@ const FlowEditor = forwardRef<any, FlowEditorAppProps>(
miniMap = true,
onNodesInit,
beforeNodesChange = () => true,
beforeEdgesChange = () => true,
onNodesChange = () => {},
afterNodesChange = () => {},

beforeConnect = () => true,
onConnect = () => {},
afterConnect = () => {},

beforeEdgesChange = () => true,
onEdgesChange,
afterEdgeChange = () => {},
},
ref,
) => {
Expand All @@ -107,20 +127,20 @@ const FlowEditor = forwardRef<any, FlowEditorAppProps>(
const editor = useFlowEditor();

const [
onNodesChange,
// onNodesChange,
updateEdgesOnConnection,
updateEdgesOnEdgeChange,
onViewPortChange,
onElementSelectChange,
onEdgesChange,
// onEdgesChange,
dispatchNodes,
] = useStore((s) => [
s.onNodesChange,
// s.onNodesChange,
s.updateEdgesOnConnection,
s.updateEdgesOnEdgeChange,
s.onViewPortChange,
s.onElementSelectChange,
s.onEdgesChange,
// s.onEdgesChange,
s.dispatchNodes,
]);

Expand Down Expand Up @@ -151,6 +171,90 @@ const FlowEditor = forwardRef<any, FlowEditorAppProps>(
}
}, [nodesInitialized]);

const handleNodesChange = useCallback((changes: NodeChange[]) => {
if (!beforeNodesChange(changes)) {
return;
}
// 选择逻辑 nodes 和 edges 一致
changes.forEach((c) => {
switch (c.type) {
case 'add':
dispatchNodes({ type: 'addNode', node: c.item });
break;
case 'position':
// 结束拖拽时,会触发一次 position,此时 dragging 为 false
if (!c.dragging) break;

dispatchNodes({ type: 'updateNodePosition', position: c.position, id: c.id });

break;

case 'remove':
dispatchNodes({ type: 'deleteNode', id: c.id });
break;
case 'select':
onElementSelectChange(c.id, c.selected);
}
});

if (onNodesChange) {
throttle(onNodesChange, 50)(changes);
}

if (afterNodesChange) {
afterNodesChange(changes);
}
}, []);

const handleEdgesChange = useCallback((changes: EdgeChange[]) => {
if (!beforeEdgesChange(changes)) {
return;
}

// reactflow 的 edges change 事件,只有 select 和 remove
updateEdgesOnEdgeChange(changes);

// 选择逻辑 nodes 和 edges 一致
changes.forEach((c) => {
switch (c.type) {
case 'select':
onElementSelectChange(c.id, c.selected);
}
});

if (onEdgesChange) {
onEdgesChange(changes);
}

if (afterEdgeChange) {
afterEdgeChange(changes);
}
}, []);

const handleConnect = useCallback((connection: Connection) => {
if (!beforeConnect(connection)) {
return;
}

if (onConnect) {
onConnect(connection);
}

const edge = updateEdgesOnConnection(connection);

if (afterConnect && edge) {
// 触发 edges change 事件
handleEdgesChange([
{
item: edge,
type: 'add',
},
]);

afterConnect(edge);
}
}, []);

return (
<Flexbox height={'100%'} width={'100%'} style={{ position: 'relative' }}>
{!flowInit && <CanvasLoading />}
Expand Down Expand Up @@ -180,61 +284,10 @@ const FlowEditor = forwardRef<any, FlowEditorAppProps>(
selectionKeyCode={['Meta', 'Shift']}
multiSelectionKeyCode={['Meta', 'Shift']}
selectNodesOnDrag
onNodesChange={(changes) => {
if (!beforeNodesChange(changes)) {
return;
}
// 选择逻辑 nodes 和 edges 一致
changes.forEach((c) => {
switch (c.type) {
case 'add':
dispatchNodes({ type: 'addNode', node: c.item });
break;
case 'position':
// 结束拖拽时,会触发一次 position,此时 dragging 为 false
if (!c.dragging) break;

dispatchNodes({ type: 'updateNodePosition', position: c.position, id: c.id });

break;

case 'remove':
dispatchNodes({ type: 'deleteNode', id: c.id });
break;
case 'select':
onElementSelectChange(c.id, c.selected);
}
});

if (onNodesChange) {
throttle(onNodesChange, 50)(changes);
}
}}
onEdgesChange={(changes) => {
if (!beforeEdgesChange(changes)) {
return;
}

updateEdgesOnEdgeChange(changes);

// 选择逻辑 nodes 和 edges 一致
changes.forEach((c) => {
switch (c.type) {
case 'select':
onElementSelectChange(c.id, c.selected);
}
});

if (onEdgesChange) {
onEdgesChange(changes);
}
}}
onConnect={(connection) => {
if (!beforeConnect(connection)) {
return;
}
updateEdgesOnConnection(connection);
}}
onNodesChange={handleNodesChange}
onEdgesChange={handleEdgesChange}
// Connect 相关逻辑
onConnect={handleConnect}
disableKeyboardA11y={true}
proOptions={{ hideAttribution: true }}
>
Expand Down

0 comments on commit bde06cc

Please sign in to comment.