power-link 1.0.11 → 2.0.0

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/README.md CHANGED
@@ -7,14 +7,8 @@ A pure TypeScript visual node connector for creating draggable connections betwe
7
7
 
8
8
  ![Node Link Connector Demo](https://github.com/Tem-man/power-link/blob/main/public/images/screen-shot.png)
9
9
 
10
- ### 📹 Demo Video
11
10
 
12
- <video width="100%" controls>
13
- <source src="https://github.com/Tem-man/power-link/blob/main/public/images/video.mp4" type="video/mp4">
14
- Your browser does not support the video tag.
15
- </video>
16
-
17
- **Watch the demo video** to see power-link in action! [Download video](https://github.com/Tem-man/node-link-utils/raw/main/packages/images/video.mp4)
11
+ ### [online demo](https://tem-man.github.io/power-link)
18
12
 
19
13
  ## ✨ Features
20
14
 
@@ -80,6 +74,12 @@ const connector = new Connector({
80
74
  console.log("View changed:", viewState);
81
75
  // viewState: { scale: 1, translateX: 0, translateY: 0 }
82
76
  // Save view state to restore later
77
+ },
78
+
79
+ onNodeMove: ({ id, x, y }) => {
80
+ console.log("Node moved:", id, "to", x, y);
81
+ // Synchronize node position with your state management
82
+ // This prevents nodes from jumping back after dragging
83
83
  }
84
84
  });
85
85
 
@@ -142,6 +142,9 @@ connector.registerNode("node2", node2, {
142
142
  | `onConnect` | Function | `() => {}` | Callback when connection is created |
143
143
  | `onDisconnect` | Function | `() => {}` | Callback when connection is removed |
144
144
  | `onViewChange` | Function | `() => {}` | Callback when view state changes (zoom/pan) |
145
+ | `onNodeMove` | Function | `() => {}` | Callback when a node is dragged (receives `{ id, x, y }`) |
146
+ | `onNodeSelect` | Function | `() => {}` | Callback when a node is selected/deselected (receives node info or `null`) |
147
+ | `onNodeDelete` | Function | `() => {}` | Callback when a node is deleted (receives `{ id, info }`) |
145
148
 
146
149
  ### Methods
147
150
 
@@ -156,9 +159,11 @@ Register a node for connection.
156
159
  - `options` (Object): Node configuration
157
160
  - `dotPositions` (String | Array): Connection dot positions
158
161
  - `'both'`: Both left and right dots
162
+ - `'left'`: Only left dot (string format)
163
+ - `'right'`: Only right dot (string format)
159
164
  - `['left', 'right']`: Array format, both sides
160
- - `['left']`: Only left dot
161
- - `['right']`: Only right dot
165
+ - `['left']`: Only left dot (array format)
166
+ - `['right']`: Only right dot (array format)
162
167
  - `info` (Object): Node extraneous information
163
168
 
164
169
  **Returns:** Node object
@@ -253,6 +258,34 @@ Update node position (called when node is moved).
253
258
 
254
259
  - `nodeId` (String): Node ID
255
260
 
261
+ #### `deleteNode(id)`
262
+
263
+ Delete a node and all its connections. This method removes the node from the connector, disconnects all associated connections, and triggers the `onNodeDelete` callback.
264
+
265
+ **Note:** This method does not remove the DOM element itself. You should handle DOM removal in your framework (e.g., Vue/React) within the `onNodeDelete` callback.
266
+
267
+ **Parameters:**
268
+
269
+ - `id` (String): Node ID to delete
270
+
271
+ **Example:**
272
+
273
+ ```javascript
274
+ // Delete a specific node
275
+ connector.deleteNode('node1');
276
+
277
+ // In your connector setup, handle the deletion callback
278
+ const connector = new Connector({
279
+ container: container,
280
+ onNodeDelete: ({ id, info }) => {
281
+ console.log('Node deleted:', id);
282
+ // Remove node from your state management
283
+ // In Vue: nodes.value = nodes.value.filter(n => n.id !== id);
284
+ // In React: setNodes(nodes.filter(n => n.id !== id));
285
+ }
286
+ });
287
+ ```
288
+
256
289
  #### `destroy(options)`
257
290
 
258
291
  Destroy the connector and clean up all resources.
@@ -370,6 +403,104 @@ Update all connection line positions (useful when container size changes or afte
370
403
  connector.updateAllConnections(); // Refresh all connection lines
371
404
  ```
372
405
 
406
+ #### `export()`
407
+
408
+ Export the current topology (nodes, connections, and view state) as a JSON object.
409
+
410
+ **Returns:** ExportData object containing:
411
+ - `nodes`: Array of node data (id, x, y, info, dotPositions)
412
+ - `connections`: Array of connection data (from, to, fromDot, toDot)
413
+ - `viewState`: Current view state (scale, translateX, translateY)
414
+
415
+ **Example:**
416
+
417
+ ```javascript
418
+ const data = connector.export();
419
+ console.log(data);
420
+ // {
421
+ // nodes: [
422
+ // { id: 'node1', x: 100, y: 100, info: {...}, dotPositions: ['right'] },
423
+ // { id: 'node2', x: 400, y: 100, info: {...}, dotPositions: ['left'] }
424
+ // ],
425
+ // connections: [
426
+ // { from: 'node1', to: 'node2', fromDot: 'right', toDot: 'left' }
427
+ // ],
428
+ // viewState: { scale: 1, translateX: 0, translateY: 0 }
429
+ // }
430
+
431
+ // Save to localStorage
432
+ localStorage.setItem('topology', JSON.stringify(data));
433
+
434
+ // Or send to server
435
+ await fetch('/api/topology', {
436
+ method: 'POST',
437
+ body: JSON.stringify(data)
438
+ });
439
+ ```
440
+
441
+ #### `import(data, nodeFactory?)`
442
+
443
+ Restore topology from exported data. Supports two modes:
444
+
445
+ **Parameters:**
446
+
447
+ - `data` (ExportData): Topology data returned by `export()`
448
+ - `nodeFactory` (Function, optional): Factory function for creating DOM elements (native JS mode only)
449
+
450
+ **Two Usage Modes:**
451
+
452
+ 1. **Framework Mode (Vue/React/etc.) - No nodeFactory**
453
+ - Framework handles DOM rendering
454
+ - Library finds elements by `id` attribute
455
+ - Use when nodes are managed by framework reactivity
456
+
457
+ 2. **Native JS Mode - With nodeFactory**
458
+ - Library calls factory to create DOM elements
459
+ - Library handles positioning and mounting
460
+ - Use for pure JavaScript applications
461
+
462
+ **Returns:** Promise<void>
463
+
464
+ **Example (Framework Mode - Vue):**
465
+
466
+ ```javascript
467
+ // Save
468
+ const data = connector.export();
469
+ localStorage.setItem('topology', JSON.stringify(data));
470
+
471
+ // Load
472
+ const savedData = JSON.parse(localStorage.getItem('topology'));
473
+ // 1. Update framework reactive state (triggers DOM rendering)
474
+ nodes.value = savedData.nodes;
475
+ // 2. Wait for DOM to be ready
476
+ await nextTick();
477
+ // 3. Import (library finds elements by id and restores connections)
478
+ await connector.import(savedData);
479
+ ```
480
+
481
+ **Example (Native JS Mode):**
482
+
483
+ ```javascript
484
+ // Save
485
+ const data = connector.export();
486
+ localStorage.setItem('topology', JSON.stringify(data));
487
+
488
+ // Load
489
+ const savedData = JSON.parse(localStorage.getItem('topology'));
490
+ await connector.import(savedData, (nodeData) => {
491
+ // Factory function: create and return DOM element
492
+ const el = document.createElement('div');
493
+ el.id = nodeData.id;
494
+ el.className = 'node';
495
+ el.textContent = nodeData.info?.name || nodeData.id;
496
+ el.style.position = 'absolute';
497
+ // Library will set left/top automatically
498
+ return el;
499
+ });
500
+ ```
501
+
502
+ **Note:** Connections are restored silently (without triggering `onConnect` callbacks) to avoid duplicate events during data restoration.
503
+
373
504
 
374
505
  ## 🎨 Usage Examples
375
506
 
@@ -749,6 +880,65 @@ const connector = new Connector({
749
880
 
750
881
  // Save view state to restore later
751
882
  saveViewState(viewState);
883
+ },
884
+
885
+ onNodeMove: ({ id, x, y }) => {
886
+ console.log("Node moved:", id, "to", x, y);
887
+ // Synchronize node position with your state management
888
+ // This prevents nodes from jumping back to original position after dragging
889
+ // In Vue: const node = nodes.value.find(n => n.id === id); if (node) { node.x = x; node.y = y; }
890
+ // In React: setNodes(nodes.map(n => n.id === id ? { ...n, x, y } : n));
891
+ },
892
+
893
+ onNodeSelect: (info) => {
894
+ if (info) {
895
+ console.log("Node selected:", info.id, info.info);
896
+ } else {
897
+ console.log("Node deselected");
898
+ }
899
+ },
900
+
901
+ onNodeDelete: ({ id, info }) => {
902
+ console.log("Node deleted:", id);
903
+ // Remove node from your state management
904
+ // In Vue: nodes.value = nodes.value.filter(n => n.id !== id);
905
+ // In React: setNodes(nodes.filter(n => n.id !== id));
906
+ }
907
+ });
908
+ ```
909
+
910
+ ### Node Management
911
+
912
+ #### Node Position Synchronization
913
+
914
+ When using frameworks like Vue or React, you need to synchronize node positions after dragging to prevent nodes from jumping back to their original position. Use the `onNodeMove` callback:
915
+
916
+ **Vue Example:**
917
+
918
+ ```javascript
919
+ const connector = new Connector({
920
+ container: containerRef.value,
921
+ onNodeMove: ({ id, x, y }) => {
922
+ // Update Vue reactive state
923
+ const node = nodes.value.find(n => n.id === id);
924
+ if (node) {
925
+ node.x = x;
926
+ node.y = y;
927
+ }
928
+ }
929
+ });
930
+ ```
931
+
932
+ **React Example:**
933
+
934
+ ```javascript
935
+ const connector = new Connector({
936
+ container: containerRef.current,
937
+ onNodeMove: ({ id, x, y }) => {
938
+ // Update React state
939
+ setNodes(prevNodes =>
940
+ prevNodes.map(n => n.id === id ? { ...n, x, y } : n)
941
+ );
752
942
  }
753
943
  });
754
944
  ```
@@ -780,6 +970,101 @@ connector.resetView(); // Reset to default (scale: 1, translateX: 0, translateY:
780
970
  connector.updateAllConnections();
781
971
  ```
782
972
 
973
+ ### Save and Restore Topology
974
+
975
+ The `export()` and `import()` methods allow you to save and restore the entire topology (nodes, connections, and view state).
976
+
977
+ **Save Topology:**
978
+
979
+ ```javascript
980
+ // Export current topology
981
+ const data = connector.export();
982
+
983
+ // Save to localStorage
984
+ localStorage.setItem('topology', JSON.stringify(data));
985
+
986
+ // Or save to server
987
+ await fetch('/api/topology', {
988
+ method: 'POST',
989
+ headers: { 'Content-Type': 'application/json' },
990
+ body: JSON.stringify(data)
991
+ });
992
+ ```
993
+
994
+ **Restore Topology (Framework Mode - Vue Example):**
995
+
996
+ ```vue
997
+ <script setup>
998
+ import { ref, onMounted, nextTick } from 'vue';
999
+ import Connector from 'power-link';
1000
+
1001
+ const containerRef = ref(null);
1002
+ const nodes = ref([]);
1003
+ let connector = null;
1004
+
1005
+ const saveTopology = () => {
1006
+ const data = connector.export();
1007
+ // Merge custom fields (label, type, etc.) if needed
1008
+ data.nodes = data.nodes.map(exportNode => {
1009
+ const origin = nodes.value.find(n => n.id === exportNode.id);
1010
+ return { ...exportNode, label: origin?.label, type: origin?.type };
1011
+ });
1012
+ localStorage.setItem('topology', JSON.stringify(data));
1013
+ };
1014
+
1015
+ const loadTopology = async () => {
1016
+ const saved = localStorage.getItem('topology');
1017
+ if (saved) {
1018
+ const data = JSON.parse(saved);
1019
+ // 1. Update reactive state (triggers framework rendering)
1020
+ nodes.value = data.nodes;
1021
+ // 2. Wait for DOM to be ready
1022
+ await nextTick();
1023
+ // 3. Import (library finds elements by id and restores connections + view state)
1024
+ await connector.import(data);
1025
+ }
1026
+ };
1027
+
1028
+ onMounted(() => {
1029
+ connector = new Connector({ container: containerRef.value });
1030
+ loadTopology();
1031
+ });
1032
+ </script>
1033
+ ```
1034
+
1035
+ **Restore Topology (Native JS Mode):**
1036
+
1037
+ ```javascript
1038
+ const loadTopology = async () => {
1039
+ const saved = localStorage.getItem('topology');
1040
+ if (saved) {
1041
+ const data = JSON.parse(saved);
1042
+ // Import with factory function
1043
+ await connector.import(data, (nodeData) => {
1044
+ const el = document.createElement('div');
1045
+ el.id = nodeData.id;
1046
+ el.className = 'node';
1047
+ el.textContent = nodeData.info?.name || nodeData.id;
1048
+ // Library will set position automatically
1049
+ return el;
1050
+ });
1051
+ }
1052
+ };
1053
+ ```
1054
+
1055
+ **What's Included in Export:**
1056
+
1057
+ - **Nodes**: ID, position (x, y), custom info, dot positions
1058
+ - **Connections**: Source/target nodes, dot positions
1059
+ - **View State**: Current zoom level and pan position (scale, translateX, translateY)
1060
+
1061
+ **Benefits:**
1062
+
1063
+ - ✅ Save/load entire graph state
1064
+ - ✅ Restore view position and zoom level
1065
+ - ✅ Preserve all node positions and connections
1066
+ - ✅ Works with any storage backend (localStorage, database, etc.)
1067
+
783
1068
  ## 🔧 Browser Support
784
1069
 
785
1070
  - Chrome (latest)
package/dist/index.d.mts CHANGED
@@ -28,6 +28,10 @@ interface ConnectorNode {
28
28
  dots: Partial<Record<DotPosition, Dot>>;
29
29
  dotPositions: DotPosition[];
30
30
  connections: Connection[];
31
+ /** 节点相对于 contentWrapper 的 x 坐标(由库内部维护) */
32
+ x: number;
33
+ /** 节点相对于 contentWrapper 的 y 坐标(由库内部维护) */
34
+ y: number;
31
35
  }
32
36
  /** 连接对象 */
33
37
  interface Connection {
@@ -38,7 +42,7 @@ interface Connection {
38
42
  toDot: Dot;
39
43
  line: SVGPathElement;
40
44
  hoverPath?: SVGPathElement;
41
- deleteButton: HTMLDivElement;
45
+ deleteButton: SVGGElement;
42
46
  }
43
47
  /** 连接信息(回调 / API 返回用) */
44
48
  interface ConnectionInfo {
@@ -66,6 +70,24 @@ interface ConnectorConfig {
66
70
  minZoom: number;
67
71
  maxZoom: number;
68
72
  zoomStep: number;
73
+ /** 节点选中时的高亮颜色 */
74
+ selectedBorderColor: string;
75
+ }
76
+ /** 节点移动信息(拖拽回调用) */
77
+ interface NodeMoveInfo {
78
+ id: string;
79
+ x: number;
80
+ y: number;
81
+ }
82
+ /** 节点选中信息 */
83
+ interface NodeSelectInfo {
84
+ id: string;
85
+ info?: Record<string, unknown>;
86
+ }
87
+ /** 节点删除信息 */
88
+ interface NodeDeleteInfo {
89
+ id: string;
90
+ info?: Record<string, unknown>;
69
91
  }
70
92
  /** 构造函数选项 */
71
93
  interface ConnectorOptions extends Partial<ConnectorConfig> {
@@ -73,17 +95,50 @@ interface ConnectorOptions extends Partial<ConnectorConfig> {
73
95
  onConnect?: (info: ConnectionInfo) => void;
74
96
  onDisconnect?: (info: ConnectionInfo) => void;
75
97
  onViewChange?: (state: ViewState) => void;
98
+ onNodeMove?: (info: NodeMoveInfo) => void;
99
+ /** 节点被选中/取消选中时触发,取消选中时 info 为 null */
100
+ onNodeSelect?: (info: NodeSelectInfo | null) => void;
101
+ /** 节点被删除时触发 */
102
+ onNodeDelete?: (info: NodeDeleteInfo) => void;
76
103
  config?: Partial<ConnectorConfig>;
77
104
  }
78
105
  /** 节点注册选项 */
79
106
  interface RegisterNodeOptions {
80
107
  info?: Record<string, unknown>;
81
- dotPositions?: 'both' | DotPosition[];
108
+ dotPositions?: 'both' | DotPosition | DotPosition[];
82
109
  }
83
110
  /** 静默操作选项 */
84
111
  interface SilentOptions {
85
112
  silent?: boolean;
86
113
  }
114
+ /** 导出的单个节点数据 */
115
+ interface ExportNodeData {
116
+ id: string;
117
+ x: number;
118
+ y: number;
119
+ info?: Record<string, unknown>;
120
+ dotPositions: DotPosition[];
121
+ }
122
+ /** 导出的单条连接数据 */
123
+ interface ExportConnectionData {
124
+ from: string;
125
+ to: string;
126
+ fromDot: DotPosition;
127
+ toDot: DotPosition;
128
+ }
129
+ /** export() 返回的完整拓扑数据 */
130
+ interface ExportData {
131
+ nodes: ExportNodeData[];
132
+ connections: ExportConnectionData[];
133
+ /** 视图状态(缩放、平移),可选 */
134
+ viewState?: ViewState;
135
+ }
136
+ /**
137
+ * 节点工厂函数(用于 import() 的原生 JS 场景)
138
+ * 接收节点数据,返回对应的 HTMLElement(或 Promise)
139
+ * 库会负责定位和挂载,工厂只需构建元素结构
140
+ */
141
+ type NodeFactory = (nodeData: ExportNodeData) => HTMLElement | Promise<HTMLElement>;
87
142
 
88
143
  /**
89
144
  * @fileoverview 连线器主类
@@ -113,6 +168,24 @@ declare class Connector {
113
168
  * 更新节点位置(当节点移动时调用)
114
169
  */
115
170
  updateNodePosition(nodeId: string): void;
171
+ /**
172
+ * 选中指定节点
173
+ * @param id - 节点 ID
174
+ */
175
+ selectNode(id: string): void;
176
+ /**
177
+ * 取消选中当前节点
178
+ */
179
+ deselectNode(): void;
180
+ /**
181
+ * 获取当前选中的节点信息
182
+ */
183
+ getSelectedNode(): ConnectorNode | null;
184
+ /**
185
+ * 删除指定节点(移除其连接与触点,触发 onNodeDelete 回调)
186
+ * @param id - 节点 ID
187
+ */
188
+ deleteNode(id: string): void;
116
189
  /**
117
190
  * 创建连接(编程式)
118
191
  */
@@ -135,6 +208,41 @@ declare class Connector {
135
208
  * 更新所有连接线位置
136
209
  */
137
210
  updateAllConnections(): void;
211
+ /**
212
+ * 导出当前拓扑快照
213
+ * 返回所有节点(含坐标)、连接关系和视图状态的标准 JSON,可直接持久化
214
+ */
215
+ export(): ExportData;
216
+ /**
217
+ * 从拓扑快照恢复节点与连接
218
+ *
219
+ * **两种使用模式:**
220
+ *
221
+ * 1. **框架模式(Vue / React 等)—— 不传 nodeFactory**
222
+ * 调用方负责将节点渲染到 DOM(使用框架响应式),等待渲染完成后再调用
223
+ * `import(data)`,库会在 contentWrapper 内按 `id` 属性查找已存在的元素,
224
+ * 完成注册并还原连接。
225
+ * ```js
226
+ * nodes.value = data.nodes; // 触发框架渲染
227
+ * await nextTick(); // 等待 DOM 就绪
228
+ * await connector.import(data); // 注册 + 连线
229
+ * ```
230
+ *
231
+ * 2. **原生 JS 模式 —— 传入 nodeFactory**
232
+ * 库调用工厂函数创建 DOM 元素,自动定位、挂载、注册,最后还原连接。
233
+ * ```js
234
+ * await connector.import(data, (nodeData) => {
235
+ * const el = document.createElement('div');
236
+ * el.className = 'node';
237
+ * el.textContent = nodeData.info?.name ?? nodeData.id;
238
+ * return el;
239
+ * });
240
+ * ```
241
+ *
242
+ * @param data 由 `export()` 返回的拓扑数据
243
+ * @param nodeFactory 可选,原生 JS 场景下的节点元素工厂
244
+ */
245
+ import(data: ExportData, nodeFactory?: NodeFactory): Promise<void>;
138
246
  /**
139
247
  * 设置缩放比例(以画布中心为基准)
140
248
  */
@@ -175,4 +283,4 @@ declare class Connector {
175
283
  * @description 导出 Connector 类及相关类型定义
176
284
  */
177
285
 
178
- export { type Connection, type ConnectionInfo, Connector, type ConnectorConfig, type ConnectorNode, type ConnectorOptions, type Dot, type DotPosition, type Point, type RegisterNodeOptions, type SilentOptions, type ViewState, Connector as default };
286
+ export { type Connection, type ConnectionInfo, Connector, type ConnectorConfig, type ConnectorNode, type ConnectorOptions, type Dot, type DotPosition, type ExportConnectionData, type ExportData, type ExportNodeData, type NodeDeleteInfo, type NodeFactory, type NodeMoveInfo, type NodeSelectInfo, type Point, type RegisterNodeOptions, type SilentOptions, type ViewState, Connector as default };
package/dist/index.d.ts CHANGED
@@ -28,6 +28,10 @@ interface ConnectorNode {
28
28
  dots: Partial<Record<DotPosition, Dot>>;
29
29
  dotPositions: DotPosition[];
30
30
  connections: Connection[];
31
+ /** 节点相对于 contentWrapper 的 x 坐标(由库内部维护) */
32
+ x: number;
33
+ /** 节点相对于 contentWrapper 的 y 坐标(由库内部维护) */
34
+ y: number;
31
35
  }
32
36
  /** 连接对象 */
33
37
  interface Connection {
@@ -38,7 +42,7 @@ interface Connection {
38
42
  toDot: Dot;
39
43
  line: SVGPathElement;
40
44
  hoverPath?: SVGPathElement;
41
- deleteButton: HTMLDivElement;
45
+ deleteButton: SVGGElement;
42
46
  }
43
47
  /** 连接信息(回调 / API 返回用) */
44
48
  interface ConnectionInfo {
@@ -66,6 +70,24 @@ interface ConnectorConfig {
66
70
  minZoom: number;
67
71
  maxZoom: number;
68
72
  zoomStep: number;
73
+ /** 节点选中时的高亮颜色 */
74
+ selectedBorderColor: string;
75
+ }
76
+ /** 节点移动信息(拖拽回调用) */
77
+ interface NodeMoveInfo {
78
+ id: string;
79
+ x: number;
80
+ y: number;
81
+ }
82
+ /** 节点选中信息 */
83
+ interface NodeSelectInfo {
84
+ id: string;
85
+ info?: Record<string, unknown>;
86
+ }
87
+ /** 节点删除信息 */
88
+ interface NodeDeleteInfo {
89
+ id: string;
90
+ info?: Record<string, unknown>;
69
91
  }
70
92
  /** 构造函数选项 */
71
93
  interface ConnectorOptions extends Partial<ConnectorConfig> {
@@ -73,17 +95,50 @@ interface ConnectorOptions extends Partial<ConnectorConfig> {
73
95
  onConnect?: (info: ConnectionInfo) => void;
74
96
  onDisconnect?: (info: ConnectionInfo) => void;
75
97
  onViewChange?: (state: ViewState) => void;
98
+ onNodeMove?: (info: NodeMoveInfo) => void;
99
+ /** 节点被选中/取消选中时触发,取消选中时 info 为 null */
100
+ onNodeSelect?: (info: NodeSelectInfo | null) => void;
101
+ /** 节点被删除时触发 */
102
+ onNodeDelete?: (info: NodeDeleteInfo) => void;
76
103
  config?: Partial<ConnectorConfig>;
77
104
  }
78
105
  /** 节点注册选项 */
79
106
  interface RegisterNodeOptions {
80
107
  info?: Record<string, unknown>;
81
- dotPositions?: 'both' | DotPosition[];
108
+ dotPositions?: 'both' | DotPosition | DotPosition[];
82
109
  }
83
110
  /** 静默操作选项 */
84
111
  interface SilentOptions {
85
112
  silent?: boolean;
86
113
  }
114
+ /** 导出的单个节点数据 */
115
+ interface ExportNodeData {
116
+ id: string;
117
+ x: number;
118
+ y: number;
119
+ info?: Record<string, unknown>;
120
+ dotPositions: DotPosition[];
121
+ }
122
+ /** 导出的单条连接数据 */
123
+ interface ExportConnectionData {
124
+ from: string;
125
+ to: string;
126
+ fromDot: DotPosition;
127
+ toDot: DotPosition;
128
+ }
129
+ /** export() 返回的完整拓扑数据 */
130
+ interface ExportData {
131
+ nodes: ExportNodeData[];
132
+ connections: ExportConnectionData[];
133
+ /** 视图状态(缩放、平移),可选 */
134
+ viewState?: ViewState;
135
+ }
136
+ /**
137
+ * 节点工厂函数(用于 import() 的原生 JS 场景)
138
+ * 接收节点数据,返回对应的 HTMLElement(或 Promise)
139
+ * 库会负责定位和挂载,工厂只需构建元素结构
140
+ */
141
+ type NodeFactory = (nodeData: ExportNodeData) => HTMLElement | Promise<HTMLElement>;
87
142
 
88
143
  /**
89
144
  * @fileoverview 连线器主类
@@ -113,6 +168,24 @@ declare class Connector {
113
168
  * 更新节点位置(当节点移动时调用)
114
169
  */
115
170
  updateNodePosition(nodeId: string): void;
171
+ /**
172
+ * 选中指定节点
173
+ * @param id - 节点 ID
174
+ */
175
+ selectNode(id: string): void;
176
+ /**
177
+ * 取消选中当前节点
178
+ */
179
+ deselectNode(): void;
180
+ /**
181
+ * 获取当前选中的节点信息
182
+ */
183
+ getSelectedNode(): ConnectorNode | null;
184
+ /**
185
+ * 删除指定节点(移除其连接与触点,触发 onNodeDelete 回调)
186
+ * @param id - 节点 ID
187
+ */
188
+ deleteNode(id: string): void;
116
189
  /**
117
190
  * 创建连接(编程式)
118
191
  */
@@ -135,6 +208,41 @@ declare class Connector {
135
208
  * 更新所有连接线位置
136
209
  */
137
210
  updateAllConnections(): void;
211
+ /**
212
+ * 导出当前拓扑快照
213
+ * 返回所有节点(含坐标)、连接关系和视图状态的标准 JSON,可直接持久化
214
+ */
215
+ export(): ExportData;
216
+ /**
217
+ * 从拓扑快照恢复节点与连接
218
+ *
219
+ * **两种使用模式:**
220
+ *
221
+ * 1. **框架模式(Vue / React 等)—— 不传 nodeFactory**
222
+ * 调用方负责将节点渲染到 DOM(使用框架响应式),等待渲染完成后再调用
223
+ * `import(data)`,库会在 contentWrapper 内按 `id` 属性查找已存在的元素,
224
+ * 完成注册并还原连接。
225
+ * ```js
226
+ * nodes.value = data.nodes; // 触发框架渲染
227
+ * await nextTick(); // 等待 DOM 就绪
228
+ * await connector.import(data); // 注册 + 连线
229
+ * ```
230
+ *
231
+ * 2. **原生 JS 模式 —— 传入 nodeFactory**
232
+ * 库调用工厂函数创建 DOM 元素,自动定位、挂载、注册,最后还原连接。
233
+ * ```js
234
+ * await connector.import(data, (nodeData) => {
235
+ * const el = document.createElement('div');
236
+ * el.className = 'node';
237
+ * el.textContent = nodeData.info?.name ?? nodeData.id;
238
+ * return el;
239
+ * });
240
+ * ```
241
+ *
242
+ * @param data 由 `export()` 返回的拓扑数据
243
+ * @param nodeFactory 可选,原生 JS 场景下的节点元素工厂
244
+ */
245
+ import(data: ExportData, nodeFactory?: NodeFactory): Promise<void>;
138
246
  /**
139
247
  * 设置缩放比例(以画布中心为基准)
140
248
  */
@@ -175,4 +283,4 @@ declare class Connector {
175
283
  * @description 导出 Connector 类及相关类型定义
176
284
  */
177
285
 
178
- export { type Connection, type ConnectionInfo, Connector, type ConnectorConfig, type ConnectorNode, type ConnectorOptions, type Dot, type DotPosition, type Point, type RegisterNodeOptions, type SilentOptions, type ViewState, Connector as default };
286
+ export { type Connection, type ConnectionInfo, Connector, type ConnectorConfig, type ConnectorNode, type ConnectorOptions, type Dot, type DotPosition, type ExportConnectionData, type ExportData, type ExportNodeData, type NodeDeleteInfo, type NodeFactory, type NodeMoveInfo, type NodeSelectInfo, type Point, type RegisterNodeOptions, type SilentOptions, type ViewState, Connector as default };