@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.
@@ -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
- }
@@ -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
- ];
@@ -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
@@ -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
- }