Skip to content

Commit

Permalink
feat(comp): optimize flow diagram layout: use snake layout when data …
Browse files Browse the repository at this point in the history
…meets linear conditions
  • Loading branch information
yvonneyx committed Dec 5, 2024
1 parent b874d66 commit 70a4cfd
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 3 deletions.
4 changes: 2 additions & 2 deletions src/ConfigProvider/hooks/useConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ function useGraphGlobalConfig(name: Charts) {
return mergeGraphOptions(graphConfig, componentConfig || {});
}

export function useGraphConfig<T extends GraphOptions>(name: Charts, defaultConfig: T, props: T) {
export function useGraphConfig<T extends GraphOptions>(name: Charts, ...configs: Partial<T>[]) {
const globalConfig = useGraphGlobalConfig(name);

return mergeGraphOptions(defaultConfig, globalConfig, props);
return mergeGraphOptions(globalConfig, ...configs);
}
71 changes: 71 additions & 0 deletions src/FlowDiagram/helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import type { FlowGraphOptions, G6 } from '@ant-design/graphs';

/**
* Adjust graph configuration based on data characteristics.
* @param data - graph data
* @returns graph options
*/
export function getGraphOptionsByData(data: G6.GraphData): FlowGraphOptions {
if (isLinearStructure(data))
return {
node: {
style: {
ports: [
{ placement: 'right' },
{ placement: 'left' },
{ placement: 'top' },
{ placement: 'bottom' },
],
},
},
layout: {
type: 'snake',
cols: 3,
rowGap: 40,
},
};
return {};
}

function isLinearStructure(data: G6.GraphData) {
const { nodes = [], edges = [] } = data;
const inDegree: { [key: G6.ID]: number } = {};
const outDegree: { [key: G6.ID]: number } = {};
const adjList: { [key: G6.ID]: G6.ID[] } = {};

nodes.forEach((node) => {
inDegree[node.id] = 0;
outDegree[node.id] = 0;
adjList[node.id] = [];
});

edges.forEach((edge) => {
inDegree[edge.target]++;
outDegree[edge.source]++;
adjList[edge.source].push(edge.target);
});

// 检查图是否连通
// Check if the graph is connected
const visited: Set<G6.ID> = new Set();
const dfs = (nodeId: G6.ID) => {
if (visited.has(nodeId)) return;
visited.add(nodeId);
adjList[nodeId].forEach(dfs);
};
dfs(nodes[0].id);
if (visited.size !== nodes.length) return false;

// 检查是否有且仅有一个源节点和一个汇节点
// Check if there is exactly one source node and one sink node
const sourceNodes = nodes.filter((node) => inDegree[node.id] === 0);
const sinkNodes = nodes.filter((node) => outDegree[node.id] === 0);
if (sourceNodes.length !== 1 || sinkNodes.length !== 1) return false;

// 检查中间节点是否只有一个前驱和一个后继
// Check if the middle nodes have only one predecessor and one successor
const middleNodes = nodes.filter((node) => inDegree[node.id] === 1 && outDegree[node.id] === 1);
if (middleNodes.length !== nodes.length - 2) return false;

return true;
}
9 changes: 8 additions & 1 deletion src/FlowDiagram/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import React, { useMemo } from 'react';
import { useGraphConfig } from '../ConfigProvider/hooks';
import type { GraphProps } from '../types';
import { visGraphData2GraphData } from '../utils/graph';
import { getGraphOptionsByData } from './helper';

const { TextNode } = RCNode;

Expand All @@ -24,6 +25,7 @@ const defaultConfig: FlowGraphOptions = {
fontSize: 12,
background: 'linear-gradient(-89deg, #64b7f2 0%, #4c95f3 100%)',
}}
borderWidth={2}
/>
);
},
Expand Down Expand Up @@ -67,7 +69,12 @@ const FlowDiagram: React.FC<FlowDiagramProps> = (props) => {

const data = useMemo(() => visGraphData2GraphData(propsData), [propsData]);

const config = useGraphConfig<FlowGraphOptions>('FlowDiagram', defaultConfig, restProps);
const config = useGraphConfig<FlowGraphOptions>(
'FlowDiagram',
defaultConfig,
getGraphOptionsByData(data),
restProps,
);

return <ADCFlowGraph data={data} {...config} />;
};
Expand Down

0 comments on commit 70a4cfd

Please sign in to comment.