mirror of
https://github.com/YuzuZensai/Minikura.git
synced 2026-03-30 17:26:38 +00:00
164 lines
4.2 KiB
TypeScript
164 lines
4.2 KiB
TypeScript
"use client";
|
|
|
|
import { Position } from "@xyflow/react";
|
|
import { useMemo } from "react";
|
|
import type {
|
|
ProxyMetadata,
|
|
ServerMetadata,
|
|
TopologyEdge,
|
|
TopologyNode,
|
|
} from "@/lib/topology-types";
|
|
|
|
interface LayoutInput {
|
|
nodes: TopologyNode[];
|
|
edges: TopologyEdge[];
|
|
}
|
|
|
|
export function useGraphLayout({ nodes, edges }: LayoutInput) {
|
|
const layoutedNodes = useMemo(() => {
|
|
const k8sNodes = nodes.filter((n) => n.data.type === "k8s-node");
|
|
const serverNodes = nodes.filter((n) => n.data.type === "server");
|
|
const proxyNodes = nodes.filter((n) => n.data.type === "proxy");
|
|
|
|
const nodeSpacing = {
|
|
x: 350,
|
|
y: 350,
|
|
};
|
|
|
|
const nodeWidth = 320;
|
|
const layoutedNodesList: TopologyNode[] = [];
|
|
|
|
const k8sNodeGroups = new Map<string, TopologyNode[]>();
|
|
const orphanedNodes: TopologyNode[] = [];
|
|
|
|
const appNodes = [...serverNodes, ...proxyNodes];
|
|
|
|
appNodes.forEach((node) => {
|
|
const metadata = node.data.metadata as ServerMetadata | ProxyMetadata;
|
|
const k8sNodeNames = metadata.k8sNodes || [];
|
|
|
|
if (k8sNodeNames.length === 0) {
|
|
orphanedNodes.push(node);
|
|
} else {
|
|
const k8sNodeName = k8sNodeNames[0];
|
|
const k8sNodeId = `k8s-node-${k8sNodeName}`;
|
|
if (!k8sNodeGroups.has(k8sNodeId)) {
|
|
k8sNodeGroups.set(k8sNodeId, []);
|
|
}
|
|
k8sNodeGroups.get(k8sNodeId)?.push(node);
|
|
}
|
|
});
|
|
|
|
const k8sWithApps = k8sNodes.filter((k8s) => {
|
|
const group = k8sNodeGroups.get(k8s.id) || [];
|
|
return group.length > 0;
|
|
});
|
|
|
|
const k8sWithoutApps = k8sNodes.filter((k8s) => {
|
|
const group = k8sNodeGroups.get(k8s.id) || [];
|
|
return group.length === 0;
|
|
});
|
|
|
|
const k8sNodeWidths = new Map<string, number>();
|
|
k8sWithApps.forEach((k8sNode) => {
|
|
const group = k8sNodeGroups.get(k8sNode.id) || [];
|
|
const width = Math.max(nodeWidth, group.length * nodeWidth);
|
|
k8sNodeWidths.set(k8sNode.id, width);
|
|
});
|
|
|
|
let currentX = 0;
|
|
const k8sNodePositions = new Map<string, { x: number; width: number }>();
|
|
|
|
k8sWithApps.forEach((k8sNode) => {
|
|
const width = k8sNodeWidths.get(k8sNode.id)!;
|
|
const centerX = currentX + width / 2;
|
|
|
|
k8sNodePositions.set(k8sNode.id, { x: centerX, width });
|
|
|
|
layoutedNodesList.push({
|
|
...k8sNode,
|
|
position: {
|
|
x: centerX,
|
|
y: 0,
|
|
},
|
|
targetPosition: Position.Top,
|
|
sourcePosition: Position.Bottom,
|
|
});
|
|
|
|
currentX += width + nodeSpacing.x;
|
|
});
|
|
|
|
k8sWithoutApps.forEach((k8sNode) => {
|
|
const centerX = currentX + nodeWidth / 2;
|
|
|
|
k8sNodePositions.set(k8sNode.id, { x: centerX, width: nodeWidth });
|
|
|
|
layoutedNodesList.push({
|
|
...k8sNode,
|
|
position: {
|
|
x: centerX,
|
|
y: 0,
|
|
},
|
|
targetPosition: Position.Top,
|
|
sourcePosition: Position.Bottom,
|
|
});
|
|
|
|
currentX += nodeWidth + nodeSpacing.x;
|
|
});
|
|
|
|
k8sNodeGroups.forEach((group, k8sNodeId) => {
|
|
const k8sPos = k8sNodePositions.get(k8sNodeId);
|
|
if (!k8sPos) return;
|
|
|
|
const groupWidth = group.length * nodeWidth;
|
|
const startX = k8sPos.x - groupWidth / 2 + nodeWidth / 2;
|
|
|
|
group.forEach((node, index) => {
|
|
const xPos = startX + index * nodeWidth;
|
|
|
|
layoutedNodesList.push({
|
|
...node,
|
|
position: {
|
|
x: xPos,
|
|
y: nodeSpacing.y,
|
|
},
|
|
targetPosition: Position.Top,
|
|
sourcePosition: Position.Bottom,
|
|
});
|
|
});
|
|
});
|
|
|
|
orphanedNodes.forEach((node, index) => {
|
|
const xPos = currentX + index * nodeWidth;
|
|
|
|
layoutedNodesList.push({
|
|
...node,
|
|
position: {
|
|
x: xPos,
|
|
y: nodeSpacing.y,
|
|
},
|
|
targetPosition: Position.Top,
|
|
sourcePosition: Position.Bottom,
|
|
});
|
|
});
|
|
|
|
if (layoutedNodesList.length > 0) {
|
|
const minX = Math.min(...layoutedNodesList.map((n) => n.position.x));
|
|
const maxX = Math.max(...layoutedNodesList.map((n) => n.position.x));
|
|
const totalWidth = maxX - minX;
|
|
const offsetX = -totalWidth / 2;
|
|
|
|
layoutedNodesList.forEach((node) => {
|
|
node.position.x += offsetX;
|
|
});
|
|
}
|
|
|
|
return layoutedNodesList;
|
|
}, [nodes]);
|
|
|
|
return {
|
|
nodes: layoutedNodes,
|
|
edges,
|
|
};
|
|
}
|