diff --git a/src/core/components/Graph/Graph.interfaces.ts b/src/core/components/Graph/Graph.interfaces.ts index 56cbe2ae..d7453b08 100644 --- a/src/core/components/Graph/Graph.interfaces.ts +++ b/src/core/components/Graph/Graph.interfaces.ts @@ -1,4 +1,4 @@ -import { GraphData } from '@antv/g6'; +import { GraphData, LayoutConfig } from '@antv/g6'; export interface GraphNode { id: string; @@ -40,9 +40,11 @@ export interface GraphReactAdaptorProps { onGetZoom?: Function; onFitScreen?: Function; legendData?: GraphData; + layout?: LayoutConfig; config?: { zoom?: string | null; fitScreen?: number | null; + fitCenter?: boolean | null; }; } diff --git a/src/core/components/Graph/GraphReactAdaptor.tsx b/src/core/components/Graph/GraphReactAdaptor.tsx index c0f94a81..4ed16ac1 100644 --- a/src/core/components/Graph/GraphReactAdaptor.tsx +++ b/src/core/components/Graph/GraphReactAdaptor.tsx @@ -14,9 +14,6 @@ import TransitionPage from '@core/components/TransitionPages/Slide'; import { DEFAULT_COMBO_CONFIG, DEFAULT_EDGE_CONFIG, - DEFAULT_LAYOUT_COMBO_FORCE_CONFIG, - DEFAULT_LAYOUT_FORCE_CONFIG, - DEFAULT_LAYOUT_GFORCE_CONFIG, DEFAULT_MODE, DEFAULT_NODE_CONFIG, DEFAULT_NODE_STATE_CONFIG @@ -37,6 +34,7 @@ const GraphReactAdaptor: FC = memo( legendData, onGetZoom, onFitScreen, + layout, config }) => { const [isGraphLoaded, setIsGraphLoaded] = useState(false); @@ -96,41 +94,23 @@ const GraphReactAdaptor: FC = memo( const graphRef = useCallback( ($node: HTMLDivElement | null) => { if ($node && nodes.length && !topologyGraphRef.current) { + const data = GraphController.getG6Model({ edges, nodes, combos }); const legend = legendData ? createLegend(legendData) : ''; + const width = $node.scrollWidth; + const height = $node.scrollHeight; + topologyGraphRef.current = new G6.Graph({ container: $node, - width: $node.scrollWidth, - height: $node.scrollHeight, + width, + height, + fitCenter: config?.fitCenter || false, plugins: [legend], modes: DEFAULT_MODE, layout: { - pipes: [ - // If nodes already have positions,load the 'force' layout without a simulation to visually arrange them in a graph. - { - type: 'force', - alpha: 0, - nodesFilter: ({ x, y }: GraphNode) => !!(x && y) - }, - { - ...DEFAULT_LAYOUT_COMBO_FORCE_CONFIG, - center: [$node.scrollWidth / 2, $node.scrollHeight / 2], - maxIteration: GraphController.calculateMaxIteration(nodes.length), - nodesFilter: ({ x, y, comboId }: GraphNode) => !!(!x || !y) && comboId - }, - { - ...DEFAULT_LAYOUT_FORCE_CONFIG, - center: [$node.scrollWidth / 2, $node.scrollHeight / 2], - nodesFilter: ({ x, y, comboId }: GraphNode) => !!(!x || !y) && !comboId && nodes.length < 250 - }, - { - ...DEFAULT_LAYOUT_GFORCE_CONFIG, - center: [$node.scrollWidth / 2, $node.scrollHeight / 2], - nodesFilter: ({ x, y, comboId }: GraphNode) => !!(!x || !y) && !comboId && nodes.length >= 250 - } - ] + ...layout, + center: [Math.floor(width / 2), Math.floor(height / 2)] }, - defaultNode: DEFAULT_NODE_CONFIG, defaultCombo: DEFAULT_COMBO_CONFIG, defaultEdge: DEFAULT_EDGE_CONFIG, @@ -344,7 +324,7 @@ const GraphReactAdaptor: FC = memo( registerCustomBehaviours(); - topologyGraph.data(GraphController.getG6Model({ edges, nodes, combos })); + topologyGraph.data(data); topologyGraph.render(); if (config?.zoom) { @@ -360,10 +340,12 @@ const GraphReactAdaptor: FC = memo( [ nodes, legendData, + layout, combos, edges, config?.zoom, config?.fitScreen, + config?.fitCenter, handleOnClickNode, handleOnClickEdge, handleOnClickCombo, diff --git a/src/core/components/Graph/config.ts b/src/core/components/Graph/config.ts index b9bc0687..8ad960ec 100644 --- a/src/core/components/Graph/config.ts +++ b/src/core/components/Graph/config.ts @@ -9,21 +9,22 @@ import { NODE_COLOR_HOVER_EDGE_DEFAULT } from './Graph.constants'; -const NODE_SIZE = 50; +const NODE_SIZE = 40; const greyColor = '#808080'; export const DEFAULT_MODE = { default: [ - { type: 'drag-node', onlyChangeComboSize: true }, + { type: 'drag-node', onlyChangeComboSize: true, optimize: true }, { type: 'drag-combo', enableDelegate: true, activeState: 'actived', onlyChangeComboSize: true, - shouldUpdate: () => true + shouldUpdate: () => true, + optimize: true }, - 'zoom-canvas', - 'drag-canvas' + { type: 'drag-canvas', optimize: true }, + { type: 'zoom-canvas', optimize: true } ] }; @@ -31,25 +32,21 @@ export const DEFAULT_LAYOUT_COMBO_FORCE_CONFIG: LayoutConfig = { type: 'comboForce', nodeSize: NODE_SIZE, nodeSpacing: NODE_SIZE / 3, + preventOverlap: true, comboSpacing: 0, linkDistance: 150, nodeStrength: 10, edgeStrength: 1, collideStrength: 0.3, - preventOverlap: true, - preventComboOverlap: true, comboCollideStrength: 0.5 }; export const DEFAULT_LAYOUT_FORCE_CONFIG: LayoutConfig = { type: 'force', nodeSize: NODE_SIZE, - nodeSpacing: NODE_SIZE / 3, - linkDistance: 150, - nodeStrength: 60, - edgeStrength: 1, - collideStrength: 0.3, + nodeSpacing: NODE_SIZE, preventOverlap: true, + linkDistance: 150, alphaMin: 0.07, alpha: 0.1 }; @@ -81,7 +78,7 @@ export const DEFAULT_NODE_CONFIG: ModelStyle = { style: { fill: NODE_COLOR_DEFAULT, cursor: 'pointer', - fontSize: 14, + fontSize: 12, background: { fill: '#FFFFFF', fillOpacity: 0.9, diff --git a/src/pages/Topology/components/TopologyProcessGroups.tsx b/src/pages/Topology/components/TopologyProcessGroups.tsx index 86c08d50..b1109d3d 100644 --- a/src/pages/Topology/components/TopologyProcessGroups.tsx +++ b/src/pages/Topology/components/TopologyProcessGroups.tsx @@ -89,6 +89,7 @@ const TopologyProcessGroups: FC<{ id?: string }> = function ({ id: processGroupI itemSelected={processGroupId} onGetZoom={handleSaveZoom} onFitScreen={handleFitScreen} + layout={TopologyController.selectLayoutFromNodes(nodes)} config={{ zoom: localStorage.getItem(ZOOM_CACHE_KEY), fitScreen: Number(localStorage.getItem(FIT_SCREEN_CACHE_KEY)) diff --git a/src/pages/Topology/components/TopologyProcesses.tsx b/src/pages/Topology/components/TopologyProcesses.tsx index 921f8d84..0509b24b 100644 --- a/src/pages/Topology/components/TopologyProcesses.tsx +++ b/src/pages/Topology/components/TopologyProcesses.tsx @@ -429,6 +429,7 @@ const TopologyProcesses: FC<{ addressId?: string | null; id: string | undefined legendData={ProcessLegendData} onGetZoom={handleSaveZoom} onFitScreen={handleFitScreen} + layout={TopologyController.selectLayoutFromNodes(nodes, 'combo')} config={{ zoom: localStorage.getItem(ZOOM_CACHE_KEY), fitScreen: Number(localStorage.getItem(FIT_SCREEN_CACHE_KEY)) diff --git a/src/pages/Topology/components/TopologySite.tsx b/src/pages/Topology/components/TopologySite.tsx index 51e2a271..c589112b 100644 --- a/src/pages/Topology/components/TopologySite.tsx +++ b/src/pages/Topology/components/TopologySite.tsx @@ -68,6 +68,7 @@ const TopologySite: FC<{ id?: string | null }> = function () { onClickNode={handleGetSelectedNode} onGetZoom={handleSaveZoom} onFitScreen={handleFitScreen} + layout={TopologyController.selectLayoutFromNodes(nodes)} config={{ zoom: localStorage.getItem(ZOOM_CACHE_KEY), fitScreen: Number(localStorage.getItem(FIT_SCREEN_CACHE_KEY)) diff --git a/src/pages/Topology/services/index.ts b/src/pages/Topology/services/index.ts index 1fb8c7a1..50681fac 100644 --- a/src/pages/Topology/services/index.ts +++ b/src/pages/Topology/services/index.ts @@ -3,7 +3,13 @@ import componentSVG from '@assets/component.svg'; import processSVG from '@assets/process.svg'; import siteSVG from '@assets/site.svg'; import skupperProcessSVG from '@assets/skupper.svg'; -import { DEFAULT_NODE_CONFIG, DEFAULT_REMOTE_NODE_CONFIG } from '@core/components/Graph/config'; +import { + DEFAULT_LAYOUT_COMBO_FORCE_CONFIG, + DEFAULT_LAYOUT_FORCE_CONFIG, + DEFAULT_LAYOUT_GFORCE_CONFIG, + DEFAULT_NODE_CONFIG, + DEFAULT_REMOTE_NODE_CONFIG +} from '@core/components/Graph/config'; import { EDGE_COLOR_ACTIVE_DEFAULT, NODE_COLOR_DEFAULT, nodeColors } from '@core/components/Graph/Graph.constants'; import { GraphEdge, GraphCombo, GraphNode } from '@core/components/Graph/Graph.interfaces'; import { GraphController } from '@core/components/Graph/services'; @@ -196,6 +202,24 @@ export const TopologyController = { label: [link.label, ...metrics].filter(Boolean).join('') }; }); + }, + selectLayoutFromNodes: (nodes: GraphNode[], type: 'combo' | 'default' = 'default') => { + let layout = {}; + + const nodesNotInitializedCount = nodes.filter(({ x, y }) => !!(!x || !y)).length; + + if (nodesNotInitializedCount) { + if (type === 'combo') { + layout = { + ...DEFAULT_LAYOUT_COMBO_FORCE_CONFIG, + maxIteration: GraphController.calculateMaxIteration(nodes.length) + }; + } else { + layout = nodesNotInitializedCount > 250 ? DEFAULT_LAYOUT_GFORCE_CONFIG : DEFAULT_LAYOUT_FORCE_CONFIG; + } + } + + return layout; } };