rendx-graph-plugin 0.1.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/LICENSE +21 -0
- package/dist/main.cjs +462 -0
- package/dist/main.d.cts +174 -0
- package/dist/main.d.ts +174 -0
- package/dist/main.js +432 -0
- package/package.json +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-present wei.liang (https://github.com/weiliang0121)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/main.cjs
ADDED
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __typeError = (msg) => {
|
|
7
|
+
throw TypeError(msg);
|
|
8
|
+
};
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
22
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
23
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
24
|
+
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
25
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
26
|
+
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
27
|
+
|
|
28
|
+
// src/main.ts
|
|
29
|
+
var main_exports = {};
|
|
30
|
+
__export(main_exports, {
|
|
31
|
+
ElementImpl: () => ElementImpl,
|
|
32
|
+
GraphPlugin: () => GraphPlugin,
|
|
33
|
+
createEdge: () => createEdge,
|
|
34
|
+
createNode: () => createNode,
|
|
35
|
+
graphPlugin: () => graphPlugin
|
|
36
|
+
});
|
|
37
|
+
module.exports = __toCommonJS(main_exports);
|
|
38
|
+
|
|
39
|
+
// src/create.ts
|
|
40
|
+
function createNode(render) {
|
|
41
|
+
return {
|
|
42
|
+
__element_def__: true,
|
|
43
|
+
role: "node",
|
|
44
|
+
render
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function createEdge(render) {
|
|
48
|
+
return {
|
|
49
|
+
__element_def__: true,
|
|
50
|
+
role: "edge",
|
|
51
|
+
render
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// src/element.ts
|
|
56
|
+
var import_rendx_engine = require("rendx-engine");
|
|
57
|
+
var _data, _def, _graph, _cleanup, _mounted, _onUpdate, _ElementImpl_instances, render_fn, teardown_fn;
|
|
58
|
+
var ElementImpl = class {
|
|
59
|
+
constructor(id, typeName, def, data, graph, layer, deps, onUpdate) {
|
|
60
|
+
__privateAdd(this, _ElementImpl_instances);
|
|
61
|
+
__privateAdd(this, _data);
|
|
62
|
+
__privateAdd(this, _def);
|
|
63
|
+
__privateAdd(this, _graph);
|
|
64
|
+
__privateAdd(this, _cleanup, null);
|
|
65
|
+
__privateAdd(this, _mounted, false);
|
|
66
|
+
__privateAdd(this, _onUpdate);
|
|
67
|
+
this.id = id;
|
|
68
|
+
this.typeName = typeName;
|
|
69
|
+
this.role = def.role;
|
|
70
|
+
__privateSet(this, _def, def);
|
|
71
|
+
__privateSet(this, _data, { ...data });
|
|
72
|
+
__privateSet(this, _graph, graph);
|
|
73
|
+
this.layer = layer;
|
|
74
|
+
this.deps = deps;
|
|
75
|
+
__privateSet(this, _onUpdate, onUpdate ?? null);
|
|
76
|
+
this.group = new import_rendx_engine.Group();
|
|
77
|
+
this.group.setName(id);
|
|
78
|
+
if (def.role === "node") {
|
|
79
|
+
const nd = __privateGet(this, _data);
|
|
80
|
+
this.group.translate(nd.x, nd.y);
|
|
81
|
+
}
|
|
82
|
+
__privateMethod(this, _ElementImpl_instances, render_fn).call(this);
|
|
83
|
+
__privateSet(this, _mounted, true);
|
|
84
|
+
}
|
|
85
|
+
get data() {
|
|
86
|
+
return __privateGet(this, _data);
|
|
87
|
+
}
|
|
88
|
+
get mounted() {
|
|
89
|
+
return __privateGet(this, _mounted);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* 部分更新数据。
|
|
93
|
+
* - id 不可变
|
|
94
|
+
* - Node: 仅 x/y 变化 → translate only(不重建子树)
|
|
95
|
+
* - Edge: source/target 变化时忽略(deps 已静态绑定)
|
|
96
|
+
*/
|
|
97
|
+
update(patch) {
|
|
98
|
+
var _a, _b;
|
|
99
|
+
if ("id" in patch) {
|
|
100
|
+
delete patch.id;
|
|
101
|
+
}
|
|
102
|
+
const merged = { ...__privateGet(this, _data), ...patch };
|
|
103
|
+
if (this.role === "node") {
|
|
104
|
+
const oldData = __privateGet(this, _data);
|
|
105
|
+
const newData = merged;
|
|
106
|
+
const positionOnly = isPositionOnlyChange(oldData, newData, patch);
|
|
107
|
+
__privateSet(this, _data, merged);
|
|
108
|
+
if (positionOnly) {
|
|
109
|
+
this.group.translate(newData.x, newData.y);
|
|
110
|
+
(_a = __privateGet(this, _onUpdate)) == null ? void 0 : _a.call(this, this.id);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (newData.x !== oldData.x || newData.y !== oldData.y) {
|
|
114
|
+
this.group.translate(newData.x, newData.y);
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
__privateSet(this, _data, merged);
|
|
118
|
+
}
|
|
119
|
+
__privateMethod(this, _ElementImpl_instances, teardown_fn).call(this);
|
|
120
|
+
this.group.removeChildren();
|
|
121
|
+
__privateMethod(this, _ElementImpl_instances, render_fn).call(this);
|
|
122
|
+
(_b = __privateGet(this, _onUpdate)) == null ? void 0 : _b.call(this, this.id);
|
|
123
|
+
}
|
|
124
|
+
dispose() {
|
|
125
|
+
__privateMethod(this, _ElementImpl_instances, teardown_fn).call(this);
|
|
126
|
+
this.group.removeChildren();
|
|
127
|
+
__privateSet(this, _mounted, false);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
_data = new WeakMap();
|
|
131
|
+
_def = new WeakMap();
|
|
132
|
+
_graph = new WeakMap();
|
|
133
|
+
_cleanup = new WeakMap();
|
|
134
|
+
_mounted = new WeakMap();
|
|
135
|
+
_onUpdate = new WeakMap();
|
|
136
|
+
_ElementImpl_instances = new WeakSet();
|
|
137
|
+
// ── internal ──
|
|
138
|
+
render_fn = function() {
|
|
139
|
+
let cleanup = null;
|
|
140
|
+
const onCleanup = (fn) => {
|
|
141
|
+
cleanup = fn;
|
|
142
|
+
};
|
|
143
|
+
if (__privateGet(this, _def).role === "node") {
|
|
144
|
+
const nd = __privateGet(this, _data);
|
|
145
|
+
const ctx = {
|
|
146
|
+
group: this.group,
|
|
147
|
+
width: nd.width ?? 0,
|
|
148
|
+
height: nd.height ?? 0,
|
|
149
|
+
onCleanup
|
|
150
|
+
};
|
|
151
|
+
__privateGet(this, _def).render(ctx, nd, __privateGet(this, _graph));
|
|
152
|
+
} else {
|
|
153
|
+
const ed = __privateGet(this, _data);
|
|
154
|
+
const ctx = {
|
|
155
|
+
group: this.group,
|
|
156
|
+
source: __privateGet(this, _graph).get(ed.source),
|
|
157
|
+
target: __privateGet(this, _graph).get(ed.target),
|
|
158
|
+
onCleanup
|
|
159
|
+
};
|
|
160
|
+
__privateGet(this, _def).render(ctx, ed, __privateGet(this, _graph));
|
|
161
|
+
}
|
|
162
|
+
__privateSet(this, _cleanup, cleanup);
|
|
163
|
+
};
|
|
164
|
+
teardown_fn = function() {
|
|
165
|
+
if (__privateGet(this, _cleanup)) {
|
|
166
|
+
__privateGet(this, _cleanup).call(this);
|
|
167
|
+
__privateSet(this, _cleanup, null);
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
function isPositionOnlyChange(oldData, newData, patch) {
|
|
171
|
+
const keys = Object.keys(patch);
|
|
172
|
+
if (keys.length === 0) return false;
|
|
173
|
+
for (const key of keys) {
|
|
174
|
+
if (key === "x" || key === "y") continue;
|
|
175
|
+
if (oldData[key] !== newData[key]) {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return oldData.x !== newData.x || oldData.y !== newData.y;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// src/graph.ts
|
|
183
|
+
var import_rendx_engine2 = require("rendx-engine");
|
|
184
|
+
var _app, _types, _elements, _dependents, _batching, _batchAdded, _batchRemoved, _edgesGroup, _nodesGroup, _GraphPlugin_instances, triggerDependents_fn, syncState_fn;
|
|
185
|
+
var GraphPlugin = class {
|
|
186
|
+
constructor() {
|
|
187
|
+
__privateAdd(this, _GraphPlugin_instances);
|
|
188
|
+
this.name = "graph";
|
|
189
|
+
this.state = [
|
|
190
|
+
{
|
|
191
|
+
key: "graph:elements",
|
|
192
|
+
description: "All element IDs in the graph",
|
|
193
|
+
initial: []
|
|
194
|
+
}
|
|
195
|
+
];
|
|
196
|
+
__privateAdd(this, _app);
|
|
197
|
+
__privateAdd(this, _types, /* @__PURE__ */ new Map());
|
|
198
|
+
__privateAdd(this, _elements, /* @__PURE__ */ new Map());
|
|
199
|
+
/** node → 依赖它的元素 id 列表 */
|
|
200
|
+
__privateAdd(this, _dependents, /* @__PURE__ */ new Map());
|
|
201
|
+
/** 批量操作标志 */
|
|
202
|
+
__privateAdd(this, _batching, false);
|
|
203
|
+
__privateAdd(this, _batchAdded, false);
|
|
204
|
+
__privateAdd(this, _batchRemoved, false);
|
|
205
|
+
/** 场景分组 */
|
|
206
|
+
__privateAdd(this, _edgesGroup);
|
|
207
|
+
__privateAdd(this, _nodesGroup);
|
|
208
|
+
}
|
|
209
|
+
get edgesGroup() {
|
|
210
|
+
return __privateGet(this, _edgesGroup);
|
|
211
|
+
}
|
|
212
|
+
get nodesGroup() {
|
|
213
|
+
return __privateGet(this, _nodesGroup);
|
|
214
|
+
}
|
|
215
|
+
// ── Plugin lifecycle ──
|
|
216
|
+
install(app) {
|
|
217
|
+
__privateSet(this, _app, app);
|
|
218
|
+
__privateSet(this, _edgesGroup, new import_rendx_engine2.Group());
|
|
219
|
+
__privateGet(this, _edgesGroup).setName("__graph_edges__");
|
|
220
|
+
app.scene.add(__privateGet(this, _edgesGroup));
|
|
221
|
+
__privateSet(this, _nodesGroup, new import_rendx_engine2.Group());
|
|
222
|
+
__privateGet(this, _nodesGroup).setName("__graph_nodes__");
|
|
223
|
+
app.scene.add(__privateGet(this, _nodesGroup));
|
|
224
|
+
}
|
|
225
|
+
dispose() {
|
|
226
|
+
for (const el of __privateGet(this, _elements).values()) {
|
|
227
|
+
el.dispose();
|
|
228
|
+
}
|
|
229
|
+
__privateGet(this, _elements).clear();
|
|
230
|
+
__privateGet(this, _dependents).clear();
|
|
231
|
+
__privateGet(this, _types).clear();
|
|
232
|
+
}
|
|
233
|
+
// ── 序列化 / 反序列化 ──
|
|
234
|
+
/**
|
|
235
|
+
* 序列化所有元素实例的数据(供 history-plugin 等消费)。
|
|
236
|
+
* 类型定义(render fn)无法序列化,需要应用层在恢复时保证 register 已调用。
|
|
237
|
+
*/
|
|
238
|
+
serialize() {
|
|
239
|
+
const elements = [];
|
|
240
|
+
for (const el of __privateGet(this, _elements).values()) {
|
|
241
|
+
elements.push({
|
|
242
|
+
typeName: el.typeName,
|
|
243
|
+
data: { ...el.data },
|
|
244
|
+
layer: el.layer,
|
|
245
|
+
deps: [...el.deps]
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
return { elements };
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* 从序列化数据恢复所有元素。
|
|
252
|
+
*
|
|
253
|
+
* 流程:
|
|
254
|
+
* 1. 清理当前所有元素和依赖追踪
|
|
255
|
+
* 2. 从恢复后的场景中移除 restoreFromJSON 反序列化出的静态 Group(它们缺少 ElementImpl 追踪)
|
|
256
|
+
* 3. 重建 edges/nodes 分组
|
|
257
|
+
* 4. 使用已注册的类型定义重新创建所有元素(会重新执行 render fn)
|
|
258
|
+
*/
|
|
259
|
+
deserialize(data) {
|
|
260
|
+
const saved = data;
|
|
261
|
+
if (!saved.elements || !Array.isArray(saved.elements)) return;
|
|
262
|
+
for (const el of __privateGet(this, _elements).values()) {
|
|
263
|
+
el.dispose();
|
|
264
|
+
}
|
|
265
|
+
__privateGet(this, _elements).clear();
|
|
266
|
+
__privateGet(this, _dependents).clear();
|
|
267
|
+
const scene = __privateGet(this, _app).scene;
|
|
268
|
+
for (const layer of scene.layers) {
|
|
269
|
+
if (layer.isEventLayer) continue;
|
|
270
|
+
const toRemove = [];
|
|
271
|
+
for (const child of layer.children) {
|
|
272
|
+
if (child.name === "__graph_edges__" || child.name === "__graph_nodes__") {
|
|
273
|
+
toRemove.push(child);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
for (const g of toRemove) {
|
|
277
|
+
layer.remove(g);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
__privateSet(this, _edgesGroup, new import_rendx_engine2.Group());
|
|
281
|
+
__privateGet(this, _edgesGroup).setName("__graph_edges__");
|
|
282
|
+
scene.add(__privateGet(this, _edgesGroup));
|
|
283
|
+
__privateSet(this, _nodesGroup, new import_rendx_engine2.Group());
|
|
284
|
+
__privateGet(this, _nodesGroup).setName("__graph_nodes__");
|
|
285
|
+
scene.add(__privateGet(this, _nodesGroup));
|
|
286
|
+
const nodes = saved.elements.filter((e) => {
|
|
287
|
+
const def = __privateGet(this, _types).get(e.typeName);
|
|
288
|
+
return def?.role === "node";
|
|
289
|
+
});
|
|
290
|
+
const edges = saved.elements.filter((e) => {
|
|
291
|
+
const def = __privateGet(this, _types).get(e.typeName);
|
|
292
|
+
return def?.role === "edge";
|
|
293
|
+
});
|
|
294
|
+
this.batch(() => {
|
|
295
|
+
for (const elem of [...nodes, ...edges]) {
|
|
296
|
+
const def = __privateGet(this, _types).get(elem.typeName);
|
|
297
|
+
if (!def) {
|
|
298
|
+
console.warn(`[rendx-graph-plugin] Cannot restore element: type "${elem.typeName}" not registered`);
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
this.add(elem.typeName, elem.data, { layer: elem.layer, deps: elem.deps });
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
// ── 类型注册 ──
|
|
306
|
+
register(name, def) {
|
|
307
|
+
__privateGet(this, _types).set(name, def);
|
|
308
|
+
}
|
|
309
|
+
// ── CRUD ──
|
|
310
|
+
add(type, data, options) {
|
|
311
|
+
const def = __privateGet(this, _types).get(type);
|
|
312
|
+
if (!def) throw new Error(`Unknown element type: "${type}"`);
|
|
313
|
+
const id = data.id;
|
|
314
|
+
if (__privateGet(this, _elements).has(id)) throw new Error(`Element "${id}" already exists`);
|
|
315
|
+
let layer;
|
|
316
|
+
let deps;
|
|
317
|
+
if (def.role === "edge") {
|
|
318
|
+
layer = options?.layer ?? "edges";
|
|
319
|
+
const edgeData = data;
|
|
320
|
+
deps = options?.deps ?? [edgeData.source, edgeData.target];
|
|
321
|
+
} else {
|
|
322
|
+
layer = options?.layer ?? "nodes";
|
|
323
|
+
deps = options?.deps ?? [];
|
|
324
|
+
}
|
|
325
|
+
const el = new ElementImpl(id, type, def, data, this, layer, deps, (elId) => this.notifyUpdate(elId));
|
|
326
|
+
const targetGroup = layer === "edges" ? __privateGet(this, _edgesGroup) : __privateGet(this, _nodesGroup);
|
|
327
|
+
targetGroup.add(el.group);
|
|
328
|
+
__privateGet(this, _elements).set(id, el);
|
|
329
|
+
for (const depId of deps) {
|
|
330
|
+
if (!__privateGet(this, _dependents).has(depId)) {
|
|
331
|
+
__privateGet(this, _dependents).set(depId, /* @__PURE__ */ new Set());
|
|
332
|
+
}
|
|
333
|
+
__privateGet(this, _dependents).get(depId).add(id);
|
|
334
|
+
}
|
|
335
|
+
if (!__privateGet(this, _batching)) {
|
|
336
|
+
__privateMethod(this, _GraphPlugin_instances, syncState_fn).call(this);
|
|
337
|
+
__privateGet(this, _app).bus.emit("graph:added", el);
|
|
338
|
+
} else {
|
|
339
|
+
__privateSet(this, _batchAdded, true);
|
|
340
|
+
}
|
|
341
|
+
return el;
|
|
342
|
+
}
|
|
343
|
+
remove(id) {
|
|
344
|
+
const el = __privateGet(this, _elements).get(id);
|
|
345
|
+
if (!el) return false;
|
|
346
|
+
for (const depId of el.deps) {
|
|
347
|
+
const set = __privateGet(this, _dependents).get(depId);
|
|
348
|
+
if (set) {
|
|
349
|
+
set.delete(id);
|
|
350
|
+
if (set.size === 0) __privateGet(this, _dependents).delete(depId);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
const parent = el.group.parent;
|
|
354
|
+
if (parent) {
|
|
355
|
+
parent.remove(el.group);
|
|
356
|
+
}
|
|
357
|
+
el.dispose();
|
|
358
|
+
__privateGet(this, _elements).delete(id);
|
|
359
|
+
if (!__privateGet(this, _batching)) {
|
|
360
|
+
__privateMethod(this, _GraphPlugin_instances, syncState_fn).call(this);
|
|
361
|
+
__privateGet(this, _app).bus.emit("graph:removed", id);
|
|
362
|
+
} else {
|
|
363
|
+
__privateSet(this, _batchRemoved, true);
|
|
364
|
+
}
|
|
365
|
+
return true;
|
|
366
|
+
}
|
|
367
|
+
// ── GraphQuery 实现 ──
|
|
368
|
+
get(id) {
|
|
369
|
+
return __privateGet(this, _elements).get(id);
|
|
370
|
+
}
|
|
371
|
+
has(id) {
|
|
372
|
+
return __privateGet(this, _elements).has(id);
|
|
373
|
+
}
|
|
374
|
+
get count() {
|
|
375
|
+
return __privateGet(this, _elements).size;
|
|
376
|
+
}
|
|
377
|
+
getIds() {
|
|
378
|
+
return [...__privateGet(this, _elements).keys()];
|
|
379
|
+
}
|
|
380
|
+
getAll() {
|
|
381
|
+
return [...__privateGet(this, _elements).values()];
|
|
382
|
+
}
|
|
383
|
+
getNodes() {
|
|
384
|
+
return [...__privateGet(this, _elements).values()].filter((el) => el.role === "node");
|
|
385
|
+
}
|
|
386
|
+
getEdges() {
|
|
387
|
+
return [...__privateGet(this, _elements).values()].filter((el) => el.role === "edge");
|
|
388
|
+
}
|
|
389
|
+
getEdgesOf(nodeId) {
|
|
390
|
+
return [...__privateGet(this, _elements).values()].filter((el) => {
|
|
391
|
+
if (el.role !== "edge") return false;
|
|
392
|
+
const data = el.data;
|
|
393
|
+
return data.source === nodeId || data.target === nodeId;
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
// ── 批量操作 ──
|
|
397
|
+
batch(fn) {
|
|
398
|
+
__privateSet(this, _batching, true);
|
|
399
|
+
__privateSet(this, _batchAdded, false);
|
|
400
|
+
__privateSet(this, _batchRemoved, false);
|
|
401
|
+
try {
|
|
402
|
+
fn();
|
|
403
|
+
} finally {
|
|
404
|
+
__privateSet(this, _batching, false);
|
|
405
|
+
__privateMethod(this, _GraphPlugin_instances, syncState_fn).call(this);
|
|
406
|
+
if (__privateGet(this, _batchAdded)) {
|
|
407
|
+
__privateGet(this, _app).bus.emit("graph:added");
|
|
408
|
+
}
|
|
409
|
+
if (__privateGet(this, _batchRemoved)) {
|
|
410
|
+
__privateGet(this, _app).bus.emit("graph:removed");
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* 通知 graph 某个元素已被更新(由 ElementImpl.update 调用的外部入口)。
|
|
416
|
+
* 这通过在 add 时对 ElementImpl 进行 monkey-patch 来实现。
|
|
417
|
+
*/
|
|
418
|
+
notifyUpdate(id) {
|
|
419
|
+
__privateMethod(this, _GraphPlugin_instances, triggerDependents_fn).call(this, id);
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
_app = new WeakMap();
|
|
423
|
+
_types = new WeakMap();
|
|
424
|
+
_elements = new WeakMap();
|
|
425
|
+
_dependents = new WeakMap();
|
|
426
|
+
_batching = new WeakMap();
|
|
427
|
+
_batchAdded = new WeakMap();
|
|
428
|
+
_batchRemoved = new WeakMap();
|
|
429
|
+
_edgesGroup = new WeakMap();
|
|
430
|
+
_nodesGroup = new WeakMap();
|
|
431
|
+
_GraphPlugin_instances = new WeakSet();
|
|
432
|
+
/**
|
|
433
|
+
* 更新元素并触发依赖链。
|
|
434
|
+
* 当外部调用 el.update() 时,GraphPlugin 监听不到,所以通过此内部路由。
|
|
435
|
+
* 但当前设计中 el.update() 直接在 ElementImpl 中执行。
|
|
436
|
+
* 依赖触发通过 add() 时注册的追踪机制实现。
|
|
437
|
+
*/
|
|
438
|
+
triggerDependents_fn = function(id) {
|
|
439
|
+
const deps = __privateGet(this, _dependents).get(id);
|
|
440
|
+
if (!deps) return;
|
|
441
|
+
for (const depId of deps) {
|
|
442
|
+
const depEl = __privateGet(this, _elements).get(depId);
|
|
443
|
+
if (depEl) {
|
|
444
|
+
depEl.update({});
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
// ── internal ──
|
|
449
|
+
syncState_fn = function() {
|
|
450
|
+
__privateGet(this, _app).setState("graph:elements", this.getIds());
|
|
451
|
+
};
|
|
452
|
+
function graphPlugin() {
|
|
453
|
+
return new GraphPlugin();
|
|
454
|
+
}
|
|
455
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
456
|
+
0 && (module.exports = {
|
|
457
|
+
ElementImpl,
|
|
458
|
+
GraphPlugin,
|
|
459
|
+
createEdge,
|
|
460
|
+
createNode,
|
|
461
|
+
graphPlugin
|
|
462
|
+
});
|
package/dist/main.d.cts
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { Group, Plugin, App } from 'rendx-engine';
|
|
2
|
+
|
|
3
|
+
/** Node 基础字段 */
|
|
4
|
+
interface NodeBase {
|
|
5
|
+
id: string;
|
|
6
|
+
x: number;
|
|
7
|
+
y: number;
|
|
8
|
+
width?: number;
|
|
9
|
+
height?: number;
|
|
10
|
+
}
|
|
11
|
+
/** Edge 基础字段 */
|
|
12
|
+
interface EdgeBase {
|
|
13
|
+
id: string;
|
|
14
|
+
source: string;
|
|
15
|
+
target: string;
|
|
16
|
+
}
|
|
17
|
+
/** Node render fn 接收的上下文 */
|
|
18
|
+
interface NodeContext {
|
|
19
|
+
group: Group;
|
|
20
|
+
width: number;
|
|
21
|
+
height: number;
|
|
22
|
+
onCleanup: (fn: () => void) => void;
|
|
23
|
+
}
|
|
24
|
+
/** Edge render fn 接收的上下文 */
|
|
25
|
+
interface EdgeContext {
|
|
26
|
+
group: Group;
|
|
27
|
+
source: Element<NodeBase> | undefined;
|
|
28
|
+
target: Element<NodeBase> | undefined;
|
|
29
|
+
onCleanup: (fn: () => void) => void;
|
|
30
|
+
}
|
|
31
|
+
type NodeRenderFn<T extends NodeBase = NodeBase> = (ctx: NodeContext, data: T & NodeBase, graph: GraphQuery) => void;
|
|
32
|
+
type EdgeRenderFn<T extends EdgeBase = EdgeBase> = (ctx: EdgeContext, data: T & EdgeBase, graph: GraphQuery) => void;
|
|
33
|
+
interface NodeDef<T extends NodeBase = NodeBase> {
|
|
34
|
+
__element_def__: true;
|
|
35
|
+
role: 'node';
|
|
36
|
+
render: NodeRenderFn<T>;
|
|
37
|
+
}
|
|
38
|
+
interface EdgeDef<T extends EdgeBase = EdgeBase> {
|
|
39
|
+
__element_def__: true;
|
|
40
|
+
role: 'edge';
|
|
41
|
+
render: EdgeRenderFn<T>;
|
|
42
|
+
}
|
|
43
|
+
type ElementDef = NodeDef | EdgeDef;
|
|
44
|
+
interface Element<T = Record<string, unknown>> {
|
|
45
|
+
readonly id: string;
|
|
46
|
+
readonly role: 'node' | 'edge';
|
|
47
|
+
readonly group: Group;
|
|
48
|
+
readonly data: T & (NodeBase | EdgeBase);
|
|
49
|
+
readonly mounted: boolean;
|
|
50
|
+
readonly layer: string;
|
|
51
|
+
readonly deps: string[];
|
|
52
|
+
update(patch: Partial<T>): void;
|
|
53
|
+
dispose(): void;
|
|
54
|
+
}
|
|
55
|
+
interface GraphQuery {
|
|
56
|
+
get<T = Record<string, unknown>>(id: string): Element<T> | undefined;
|
|
57
|
+
has(id: string): boolean;
|
|
58
|
+
count: number;
|
|
59
|
+
getIds(): string[];
|
|
60
|
+
getAll(): Element[];
|
|
61
|
+
getNodes(): Element<NodeBase>[];
|
|
62
|
+
getEdges(): Element<EdgeBase>[];
|
|
63
|
+
getEdgesOf(nodeId: string): Element<EdgeBase>[];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 定义一个 Node 类型的元素。
|
|
68
|
+
*
|
|
69
|
+
* Node 是有位置 (x, y) 的实体,render fn 接收 NodeContext(group + width/height)。
|
|
70
|
+
* 框架会自动为 Node 设置 group.translate(x, y),仅位移变化时跳过子树重建。
|
|
71
|
+
*/
|
|
72
|
+
declare function createNode<T extends NodeBase = NodeBase>(render: NodeRenderFn<T>): NodeDef<T>;
|
|
73
|
+
/**
|
|
74
|
+
* 定义一个 Edge 类型的元素。
|
|
75
|
+
*
|
|
76
|
+
* Edge 连接两个 Node(source → target),render fn 接收 EdgeContext(group + source/target 实例)。
|
|
77
|
+
* 框架自动从 source/target 派生 deps 和 layer='edges'。
|
|
78
|
+
* Edge 的 group 不设置 translate — 由 render fn 自行计算坐标。
|
|
79
|
+
*/
|
|
80
|
+
declare function createEdge<T extends EdgeBase = EdgeBase>(render: EdgeRenderFn<T>): EdgeDef<T>;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* ElementImpl — 元素运行时实例。
|
|
84
|
+
*
|
|
85
|
+
* - Node: translate(x, y),仅位移变化时 skip rebuild
|
|
86
|
+
* - Edge: 不 translate,render 时自动注入 source/target
|
|
87
|
+
*/
|
|
88
|
+
declare class ElementImpl<T = Record<string, unknown>> implements Element<T> {
|
|
89
|
+
#private;
|
|
90
|
+
readonly id: string;
|
|
91
|
+
readonly role: 'node' | 'edge';
|
|
92
|
+
readonly group: Group;
|
|
93
|
+
readonly layer: string;
|
|
94
|
+
readonly deps: string[];
|
|
95
|
+
/** 创建时使用的类型名称(对应 register 的 name),用于序列化 */
|
|
96
|
+
readonly typeName: string;
|
|
97
|
+
constructor(id: string, typeName: string, def: ElementDef, data: T & (NodeBase | EdgeBase), graph: GraphQuery, layer: string, deps: string[], onUpdate?: (id: string) => void);
|
|
98
|
+
get data(): T & (NodeBase | EdgeBase);
|
|
99
|
+
get mounted(): boolean;
|
|
100
|
+
/**
|
|
101
|
+
* 部分更新数据。
|
|
102
|
+
* - id 不可变
|
|
103
|
+
* - Node: 仅 x/y 变化 → translate only(不重建子树)
|
|
104
|
+
* - Edge: source/target 变化时忽略(deps 已静态绑定)
|
|
105
|
+
*/
|
|
106
|
+
update(patch: Partial<T>): void;
|
|
107
|
+
dispose(): void;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* GraphPlugin — 元素生命周期管理器。
|
|
112
|
+
*
|
|
113
|
+
* 核心职责:
|
|
114
|
+
* 1. 注册元素类型定义 (register)
|
|
115
|
+
* 2. 增删查改元素实例 (add / remove / get / update)
|
|
116
|
+
* 3. 自动分层: Node → 'nodes' Group, Edge → 'edges' Group
|
|
117
|
+
* 4. 自动依赖: Edge 的 deps 从 source/target 自动派生
|
|
118
|
+
* 5. 依赖追踪: 被依赖元素更新时自动重绘依赖方
|
|
119
|
+
* 6. Graph 查询: getNodes / getEdges / getEdgesOf
|
|
120
|
+
*/
|
|
121
|
+
declare class GraphPlugin implements Plugin, GraphQuery {
|
|
122
|
+
#private;
|
|
123
|
+
readonly name = "graph";
|
|
124
|
+
readonly state: {
|
|
125
|
+
key: string;
|
|
126
|
+
description: string;
|
|
127
|
+
initial: string[];
|
|
128
|
+
}[];
|
|
129
|
+
get edgesGroup(): Group;
|
|
130
|
+
get nodesGroup(): Group;
|
|
131
|
+
install(app: App): void;
|
|
132
|
+
dispose(): void;
|
|
133
|
+
/**
|
|
134
|
+
* 序列化所有元素实例的数据(供 history-plugin 等消费)。
|
|
135
|
+
* 类型定义(render fn)无法序列化,需要应用层在恢复时保证 register 已调用。
|
|
136
|
+
*/
|
|
137
|
+
serialize(): Record<string, unknown>;
|
|
138
|
+
/**
|
|
139
|
+
* 从序列化数据恢复所有元素。
|
|
140
|
+
*
|
|
141
|
+
* 流程:
|
|
142
|
+
* 1. 清理当前所有元素和依赖追踪
|
|
143
|
+
* 2. 从恢复后的场景中移除 restoreFromJSON 反序列化出的静态 Group(它们缺少 ElementImpl 追踪)
|
|
144
|
+
* 3. 重建 edges/nodes 分组
|
|
145
|
+
* 4. 使用已注册的类型定义重新创建所有元素(会重新执行 render fn)
|
|
146
|
+
*/
|
|
147
|
+
deserialize(data: Record<string, unknown>): void;
|
|
148
|
+
register(name: string, def: ElementDef): void;
|
|
149
|
+
add<T>(type: string, data: T & (NodeBase | EdgeBase), options?: {
|
|
150
|
+
layer?: string;
|
|
151
|
+
deps?: string[];
|
|
152
|
+
}): Element<T>;
|
|
153
|
+
remove(id: string): boolean;
|
|
154
|
+
get<T = Record<string, unknown>>(id: string): Element<T> | undefined;
|
|
155
|
+
has(id: string): boolean;
|
|
156
|
+
get count(): number;
|
|
157
|
+
getIds(): string[];
|
|
158
|
+
getAll(): Element[];
|
|
159
|
+
getNodes(): Element<NodeBase>[];
|
|
160
|
+
getEdges(): Element<EdgeBase>[];
|
|
161
|
+
getEdgesOf(nodeId: string): Element<EdgeBase>[];
|
|
162
|
+
batch(fn: () => void): void;
|
|
163
|
+
/**
|
|
164
|
+
* 通知 graph 某个元素已被更新(由 ElementImpl.update 调用的外部入口)。
|
|
165
|
+
* 这通过在 add 时对 ElementImpl 进行 monkey-patch 来实现。
|
|
166
|
+
*/
|
|
167
|
+
notifyUpdate(id: string): void;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* 创建 GraphPlugin 实例。
|
|
171
|
+
*/
|
|
172
|
+
declare function graphPlugin(): GraphPlugin;
|
|
173
|
+
|
|
174
|
+
export { type EdgeBase, type EdgeContext, type EdgeDef, type EdgeRenderFn, type Element, type ElementDef, ElementImpl, GraphPlugin, type GraphQuery, type NodeBase, type NodeContext, type NodeDef, type NodeRenderFn, createEdge, createNode, graphPlugin };
|
package/dist/main.d.ts
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { Group, Plugin, App } from 'rendx-engine';
|
|
2
|
+
|
|
3
|
+
/** Node 基础字段 */
|
|
4
|
+
interface NodeBase {
|
|
5
|
+
id: string;
|
|
6
|
+
x: number;
|
|
7
|
+
y: number;
|
|
8
|
+
width?: number;
|
|
9
|
+
height?: number;
|
|
10
|
+
}
|
|
11
|
+
/** Edge 基础字段 */
|
|
12
|
+
interface EdgeBase {
|
|
13
|
+
id: string;
|
|
14
|
+
source: string;
|
|
15
|
+
target: string;
|
|
16
|
+
}
|
|
17
|
+
/** Node render fn 接收的上下文 */
|
|
18
|
+
interface NodeContext {
|
|
19
|
+
group: Group;
|
|
20
|
+
width: number;
|
|
21
|
+
height: number;
|
|
22
|
+
onCleanup: (fn: () => void) => void;
|
|
23
|
+
}
|
|
24
|
+
/** Edge render fn 接收的上下文 */
|
|
25
|
+
interface EdgeContext {
|
|
26
|
+
group: Group;
|
|
27
|
+
source: Element<NodeBase> | undefined;
|
|
28
|
+
target: Element<NodeBase> | undefined;
|
|
29
|
+
onCleanup: (fn: () => void) => void;
|
|
30
|
+
}
|
|
31
|
+
type NodeRenderFn<T extends NodeBase = NodeBase> = (ctx: NodeContext, data: T & NodeBase, graph: GraphQuery) => void;
|
|
32
|
+
type EdgeRenderFn<T extends EdgeBase = EdgeBase> = (ctx: EdgeContext, data: T & EdgeBase, graph: GraphQuery) => void;
|
|
33
|
+
interface NodeDef<T extends NodeBase = NodeBase> {
|
|
34
|
+
__element_def__: true;
|
|
35
|
+
role: 'node';
|
|
36
|
+
render: NodeRenderFn<T>;
|
|
37
|
+
}
|
|
38
|
+
interface EdgeDef<T extends EdgeBase = EdgeBase> {
|
|
39
|
+
__element_def__: true;
|
|
40
|
+
role: 'edge';
|
|
41
|
+
render: EdgeRenderFn<T>;
|
|
42
|
+
}
|
|
43
|
+
type ElementDef = NodeDef | EdgeDef;
|
|
44
|
+
interface Element<T = Record<string, unknown>> {
|
|
45
|
+
readonly id: string;
|
|
46
|
+
readonly role: 'node' | 'edge';
|
|
47
|
+
readonly group: Group;
|
|
48
|
+
readonly data: T & (NodeBase | EdgeBase);
|
|
49
|
+
readonly mounted: boolean;
|
|
50
|
+
readonly layer: string;
|
|
51
|
+
readonly deps: string[];
|
|
52
|
+
update(patch: Partial<T>): void;
|
|
53
|
+
dispose(): void;
|
|
54
|
+
}
|
|
55
|
+
interface GraphQuery {
|
|
56
|
+
get<T = Record<string, unknown>>(id: string): Element<T> | undefined;
|
|
57
|
+
has(id: string): boolean;
|
|
58
|
+
count: number;
|
|
59
|
+
getIds(): string[];
|
|
60
|
+
getAll(): Element[];
|
|
61
|
+
getNodes(): Element<NodeBase>[];
|
|
62
|
+
getEdges(): Element<EdgeBase>[];
|
|
63
|
+
getEdgesOf(nodeId: string): Element<EdgeBase>[];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 定义一个 Node 类型的元素。
|
|
68
|
+
*
|
|
69
|
+
* Node 是有位置 (x, y) 的实体,render fn 接收 NodeContext(group + width/height)。
|
|
70
|
+
* 框架会自动为 Node 设置 group.translate(x, y),仅位移变化时跳过子树重建。
|
|
71
|
+
*/
|
|
72
|
+
declare function createNode<T extends NodeBase = NodeBase>(render: NodeRenderFn<T>): NodeDef<T>;
|
|
73
|
+
/**
|
|
74
|
+
* 定义一个 Edge 类型的元素。
|
|
75
|
+
*
|
|
76
|
+
* Edge 连接两个 Node(source → target),render fn 接收 EdgeContext(group + source/target 实例)。
|
|
77
|
+
* 框架自动从 source/target 派生 deps 和 layer='edges'。
|
|
78
|
+
* Edge 的 group 不设置 translate — 由 render fn 自行计算坐标。
|
|
79
|
+
*/
|
|
80
|
+
declare function createEdge<T extends EdgeBase = EdgeBase>(render: EdgeRenderFn<T>): EdgeDef<T>;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* ElementImpl — 元素运行时实例。
|
|
84
|
+
*
|
|
85
|
+
* - Node: translate(x, y),仅位移变化时 skip rebuild
|
|
86
|
+
* - Edge: 不 translate,render 时自动注入 source/target
|
|
87
|
+
*/
|
|
88
|
+
declare class ElementImpl<T = Record<string, unknown>> implements Element<T> {
|
|
89
|
+
#private;
|
|
90
|
+
readonly id: string;
|
|
91
|
+
readonly role: 'node' | 'edge';
|
|
92
|
+
readonly group: Group;
|
|
93
|
+
readonly layer: string;
|
|
94
|
+
readonly deps: string[];
|
|
95
|
+
/** 创建时使用的类型名称(对应 register 的 name),用于序列化 */
|
|
96
|
+
readonly typeName: string;
|
|
97
|
+
constructor(id: string, typeName: string, def: ElementDef, data: T & (NodeBase | EdgeBase), graph: GraphQuery, layer: string, deps: string[], onUpdate?: (id: string) => void);
|
|
98
|
+
get data(): T & (NodeBase | EdgeBase);
|
|
99
|
+
get mounted(): boolean;
|
|
100
|
+
/**
|
|
101
|
+
* 部分更新数据。
|
|
102
|
+
* - id 不可变
|
|
103
|
+
* - Node: 仅 x/y 变化 → translate only(不重建子树)
|
|
104
|
+
* - Edge: source/target 变化时忽略(deps 已静态绑定)
|
|
105
|
+
*/
|
|
106
|
+
update(patch: Partial<T>): void;
|
|
107
|
+
dispose(): void;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* GraphPlugin — 元素生命周期管理器。
|
|
112
|
+
*
|
|
113
|
+
* 核心职责:
|
|
114
|
+
* 1. 注册元素类型定义 (register)
|
|
115
|
+
* 2. 增删查改元素实例 (add / remove / get / update)
|
|
116
|
+
* 3. 自动分层: Node → 'nodes' Group, Edge → 'edges' Group
|
|
117
|
+
* 4. 自动依赖: Edge 的 deps 从 source/target 自动派生
|
|
118
|
+
* 5. 依赖追踪: 被依赖元素更新时自动重绘依赖方
|
|
119
|
+
* 6. Graph 查询: getNodes / getEdges / getEdgesOf
|
|
120
|
+
*/
|
|
121
|
+
declare class GraphPlugin implements Plugin, GraphQuery {
|
|
122
|
+
#private;
|
|
123
|
+
readonly name = "graph";
|
|
124
|
+
readonly state: {
|
|
125
|
+
key: string;
|
|
126
|
+
description: string;
|
|
127
|
+
initial: string[];
|
|
128
|
+
}[];
|
|
129
|
+
get edgesGroup(): Group;
|
|
130
|
+
get nodesGroup(): Group;
|
|
131
|
+
install(app: App): void;
|
|
132
|
+
dispose(): void;
|
|
133
|
+
/**
|
|
134
|
+
* 序列化所有元素实例的数据(供 history-plugin 等消费)。
|
|
135
|
+
* 类型定义(render fn)无法序列化,需要应用层在恢复时保证 register 已调用。
|
|
136
|
+
*/
|
|
137
|
+
serialize(): Record<string, unknown>;
|
|
138
|
+
/**
|
|
139
|
+
* 从序列化数据恢复所有元素。
|
|
140
|
+
*
|
|
141
|
+
* 流程:
|
|
142
|
+
* 1. 清理当前所有元素和依赖追踪
|
|
143
|
+
* 2. 从恢复后的场景中移除 restoreFromJSON 反序列化出的静态 Group(它们缺少 ElementImpl 追踪)
|
|
144
|
+
* 3. 重建 edges/nodes 分组
|
|
145
|
+
* 4. 使用已注册的类型定义重新创建所有元素(会重新执行 render fn)
|
|
146
|
+
*/
|
|
147
|
+
deserialize(data: Record<string, unknown>): void;
|
|
148
|
+
register(name: string, def: ElementDef): void;
|
|
149
|
+
add<T>(type: string, data: T & (NodeBase | EdgeBase), options?: {
|
|
150
|
+
layer?: string;
|
|
151
|
+
deps?: string[];
|
|
152
|
+
}): Element<T>;
|
|
153
|
+
remove(id: string): boolean;
|
|
154
|
+
get<T = Record<string, unknown>>(id: string): Element<T> | undefined;
|
|
155
|
+
has(id: string): boolean;
|
|
156
|
+
get count(): number;
|
|
157
|
+
getIds(): string[];
|
|
158
|
+
getAll(): Element[];
|
|
159
|
+
getNodes(): Element<NodeBase>[];
|
|
160
|
+
getEdges(): Element<EdgeBase>[];
|
|
161
|
+
getEdgesOf(nodeId: string): Element<EdgeBase>[];
|
|
162
|
+
batch(fn: () => void): void;
|
|
163
|
+
/**
|
|
164
|
+
* 通知 graph 某个元素已被更新(由 ElementImpl.update 调用的外部入口)。
|
|
165
|
+
* 这通过在 add 时对 ElementImpl 进行 monkey-patch 来实现。
|
|
166
|
+
*/
|
|
167
|
+
notifyUpdate(id: string): void;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* 创建 GraphPlugin 实例。
|
|
171
|
+
*/
|
|
172
|
+
declare function graphPlugin(): GraphPlugin;
|
|
173
|
+
|
|
174
|
+
export { type EdgeBase, type EdgeContext, type EdgeDef, type EdgeRenderFn, type Element, type ElementDef, ElementImpl, GraphPlugin, type GraphQuery, type NodeBase, type NodeContext, type NodeDef, type NodeRenderFn, createEdge, createNode, graphPlugin };
|
package/dist/main.js
ADDED
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
var __typeError = (msg) => {
|
|
2
|
+
throw TypeError(msg);
|
|
3
|
+
};
|
|
4
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
5
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
6
|
+
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
7
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
8
|
+
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
9
|
+
|
|
10
|
+
// src/create.ts
|
|
11
|
+
function createNode(render) {
|
|
12
|
+
return {
|
|
13
|
+
__element_def__: true,
|
|
14
|
+
role: "node",
|
|
15
|
+
render
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function createEdge(render) {
|
|
19
|
+
return {
|
|
20
|
+
__element_def__: true,
|
|
21
|
+
role: "edge",
|
|
22
|
+
render
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// src/element.ts
|
|
27
|
+
import { Group } from "rendx-engine";
|
|
28
|
+
var _data, _def, _graph, _cleanup, _mounted, _onUpdate, _ElementImpl_instances, render_fn, teardown_fn;
|
|
29
|
+
var ElementImpl = class {
|
|
30
|
+
constructor(id, typeName, def, data, graph, layer, deps, onUpdate) {
|
|
31
|
+
__privateAdd(this, _ElementImpl_instances);
|
|
32
|
+
__privateAdd(this, _data);
|
|
33
|
+
__privateAdd(this, _def);
|
|
34
|
+
__privateAdd(this, _graph);
|
|
35
|
+
__privateAdd(this, _cleanup, null);
|
|
36
|
+
__privateAdd(this, _mounted, false);
|
|
37
|
+
__privateAdd(this, _onUpdate);
|
|
38
|
+
this.id = id;
|
|
39
|
+
this.typeName = typeName;
|
|
40
|
+
this.role = def.role;
|
|
41
|
+
__privateSet(this, _def, def);
|
|
42
|
+
__privateSet(this, _data, { ...data });
|
|
43
|
+
__privateSet(this, _graph, graph);
|
|
44
|
+
this.layer = layer;
|
|
45
|
+
this.deps = deps;
|
|
46
|
+
__privateSet(this, _onUpdate, onUpdate ?? null);
|
|
47
|
+
this.group = new Group();
|
|
48
|
+
this.group.setName(id);
|
|
49
|
+
if (def.role === "node") {
|
|
50
|
+
const nd = __privateGet(this, _data);
|
|
51
|
+
this.group.translate(nd.x, nd.y);
|
|
52
|
+
}
|
|
53
|
+
__privateMethod(this, _ElementImpl_instances, render_fn).call(this);
|
|
54
|
+
__privateSet(this, _mounted, true);
|
|
55
|
+
}
|
|
56
|
+
get data() {
|
|
57
|
+
return __privateGet(this, _data);
|
|
58
|
+
}
|
|
59
|
+
get mounted() {
|
|
60
|
+
return __privateGet(this, _mounted);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* 部分更新数据。
|
|
64
|
+
* - id 不可变
|
|
65
|
+
* - Node: 仅 x/y 变化 → translate only(不重建子树)
|
|
66
|
+
* - Edge: source/target 变化时忽略(deps 已静态绑定)
|
|
67
|
+
*/
|
|
68
|
+
update(patch) {
|
|
69
|
+
var _a, _b;
|
|
70
|
+
if ("id" in patch) {
|
|
71
|
+
delete patch.id;
|
|
72
|
+
}
|
|
73
|
+
const merged = { ...__privateGet(this, _data), ...patch };
|
|
74
|
+
if (this.role === "node") {
|
|
75
|
+
const oldData = __privateGet(this, _data);
|
|
76
|
+
const newData = merged;
|
|
77
|
+
const positionOnly = isPositionOnlyChange(oldData, newData, patch);
|
|
78
|
+
__privateSet(this, _data, merged);
|
|
79
|
+
if (positionOnly) {
|
|
80
|
+
this.group.translate(newData.x, newData.y);
|
|
81
|
+
(_a = __privateGet(this, _onUpdate)) == null ? void 0 : _a.call(this, this.id);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (newData.x !== oldData.x || newData.y !== oldData.y) {
|
|
85
|
+
this.group.translate(newData.x, newData.y);
|
|
86
|
+
}
|
|
87
|
+
} else {
|
|
88
|
+
__privateSet(this, _data, merged);
|
|
89
|
+
}
|
|
90
|
+
__privateMethod(this, _ElementImpl_instances, teardown_fn).call(this);
|
|
91
|
+
this.group.removeChildren();
|
|
92
|
+
__privateMethod(this, _ElementImpl_instances, render_fn).call(this);
|
|
93
|
+
(_b = __privateGet(this, _onUpdate)) == null ? void 0 : _b.call(this, this.id);
|
|
94
|
+
}
|
|
95
|
+
dispose() {
|
|
96
|
+
__privateMethod(this, _ElementImpl_instances, teardown_fn).call(this);
|
|
97
|
+
this.group.removeChildren();
|
|
98
|
+
__privateSet(this, _mounted, false);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
_data = new WeakMap();
|
|
102
|
+
_def = new WeakMap();
|
|
103
|
+
_graph = new WeakMap();
|
|
104
|
+
_cleanup = new WeakMap();
|
|
105
|
+
_mounted = new WeakMap();
|
|
106
|
+
_onUpdate = new WeakMap();
|
|
107
|
+
_ElementImpl_instances = new WeakSet();
|
|
108
|
+
// ── internal ──
|
|
109
|
+
render_fn = function() {
|
|
110
|
+
let cleanup = null;
|
|
111
|
+
const onCleanup = (fn) => {
|
|
112
|
+
cleanup = fn;
|
|
113
|
+
};
|
|
114
|
+
if (__privateGet(this, _def).role === "node") {
|
|
115
|
+
const nd = __privateGet(this, _data);
|
|
116
|
+
const ctx = {
|
|
117
|
+
group: this.group,
|
|
118
|
+
width: nd.width ?? 0,
|
|
119
|
+
height: nd.height ?? 0,
|
|
120
|
+
onCleanup
|
|
121
|
+
};
|
|
122
|
+
__privateGet(this, _def).render(ctx, nd, __privateGet(this, _graph));
|
|
123
|
+
} else {
|
|
124
|
+
const ed = __privateGet(this, _data);
|
|
125
|
+
const ctx = {
|
|
126
|
+
group: this.group,
|
|
127
|
+
source: __privateGet(this, _graph).get(ed.source),
|
|
128
|
+
target: __privateGet(this, _graph).get(ed.target),
|
|
129
|
+
onCleanup
|
|
130
|
+
};
|
|
131
|
+
__privateGet(this, _def).render(ctx, ed, __privateGet(this, _graph));
|
|
132
|
+
}
|
|
133
|
+
__privateSet(this, _cleanup, cleanup);
|
|
134
|
+
};
|
|
135
|
+
teardown_fn = function() {
|
|
136
|
+
if (__privateGet(this, _cleanup)) {
|
|
137
|
+
__privateGet(this, _cleanup).call(this);
|
|
138
|
+
__privateSet(this, _cleanup, null);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
function isPositionOnlyChange(oldData, newData, patch) {
|
|
142
|
+
const keys = Object.keys(patch);
|
|
143
|
+
if (keys.length === 0) return false;
|
|
144
|
+
for (const key of keys) {
|
|
145
|
+
if (key === "x" || key === "y") continue;
|
|
146
|
+
if (oldData[key] !== newData[key]) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return oldData.x !== newData.x || oldData.y !== newData.y;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// src/graph.ts
|
|
154
|
+
import { Group as Group2 } from "rendx-engine";
|
|
155
|
+
var _app, _types, _elements, _dependents, _batching, _batchAdded, _batchRemoved, _edgesGroup, _nodesGroup, _GraphPlugin_instances, triggerDependents_fn, syncState_fn;
|
|
156
|
+
var GraphPlugin = class {
|
|
157
|
+
constructor() {
|
|
158
|
+
__privateAdd(this, _GraphPlugin_instances);
|
|
159
|
+
this.name = "graph";
|
|
160
|
+
this.state = [
|
|
161
|
+
{
|
|
162
|
+
key: "graph:elements",
|
|
163
|
+
description: "All element IDs in the graph",
|
|
164
|
+
initial: []
|
|
165
|
+
}
|
|
166
|
+
];
|
|
167
|
+
__privateAdd(this, _app);
|
|
168
|
+
__privateAdd(this, _types, /* @__PURE__ */ new Map());
|
|
169
|
+
__privateAdd(this, _elements, /* @__PURE__ */ new Map());
|
|
170
|
+
/** node → 依赖它的元素 id 列表 */
|
|
171
|
+
__privateAdd(this, _dependents, /* @__PURE__ */ new Map());
|
|
172
|
+
/** 批量操作标志 */
|
|
173
|
+
__privateAdd(this, _batching, false);
|
|
174
|
+
__privateAdd(this, _batchAdded, false);
|
|
175
|
+
__privateAdd(this, _batchRemoved, false);
|
|
176
|
+
/** 场景分组 */
|
|
177
|
+
__privateAdd(this, _edgesGroup);
|
|
178
|
+
__privateAdd(this, _nodesGroup);
|
|
179
|
+
}
|
|
180
|
+
get edgesGroup() {
|
|
181
|
+
return __privateGet(this, _edgesGroup);
|
|
182
|
+
}
|
|
183
|
+
get nodesGroup() {
|
|
184
|
+
return __privateGet(this, _nodesGroup);
|
|
185
|
+
}
|
|
186
|
+
// ── Plugin lifecycle ──
|
|
187
|
+
install(app) {
|
|
188
|
+
__privateSet(this, _app, app);
|
|
189
|
+
__privateSet(this, _edgesGroup, new Group2());
|
|
190
|
+
__privateGet(this, _edgesGroup).setName("__graph_edges__");
|
|
191
|
+
app.scene.add(__privateGet(this, _edgesGroup));
|
|
192
|
+
__privateSet(this, _nodesGroup, new Group2());
|
|
193
|
+
__privateGet(this, _nodesGroup).setName("__graph_nodes__");
|
|
194
|
+
app.scene.add(__privateGet(this, _nodesGroup));
|
|
195
|
+
}
|
|
196
|
+
dispose() {
|
|
197
|
+
for (const el of __privateGet(this, _elements).values()) {
|
|
198
|
+
el.dispose();
|
|
199
|
+
}
|
|
200
|
+
__privateGet(this, _elements).clear();
|
|
201
|
+
__privateGet(this, _dependents).clear();
|
|
202
|
+
__privateGet(this, _types).clear();
|
|
203
|
+
}
|
|
204
|
+
// ── 序列化 / 反序列化 ──
|
|
205
|
+
/**
|
|
206
|
+
* 序列化所有元素实例的数据(供 history-plugin 等消费)。
|
|
207
|
+
* 类型定义(render fn)无法序列化,需要应用层在恢复时保证 register 已调用。
|
|
208
|
+
*/
|
|
209
|
+
serialize() {
|
|
210
|
+
const elements = [];
|
|
211
|
+
for (const el of __privateGet(this, _elements).values()) {
|
|
212
|
+
elements.push({
|
|
213
|
+
typeName: el.typeName,
|
|
214
|
+
data: { ...el.data },
|
|
215
|
+
layer: el.layer,
|
|
216
|
+
deps: [...el.deps]
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
return { elements };
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* 从序列化数据恢复所有元素。
|
|
223
|
+
*
|
|
224
|
+
* 流程:
|
|
225
|
+
* 1. 清理当前所有元素和依赖追踪
|
|
226
|
+
* 2. 从恢复后的场景中移除 restoreFromJSON 反序列化出的静态 Group(它们缺少 ElementImpl 追踪)
|
|
227
|
+
* 3. 重建 edges/nodes 分组
|
|
228
|
+
* 4. 使用已注册的类型定义重新创建所有元素(会重新执行 render fn)
|
|
229
|
+
*/
|
|
230
|
+
deserialize(data) {
|
|
231
|
+
const saved = data;
|
|
232
|
+
if (!saved.elements || !Array.isArray(saved.elements)) return;
|
|
233
|
+
for (const el of __privateGet(this, _elements).values()) {
|
|
234
|
+
el.dispose();
|
|
235
|
+
}
|
|
236
|
+
__privateGet(this, _elements).clear();
|
|
237
|
+
__privateGet(this, _dependents).clear();
|
|
238
|
+
const scene = __privateGet(this, _app).scene;
|
|
239
|
+
for (const layer of scene.layers) {
|
|
240
|
+
if (layer.isEventLayer) continue;
|
|
241
|
+
const toRemove = [];
|
|
242
|
+
for (const child of layer.children) {
|
|
243
|
+
if (child.name === "__graph_edges__" || child.name === "__graph_nodes__") {
|
|
244
|
+
toRemove.push(child);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
for (const g of toRemove) {
|
|
248
|
+
layer.remove(g);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
__privateSet(this, _edgesGroup, new Group2());
|
|
252
|
+
__privateGet(this, _edgesGroup).setName("__graph_edges__");
|
|
253
|
+
scene.add(__privateGet(this, _edgesGroup));
|
|
254
|
+
__privateSet(this, _nodesGroup, new Group2());
|
|
255
|
+
__privateGet(this, _nodesGroup).setName("__graph_nodes__");
|
|
256
|
+
scene.add(__privateGet(this, _nodesGroup));
|
|
257
|
+
const nodes = saved.elements.filter((e) => {
|
|
258
|
+
const def = __privateGet(this, _types).get(e.typeName);
|
|
259
|
+
return def?.role === "node";
|
|
260
|
+
});
|
|
261
|
+
const edges = saved.elements.filter((e) => {
|
|
262
|
+
const def = __privateGet(this, _types).get(e.typeName);
|
|
263
|
+
return def?.role === "edge";
|
|
264
|
+
});
|
|
265
|
+
this.batch(() => {
|
|
266
|
+
for (const elem of [...nodes, ...edges]) {
|
|
267
|
+
const def = __privateGet(this, _types).get(elem.typeName);
|
|
268
|
+
if (!def) {
|
|
269
|
+
console.warn(`[rendx-graph-plugin] Cannot restore element: type "${elem.typeName}" not registered`);
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
this.add(elem.typeName, elem.data, { layer: elem.layer, deps: elem.deps });
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
// ── 类型注册 ──
|
|
277
|
+
register(name, def) {
|
|
278
|
+
__privateGet(this, _types).set(name, def);
|
|
279
|
+
}
|
|
280
|
+
// ── CRUD ──
|
|
281
|
+
add(type, data, options) {
|
|
282
|
+
const def = __privateGet(this, _types).get(type);
|
|
283
|
+
if (!def) throw new Error(`Unknown element type: "${type}"`);
|
|
284
|
+
const id = data.id;
|
|
285
|
+
if (__privateGet(this, _elements).has(id)) throw new Error(`Element "${id}" already exists`);
|
|
286
|
+
let layer;
|
|
287
|
+
let deps;
|
|
288
|
+
if (def.role === "edge") {
|
|
289
|
+
layer = options?.layer ?? "edges";
|
|
290
|
+
const edgeData = data;
|
|
291
|
+
deps = options?.deps ?? [edgeData.source, edgeData.target];
|
|
292
|
+
} else {
|
|
293
|
+
layer = options?.layer ?? "nodes";
|
|
294
|
+
deps = options?.deps ?? [];
|
|
295
|
+
}
|
|
296
|
+
const el = new ElementImpl(id, type, def, data, this, layer, deps, (elId) => this.notifyUpdate(elId));
|
|
297
|
+
const targetGroup = layer === "edges" ? __privateGet(this, _edgesGroup) : __privateGet(this, _nodesGroup);
|
|
298
|
+
targetGroup.add(el.group);
|
|
299
|
+
__privateGet(this, _elements).set(id, el);
|
|
300
|
+
for (const depId of deps) {
|
|
301
|
+
if (!__privateGet(this, _dependents).has(depId)) {
|
|
302
|
+
__privateGet(this, _dependents).set(depId, /* @__PURE__ */ new Set());
|
|
303
|
+
}
|
|
304
|
+
__privateGet(this, _dependents).get(depId).add(id);
|
|
305
|
+
}
|
|
306
|
+
if (!__privateGet(this, _batching)) {
|
|
307
|
+
__privateMethod(this, _GraphPlugin_instances, syncState_fn).call(this);
|
|
308
|
+
__privateGet(this, _app).bus.emit("graph:added", el);
|
|
309
|
+
} else {
|
|
310
|
+
__privateSet(this, _batchAdded, true);
|
|
311
|
+
}
|
|
312
|
+
return el;
|
|
313
|
+
}
|
|
314
|
+
remove(id) {
|
|
315
|
+
const el = __privateGet(this, _elements).get(id);
|
|
316
|
+
if (!el) return false;
|
|
317
|
+
for (const depId of el.deps) {
|
|
318
|
+
const set = __privateGet(this, _dependents).get(depId);
|
|
319
|
+
if (set) {
|
|
320
|
+
set.delete(id);
|
|
321
|
+
if (set.size === 0) __privateGet(this, _dependents).delete(depId);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
const parent = el.group.parent;
|
|
325
|
+
if (parent) {
|
|
326
|
+
parent.remove(el.group);
|
|
327
|
+
}
|
|
328
|
+
el.dispose();
|
|
329
|
+
__privateGet(this, _elements).delete(id);
|
|
330
|
+
if (!__privateGet(this, _batching)) {
|
|
331
|
+
__privateMethod(this, _GraphPlugin_instances, syncState_fn).call(this);
|
|
332
|
+
__privateGet(this, _app).bus.emit("graph:removed", id);
|
|
333
|
+
} else {
|
|
334
|
+
__privateSet(this, _batchRemoved, true);
|
|
335
|
+
}
|
|
336
|
+
return true;
|
|
337
|
+
}
|
|
338
|
+
// ── GraphQuery 实现 ──
|
|
339
|
+
get(id) {
|
|
340
|
+
return __privateGet(this, _elements).get(id);
|
|
341
|
+
}
|
|
342
|
+
has(id) {
|
|
343
|
+
return __privateGet(this, _elements).has(id);
|
|
344
|
+
}
|
|
345
|
+
get count() {
|
|
346
|
+
return __privateGet(this, _elements).size;
|
|
347
|
+
}
|
|
348
|
+
getIds() {
|
|
349
|
+
return [...__privateGet(this, _elements).keys()];
|
|
350
|
+
}
|
|
351
|
+
getAll() {
|
|
352
|
+
return [...__privateGet(this, _elements).values()];
|
|
353
|
+
}
|
|
354
|
+
getNodes() {
|
|
355
|
+
return [...__privateGet(this, _elements).values()].filter((el) => el.role === "node");
|
|
356
|
+
}
|
|
357
|
+
getEdges() {
|
|
358
|
+
return [...__privateGet(this, _elements).values()].filter((el) => el.role === "edge");
|
|
359
|
+
}
|
|
360
|
+
getEdgesOf(nodeId) {
|
|
361
|
+
return [...__privateGet(this, _elements).values()].filter((el) => {
|
|
362
|
+
if (el.role !== "edge") return false;
|
|
363
|
+
const data = el.data;
|
|
364
|
+
return data.source === nodeId || data.target === nodeId;
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
// ── 批量操作 ──
|
|
368
|
+
batch(fn) {
|
|
369
|
+
__privateSet(this, _batching, true);
|
|
370
|
+
__privateSet(this, _batchAdded, false);
|
|
371
|
+
__privateSet(this, _batchRemoved, false);
|
|
372
|
+
try {
|
|
373
|
+
fn();
|
|
374
|
+
} finally {
|
|
375
|
+
__privateSet(this, _batching, false);
|
|
376
|
+
__privateMethod(this, _GraphPlugin_instances, syncState_fn).call(this);
|
|
377
|
+
if (__privateGet(this, _batchAdded)) {
|
|
378
|
+
__privateGet(this, _app).bus.emit("graph:added");
|
|
379
|
+
}
|
|
380
|
+
if (__privateGet(this, _batchRemoved)) {
|
|
381
|
+
__privateGet(this, _app).bus.emit("graph:removed");
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* 通知 graph 某个元素已被更新(由 ElementImpl.update 调用的外部入口)。
|
|
387
|
+
* 这通过在 add 时对 ElementImpl 进行 monkey-patch 来实现。
|
|
388
|
+
*/
|
|
389
|
+
notifyUpdate(id) {
|
|
390
|
+
__privateMethod(this, _GraphPlugin_instances, triggerDependents_fn).call(this, id);
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
_app = new WeakMap();
|
|
394
|
+
_types = new WeakMap();
|
|
395
|
+
_elements = new WeakMap();
|
|
396
|
+
_dependents = new WeakMap();
|
|
397
|
+
_batching = new WeakMap();
|
|
398
|
+
_batchAdded = new WeakMap();
|
|
399
|
+
_batchRemoved = new WeakMap();
|
|
400
|
+
_edgesGroup = new WeakMap();
|
|
401
|
+
_nodesGroup = new WeakMap();
|
|
402
|
+
_GraphPlugin_instances = new WeakSet();
|
|
403
|
+
/**
|
|
404
|
+
* 更新元素并触发依赖链。
|
|
405
|
+
* 当外部调用 el.update() 时,GraphPlugin 监听不到,所以通过此内部路由。
|
|
406
|
+
* 但当前设计中 el.update() 直接在 ElementImpl 中执行。
|
|
407
|
+
* 依赖触发通过 add() 时注册的追踪机制实现。
|
|
408
|
+
*/
|
|
409
|
+
triggerDependents_fn = function(id) {
|
|
410
|
+
const deps = __privateGet(this, _dependents).get(id);
|
|
411
|
+
if (!deps) return;
|
|
412
|
+
for (const depId of deps) {
|
|
413
|
+
const depEl = __privateGet(this, _elements).get(depId);
|
|
414
|
+
if (depEl) {
|
|
415
|
+
depEl.update({});
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
// ── internal ──
|
|
420
|
+
syncState_fn = function() {
|
|
421
|
+
__privateGet(this, _app).setState("graph:elements", this.getIds());
|
|
422
|
+
};
|
|
423
|
+
function graphPlugin() {
|
|
424
|
+
return new GraphPlugin();
|
|
425
|
+
}
|
|
426
|
+
export {
|
|
427
|
+
ElementImpl,
|
|
428
|
+
GraphPlugin,
|
|
429
|
+
createEdge,
|
|
430
|
+
createNode,
|
|
431
|
+
graphPlugin
|
|
432
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "rendx-graph-plugin",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Graph element management plugin for Rendx engine — node/edge CRUD, dependency tracking, and custom type registry",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "wei.liang (https://github.com/weiliang0121)",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"rendx",
|
|
10
|
+
"2d",
|
|
11
|
+
"canvas",
|
|
12
|
+
"graph",
|
|
13
|
+
"element",
|
|
14
|
+
"node",
|
|
15
|
+
"plugin"
|
|
16
|
+
],
|
|
17
|
+
"homepage": "https://weiliang0121.github.io/rendx/",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/weiliang0121/rendx.git",
|
|
21
|
+
"directory": "packages/graph-plugin"
|
|
22
|
+
},
|
|
23
|
+
"main": "dist/main.cjs",
|
|
24
|
+
"module": "dist/main.js",
|
|
25
|
+
"types": "dist/main.d.ts",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./dist/main.d.ts",
|
|
29
|
+
"import": "./dist/main.js",
|
|
30
|
+
"require": "./dist/main.cjs"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"rendx-engine": "^0.3.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"rendx-canvas": "^0.1.1",
|
|
38
|
+
"rendx-svg": "^0.1.1"
|
|
39
|
+
},
|
|
40
|
+
"sideEffects": false,
|
|
41
|
+
"files": [
|
|
42
|
+
"dist"
|
|
43
|
+
],
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsup",
|
|
49
|
+
"dev": "tsup --watch"
|
|
50
|
+
}
|
|
51
|
+
}
|