@super-linear/supertopo 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.js +4 -4
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.js +4 -4
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.ts +83 -0
- package/dist/types.d.ts +23 -0
- package/package.json +4 -1
- package/rollup.config.js +0 -39
- package/src/core/config.ts +0 -44
- package/src/core/graph.ts +0 -298
- package/src/index.ts +0 -2
- package/src/layouts/clos.ts +0 -590
- package/src/layouts/index.ts +0 -9
- package/src/react/supertopo.tsx +0 -139
- package/src/react/topology.tsx +0 -148
- package/src/styles/graph-style.ts +0 -30
- package/src/types/graph.ts +0 -76
- package/src/types/index.ts +0 -1
- package/tsconfig.json +0 -48
package/src/react/supertopo.tsx
DELETED
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import { useEffect, useRef } from "react";
|
|
2
|
-
import Graph from "../core/graph";
|
|
3
|
-
import { GraphData } from "../types";
|
|
4
|
-
|
|
5
|
-
type EventListeners = Record<
|
|
6
|
-
string, // 格式: "click" 或 "click:node" 或 "click:node[type='server']"
|
|
7
|
-
(event: cytoscape.EventObject) => void
|
|
8
|
-
>;
|
|
9
|
-
|
|
10
|
-
interface SuperTopoProps {
|
|
11
|
-
data: GraphData;
|
|
12
|
-
layout?: string;
|
|
13
|
-
height?: string | number;
|
|
14
|
-
width?: string | number;
|
|
15
|
-
className?: string;
|
|
16
|
-
style?: React.CSSProperties;
|
|
17
|
-
layoutOptions?: Record<string, unknown>;
|
|
18
|
-
fitView?: boolean;
|
|
19
|
-
events?: EventListeners;
|
|
20
|
-
/** 节点属性,key 是节点 id,value 是属性字典,会合并到节点的 data 中 */
|
|
21
|
-
nodeAttributes?: Record<string, Record<string, unknown>>;
|
|
22
|
-
/** 边属性,key 是边 id,value 是属性字典,会合并到边的 data 中 */
|
|
23
|
-
edgeAttributes?: Record<string, Record<string, unknown>>;
|
|
24
|
-
/** 自定义样式,key 是 Cytoscape selector,value 是样式属性 */
|
|
25
|
-
renderFunction?: Record<string, Record<string, unknown>>;
|
|
26
|
-
}
|
|
27
|
-
export default function SuperTopo({
|
|
28
|
-
data: initialData,
|
|
29
|
-
layout = "clos",
|
|
30
|
-
height = "600px",
|
|
31
|
-
width = "100%",
|
|
32
|
-
className,
|
|
33
|
-
style,
|
|
34
|
-
layoutOptions,
|
|
35
|
-
fitView = true,
|
|
36
|
-
events,
|
|
37
|
-
nodeAttributes,
|
|
38
|
-
edgeAttributes,
|
|
39
|
-
renderFunction,
|
|
40
|
-
}: SuperTopoProps) {
|
|
41
|
-
const containerRef = useRef<HTMLDivElement>(null);
|
|
42
|
-
const graphRef = useRef<Graph | null>(null);
|
|
43
|
-
|
|
44
|
-
// destroy graph when component unmount
|
|
45
|
-
useEffect(() => {
|
|
46
|
-
return () => {
|
|
47
|
-
if (graphRef.current) {
|
|
48
|
-
graphRef.current.destroy();
|
|
49
|
-
graphRef.current = null;
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
}, []);
|
|
53
|
-
|
|
54
|
-
useEffect(() => {
|
|
55
|
-
if (!containerRef.current) return;
|
|
56
|
-
|
|
57
|
-
// 销毁旧实例
|
|
58
|
-
if (graphRef.current) {
|
|
59
|
-
graphRef.current.destroy();
|
|
60
|
-
graphRef.current = null;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// 创建新实例
|
|
64
|
-
graphRef.current = new Graph();
|
|
65
|
-
|
|
66
|
-
// 如果需要,先加载数据
|
|
67
|
-
graphRef.current.load(initialData);
|
|
68
|
-
// 最后 render 到容器
|
|
69
|
-
graphRef.current.render(containerRef.current);
|
|
70
|
-
|
|
71
|
-
// 应用节点属性
|
|
72
|
-
if (nodeAttributes) {
|
|
73
|
-
Object.entries(nodeAttributes).forEach(([id, attrs]) => {
|
|
74
|
-
Object.entries(attrs).forEach(([key, value]) => {
|
|
75
|
-
graphRef.current?.setNodeAttribute(id, key, value);
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// 应用边属性
|
|
81
|
-
if (edgeAttributes) {
|
|
82
|
-
Object.entries(edgeAttributes).forEach(([id, attrs]) => {
|
|
83
|
-
Object.entries(attrs).forEach(([key, value]) => {
|
|
84
|
-
graphRef.current?.setEdgeAttribute(id, key, value);
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// 应用自定义样式
|
|
90
|
-
if (renderFunction) {
|
|
91
|
-
graphRef.current.batchSetStyles(renderFunction);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// 绑定事件
|
|
95
|
-
if (events) {
|
|
96
|
-
Object.entries(events).forEach(([key, callback]) => {
|
|
97
|
-
const [eventType, selector] = key.includes(":")
|
|
98
|
-
? key.split(":")
|
|
99
|
-
: [key, undefined];
|
|
100
|
-
|
|
101
|
-
if (graphRef.current) {
|
|
102
|
-
if (selector) {
|
|
103
|
-
// 有 selector: graph.on('click', 'node', callback)
|
|
104
|
-
graphRef.current.on(eventType, selector, callback);
|
|
105
|
-
} else {
|
|
106
|
-
// 没有 selector: graph.on('click', callback)
|
|
107
|
-
graphRef.current.on(eventType, callback);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// 应用指定的布局
|
|
114
|
-
if (layout) {
|
|
115
|
-
graphRef.current.applyLayout(layout, layoutOptions);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// 如果需要,调用 fit
|
|
119
|
-
if (fitView) {
|
|
120
|
-
graphRef.current.fit(10);
|
|
121
|
-
}
|
|
122
|
-
}, [
|
|
123
|
-
initialData,
|
|
124
|
-
layout,
|
|
125
|
-
layoutOptions,
|
|
126
|
-
fitView,
|
|
127
|
-
nodeAttributes,
|
|
128
|
-
edgeAttributes,
|
|
129
|
-
renderFunction,
|
|
130
|
-
]);
|
|
131
|
-
|
|
132
|
-
return (
|
|
133
|
-
<div
|
|
134
|
-
ref={containerRef}
|
|
135
|
-
className={className}
|
|
136
|
-
style={{ position: "relative", touchAction: "none", ...style }}
|
|
137
|
-
/>
|
|
138
|
-
);
|
|
139
|
-
}
|
package/src/react/topology.tsx
DELETED
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
// SuperTopo React 组件
|
|
2
|
-
|
|
3
|
-
"use client";
|
|
4
|
-
|
|
5
|
-
import React, {
|
|
6
|
-
useEffect,
|
|
7
|
-
useRef,
|
|
8
|
-
} from "react";
|
|
9
|
-
|
|
10
|
-
import Graph from "../core/graph";
|
|
11
|
-
import type { GraphData, SUScope } from "../types";
|
|
12
|
-
import cytoscape from "cytoscape";
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* 事件监听器类型
|
|
16
|
-
* - key 格式: "event" 或 "event:selector"
|
|
17
|
-
* 支持的 selector 语法: "node", "edge", "node[type='server']", "node[type^='gpu']", ".selected" 等
|
|
18
|
-
*/
|
|
19
|
-
export type EventListeners = Record<
|
|
20
|
-
string, // 格式: "click" 或 "click:node" 或 "click:node[type='server']"
|
|
21
|
-
(event: cytoscape.EventObject) => void
|
|
22
|
-
>
|
|
23
|
-
|
|
24
|
-
export interface TopologyProps {
|
|
25
|
-
data: GraphData;
|
|
26
|
-
layout?: string;
|
|
27
|
-
height?: string | number;
|
|
28
|
-
width?: string | number;
|
|
29
|
-
className?: string;
|
|
30
|
-
style?: React.CSSProperties;
|
|
31
|
-
layoutOptions?: Record<string, unknown>;
|
|
32
|
-
fitView?: boolean;
|
|
33
|
-
events?: EventListeners;
|
|
34
|
-
/** 节点属性,key 是节点 id,value 是属性字典,会合并到节点的 data 中 */
|
|
35
|
-
nodeAttributes?: Record<string, Record<string, unknown>>;
|
|
36
|
-
/** 边属性,key 是边 id,value 是属性字典,会合并到边的 data 中 */
|
|
37
|
-
edgeAttributes?: Record<string, Record<string, unknown>>;
|
|
38
|
-
/** 自定义样式,key 是 Cytoscape selector,value 是样式属性 */
|
|
39
|
-
renderFunction?: Record<string, Record<string, unknown>>;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function SuperTopo({
|
|
43
|
-
data: initialData,
|
|
44
|
-
layout = "clos",
|
|
45
|
-
height = "600px",
|
|
46
|
-
width = "100%",
|
|
47
|
-
className,
|
|
48
|
-
style,
|
|
49
|
-
layoutOptions,
|
|
50
|
-
fitView = true,
|
|
51
|
-
events,
|
|
52
|
-
nodeAttributes,
|
|
53
|
-
edgeAttributes,
|
|
54
|
-
renderFunction,
|
|
55
|
-
}: TopologyProps) {
|
|
56
|
-
const containerRef = useRef<HTMLDivElement>(null);
|
|
57
|
-
const graphRef = useRef<Graph | null>(null);
|
|
58
|
-
|
|
59
|
-
// 组件卸载时销毁 Graph 实例
|
|
60
|
-
useEffect(() => {
|
|
61
|
-
return () => {
|
|
62
|
-
if (graphRef.current) {
|
|
63
|
-
graphRef.current.destroy();
|
|
64
|
-
graphRef.current = null;
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
}, []);
|
|
68
|
-
|
|
69
|
-
// 渲染图表并应用布局
|
|
70
|
-
useEffect(() => {
|
|
71
|
-
if (!containerRef.current) return;
|
|
72
|
-
|
|
73
|
-
// 销毁旧实例
|
|
74
|
-
if (graphRef.current) {
|
|
75
|
-
graphRef.current.destroy();
|
|
76
|
-
graphRef.current = null;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// 创建新实例
|
|
80
|
-
graphRef.current = new Graph();
|
|
81
|
-
|
|
82
|
-
// 如果需要,先加载数据
|
|
83
|
-
graphRef.current.load(initialData);
|
|
84
|
-
// 最后 render 到容器
|
|
85
|
-
graphRef.current.render(containerRef.current);
|
|
86
|
-
|
|
87
|
-
// 应用节点属性
|
|
88
|
-
if (nodeAttributes) {
|
|
89
|
-
Object.entries(nodeAttributes).forEach(([id, attrs]) => {
|
|
90
|
-
Object.entries(attrs).forEach(([key, value]) => {
|
|
91
|
-
graphRef.current?.setNodeAttribute(id, key, value);
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// 应用边属性
|
|
97
|
-
if (edgeAttributes) {
|
|
98
|
-
Object.entries(edgeAttributes).forEach(([id, attrs]) => {
|
|
99
|
-
Object.entries(attrs).forEach(([key, value]) => {
|
|
100
|
-
graphRef.current?.setEdgeAttribute(id, key, value);
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// 应用自定义样式
|
|
106
|
-
if (renderFunction) {
|
|
107
|
-
graphRef.current.batchSetStyles(renderFunction);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// 绑定事件
|
|
111
|
-
if (events) {
|
|
112
|
-
Object.entries(events).forEach(([key, callback]) => {
|
|
113
|
-
const [eventType, selector] = key.includes(':') ? key.split(':') : [key, undefined]
|
|
114
|
-
|
|
115
|
-
if (graphRef.current) {
|
|
116
|
-
if (selector) {
|
|
117
|
-
// 有 selector: graph.on('click', 'node', callback)
|
|
118
|
-
graphRef.current.on(eventType, selector, callback)
|
|
119
|
-
} else {
|
|
120
|
-
// 没有 selector: graph.on('click', callback)
|
|
121
|
-
graphRef.current.on(eventType, callback)
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
// 应用指定的布局
|
|
129
|
-
if (layout) {
|
|
130
|
-
graphRef.current.applyLayout(layout, layoutOptions);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// 如果需要,调用 fit
|
|
134
|
-
if (fitView) {
|
|
135
|
-
graphRef.current.fit(10);
|
|
136
|
-
}
|
|
137
|
-
}, [initialData, layout, layoutOptions, fitView, nodeAttributes, edgeAttributes, renderFunction]);
|
|
138
|
-
|
|
139
|
-
return (
|
|
140
|
-
<div
|
|
141
|
-
ref={containerRef}
|
|
142
|
-
className={className}
|
|
143
|
-
style={{ width, height, position: "relative", touchAction: "none", ...style }}
|
|
144
|
-
/>
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
export default SuperTopo;
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import type { StylesheetJson } from 'cytoscape';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Cytoscape 默认样式配置
|
|
5
|
-
* 用于渲染节点和边的视觉样式
|
|
6
|
-
*/
|
|
7
|
-
export const defaultGraphStyles: StylesheetJson = [
|
|
8
|
-
{
|
|
9
|
-
selector: 'node',
|
|
10
|
-
style: {
|
|
11
|
-
'background-color': 'data(color)',
|
|
12
|
-
label: 'data(label)',
|
|
13
|
-
'text-valign': 'center',
|
|
14
|
-
'text-halign': 'center',
|
|
15
|
-
color: '#ffffff',
|
|
16
|
-
'text-wrap': 'wrap',
|
|
17
|
-
'font-size': 'data(fontSize)',
|
|
18
|
-
width: 'data(size)',
|
|
19
|
-
height: 'data(size)',
|
|
20
|
-
shape: 'ellipse',
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
selector: 'edge',
|
|
25
|
-
style: {
|
|
26
|
-
'line-color': '#22c55e',
|
|
27
|
-
'curve-style': 'bezier',
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
];
|
package/src/types/graph.ts
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
Position,
|
|
3
|
-
NodeDataDefinition,
|
|
4
|
-
EdgeDataDefinition,
|
|
5
|
-
ElementDefinition,
|
|
6
|
-
NodeSingular,
|
|
7
|
-
} from "cytoscape"
|
|
8
|
-
|
|
9
|
-
// 使用 Cytoscape 原生类型,避免不必要的扩展
|
|
10
|
-
export type NodeData = NodeDataDefinition
|
|
11
|
-
export type EdgeData = EdgeDataDefinition
|
|
12
|
-
|
|
13
|
-
// Cytoscape 节点类型别名,用于 suScope 回调函数
|
|
14
|
-
export type CytoNode = NodeSingular
|
|
15
|
-
|
|
16
|
-
// 事件回调函数类型
|
|
17
|
-
export type EventCallback = (id: string, data: any) => void
|
|
18
|
-
export type Events = Partial<Record<string, EventCallback>>
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Topology 节点元素定义 - group 为可选,方便使用
|
|
22
|
-
*/
|
|
23
|
-
export interface TopoNode extends Omit<ElementDefinition, 'group'> {
|
|
24
|
-
group?: "nodes"
|
|
25
|
-
data: NodeData
|
|
26
|
-
position?: Position
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Topology 边元素定义 - group 为可选,方便使用
|
|
31
|
-
*/
|
|
32
|
-
export interface TopoEdge extends Omit<ElementDefinition, 'group'> {
|
|
33
|
-
group?: "edges"
|
|
34
|
-
data: EdgeData
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* 图数据
|
|
39
|
-
*/
|
|
40
|
-
export interface GraphData {
|
|
41
|
-
nodes: TopoNode[]
|
|
42
|
-
edges: TopoEdge[]
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* SU 范围配置 - 用于控制 SU 分组时使用哪些服务器
|
|
47
|
-
* include/exclude 回调函数接收 Cytoscape 节点对象(CytoNode)
|
|
48
|
-
*
|
|
49
|
-
* @example
|
|
50
|
-
* suScope: {
|
|
51
|
-
* // Cytoscape 节点对象,可以使用 node.id() 或 node.data() 访问数据
|
|
52
|
-
* include: (node) => String(node.data("id") || node.id()).toLowerCase().includes("gpu"),
|
|
53
|
-
*
|
|
54
|
-
* // 根据类型过滤
|
|
55
|
-
* exclude: (node) => node.data("type") === "switch"
|
|
56
|
-
* }
|
|
57
|
-
*/
|
|
58
|
-
export interface SUScope {
|
|
59
|
-
/** 只考虑满足条件的服务器参与 SU 分组判断 */
|
|
60
|
-
include?: (node: CytoNode) => boolean
|
|
61
|
-
/** 排除满足条件的服务器后参与 SU 分组判断 */
|
|
62
|
-
exclude?: (node: CytoNode) => boolean
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* 用于过滤回调的简化节点数据结构
|
|
67
|
-
* 这是扁平结构,直接包含 id, type, label, size 等属性
|
|
68
|
-
*/
|
|
69
|
-
export interface NodeDataForFilter {
|
|
70
|
-
id: string
|
|
71
|
-
label: string
|
|
72
|
-
type?: string
|
|
73
|
-
size: number
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export default GraphData
|
package/src/types/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./graph"
|
package/tsconfig.json
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
// Visit https://aka.ms/tsconfig to read more about this file
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
// File Layout
|
|
5
|
-
"rootDir": "./src",
|
|
6
|
-
"outDir": "./dist",
|
|
7
|
-
|
|
8
|
-
// Environment Settings
|
|
9
|
-
// See also https://aka.ms/tsconfig/module
|
|
10
|
-
"module": "esnext",
|
|
11
|
-
"target": "esnext",
|
|
12
|
-
"types": [],
|
|
13
|
-
"lib": ["dom", "dom.iterable", "esnext"],
|
|
14
|
-
"esModuleInterop": true,
|
|
15
|
-
"moduleResolution": "node",
|
|
16
|
-
"resolveJsonModule": true,
|
|
17
|
-
// For nodejs:
|
|
18
|
-
// "lib": ["esnext"],
|
|
19
|
-
// "types": ["node"],
|
|
20
|
-
// and npm install -D @types/node
|
|
21
|
-
|
|
22
|
-
// Other Outputs
|
|
23
|
-
"sourceMap": true,
|
|
24
|
-
"declaration": true,
|
|
25
|
-
"declarationMap": false,
|
|
26
|
-
|
|
27
|
-
// Stricter Typechecking Options
|
|
28
|
-
"noUncheckedIndexedAccess": true,
|
|
29
|
-
"exactOptionalPropertyTypes": true,
|
|
30
|
-
|
|
31
|
-
// Style Options
|
|
32
|
-
// "noImplicitReturns": true,
|
|
33
|
-
// "noImplicitOverride": true,
|
|
34
|
-
// "noUnusedLocals": true,
|
|
35
|
-
// "noUnusedParameters": true,
|
|
36
|
-
// "noFallthroughCasesInSwitch": true,
|
|
37
|
-
// "noPropertyAccessFromIndexSignature": true,
|
|
38
|
-
|
|
39
|
-
// Recommended Options
|
|
40
|
-
"strict": true,
|
|
41
|
-
"jsx": "react-jsx",
|
|
42
|
-
"verbatimModuleSyntax": false,
|
|
43
|
-
"isolatedModules": true,
|
|
44
|
-
"noUncheckedSideEffectImports": true,
|
|
45
|
-
"moduleDetection": "force",
|
|
46
|
-
"skipLibCheck": true,
|
|
47
|
-
}
|
|
48
|
-
}
|