@3plate/graph-core 0.1.2 → 0.1.4
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/index.cjs +1194 -705
- package/dist/index.d.cts +143 -83
- package/dist/index.d.ts +143 -83
- package/dist/index.js +1186 -707
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
import { jsx } from "jsx-dom/jsx-runtime";
|
|
3
|
-
function renderNode(node) {
|
|
4
|
-
return /* @__PURE__ */ jsx("div", { children: node?.id || "" });
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
// src/graph/types/graph.ts
|
|
1
|
+
// src/graph/graph.ts
|
|
8
2
|
import { Map as IMap, List as IList, Set as ISet6 } from "immutable";
|
|
9
3
|
|
|
10
|
-
// src/graph/
|
|
4
|
+
// src/graph/node.ts
|
|
11
5
|
import { Record, Set as ISet } from "immutable";
|
|
12
|
-
var
|
|
6
|
+
var defNodeData = {
|
|
13
7
|
id: "",
|
|
8
|
+
data: void 0,
|
|
9
|
+
version: 0,
|
|
10
|
+
title: void 0,
|
|
11
|
+
text: void 0,
|
|
12
|
+
type: void 0,
|
|
13
|
+
render: void 0,
|
|
14
|
+
ports: { in: null, out: null },
|
|
14
15
|
aligned: {},
|
|
15
16
|
edges: { in: ISet(), out: ISet() },
|
|
16
17
|
segs: { in: ISet(), out: ISet() },
|
|
17
|
-
ports: { in: [], out: [] },
|
|
18
18
|
layerId: "",
|
|
19
19
|
isDummy: false,
|
|
20
20
|
isMerged: false,
|
|
@@ -25,29 +25,35 @@ var defaultNodeProps = {
|
|
|
25
25
|
dims: void 0,
|
|
26
26
|
mutable: false
|
|
27
27
|
};
|
|
28
|
-
var Node = class _Node extends Record(
|
|
28
|
+
var Node = class _Node extends Record(defNodeData) {
|
|
29
29
|
static dummyPrefix = "d:";
|
|
30
|
-
get edgeId() {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
get edgeIds() {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
30
|
+
// get edgeId(): EdgeId {
|
|
31
|
+
// if (!this.isDummy)
|
|
32
|
+
// throw new Error(`node ${this.id} is not a dummy`)
|
|
33
|
+
// if (this.isMerged)
|
|
34
|
+
// throw new Error(`node ${this.id} is merged`)
|
|
35
|
+
// return this.get('edgeIds')[0]
|
|
36
|
+
// }
|
|
37
|
+
// get edgeIds(): EdgeId[] {
|
|
38
|
+
// if (!this.isDummy)
|
|
39
|
+
// throw new Error(`node ${this.id} is not a dummy`)
|
|
40
|
+
// if (!this.isMerged)
|
|
41
|
+
// throw new Error(`node ${this.id} is not merged`)
|
|
42
|
+
// return this.get('edgeIds')
|
|
43
|
+
// }
|
|
44
|
+
get key() {
|
|
45
|
+
return this.isDummy ? this.id : _Node.key(this);
|
|
46
|
+
}
|
|
47
|
+
static key(node) {
|
|
48
|
+
return `${node.id}:${node.version}`;
|
|
43
49
|
}
|
|
44
50
|
static isDummyId(nodeId) {
|
|
45
51
|
return nodeId.startsWith(_Node.dummyPrefix);
|
|
46
52
|
}
|
|
47
|
-
static addNormal(g,
|
|
53
|
+
static addNormal(g, data) {
|
|
48
54
|
const layer = g.layerAt(0);
|
|
49
55
|
const node = new _Node({
|
|
50
|
-
...
|
|
56
|
+
...data,
|
|
51
57
|
edges: { in: ISet(), out: ISet() },
|
|
52
58
|
segs: { in: ISet(), out: ISet() },
|
|
53
59
|
aligned: {},
|
|
@@ -59,10 +65,10 @@ var Node = class _Node extends Record(defaultNodeProps) {
|
|
|
59
65
|
g.dirtyNodes.add(node.id);
|
|
60
66
|
return node;
|
|
61
67
|
}
|
|
62
|
-
static addDummy(g,
|
|
63
|
-
const layer = g.getLayer(
|
|
68
|
+
static addDummy(g, data) {
|
|
69
|
+
const layer = g.getLayer(data.layerId);
|
|
64
70
|
const node = new _Node({
|
|
65
|
-
...
|
|
71
|
+
...data,
|
|
66
72
|
id: `${_Node.dummyPrefix}${g.nextDummyId++}`,
|
|
67
73
|
edges: { in: ISet(), out: ISet() },
|
|
68
74
|
segs: { in: ISet(), out: ISet() },
|
|
@@ -78,6 +84,12 @@ var Node = class _Node extends Record(defaultNodeProps) {
|
|
|
78
84
|
g.dirtyNodes.add(node.id);
|
|
79
85
|
return node;
|
|
80
86
|
}
|
|
87
|
+
static del(g, node) {
|
|
88
|
+
return g.getNode(node.id).delSelf(g);
|
|
89
|
+
}
|
|
90
|
+
static update(g, data) {
|
|
91
|
+
return g.getNode(data.id).mut(g).merge(data);
|
|
92
|
+
}
|
|
81
93
|
mut(g) {
|
|
82
94
|
if (this.mutable) return this;
|
|
83
95
|
return g.mutateNode(this);
|
|
@@ -100,6 +112,9 @@ var Node = class _Node extends Record(defaultNodeProps) {
|
|
|
100
112
|
isUnlinked() {
|
|
101
113
|
return this.edges.in.size == 0 && this.edges.out.size == 0 && this.segs.in.size == 0 && this.segs.out.size == 0;
|
|
102
114
|
}
|
|
115
|
+
hasPorts() {
|
|
116
|
+
return !!this.ports?.in?.length || !!this.ports?.out?.length;
|
|
117
|
+
}
|
|
103
118
|
layerIndex(g) {
|
|
104
119
|
return this.getLayer(g).index;
|
|
105
120
|
}
|
|
@@ -108,11 +123,9 @@ var Node = class _Node extends Record(defaultNodeProps) {
|
|
|
108
123
|
}
|
|
109
124
|
setIndex(g, index) {
|
|
110
125
|
if (this.index == index) return this;
|
|
111
|
-
console.log(`set index of ${this.id} to ${index}`);
|
|
112
126
|
return this.mut(g).set("index", index);
|
|
113
127
|
}
|
|
114
128
|
setLayerPos(g, lpos) {
|
|
115
|
-
console.log("setLayerPos", this.id, lpos);
|
|
116
129
|
if (this.lpos == lpos) return this;
|
|
117
130
|
return this.mut(g).set("lpos", lpos);
|
|
118
131
|
}
|
|
@@ -126,7 +139,6 @@ var Node = class _Node extends Record(defaultNodeProps) {
|
|
|
126
139
|
return this;
|
|
127
140
|
}
|
|
128
141
|
moveToLayer(g, layer) {
|
|
129
|
-
console.log("moveToLayer", this, this.getLayer(g), layer);
|
|
130
142
|
this.getLayer(g).delNode(g, this.id);
|
|
131
143
|
layer.addNode(g, this.id);
|
|
132
144
|
return this.setLayer(g, layer.id);
|
|
@@ -248,16 +260,44 @@ var Node = class _Node extends Record(defaultNodeProps) {
|
|
|
248
260
|
}
|
|
249
261
|
};
|
|
250
262
|
|
|
251
|
-
// src/graph/
|
|
263
|
+
// src/graph/edge.ts
|
|
252
264
|
import { Record as Record2 } from "immutable";
|
|
253
|
-
|
|
265
|
+
|
|
266
|
+
// src/log.ts
|
|
267
|
+
var levels = {
|
|
268
|
+
error: 0,
|
|
269
|
+
warn: 1,
|
|
270
|
+
info: 2,
|
|
271
|
+
debug: 3
|
|
272
|
+
};
|
|
273
|
+
var currentLevel = "debug";
|
|
274
|
+
function shouldLog(level) {
|
|
275
|
+
return levels[level] <= levels[currentLevel];
|
|
276
|
+
}
|
|
277
|
+
function logger(module) {
|
|
278
|
+
return {
|
|
279
|
+
error: (msg, ...args) => shouldLog("error") && console.error(`[${module}] ${msg}`, ...args),
|
|
280
|
+
warn: (msg, ...args) => shouldLog("warn") && console.warn(`[${module}] ${msg}`, ...args),
|
|
281
|
+
info: (msg, ...args) => shouldLog("info") && console.info(`[${module}] ${msg}`, ...args),
|
|
282
|
+
debug: (msg, ...args) => shouldLog("debug") && console.debug(`[${module}] ${msg}`, ...args)
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
var log = logger("core");
|
|
286
|
+
|
|
287
|
+
// src/graph/edge.ts
|
|
288
|
+
var log2 = logger("edge");
|
|
289
|
+
var defEdgeData = {
|
|
254
290
|
id: "",
|
|
291
|
+
data: null,
|
|
292
|
+
label: void 0,
|
|
255
293
|
source: { id: "" },
|
|
256
294
|
target: { id: "" },
|
|
295
|
+
type: void 0,
|
|
296
|
+
style: void 0,
|
|
257
297
|
mutable: false,
|
|
258
298
|
segIds: []
|
|
259
299
|
};
|
|
260
|
-
var Edge = class _Edge extends Record2(
|
|
300
|
+
var Edge = class _Edge extends Record2(defEdgeData) {
|
|
261
301
|
static prefix = "e:";
|
|
262
302
|
mut(g) {
|
|
263
303
|
if (this.mutable) return this;
|
|
@@ -315,37 +355,44 @@ var Edge = class _Edge extends Record2(defaultEdgeProps) {
|
|
|
315
355
|
return _Edge.str(this);
|
|
316
356
|
}
|
|
317
357
|
static str(edge) {
|
|
318
|
-
let source = edge.source
|
|
319
|
-
if (edge
|
|
358
|
+
let source = edge.source?.id;
|
|
359
|
+
if (!source) throw new Error("edge source is undefined");
|
|
360
|
+
if (edge.source?.port)
|
|
320
361
|
source = `${source} (port ${edge.source.port})`;
|
|
321
|
-
let target = edge.target
|
|
322
|
-
if (edge
|
|
362
|
+
let target = edge.target?.id;
|
|
363
|
+
if (!target) throw new Error("edge target is undefined");
|
|
364
|
+
if (edge.target?.port)
|
|
323
365
|
target = `${target} (port ${edge.target.port})`;
|
|
324
366
|
let str = `edge from ${source} to ${target}`;
|
|
325
367
|
if (edge.type) str += ` of type ${edge.type}`;
|
|
326
368
|
return str;
|
|
327
369
|
}
|
|
328
|
-
static
|
|
370
|
+
static key(edge, prefix = _Edge.prefix, side = "both") {
|
|
329
371
|
let source = "", target = "";
|
|
330
372
|
if (side == "source" || side == "both") {
|
|
373
|
+
if (!edge.source?.id) throw new Error("edge source is undefined");
|
|
331
374
|
source = edge.source.id;
|
|
332
|
-
if (edge.source
|
|
375
|
+
if (edge.source?.port)
|
|
333
376
|
source = `${source}.${edge.source.port}`;
|
|
377
|
+
const marker = edge.source?.marker ?? edge.style?.marker?.source;
|
|
378
|
+
if (marker && marker != "none") source += `[${marker}]`;
|
|
334
379
|
source += "-";
|
|
335
380
|
}
|
|
336
381
|
if (side == "target" || side == "both") {
|
|
382
|
+
if (!edge.target?.id) throw new Error("edge target is undefined");
|
|
337
383
|
target = edge.target.id;
|
|
338
384
|
if (edge.target.port)
|
|
339
385
|
target = `${target}.${edge.target.port}`;
|
|
340
386
|
target = "-" + target;
|
|
387
|
+
const marker = edge.target?.marker ?? edge.style?.marker?.target ?? "arrow";
|
|
388
|
+
if (marker && marker != "none") target += `[${marker}]`;
|
|
341
389
|
}
|
|
342
390
|
const type = edge.type || "";
|
|
343
391
|
return `${prefix}${source}${type}${target}`;
|
|
344
392
|
}
|
|
345
|
-
static add(g,
|
|
393
|
+
static add(g, data) {
|
|
346
394
|
const edge = new _Edge({
|
|
347
|
-
...
|
|
348
|
-
id: _Edge.id(props),
|
|
395
|
+
...data,
|
|
349
396
|
segIds: []
|
|
350
397
|
});
|
|
351
398
|
edge.link(g);
|
|
@@ -353,18 +400,39 @@ var Edge = class _Edge extends Record2(defaultEdgeProps) {
|
|
|
353
400
|
g.dirtyEdges.add(edge.id);
|
|
354
401
|
return edge;
|
|
355
402
|
}
|
|
403
|
+
static del(g, data) {
|
|
404
|
+
return g.getEdge(data.id).delSelf(g);
|
|
405
|
+
}
|
|
406
|
+
static update(g, data) {
|
|
407
|
+
let edge = g.getEdge(data.id);
|
|
408
|
+
let relink = false;
|
|
409
|
+
if (data.source.id !== edge.source.id || data.target.id !== edge.target.id || data.source.port !== edge.source.port || data.target.port !== edge.target.port || data.type !== edge.type) {
|
|
410
|
+
for (const seg of edge.segs(g))
|
|
411
|
+
seg.delEdgeId(g, edge.id);
|
|
412
|
+
edge.unlink(g);
|
|
413
|
+
relink = true;
|
|
414
|
+
}
|
|
415
|
+
edge = edge.mut(g).merge(data);
|
|
416
|
+
if (relink)
|
|
417
|
+
edge.link(g);
|
|
418
|
+
return edge;
|
|
419
|
+
}
|
|
356
420
|
};
|
|
357
421
|
|
|
358
|
-
// src/graph/
|
|
422
|
+
// src/graph/seg.ts
|
|
359
423
|
import { Record as Record3, Set as ISet2 } from "immutable";
|
|
360
|
-
var
|
|
424
|
+
var defSegData = {
|
|
361
425
|
id: "",
|
|
362
426
|
source: { id: "" },
|
|
363
427
|
target: { id: "" },
|
|
428
|
+
type: void 0,
|
|
429
|
+
style: void 0,
|
|
364
430
|
edgeIds: ISet2(),
|
|
431
|
+
trackPos: void 0,
|
|
432
|
+
svg: void 0,
|
|
365
433
|
mutable: false
|
|
366
434
|
};
|
|
367
|
-
var Seg = class _Seg extends Record3(
|
|
435
|
+
var Seg = class _Seg extends Record3(defSegData) {
|
|
368
436
|
static prefix = "s:";
|
|
369
437
|
mut(g) {
|
|
370
438
|
if (this.mutable) return this;
|
|
@@ -389,7 +457,7 @@ var Seg = class _Seg extends Record3(defaultSegProps) {
|
|
|
389
457
|
sameEnd(other, side) {
|
|
390
458
|
const mine = this[side];
|
|
391
459
|
const yours = other[side];
|
|
392
|
-
return mine.id === yours.id && mine.port === yours.port;
|
|
460
|
+
return mine.id === yours.id && mine.port === yours.port && mine.marker === yours.marker;
|
|
393
461
|
}
|
|
394
462
|
setPos(g, source, target) {
|
|
395
463
|
return this.mut(g).merge({
|
|
@@ -447,10 +515,10 @@ var Seg = class _Seg extends Record3(defaultSegProps) {
|
|
|
447
515
|
}
|
|
448
516
|
return this.mut(g).set("edgeIds", this.edgeIds.asMutable().remove(edgeId));
|
|
449
517
|
}
|
|
450
|
-
static add(g,
|
|
518
|
+
static add(g, data) {
|
|
451
519
|
const seg = new _Seg({
|
|
452
|
-
...
|
|
453
|
-
id: Edge.
|
|
520
|
+
...data,
|
|
521
|
+
id: Edge.key(data, _Seg.prefix)
|
|
454
522
|
});
|
|
455
523
|
seg.link(g);
|
|
456
524
|
g.segs.set(seg.id, seg);
|
|
@@ -459,33 +527,10 @@ var Seg = class _Seg extends Record3(defaultSegProps) {
|
|
|
459
527
|
}
|
|
460
528
|
};
|
|
461
529
|
|
|
462
|
-
// src/graph/
|
|
530
|
+
// src/graph/layer.ts
|
|
463
531
|
import { Record as Record4, Set as ISet3 } from "immutable";
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
var levels = {
|
|
467
|
-
error: 0,
|
|
468
|
-
warn: 1,
|
|
469
|
-
info: 2,
|
|
470
|
-
debug: 3
|
|
471
|
-
};
|
|
472
|
-
var currentLevel = "debug";
|
|
473
|
-
function shouldLog(level) {
|
|
474
|
-
return levels[level] <= levels[currentLevel];
|
|
475
|
-
}
|
|
476
|
-
function logger(module) {
|
|
477
|
-
return {
|
|
478
|
-
error: (msg, ...args) => shouldLog("error") && console.error(`[${module}] ${msg}`, ...args),
|
|
479
|
-
warn: (msg, ...args) => shouldLog("warn") && console.warn(`[${module}] ${msg}`, ...args),
|
|
480
|
-
info: (msg, ...args) => shouldLog("info") && console.info(`[${module}] ${msg}`, ...args),
|
|
481
|
-
debug: (msg, ...args) => shouldLog("debug") && console.debug(`[${module}] ${msg}`, ...args)
|
|
482
|
-
};
|
|
483
|
-
}
|
|
484
|
-
var log = logger("core");
|
|
485
|
-
|
|
486
|
-
// src/graph/types/layer.ts
|
|
487
|
-
var log2 = logger("layer");
|
|
488
|
-
var defaultLayerProps = {
|
|
532
|
+
var log3 = logger("layer");
|
|
533
|
+
var defLayerData = {
|
|
489
534
|
id: "",
|
|
490
535
|
index: 0,
|
|
491
536
|
nodeIds: ISet3(),
|
|
@@ -496,7 +541,7 @@ var defaultLayerProps = {
|
|
|
496
541
|
isSorted: false,
|
|
497
542
|
mutable: false
|
|
498
543
|
};
|
|
499
|
-
var Layer = class extends Record4(
|
|
544
|
+
var Layer = class extends Record4(defLayerData) {
|
|
500
545
|
static prefix = "l:";
|
|
501
546
|
mut(g) {
|
|
502
547
|
if (this.mutable) return this;
|
|
@@ -578,7 +623,6 @@ var Layer = class extends Record4(defaultLayerProps) {
|
|
|
578
623
|
}
|
|
579
624
|
setSorted(g, nodeIds) {
|
|
580
625
|
if (this.hasSortOrder(nodeIds)) return this;
|
|
581
|
-
console.log(`setting sorted for layer ${this.id}`);
|
|
582
626
|
nodeIds.forEach((nodeId, i) => g.getNode(nodeId).setIndex(g, i));
|
|
583
627
|
return this.mut(g).merge({ sorted: nodeIds, isSorted: true });
|
|
584
628
|
}
|
|
@@ -588,7 +632,7 @@ var Layer = class extends Record4(defaultLayerProps) {
|
|
|
588
632
|
}
|
|
589
633
|
};
|
|
590
634
|
|
|
591
|
-
// src/graph/
|
|
635
|
+
// src/graph/mutator.ts
|
|
592
636
|
var Mutator = class {
|
|
593
637
|
changes;
|
|
594
638
|
constructor() {
|
|
@@ -597,20 +641,27 @@ var Mutator = class {
|
|
|
597
641
|
removedNodes: [],
|
|
598
642
|
updatedNodes: [],
|
|
599
643
|
addedEdges: [],
|
|
600
|
-
removedEdges: []
|
|
644
|
+
removedEdges: [],
|
|
645
|
+
updatedEdges: []
|
|
601
646
|
};
|
|
602
647
|
}
|
|
648
|
+
describe(description) {
|
|
649
|
+
this.changes.description = description;
|
|
650
|
+
}
|
|
603
651
|
addNode(node) {
|
|
604
652
|
this.changes.addedNodes.push(node);
|
|
605
653
|
}
|
|
606
654
|
addNodes(...nodes) {
|
|
607
655
|
nodes.forEach((node) => this.addNode(node));
|
|
608
656
|
}
|
|
657
|
+
removeNode(node) {
|
|
658
|
+
this.changes.removedNodes.push(node);
|
|
659
|
+
}
|
|
660
|
+
removeNodes(...nodes) {
|
|
661
|
+
nodes.forEach((node) => this.removeNode(node));
|
|
662
|
+
}
|
|
609
663
|
updateNode(node) {
|
|
610
|
-
|
|
611
|
-
this.changes.updatedNodes.push({ id: node });
|
|
612
|
-
else
|
|
613
|
-
this.changes.updatedNodes.push(node);
|
|
664
|
+
this.changes.updatedNodes.push(node);
|
|
614
665
|
}
|
|
615
666
|
updateNodes(...nodes) {
|
|
616
667
|
nodes.forEach((node) => this.updateNode(node));
|
|
@@ -621,21 +672,18 @@ var Mutator = class {
|
|
|
621
672
|
addEdges(...edges) {
|
|
622
673
|
edges.forEach((edge) => this.addEdge(edge));
|
|
623
674
|
}
|
|
624
|
-
removeNode(node) {
|
|
625
|
-
if (typeof node === "string")
|
|
626
|
-
this.changes.removedNodes.push({ id: node });
|
|
627
|
-
else
|
|
628
|
-
this.changes.removedNodes.push(node);
|
|
629
|
-
}
|
|
630
|
-
removeNodes(...nodes) {
|
|
631
|
-
nodes.forEach((node) => this.removeNode(node));
|
|
632
|
-
}
|
|
633
675
|
removeEdge(edge) {
|
|
634
676
|
this.changes.removedEdges.push(edge);
|
|
635
677
|
}
|
|
636
678
|
removeEdges(...edges) {
|
|
637
679
|
edges.forEach((edge) => this.removeEdge(edge));
|
|
638
680
|
}
|
|
681
|
+
updateEdge(edge) {
|
|
682
|
+
this.changes.updatedEdges.push(edge);
|
|
683
|
+
}
|
|
684
|
+
updateEdges(...edges) {
|
|
685
|
+
edges.forEach((edge) => this.updateEdge(edge));
|
|
686
|
+
}
|
|
639
687
|
};
|
|
640
688
|
|
|
641
689
|
// src/graph/services/cycles.ts
|
|
@@ -735,14 +783,12 @@ var Cycles = class _Cycles {
|
|
|
735
783
|
|
|
736
784
|
// src/graph/services/dummy.ts
|
|
737
785
|
import { Set as ISet4 } from "immutable";
|
|
738
|
-
var
|
|
786
|
+
var log4 = logger("dummy");
|
|
739
787
|
var Dummy = class _Dummy {
|
|
740
788
|
static updateDummies(g) {
|
|
741
|
-
log3.debug(`updating dummies:`, [...g.dirtyEdges]);
|
|
742
789
|
for (const edgeId of g.dirtyEdges) {
|
|
743
|
-
log3.debug(`updating dummies of edge ${edgeId}`);
|
|
744
790
|
const edge = g.getEdge(edgeId);
|
|
745
|
-
const { type } = edge;
|
|
791
|
+
const { type, style } = edge;
|
|
746
792
|
const sourceLayer = edge.sourceNode(g).layerIndex(g);
|
|
747
793
|
const targetLayer = edge.targetNode(g).layerIndex(g);
|
|
748
794
|
let segIndex = 0;
|
|
@@ -766,12 +812,10 @@ var Dummy = class _Dummy {
|
|
|
766
812
|
});
|
|
767
813
|
target = { id: dummy.id };
|
|
768
814
|
}
|
|
769
|
-
seg = Seg.add(g, { source, target, type, edgeIds: ISet4([edgeId]) });
|
|
770
|
-
log3.debug(`edge ${edgeId}: adding segment ${seg.id} from ${source.id} at layer ${layerIndex - 1} to ${target.id} at layer ${layerIndex}`);
|
|
815
|
+
seg = Seg.add(g, { source, target, type, style, edgeIds: ISet4([edgeId]) });
|
|
771
816
|
segs.splice(segIndex, 0, seg.id);
|
|
772
817
|
changed = true;
|
|
773
818
|
} else if (segLayer < layerIndex || seg.source.id != source.id || seg.source.port != source.port || layerIndex == targetLayer && (seg.target.id != edge.target.id || seg.target.port != edge.target.port)) {
|
|
774
|
-
log3.debug(`edge ${edgeId}: removing segment ${seg.id} from layer ${layerIndex - 1} to layer ${layerIndex}`);
|
|
775
819
|
seg = seg.delEdgeId(g, edgeId);
|
|
776
820
|
segs.splice(segIndex, 1);
|
|
777
821
|
changed = true;
|
|
@@ -783,14 +827,12 @@ var Dummy = class _Dummy {
|
|
|
783
827
|
}
|
|
784
828
|
}
|
|
785
829
|
while (segIndex < segs.length) {
|
|
786
|
-
log3.debug(`edge ${edgeId}: removing trailing segment ${segs[segIndex]}`);
|
|
787
830
|
g.getSeg(segs[segIndex]).delEdgeId(g, edgeId);
|
|
788
831
|
segs.splice(segIndex, 1);
|
|
789
832
|
changed = true;
|
|
790
833
|
segIndex++;
|
|
791
834
|
}
|
|
792
835
|
if (changed) {
|
|
793
|
-
log3.debug(`edge ${edgeId}: updated segments to ${segs.join(", ")}`);
|
|
794
836
|
edge.setSegIds(g, segs);
|
|
795
837
|
}
|
|
796
838
|
}
|
|
@@ -805,7 +847,6 @@ var Dummy = class _Dummy {
|
|
|
805
847
|
const dir = side == "source" ? "in" : "out";
|
|
806
848
|
const altSide = side == "source" ? "target" : "source";
|
|
807
849
|
const altDir = altSide == "source" ? "in" : "out";
|
|
808
|
-
log3.debug(`merging dummies by ${side}`);
|
|
809
850
|
for (const layerId of layerIds) {
|
|
810
851
|
let layer = g.getLayer(layerId);
|
|
811
852
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -814,7 +855,7 @@ var Dummy = class _Dummy {
|
|
|
814
855
|
const node = g.getNode(nodeId);
|
|
815
856
|
if (node.isMerged) continue;
|
|
816
857
|
const edge = g.getEdge(node.edgeIds[0]);
|
|
817
|
-
const key = Edge.
|
|
858
|
+
const key = Edge.key(edge, "k:", side);
|
|
818
859
|
if (!groups.has(key)) groups.set(key, /* @__PURE__ */ new Set());
|
|
819
860
|
groups.get(key).add(node);
|
|
820
861
|
}
|
|
@@ -856,7 +897,7 @@ var Dummy = class _Dummy {
|
|
|
856
897
|
|
|
857
898
|
// src/graph/services/layers.ts
|
|
858
899
|
import { Seq } from "immutable";
|
|
859
|
-
var
|
|
900
|
+
var log5 = logger("layers");
|
|
860
901
|
var Layers = class {
|
|
861
902
|
static updateLayers(g) {
|
|
862
903
|
const stack = [...g.dirtyNodes].map((id) => g.getNode(id)).filter((node) => !node.isDummy).sort((a, b) => b.layerIndex(g) - a.layerIndex(g)).map((node) => node.id);
|
|
@@ -919,13 +960,11 @@ var Layers = class {
|
|
|
919
960
|
|
|
920
961
|
// src/graph/services/layout.ts
|
|
921
962
|
import { Seq as Seq2 } from "immutable";
|
|
922
|
-
var
|
|
963
|
+
var log6 = logger("layout");
|
|
923
964
|
var Layout = class _Layout {
|
|
924
965
|
static parentIndex(g, node) {
|
|
925
966
|
const parents = Seq2([...node.adjs(g, "segs", "in")]);
|
|
926
|
-
console.log(`parents of ${node.id}:`, [...parents], [...parents.map((p) => p.index)]);
|
|
927
967
|
const pidx = parents.map((p) => p.index).min();
|
|
928
|
-
log5.debug(`node ${node.id}: parent index ${pidx}`);
|
|
929
968
|
if (pidx !== void 0) return pidx;
|
|
930
969
|
return node.isDummy ? -Infinity : Infinity;
|
|
931
970
|
}
|
|
@@ -943,7 +982,6 @@ var Layout = class _Layout {
|
|
|
943
982
|
return minA.localeCompare(minB);
|
|
944
983
|
}
|
|
945
984
|
static positionNodes(g) {
|
|
946
|
-
console.log("positionNodes", g.dirtyNodes);
|
|
947
985
|
for (const nodeId of g.dirtyNodes)
|
|
948
986
|
g.dirtyLayers.add(g.getNode(nodeId).layerId);
|
|
949
987
|
let adjustNext = false;
|
|
@@ -951,15 +989,12 @@ var Layout = class _Layout {
|
|
|
951
989
|
if (!adjustNext && !g.dirtyLayers.has(layerId)) continue;
|
|
952
990
|
adjustNext = false;
|
|
953
991
|
let layer = g.getLayer(layerId);
|
|
954
|
-
console.log(`positioning layer ${layerId} at ${layer.index}`);
|
|
955
992
|
const pidxs = /* @__PURE__ */ new Map();
|
|
956
993
|
for (const nodeId of layer.nodeIds)
|
|
957
994
|
pidxs.set(nodeId, _Layout.parentIndex(g, g.getNode(nodeId)));
|
|
958
|
-
console.log("pidxs", pidxs);
|
|
959
995
|
const sorted = [...layer.nodeIds].sort(
|
|
960
996
|
(aId, bId) => _Layout.compareNodes(g, aId, bId, pidxs)
|
|
961
997
|
);
|
|
962
|
-
console.log(`sorted:`, sorted);
|
|
963
998
|
if (layer.hasSortOrder(sorted)) continue;
|
|
964
999
|
g.dirtyLayers.add(layerId);
|
|
965
1000
|
layer = layer.setSorted(g, sorted);
|
|
@@ -967,7 +1002,6 @@ var Layout = class _Layout {
|
|
|
967
1002
|
let lpos = 0;
|
|
968
1003
|
for (let i = 0; i < sorted.length; i++) {
|
|
969
1004
|
let node = g.getNode(sorted[i]);
|
|
970
|
-
log5.debug(`node ${node.id}: final index ${i}`);
|
|
971
1005
|
node = node.setIndex(g, i).setLayerPos(g, lpos);
|
|
972
1006
|
const size = node.dims?.[g.w] ?? 0;
|
|
973
1007
|
lpos += size + g.options.nodeMargin;
|
|
@@ -999,7 +1033,12 @@ var Layout = class _Layout {
|
|
|
999
1033
|
for (const layerId of layerIds) {
|
|
1000
1034
|
if (!adjustNext && !g.dirtyLayers.has(layerId)) continue;
|
|
1001
1035
|
adjustNext = false;
|
|
1036
|
+
let iterations = 0;
|
|
1002
1037
|
while (true) {
|
|
1038
|
+
if (++iterations > 10) {
|
|
1039
|
+
log6.error(`alignNodes: infinite loop detected in layer ${layerId}`);
|
|
1040
|
+
break;
|
|
1041
|
+
}
|
|
1003
1042
|
let changed = false;
|
|
1004
1043
|
const nodeIds = _Layout.sortLayer(g, layerId, reverseNodes);
|
|
1005
1044
|
for (const nodeId of nodeIds) {
|
|
@@ -1065,17 +1104,45 @@ var Layout = class _Layout {
|
|
|
1065
1104
|
[g.x]: p[g.x] + w / 2,
|
|
1066
1105
|
[g.y]: p[g.y] + h / 2
|
|
1067
1106
|
};
|
|
1068
|
-
p[g.x] += _Layout.nodePortOffset(g, nodeId, seg
|
|
1069
|
-
if (side == "
|
|
1107
|
+
p[g.x] += _Layout.nodePortOffset(g, nodeId, seg, side);
|
|
1108
|
+
if (side == "target" == g.r)
|
|
1070
1109
|
p[g.y] += h;
|
|
1071
1110
|
return p;
|
|
1072
1111
|
}
|
|
1073
|
-
static nodePortOffset(g, nodeId,
|
|
1074
|
-
|
|
1075
|
-
|
|
1112
|
+
static nodePortOffset(g, nodeId, seg, side) {
|
|
1113
|
+
const node = g.getNode(nodeId);
|
|
1114
|
+
const dir = side == "source" ? "out" : "in";
|
|
1115
|
+
const portId = seg[side].port;
|
|
1116
|
+
let min = 0, size = node.dims?.[g.w] ?? 0;
|
|
1117
|
+
if (portId) {
|
|
1118
|
+
const ports = node.ports?.[dir];
|
|
1119
|
+
const port = ports?.find((p) => p.id === portId);
|
|
1120
|
+
if (port?.offset !== void 0) {
|
|
1121
|
+
min = port.offset;
|
|
1122
|
+
size = port.size ?? 0;
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
const alt = side == "source" ? "target" : "source";
|
|
1126
|
+
let segs = [];
|
|
1127
|
+
const keyOf = (seg2) => `${seg2.type ?? ""}:${seg2[side].marker ?? ""}`;
|
|
1128
|
+
for (const segId of node.segs[dir])
|
|
1129
|
+
segs.push(g.getSeg(segId));
|
|
1130
|
+
if (portId) segs = segs.filter((s) => s[side].port == portId);
|
|
1131
|
+
const groups = Object.groupBy(segs, (s) => keyOf(s));
|
|
1132
|
+
const posMap = /* @__PURE__ */ new Map();
|
|
1133
|
+
for (const [key, segs2] of Object.entries(groups)) {
|
|
1134
|
+
let pos = Infinity;
|
|
1135
|
+
for (const seg2 of segs2) pos = Math.min(pos, seg2.node(g, alt).lpos);
|
|
1136
|
+
posMap.set(key, pos);
|
|
1137
|
+
}
|
|
1138
|
+
const keys = [...posMap.keys()].sort((a, b) => posMap.get(a) - posMap.get(b));
|
|
1139
|
+
const gap = size / (keys.length + 1);
|
|
1140
|
+
const index = keys.indexOf(keyOf(seg));
|
|
1141
|
+
return min + (index + 1) * gap;
|
|
1076
1142
|
}
|
|
1077
1143
|
static shiftNode(g, nodeId, alignId, dir, lpos, reverseMove, conservative) {
|
|
1078
1144
|
const node = g.getNode(nodeId);
|
|
1145
|
+
log6.debug(`shift ${nodeId} (at ${node.lpos}) to ${alignId} (at ${lpos})`);
|
|
1079
1146
|
if (!conservative)
|
|
1080
1147
|
_Layout.markAligned(g, nodeId, alignId, dir, lpos);
|
|
1081
1148
|
const space = g.options.nodeMargin;
|
|
@@ -1106,7 +1173,7 @@ var Layout = class _Layout {
|
|
|
1106
1173
|
g.getNode(node.aligned[dir]).setAligned(g, alt, void 0);
|
|
1107
1174
|
if (otherId)
|
|
1108
1175
|
g.getNode(otherId).setAligned(g, alt, nodeId);
|
|
1109
|
-
node.setAligned(g, dir, otherId);
|
|
1176
|
+
node.setAligned(g, dir, otherId).setLayerPos(g, lpos);
|
|
1110
1177
|
}
|
|
1111
1178
|
static *aligned(g, nodeId, dir) {
|
|
1112
1179
|
const visit = function* (node2, dir2) {
|
|
@@ -1166,39 +1233,141 @@ var Layout = class _Layout {
|
|
|
1166
1233
|
let pos = 0;
|
|
1167
1234
|
const dir = g.r ? -1 : 1;
|
|
1168
1235
|
const trackSep = Math.max(
|
|
1169
|
-
g.options.layerMargin,
|
|
1170
1236
|
g.options.edgeSpacing,
|
|
1171
1237
|
g.options.turnRadius
|
|
1172
1238
|
);
|
|
1239
|
+
const marginSep = Math.max(
|
|
1240
|
+
g.options.edgeSpacing,
|
|
1241
|
+
g.options.layerMargin,
|
|
1242
|
+
g.options.turnRadius + g.options.markerSize
|
|
1243
|
+
);
|
|
1173
1244
|
for (const layerId of g.layerList) {
|
|
1174
1245
|
let layer = g.getLayer(layerId);
|
|
1175
1246
|
let height;
|
|
1176
|
-
console.log(`getCoords: layer = ${layerId} at ${layer.index}`);
|
|
1177
1247
|
if (g.dirtyLayers.has(layerId)) {
|
|
1178
1248
|
height = Seq2(layer.nodes(g)).map((node) => node.dims?.[g.h] ?? 0).max() ?? 0;
|
|
1179
1249
|
layer = layer.setSize(g, height);
|
|
1180
1250
|
} else height = layer.size;
|
|
1181
|
-
console.log(`getCoords: layer = ${layerId}: pos = ${pos}, height = ${height}`);
|
|
1182
1251
|
for (const node of layer.nodes(g)) {
|
|
1183
1252
|
if (!g.dirtyNodes.has(node.id) && pos == layer.pos) continue;
|
|
1184
1253
|
const npos = { [g.x]: node.lpos, [g.y]: pos };
|
|
1185
1254
|
if (!g.n) npos[g.y] += dir * height;
|
|
1186
1255
|
if (g.r == g.n) npos[g.y] -= node.dims?.[g.h] ?? 0;
|
|
1187
|
-
console.log(`getCoords: node = ${node.id}: pos:`, npos);
|
|
1188
1256
|
node.setPos(g, npos);
|
|
1189
1257
|
}
|
|
1190
1258
|
layer = layer.setPos(g, pos);
|
|
1191
|
-
pos += dir * (height +
|
|
1259
|
+
pos += dir * (height + marginSep);
|
|
1192
1260
|
for (const track of layer.tracks) {
|
|
1193
1261
|
for (const segId of track)
|
|
1194
1262
|
g.getSeg(segId).setTrackPos(g, pos);
|
|
1195
|
-
pos += dir *
|
|
1263
|
+
pos += dir * trackSep;
|
|
1196
1264
|
}
|
|
1265
|
+
pos += dir * (marginSep - trackSep);
|
|
1197
1266
|
}
|
|
1198
1267
|
}
|
|
1199
1268
|
};
|
|
1200
1269
|
|
|
1270
|
+
// src/canvas/marker.tsx
|
|
1271
|
+
import { default as default2 } from "./marker.css?raw";
|
|
1272
|
+
import { jsx } from "jsx-dom/jsx-runtime";
|
|
1273
|
+
function arrow(size, classPrefix, reverse = false) {
|
|
1274
|
+
const h = size / 1.5;
|
|
1275
|
+
const w = size;
|
|
1276
|
+
const ry = h / 2;
|
|
1277
|
+
const suffix = reverse ? "-reverse" : "";
|
|
1278
|
+
return /* @__PURE__ */ jsx(
|
|
1279
|
+
"marker",
|
|
1280
|
+
{
|
|
1281
|
+
id: `g3p-marker-arrow${suffix}`,
|
|
1282
|
+
className: `${classPrefix}-marker ${classPrefix}-marker-arrow`,
|
|
1283
|
+
markerWidth: size,
|
|
1284
|
+
markerHeight: size,
|
|
1285
|
+
refX: "2",
|
|
1286
|
+
refY: ry,
|
|
1287
|
+
orient: reverse ? "auto-start-reverse" : "auto",
|
|
1288
|
+
markerUnits: "userSpaceOnUse",
|
|
1289
|
+
children: /* @__PURE__ */ jsx("path", { d: `M0,0 L0,${h} L${w},${ry} z` })
|
|
1290
|
+
}
|
|
1291
|
+
);
|
|
1292
|
+
}
|
|
1293
|
+
function circle(size, classPrefix, reverse = false) {
|
|
1294
|
+
const r = size / 3;
|
|
1295
|
+
const cy = size / 2;
|
|
1296
|
+
const suffix = reverse ? "-reverse" : "";
|
|
1297
|
+
return /* @__PURE__ */ jsx(
|
|
1298
|
+
"marker",
|
|
1299
|
+
{
|
|
1300
|
+
id: `g3p-marker-circle${suffix}`,
|
|
1301
|
+
className: `${classPrefix}-marker ${classPrefix}-marker-circle`,
|
|
1302
|
+
markerWidth: size,
|
|
1303
|
+
markerHeight: size,
|
|
1304
|
+
refX: "2",
|
|
1305
|
+
refY: cy,
|
|
1306
|
+
orient: reverse ? "auto-start-reverse" : "auto",
|
|
1307
|
+
markerUnits: "userSpaceOnUse",
|
|
1308
|
+
children: /* @__PURE__ */ jsx("circle", { cx: r + 2, cy, r })
|
|
1309
|
+
}
|
|
1310
|
+
);
|
|
1311
|
+
}
|
|
1312
|
+
function diamond(size, classPrefix, reverse = false) {
|
|
1313
|
+
const w = size * 0.7;
|
|
1314
|
+
const h = size / 2;
|
|
1315
|
+
const cy = size / 2;
|
|
1316
|
+
const suffix = reverse ? "-reverse" : "";
|
|
1317
|
+
return /* @__PURE__ */ jsx(
|
|
1318
|
+
"marker",
|
|
1319
|
+
{
|
|
1320
|
+
id: `g3p-marker-diamond${suffix}`,
|
|
1321
|
+
className: `${classPrefix}-marker ${classPrefix}-marker-diamond`,
|
|
1322
|
+
markerWidth: size,
|
|
1323
|
+
markerHeight: size,
|
|
1324
|
+
refX: "2",
|
|
1325
|
+
refY: cy,
|
|
1326
|
+
orient: reverse ? "auto-start-reverse" : "auto",
|
|
1327
|
+
markerUnits: "userSpaceOnUse",
|
|
1328
|
+
children: /* @__PURE__ */ jsx("path", { d: `M2,${cy} L${2 + w / 2},${cy - h / 2} L${2 + w},${cy} L${2 + w / 2},${cy + h / 2} z` })
|
|
1329
|
+
}
|
|
1330
|
+
);
|
|
1331
|
+
}
|
|
1332
|
+
function bar(size, classPrefix, reverse = false) {
|
|
1333
|
+
const h = size * 0.6;
|
|
1334
|
+
const cy = size / 2;
|
|
1335
|
+
const suffix = reverse ? "-reverse" : "";
|
|
1336
|
+
return /* @__PURE__ */ jsx(
|
|
1337
|
+
"marker",
|
|
1338
|
+
{
|
|
1339
|
+
id: `g3p-marker-bar${suffix}`,
|
|
1340
|
+
className: `${classPrefix}-marker ${classPrefix}-marker-bar`,
|
|
1341
|
+
markerWidth: size,
|
|
1342
|
+
markerHeight: size,
|
|
1343
|
+
refX: "2",
|
|
1344
|
+
refY: cy,
|
|
1345
|
+
orient: reverse ? "auto-start-reverse" : "auto",
|
|
1346
|
+
markerUnits: "userSpaceOnUse",
|
|
1347
|
+
children: /* @__PURE__ */ jsx("line", { x1: "2", y1: cy - h / 2, x2: "2", y2: cy + h / 2, "stroke-width": "2" })
|
|
1348
|
+
}
|
|
1349
|
+
);
|
|
1350
|
+
}
|
|
1351
|
+
function none(size, classPrefix, reverse = false) {
|
|
1352
|
+
return void 0;
|
|
1353
|
+
}
|
|
1354
|
+
function normalize(data) {
|
|
1355
|
+
let source = data.source?.marker ?? data.style?.marker?.source;
|
|
1356
|
+
let target = data.target?.marker ?? data.style?.marker?.target ?? "arrow";
|
|
1357
|
+
if (source == "none") source = void 0;
|
|
1358
|
+
if (target == "none") target = void 0;
|
|
1359
|
+
return { source, target };
|
|
1360
|
+
}
|
|
1361
|
+
var markerDefs = {
|
|
1362
|
+
arrow,
|
|
1363
|
+
circle,
|
|
1364
|
+
diamond,
|
|
1365
|
+
bar,
|
|
1366
|
+
none
|
|
1367
|
+
};
|
|
1368
|
+
|
|
1201
1369
|
// src/graph/services/lines.ts
|
|
1370
|
+
var log7 = logger("lines");
|
|
1202
1371
|
var Lines = class _Lines {
|
|
1203
1372
|
static layoutSeg(g, seg) {
|
|
1204
1373
|
const sourcePos = Layout.anchorPos(g, seg, "source");
|
|
@@ -1273,7 +1442,12 @@ var Lines = class _Lines {
|
|
|
1273
1442
|
const radius = g.options.turnRadius;
|
|
1274
1443
|
const p1 = Layout.anchorPos(g, seg, "source");
|
|
1275
1444
|
const p2 = Layout.anchorPos(g, seg, "target");
|
|
1276
|
-
const
|
|
1445
|
+
const source = seg.sourceNode(g);
|
|
1446
|
+
const target = seg.targetNode(g);
|
|
1447
|
+
const marker = normalize(seg);
|
|
1448
|
+
if (source.isDummy) marker.source = void 0;
|
|
1449
|
+
if (target.isDummy) marker.target = void 0;
|
|
1450
|
+
const path = seg.trackPos !== void 0 ? _Lines.createRailroadPath(g, p1, p2, seg.trackPos, radius, marker) : _Lines.createDirectPath(g, p1, p2, radius, marker);
|
|
1277
1451
|
const svg = _Lines.pathToSVG(path);
|
|
1278
1452
|
seg.setSVG(g, svg);
|
|
1279
1453
|
}
|
|
@@ -1299,7 +1473,7 @@ var Lines = class _Lines {
|
|
|
1299
1473
|
}
|
|
1300
1474
|
return line;
|
|
1301
1475
|
}
|
|
1302
|
-
static pathBuilder(g, start, end, trackPos, radius) {
|
|
1476
|
+
static pathBuilder(g, start, end, trackPos, radius, marker) {
|
|
1303
1477
|
const { x, y } = g;
|
|
1304
1478
|
const lr = end[x] > start[x];
|
|
1305
1479
|
const d = lr ? 1 : -1;
|
|
@@ -1311,6 +1485,8 @@ var Lines = class _Lines {
|
|
|
1311
1485
|
if (g.r) s = 1 - s;
|
|
1312
1486
|
if (!lr) s = 1 - s;
|
|
1313
1487
|
if (!g.v) s = 1 - s;
|
|
1488
|
+
if (marker.source) start[y] += o * (g.options.markerSize - 1);
|
|
1489
|
+
if (marker.target) end[y] -= o * (g.options.markerSize - 1);
|
|
1314
1490
|
const p = { ...start, s };
|
|
1315
1491
|
const path = [];
|
|
1316
1492
|
const advance = (p2, type) => {
|
|
@@ -1319,8 +1495,8 @@ var Lines = class _Lines {
|
|
|
1319
1495
|
return { x, y, lr, d, o, rd, ro, t, s, p, path, advance };
|
|
1320
1496
|
}
|
|
1321
1497
|
// Create a railroad-style path with two 90-degree turns
|
|
1322
|
-
static createRailroadPath(g, start, end, trackPos, radius) {
|
|
1323
|
-
const { x, y, rd, ro, t, s, p, path, advance } = this.pathBuilder(g, start, end, trackPos, radius);
|
|
1498
|
+
static createRailroadPath(g, start, end, trackPos, radius, marker) {
|
|
1499
|
+
const { x, y, rd, ro, t, s, p, path, advance } = this.pathBuilder(g, start, end, trackPos, radius, marker);
|
|
1324
1500
|
advance({ [y]: t - ro }, "line");
|
|
1325
1501
|
advance({ [x]: p[x] + rd, [y]: t }, "arc");
|
|
1326
1502
|
advance({ [x]: end[x] - rd }, "line");
|
|
@@ -1329,8 +1505,8 @@ var Lines = class _Lines {
|
|
|
1329
1505
|
return path;
|
|
1330
1506
|
}
|
|
1331
1507
|
// Create a mostly-vertical path with optional S-curve
|
|
1332
|
-
static createDirectPath(g, start, end, radius) {
|
|
1333
|
-
const { x, y, d, o, s, p, path, advance } = this.pathBuilder(g, start, end, 0, radius);
|
|
1508
|
+
static createDirectPath(g, start, end, radius, marker) {
|
|
1509
|
+
const { x, y, d, o, s, p, path, advance } = this.pathBuilder(g, start, end, 0, radius, marker);
|
|
1334
1510
|
const dx = Math.abs(end.x - start.x);
|
|
1335
1511
|
const dy = Math.abs(end.y - start.y);
|
|
1336
1512
|
const d_ = { x: dx, y: dy };
|
|
@@ -1421,21 +1597,14 @@ var Lines = class _Lines {
|
|
|
1421
1597
|
}
|
|
1422
1598
|
};
|
|
1423
1599
|
|
|
1424
|
-
// src/graph/
|
|
1425
|
-
var
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
turnRadius: 10,
|
|
1433
|
-
orientation: "TB",
|
|
1434
|
-
layerMargin: 5,
|
|
1435
|
-
alignIterations: 5,
|
|
1436
|
-
alignThreshold: 10,
|
|
1437
|
-
separateTrackSets: true,
|
|
1438
|
-
layoutSteps: null
|
|
1600
|
+
// src/graph/graph.ts
|
|
1601
|
+
var emptyChanges = {
|
|
1602
|
+
addedNodes: [],
|
|
1603
|
+
removedNodes: [],
|
|
1604
|
+
updatedNodes: [],
|
|
1605
|
+
addedEdges: [],
|
|
1606
|
+
removedEdges: [],
|
|
1607
|
+
updatedEdges: []
|
|
1439
1608
|
};
|
|
1440
1609
|
var Graph = class _Graph {
|
|
1441
1610
|
prior;
|
|
@@ -1464,30 +1633,10 @@ var Graph = class _Graph {
|
|
|
1464
1633
|
x;
|
|
1465
1634
|
y;
|
|
1466
1635
|
d;
|
|
1467
|
-
constructor({ prior, changes, options
|
|
1636
|
+
constructor({ prior, changes, options }) {
|
|
1637
|
+
this.options = prior?.options ?? options;
|
|
1638
|
+
this.changes = changes ?? emptyChanges;
|
|
1468
1639
|
this.initFromPrior(prior);
|
|
1469
|
-
this.dirtyNodes = /* @__PURE__ */ new Set();
|
|
1470
|
-
this.dirtyEdges = /* @__PURE__ */ new Set();
|
|
1471
|
-
this.dirtyLayers = /* @__PURE__ */ new Set();
|
|
1472
|
-
this.dirtySegs = /* @__PURE__ */ new Set();
|
|
1473
|
-
this.delNodes = /* @__PURE__ */ new Set();
|
|
1474
|
-
this.delEdges = /* @__PURE__ */ new Set();
|
|
1475
|
-
this.delSegs = /* @__PURE__ */ new Set();
|
|
1476
|
-
this.options = {
|
|
1477
|
-
...defaultOptions,
|
|
1478
|
-
...prior?.options,
|
|
1479
|
-
...options
|
|
1480
|
-
};
|
|
1481
|
-
this.changes = changes ?? {
|
|
1482
|
-
addedNodes: [],
|
|
1483
|
-
removedNodes: [],
|
|
1484
|
-
updatedNodes: [],
|
|
1485
|
-
addedEdges: [],
|
|
1486
|
-
removedEdges: []
|
|
1487
|
-
};
|
|
1488
|
-
this.changes.addedNodes.push(...nodes || []);
|
|
1489
|
-
this.changes.addedEdges.push(...edges || []);
|
|
1490
|
-
this.dirty = this.changes.addedNodes.length > 0 || this.changes.removedNodes.length > 0 || this.changes.addedEdges.length > 0 || this.changes.removedEdges.length > 0;
|
|
1491
1640
|
this.r = this.options.orientation === "BT" || this.options.orientation === "RL";
|
|
1492
1641
|
this.v = this.options.orientation === "TB" || this.options.orientation === "BT";
|
|
1493
1642
|
this.h = this.v ? "h" : "w";
|
|
@@ -1503,38 +1652,41 @@ var Graph = class _Graph {
|
|
|
1503
1652
|
this.n = true;
|
|
1504
1653
|
else
|
|
1505
1654
|
this.n = natAligns[this.options.orientation] == this.options.nodeAlign;
|
|
1506
|
-
if (this.dirty)
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1655
|
+
if (this.dirty) this.processUpdate();
|
|
1656
|
+
}
|
|
1657
|
+
processUpdate() {
|
|
1658
|
+
try {
|
|
1659
|
+
this.beginMutate();
|
|
1660
|
+
this.applyChanges();
|
|
1661
|
+
Cycles.checkCycles(this);
|
|
1662
|
+
Layers.updateLayers(this);
|
|
1663
|
+
Dummy.updateDummies(this);
|
|
1664
|
+
Dummy.mergeDummies(this);
|
|
1665
|
+
Layout.positionNodes(this);
|
|
1666
|
+
Layout.alignAll(this);
|
|
1667
|
+
Lines.trackEdges(this);
|
|
1668
|
+
Layout.getCoords(this);
|
|
1669
|
+
Lines.pathEdges(this);
|
|
1670
|
+
} catch (e) {
|
|
1671
|
+
this.initFromPrior(this.prior);
|
|
1672
|
+
throw e;
|
|
1673
|
+
} finally {
|
|
1674
|
+
this.endMutate();
|
|
1525
1675
|
}
|
|
1526
1676
|
}
|
|
1527
1677
|
applyChanges() {
|
|
1528
1678
|
for (const edge of this.changes.removedEdges)
|
|
1529
|
-
|
|
1679
|
+
Edge.del(this, edge);
|
|
1530
1680
|
for (const node of this.changes.removedNodes)
|
|
1531
|
-
|
|
1681
|
+
Node.del(this, node);
|
|
1532
1682
|
for (const node of this.changes.addedNodes)
|
|
1533
1683
|
Node.addNormal(this, node);
|
|
1534
1684
|
for (const edge of this.changes.addedEdges)
|
|
1535
1685
|
Edge.add(this, edge);
|
|
1536
1686
|
for (const node of this.changes.updatedNodes)
|
|
1537
|
-
|
|
1687
|
+
Node.update(this, node);
|
|
1688
|
+
for (const edge of this.changes.updatedEdges)
|
|
1689
|
+
Edge.update(this, edge);
|
|
1538
1690
|
}
|
|
1539
1691
|
layerAt(index) {
|
|
1540
1692
|
while (index >= this.layerList.size)
|
|
@@ -1611,24 +1763,24 @@ var Graph = class _Graph {
|
|
|
1611
1763
|
nodes.forEach((node) => mutator.addNode(node));
|
|
1612
1764
|
});
|
|
1613
1765
|
}
|
|
1614
|
-
|
|
1766
|
+
removeNodes(...nodes) {
|
|
1615
1767
|
return this.withMutations((mutator) => {
|
|
1616
|
-
|
|
1768
|
+
nodes.forEach((node) => mutator.removeNode(node));
|
|
1617
1769
|
});
|
|
1618
1770
|
}
|
|
1619
|
-
|
|
1771
|
+
removeNode(node) {
|
|
1620
1772
|
return this.withMutations((mutator) => {
|
|
1621
|
-
mutator.
|
|
1773
|
+
mutator.removeNode(node);
|
|
1622
1774
|
});
|
|
1623
1775
|
}
|
|
1624
|
-
|
|
1776
|
+
addEdges(...edges) {
|
|
1625
1777
|
return this.withMutations((mutator) => {
|
|
1626
|
-
|
|
1778
|
+
edges.forEach((edge) => mutator.addEdge(edge));
|
|
1627
1779
|
});
|
|
1628
1780
|
}
|
|
1629
|
-
|
|
1781
|
+
addEdge(edge) {
|
|
1630
1782
|
return this.withMutations((mutator) => {
|
|
1631
|
-
mutator.
|
|
1783
|
+
mutator.addEdge(edge);
|
|
1632
1784
|
});
|
|
1633
1785
|
}
|
|
1634
1786
|
removeEdges(...edges) {
|
|
@@ -1678,6 +1830,14 @@ var Graph = class _Graph {
|
|
|
1678
1830
|
this.nextLayerId = prior?.nextLayerId ?? 0;
|
|
1679
1831
|
this.nextDummyId = prior?.nextDummyId ?? 0;
|
|
1680
1832
|
this.prior = prior;
|
|
1833
|
+
this.dirtyNodes = /* @__PURE__ */ new Set();
|
|
1834
|
+
this.dirtyEdges = /* @__PURE__ */ new Set();
|
|
1835
|
+
this.dirtyLayers = /* @__PURE__ */ new Set();
|
|
1836
|
+
this.dirtySegs = /* @__PURE__ */ new Set();
|
|
1837
|
+
this.delNodes = /* @__PURE__ */ new Set();
|
|
1838
|
+
this.delEdges = /* @__PURE__ */ new Set();
|
|
1839
|
+
this.delSegs = /* @__PURE__ */ new Set();
|
|
1840
|
+
this.dirty = this.changes.addedNodes.length > 0 || this.changes.removedNodes.length > 0 || this.changes.updatedNodes.length > 0 || this.changes.addedEdges.length > 0 || this.changes.removedEdges.length > 0;
|
|
1681
1841
|
}
|
|
1682
1842
|
beginMutate() {
|
|
1683
1843
|
this.nodes = this.nodes.asMutable();
|
|
@@ -1707,15 +1867,20 @@ var Graph = class _Graph {
|
|
|
1707
1867
|
}
|
|
1708
1868
|
};
|
|
1709
1869
|
|
|
1710
|
-
// src/
|
|
1711
|
-
var
|
|
1870
|
+
// src/common.ts
|
|
1871
|
+
var screenPos = (x, y) => ({ x, y });
|
|
1872
|
+
var canvasPos = (x, y) => ({ x, y });
|
|
1873
|
+
var graphPos = (x, y) => ({ x, y });
|
|
1874
|
+
|
|
1875
|
+
// src/canvas/node.tsx
|
|
1876
|
+
import styles from "./node.css?raw";
|
|
1712
1877
|
|
|
1713
1878
|
// src/canvas/styler.ts
|
|
1714
1879
|
var injected = {};
|
|
1715
|
-
function styler(name,
|
|
1880
|
+
function styler(name, styles4, prefix) {
|
|
1716
1881
|
if (prefix === "g3p" && !injected[name]) {
|
|
1717
1882
|
const style = document.createElement("style");
|
|
1718
|
-
style.textContent =
|
|
1883
|
+
style.textContent = styles4;
|
|
1719
1884
|
document.head.appendChild(style);
|
|
1720
1885
|
injected[name] = true;
|
|
1721
1886
|
}
|
|
@@ -1727,98 +1892,106 @@ function styler(name, styles, prefix) {
|
|
|
1727
1892
|
};
|
|
1728
1893
|
}
|
|
1729
1894
|
|
|
1730
|
-
// src/canvas/node.css
|
|
1731
|
-
var node_default = ".g3p-node-container {\n transition: opacity 0.2s ease;\n}\n\n.g3p-node-background {\n fill: var(--graph-node-bg, #ffffff);\n filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.1));\n}\n\n.g3p-node-border {\n stroke: var(--graph-node-border, #cbd5e1);\n transition: stroke 0.2s ease, stroke-width 0.2s ease;\n}\n\n.g3p-node-background.hovered {\n fill: var(--graph-node-hover, #f1f5f9);\n}\n\n.g3p-node-border.hovered {\n stroke: #94a3b8;\n}\n\n.g3p-node-border.selected {\n stroke: var(--graph-node-border-selected, #3b82f6);\n stroke-width: 3;\n}\n\n.g3p-node-content-wrapper {\n pointer-events: none;\n}\n\n.g3p-node-content {\n pointer-events: auto;\n box-sizing: border-box;\n}\n\n.g3p-node-content>div {\n width: 100%;\n height: 100%;\n}\n\n/* Dummy node styles */\n.g3p-node-dummy .g3p-node-background {\n fill: var(--graph-dummy-node-bg, #f8fafc);\n opacity: 0.8;\n}\n\n.g3p-node-dummy .g3p-node-border {\n stroke: var(--graph-dummy-node-border, #cbd5e1);\n stroke-dasharray: 3, 3;\n}";
|
|
1732
|
-
|
|
1733
1895
|
// src/canvas/node.tsx
|
|
1734
|
-
import {
|
|
1896
|
+
import { jsx as jsx2, jsxs } from "jsx-dom/jsx-runtime";
|
|
1897
|
+
var log8 = logger("canvas");
|
|
1735
1898
|
var Node2 = class {
|
|
1736
1899
|
selected;
|
|
1737
1900
|
hovered;
|
|
1738
1901
|
container;
|
|
1739
|
-
dims;
|
|
1740
1902
|
content;
|
|
1741
|
-
|
|
1903
|
+
canvas;
|
|
1904
|
+
data;
|
|
1905
|
+
classPrefix;
|
|
1742
1906
|
isDummy;
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
onMouseDown: () => null,
|
|
1754
|
-
onMouseUp: () => null,
|
|
1755
|
-
classPrefix: "g3p",
|
|
1756
|
-
...options
|
|
1757
|
-
});
|
|
1758
|
-
if (!this.isDummy) {
|
|
1759
|
-
this.content = this.renderNode(this.data);
|
|
1760
|
-
this.measured = false;
|
|
1907
|
+
pos;
|
|
1908
|
+
constructor(canvas, data, isDummy = false) {
|
|
1909
|
+
this.canvas = canvas;
|
|
1910
|
+
this.data = data;
|
|
1911
|
+
this.selected = false;
|
|
1912
|
+
this.hovered = false;
|
|
1913
|
+
this.classPrefix = canvas.classPrefix;
|
|
1914
|
+
this.isDummy = isDummy;
|
|
1915
|
+
if (this.isDummy) {
|
|
1916
|
+
const size = canvas.dummyNodeSize;
|
|
1761
1917
|
} else {
|
|
1762
|
-
|
|
1918
|
+
const render = data.render ?? canvas.renderNode;
|
|
1919
|
+
this.content = this.renderContent(render(data.data));
|
|
1763
1920
|
}
|
|
1764
1921
|
}
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1922
|
+
remove() {
|
|
1923
|
+
this.container.remove();
|
|
1924
|
+
}
|
|
1925
|
+
append() {
|
|
1926
|
+
console.log("append", this);
|
|
1927
|
+
this.canvas.group.appendChild(this.container);
|
|
1928
|
+
}
|
|
1929
|
+
needsContentSize() {
|
|
1930
|
+
return !this.isDummy && this.content instanceof HTMLElement;
|
|
1931
|
+
}
|
|
1932
|
+
needsContainerSize() {
|
|
1933
|
+
return !this.isDummy;
|
|
1769
1934
|
}
|
|
1770
1935
|
handleClick(e) {
|
|
1771
1936
|
e.stopPropagation();
|
|
1772
|
-
this.onClick?.(this.data, e);
|
|
1773
1937
|
}
|
|
1774
1938
|
handleMouseEnter(e) {
|
|
1775
|
-
this.onMouseEnter?.(this.data, e);
|
|
1776
1939
|
}
|
|
1777
1940
|
handleMouseLeave(e) {
|
|
1778
|
-
this.onMouseLeave?.(this.data, e);
|
|
1779
1941
|
}
|
|
1780
1942
|
handleContextMenu(e) {
|
|
1781
|
-
if (this.onContextMenu) {
|
|
1782
|
-
e.stopPropagation();
|
|
1783
|
-
this.onContextMenu(this.data, e);
|
|
1784
|
-
}
|
|
1785
1943
|
}
|
|
1786
1944
|
handleMouseDown(e) {
|
|
1787
|
-
this.onMouseDown?.(this.data, e);
|
|
1788
1945
|
}
|
|
1789
1946
|
handleMouseUp(e) {
|
|
1790
|
-
this.onMouseUp?.(this.data, e);
|
|
1791
1947
|
}
|
|
1792
1948
|
setPos(pos) {
|
|
1793
|
-
console.log(`setPos:`, this, pos);
|
|
1794
1949
|
this.pos = pos;
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1950
|
+
const { x, y } = pos;
|
|
1951
|
+
this.container.setAttribute("transform", `translate(${x}, ${y})`);
|
|
1952
|
+
}
|
|
1953
|
+
hasPorts() {
|
|
1954
|
+
return !!this.data?.ports?.in?.length || !!this.data?.ports?.out?.length;
|
|
1955
|
+
}
|
|
1956
|
+
renderContent(el) {
|
|
1957
|
+
const hasPorts = this.hasPorts();
|
|
1958
|
+
el = this.renderBorder(el);
|
|
1959
|
+
if (hasPorts)
|
|
1960
|
+
el = this.renderOutsidePorts(el);
|
|
1961
|
+
return el;
|
|
1962
|
+
}
|
|
1963
|
+
renderContainer() {
|
|
1964
|
+
const c = styler("node", styles, this.classPrefix);
|
|
1965
|
+
const hasPorts = this.hasPorts();
|
|
1966
|
+
const inner = this.isDummy ? this.renderDummy() : this.renderForeign();
|
|
1967
|
+
const nodeType = this.data?.type;
|
|
1968
|
+
const typeClass = nodeType ? `g3p-node-type-${nodeType}` : "";
|
|
1969
|
+
this.container = /* @__PURE__ */ jsx2(
|
|
1801
1970
|
"g",
|
|
1802
1971
|
{
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
onMouseUp: this.handleMouseUp.bind(this),
|
|
1972
|
+
className: `${c("container")} ${c("dummy", this.isDummy)} ${typeClass}`.trim(),
|
|
1973
|
+
onClick: (e) => this.handleClick(e),
|
|
1974
|
+
onMouseEnter: (e) => this.handleMouseEnter(e),
|
|
1975
|
+
onMouseLeave: (e) => this.handleMouseLeave(e),
|
|
1976
|
+
onContextMenu: (e) => this.handleContextMenu(e),
|
|
1977
|
+
onMouseDown: (e) => this.handleMouseDown(e),
|
|
1978
|
+
onMouseUp: (e) => this.handleMouseUp(e),
|
|
1811
1979
|
style: { cursor: "pointer" },
|
|
1812
|
-
children:
|
|
1980
|
+
children: inner
|
|
1813
1981
|
}
|
|
1814
1982
|
);
|
|
1815
1983
|
}
|
|
1984
|
+
renderForeign() {
|
|
1985
|
+
const { w, h } = this.data.dims;
|
|
1986
|
+
return /* @__PURE__ */ jsx2("foreignObject", { width: w, height: h, children: this.content });
|
|
1987
|
+
}
|
|
1816
1988
|
renderDummy() {
|
|
1817
|
-
const c = styler("node",
|
|
1818
|
-
let
|
|
1989
|
+
const c = styler("node", styles, this.classPrefix);
|
|
1990
|
+
let w = this.canvas.dummyNodeSize;
|
|
1991
|
+
let h = this.canvas.dummyNodeSize;
|
|
1819
1992
|
w /= 2;
|
|
1820
1993
|
h /= 2;
|
|
1821
|
-
return /* @__PURE__ */ jsxs(
|
|
1994
|
+
return /* @__PURE__ */ jsxs("g", { children: [
|
|
1822
1995
|
/* @__PURE__ */ jsx2(
|
|
1823
1996
|
"ellipse",
|
|
1824
1997
|
{
|
|
@@ -1837,157 +2010,190 @@ var Node2 = class {
|
|
|
1837
2010
|
rx: w,
|
|
1838
2011
|
ry: h,
|
|
1839
2012
|
fill: "none",
|
|
1840
|
-
className: c("border")
|
|
1841
|
-
strokeWidth: "2"
|
|
2013
|
+
className: c("border")
|
|
1842
2014
|
}
|
|
1843
2015
|
)
|
|
1844
2016
|
] });
|
|
1845
2017
|
}
|
|
1846
|
-
|
|
1847
|
-
const
|
|
1848
|
-
const
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
className: c("border"),
|
|
1864
|
-
width: w,
|
|
1865
|
-
height: h,
|
|
1866
|
-
rx: 8,
|
|
1867
|
-
ry: 8,
|
|
1868
|
-
fill: "none",
|
|
1869
|
-
strokeWidth: "2"
|
|
1870
|
-
}
|
|
1871
|
-
),
|
|
1872
|
-
/* @__PURE__ */ jsx2(
|
|
1873
|
-
"foreignObject",
|
|
1874
|
-
{
|
|
1875
|
-
width: w,
|
|
1876
|
-
height: h,
|
|
1877
|
-
className: c("content-wrapper"),
|
|
1878
|
-
children: /* @__PURE__ */ jsx2(
|
|
1879
|
-
"div",
|
|
1880
|
-
{
|
|
1881
|
-
className: c("content"),
|
|
1882
|
-
style: {
|
|
1883
|
-
width: `${w}px`,
|
|
1884
|
-
height: `${h}px`,
|
|
1885
|
-
overflow: "hidden"
|
|
1886
|
-
},
|
|
1887
|
-
children: this.content
|
|
1888
|
-
}
|
|
1889
|
-
)
|
|
2018
|
+
measure(isVertical) {
|
|
2019
|
+
const rect = this.content.getBoundingClientRect();
|
|
2020
|
+
const data = this.data;
|
|
2021
|
+
data.dims = { w: rect.width, h: rect.height };
|
|
2022
|
+
for (const dir of ["in", "out"]) {
|
|
2023
|
+
const ports = data.ports?.[dir];
|
|
2024
|
+
if (!ports) continue;
|
|
2025
|
+
for (const port of ports) {
|
|
2026
|
+
const el = this.content.querySelector(`#g3p-port-${data.id}-${port.id}`);
|
|
2027
|
+
if (!el) continue;
|
|
2028
|
+
const portRect = el.getBoundingClientRect();
|
|
2029
|
+
if (isVertical) {
|
|
2030
|
+
port.offset = portRect.left - rect.left;
|
|
2031
|
+
port.size = portRect.width;
|
|
2032
|
+
} else {
|
|
2033
|
+
port.offset = portRect.top - rect.top;
|
|
2034
|
+
port.size = portRect.height;
|
|
1890
2035
|
}
|
|
1891
|
-
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
getPortPosition(dir) {
|
|
2040
|
+
const o = this.canvas.orientation;
|
|
2041
|
+
if (dir === "in") {
|
|
2042
|
+
if (o === "TB") return "top";
|
|
2043
|
+
if (o === "BT") return "bottom";
|
|
2044
|
+
if (o === "LR") return "left";
|
|
2045
|
+
return "right";
|
|
2046
|
+
} else {
|
|
2047
|
+
if (o === "TB") return "bottom";
|
|
2048
|
+
if (o === "BT") return "top";
|
|
2049
|
+
if (o === "LR") return "right";
|
|
2050
|
+
return "left";
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
isVerticalOrientation() {
|
|
2054
|
+
const o = this.canvas.orientation;
|
|
2055
|
+
return o === "TB" || o === "BT";
|
|
2056
|
+
}
|
|
2057
|
+
isReversedOrientation() {
|
|
2058
|
+
const o = this.canvas.orientation;
|
|
2059
|
+
return o === "BT" || o === "RL";
|
|
2060
|
+
}
|
|
2061
|
+
renderPortRow(dir, inout) {
|
|
2062
|
+
const ports = this.data?.ports?.[dir];
|
|
2063
|
+
if (!ports?.length) return null;
|
|
2064
|
+
const c = styler("node", styles, this.classPrefix);
|
|
2065
|
+
const pos = this.getPortPosition(dir);
|
|
2066
|
+
const isVertical = this.isVerticalOrientation();
|
|
2067
|
+
const layoutClass = isVertical ? "row" : "col";
|
|
2068
|
+
const rotateLabels = false;
|
|
2069
|
+
const rotateClass = rotateLabels ? `port-rotated-${pos}` : "";
|
|
2070
|
+
return /* @__PURE__ */ jsx2("div", { className: `${c("ports")} ${c(`ports-${layoutClass}`)}`, children: ports.map((port) => /* @__PURE__ */ jsx2(
|
|
2071
|
+
"div",
|
|
2072
|
+
{
|
|
2073
|
+
id: `g3p-port-${this.data.id}-${port.id}`,
|
|
2074
|
+
className: `${c("port")} ${c(`port-${inout}-${pos}`)} ${c(rotateClass)}`,
|
|
2075
|
+
children: port.label ?? port.id
|
|
2076
|
+
}
|
|
2077
|
+
)) });
|
|
2078
|
+
}
|
|
2079
|
+
renderInsidePorts(el) {
|
|
2080
|
+
const c = styler("node", styles, this.classPrefix);
|
|
2081
|
+
const isVertical = this.isVerticalOrientation();
|
|
2082
|
+
const isReversed = this.isReversedOrientation();
|
|
2083
|
+
let inPorts = this.renderPortRow("in", "in");
|
|
2084
|
+
let outPorts = this.renderPortRow("out", "in");
|
|
2085
|
+
if (!inPorts && !outPorts) return el;
|
|
2086
|
+
if (isReversed) [inPorts, outPorts] = [outPorts, inPorts];
|
|
2087
|
+
const wrapperClass = isVertical ? "v" : "h";
|
|
2088
|
+
return /* @__PURE__ */ jsxs("div", { className: `${c("with-ports")} ${c(`with-ports-${wrapperClass}`)}`, children: [
|
|
2089
|
+
inPorts,
|
|
2090
|
+
el,
|
|
2091
|
+
outPorts
|
|
1892
2092
|
] });
|
|
1893
2093
|
}
|
|
2094
|
+
renderOutsidePorts(el) {
|
|
2095
|
+
const c = styler("node", styles, this.classPrefix);
|
|
2096
|
+
const isVertical = this.isVerticalOrientation();
|
|
2097
|
+
const isReversed = this.isReversedOrientation();
|
|
2098
|
+
let inPorts = this.renderPortRow("in", "out");
|
|
2099
|
+
let outPorts = this.renderPortRow("out", "out");
|
|
2100
|
+
if (!inPorts && !outPorts) return el;
|
|
2101
|
+
if (isReversed) [inPorts, outPorts] = [outPorts, inPorts];
|
|
2102
|
+
const wrapperClass = isVertical ? "v" : "h";
|
|
2103
|
+
return /* @__PURE__ */ jsxs("div", { className: `${c("with-ports")} ${c(`with-ports-${wrapperClass}`)}`, children: [
|
|
2104
|
+
inPorts,
|
|
2105
|
+
el,
|
|
2106
|
+
outPorts
|
|
2107
|
+
] });
|
|
2108
|
+
}
|
|
2109
|
+
renderBorder(el) {
|
|
2110
|
+
const c = styler("node", styles, this.classPrefix);
|
|
2111
|
+
return /* @__PURE__ */ jsx2("div", { className: c("border"), children: el });
|
|
2112
|
+
}
|
|
1894
2113
|
};
|
|
1895
2114
|
|
|
1896
|
-
// src/canvas/seg.css
|
|
1897
|
-
var seg_default = ".g3p-seg-container {\n transition: opacity 0.2s ease;\n}\n\n.g3p-seg-line {\n transition: stroke 0.2s ease, stroke-width 0.2s ease;\n}\n\n.g3p-seg-line.hovered {\n stroke-width: 4;\n opacity: 1;\n}\n\n.g3p-seg-line.selected {\n stroke: var(--graph-node-border-selected, #3b82f6);\n stroke-width: 3;\n}\n\n.g3p-seg-hitbox {\n cursor: pointer;\n}";
|
|
1898
|
-
|
|
1899
2115
|
// src/canvas/seg.tsx
|
|
2116
|
+
import styles2 from "./seg.css?raw";
|
|
1900
2117
|
import { jsx as jsx3, jsxs as jsxs2 } from "jsx-dom/jsx-runtime";
|
|
2118
|
+
var log9 = logger("canvas");
|
|
1901
2119
|
var Seg2 = class {
|
|
2120
|
+
id;
|
|
1902
2121
|
selected;
|
|
1903
2122
|
hovered;
|
|
1904
|
-
|
|
2123
|
+
canvas;
|
|
2124
|
+
classPrefix;
|
|
2125
|
+
type;
|
|
2126
|
+
svg;
|
|
2127
|
+
el;
|
|
2128
|
+
source;
|
|
2129
|
+
target;
|
|
2130
|
+
constructor(canvas, data, g) {
|
|
2131
|
+
this.id = data.id;
|
|
2132
|
+
this.canvas = canvas;
|
|
1905
2133
|
this.selected = false;
|
|
1906
2134
|
this.hovered = false;
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
},
|
|
1914
|
-
onContextMenu: () => {
|
|
1915
|
-
},
|
|
1916
|
-
classPrefix: "g3p",
|
|
1917
|
-
...options
|
|
1918
|
-
});
|
|
1919
|
-
this.attrs ??= {};
|
|
1920
|
-
this.attrs.targetTerminal ??= "arrow";
|
|
2135
|
+
this.svg = data.svg;
|
|
2136
|
+
this.classPrefix = canvas.classPrefix;
|
|
2137
|
+
this.source = { ...data.source, isDummy: data.sourceNode(g).isDummy };
|
|
2138
|
+
this.target = { ...data.target, isDummy: data.targetNode(g).isDummy };
|
|
2139
|
+
this.type = data.type;
|
|
2140
|
+
this.el = this.render();
|
|
1921
2141
|
}
|
|
1922
2142
|
handleClick(e) {
|
|
1923
2143
|
e.stopPropagation();
|
|
1924
|
-
this.onClick?.(this.edgeData, e);
|
|
1925
2144
|
}
|
|
1926
2145
|
handleMouseEnter(e) {
|
|
1927
|
-
this.onMouseEnter?.(this.edgeData, e);
|
|
1928
2146
|
}
|
|
1929
2147
|
handleMouseLeave(e) {
|
|
1930
|
-
this.onMouseLeave?.(this.edgeData, e);
|
|
1931
2148
|
}
|
|
1932
2149
|
handleContextMenu(e) {
|
|
1933
|
-
if (this.onContextMenu) {
|
|
1934
|
-
e.stopPropagation();
|
|
1935
|
-
this.onContextMenu(this.edgeData, e);
|
|
1936
|
-
}
|
|
1937
2150
|
}
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
source: this.renderTerminal(this.attrs.sourceTerminal, "source"),
|
|
1941
|
-
target: this.renderTerminal(this.attrs.targetTerminal, "target")
|
|
1942
|
-
};
|
|
2151
|
+
append() {
|
|
2152
|
+
this.canvas.group.appendChild(this.el);
|
|
1943
2153
|
}
|
|
1944
|
-
|
|
1945
|
-
this.
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
this.
|
|
2154
|
+
remove() {
|
|
2155
|
+
this.el.remove();
|
|
2156
|
+
}
|
|
2157
|
+
update(data) {
|
|
2158
|
+
this.svg = data.svg;
|
|
2159
|
+
this.type = data.type;
|
|
2160
|
+
this.source = data.source;
|
|
2161
|
+
this.target = data.target;
|
|
2162
|
+
this.remove();
|
|
2163
|
+
this.el = this.render();
|
|
2164
|
+
this.append();
|
|
1949
2165
|
}
|
|
1950
2166
|
render() {
|
|
1951
|
-
const c = styler("
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
};
|
|
1957
|
-
const hoverAttrs = {
|
|
1958
|
-
...styleAttrs,
|
|
1959
|
-
strokeWidth: styleAttrs.strokeWidth ? Math.max(styleAttrs.strokeWidth * 3, 10) : void 0
|
|
1960
|
-
};
|
|
1961
|
-
const { source, target } = this.renderTerminals();
|
|
2167
|
+
const c = styler("seg", styles2, this.classPrefix);
|
|
2168
|
+
let { source, target } = normalize(this);
|
|
2169
|
+
if (this.source.isDummy) source = void 0;
|
|
2170
|
+
if (this.target.isDummy) target = void 0;
|
|
2171
|
+
const typeClass = this.type ? `g3p-edge-type-${this.type}` : "";
|
|
1962
2172
|
return /* @__PURE__ */ jsxs2(
|
|
1963
2173
|
"g",
|
|
1964
2174
|
{
|
|
1965
2175
|
ref: (el) => this.el = el,
|
|
1966
|
-
id: `g3p-seg-${this.
|
|
1967
|
-
className: c("container"),
|
|
2176
|
+
id: `g3p-seg-${this.id}`,
|
|
2177
|
+
className: `${c("container")} ${typeClass}`.trim(),
|
|
1968
2178
|
onClick: this.handleClick.bind(this),
|
|
1969
2179
|
onMouseEnter: this.handleMouseEnter.bind(this),
|
|
1970
2180
|
onMouseLeave: this.handleMouseLeave.bind(this),
|
|
1971
2181
|
onContextMenu: this.handleContextMenu.bind(this),
|
|
1972
2182
|
children: [
|
|
1973
|
-
source?.defs,
|
|
1974
|
-
target?.defs,
|
|
1975
2183
|
/* @__PURE__ */ jsx3(
|
|
1976
2184
|
"path",
|
|
1977
2185
|
{
|
|
1978
2186
|
d: this.svg,
|
|
1979
|
-
...styleAttrs,
|
|
1980
2187
|
fill: "none",
|
|
1981
2188
|
className: c("line"),
|
|
1982
|
-
markerStart: source ? `url(
|
|
1983
|
-
markerEnd: target ? `url(
|
|
2189
|
+
markerStart: source ? `url(#g3p-marker-${source}-reverse)` : void 0,
|
|
2190
|
+
markerEnd: target ? `url(#g3p-marker-${target})` : void 0
|
|
1984
2191
|
}
|
|
1985
2192
|
),
|
|
1986
2193
|
/* @__PURE__ */ jsx3(
|
|
1987
2194
|
"path",
|
|
1988
2195
|
{
|
|
1989
2196
|
d: this.svg,
|
|
1990
|
-
...hoverAttrs,
|
|
1991
2197
|
stroke: "transparent",
|
|
1992
2198
|
fill: "none",
|
|
1993
2199
|
className: c("hitbox"),
|
|
@@ -1998,29 +2204,48 @@ var Seg2 = class {
|
|
|
1998
2204
|
}
|
|
1999
2205
|
);
|
|
2000
2206
|
}
|
|
2001
|
-
renderTerminal(type, side) {
|
|
2002
|
-
if (!type)
|
|
2003
|
-
return null;
|
|
2004
|
-
const id = `g3p-seg-${this.segId}-${side}-${type}`;
|
|
2005
|
-
const defs = /* @__PURE__ */ jsx3("defs", { children: /* @__PURE__ */ jsx3(
|
|
2006
|
-
"marker",
|
|
2007
|
-
{
|
|
2008
|
-
id,
|
|
2009
|
-
markerWidth: "10",
|
|
2010
|
-
markerHeight: "10",
|
|
2011
|
-
refX: "9",
|
|
2012
|
-
refY: "3",
|
|
2013
|
-
orient: "auto",
|
|
2014
|
-
markerUnits: "userSpaceOnUse",
|
|
2015
|
-
children: /* @__PURE__ */ jsx3("path", { d: "M0,0 L0,6 L9,3 z" })
|
|
2016
|
-
}
|
|
2017
|
-
) });
|
|
2018
|
-
return { id, defs };
|
|
2019
|
-
}
|
|
2020
2207
|
};
|
|
2021
2208
|
|
|
2022
2209
|
// src/canvas/canvas.tsx
|
|
2023
|
-
import
|
|
2210
|
+
import styles3 from "./canvas.css?raw";
|
|
2211
|
+
import zoomStyles from "./zoom.css?raw";
|
|
2212
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "jsx-dom/jsx-runtime";
|
|
2213
|
+
var log10 = logger("canvas");
|
|
2214
|
+
var themeVarMap = {
|
|
2215
|
+
// Canvas
|
|
2216
|
+
bg: "--g3p-bg",
|
|
2217
|
+
shadow: "--g3p-shadow",
|
|
2218
|
+
// Node
|
|
2219
|
+
border: "--g3p-border",
|
|
2220
|
+
borderHover: "--g3p-border-hover",
|
|
2221
|
+
borderSelected: "--g3p-border-selected",
|
|
2222
|
+
text: "--g3p-text",
|
|
2223
|
+
textMuted: "--g3p-text-muted",
|
|
2224
|
+
// Port
|
|
2225
|
+
bgHover: "--g3p-port-bg-hover",
|
|
2226
|
+
// Edge
|
|
2227
|
+
color: "--g3p-edge-color"
|
|
2228
|
+
};
|
|
2229
|
+
function themeToCSS(theme, selector, prefix = "") {
|
|
2230
|
+
const entries = Object.entries(theme).filter(([_, v]) => v !== void 0);
|
|
2231
|
+
if (!entries.length) return "";
|
|
2232
|
+
let css = `${selector} {
|
|
2233
|
+
`;
|
|
2234
|
+
for (const [key, value] of entries) {
|
|
2235
|
+
let cssVar = themeVarMap[key];
|
|
2236
|
+
if (key === "bg" && prefix === "node") {
|
|
2237
|
+
cssVar = "--g3p-bg-node";
|
|
2238
|
+
} else if (key === "bg" && prefix === "port") {
|
|
2239
|
+
cssVar = "--g3p-port-bg";
|
|
2240
|
+
}
|
|
2241
|
+
if (cssVar) {
|
|
2242
|
+
css += ` ${cssVar}: ${value};
|
|
2243
|
+
`;
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2246
|
+
css += "}\n";
|
|
2247
|
+
return css;
|
|
2248
|
+
}
|
|
2024
2249
|
var Canvas = class {
|
|
2025
2250
|
container;
|
|
2026
2251
|
root;
|
|
@@ -2028,26 +2253,27 @@ var Canvas = class {
|
|
|
2028
2253
|
transform;
|
|
2029
2254
|
bounds;
|
|
2030
2255
|
measurement;
|
|
2031
|
-
|
|
2032
|
-
|
|
2256
|
+
allNodes;
|
|
2257
|
+
curNodes;
|
|
2258
|
+
curSegs;
|
|
2033
2259
|
updating;
|
|
2260
|
+
// Pan-zoom state
|
|
2261
|
+
isPanning = false;
|
|
2262
|
+
panStart = null;
|
|
2263
|
+
transformStart = null;
|
|
2264
|
+
panScale = null;
|
|
2265
|
+
zoomControls;
|
|
2034
2266
|
constructor(options) {
|
|
2035
|
-
Object.assign(this,
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
portStyle: "outside",
|
|
2040
|
-
classPrefix: "g3p",
|
|
2041
|
-
width: "100%",
|
|
2042
|
-
height: "100%",
|
|
2043
|
-
transform: { x: 0, y: 0, scale: 1 },
|
|
2044
|
-
bounds: { min: { x: 0, y: 0 }, max: { x: 1, y: 1 } },
|
|
2045
|
-
...options
|
|
2046
|
-
});
|
|
2047
|
-
this.nodes = /* @__PURE__ */ new Map();
|
|
2048
|
-
this.segs = /* @__PURE__ */ new Map();
|
|
2267
|
+
Object.assign(this, options);
|
|
2268
|
+
this.allNodes = /* @__PURE__ */ new Map();
|
|
2269
|
+
this.curNodes = /* @__PURE__ */ new Map();
|
|
2270
|
+
this.curSegs = /* @__PURE__ */ new Map();
|
|
2049
2271
|
this.updating = false;
|
|
2272
|
+
this.bounds = { min: { x: 0, y: 0 }, max: { x: 0, y: 0 } };
|
|
2273
|
+
this.transform = { x: 0, y: 0, scale: 1 };
|
|
2050
2274
|
this.createMeasurementContainer();
|
|
2275
|
+
this.createCanvasContainer();
|
|
2276
|
+
if (this.panZoom) this.setupPanZoom();
|
|
2051
2277
|
}
|
|
2052
2278
|
createMeasurementContainer() {
|
|
2053
2279
|
this.measurement = document.createElement("div");
|
|
@@ -2060,85 +2286,94 @@ var Canvas = class {
|
|
|
2060
2286
|
`;
|
|
2061
2287
|
document.body.appendChild(this.measurement);
|
|
2062
2288
|
}
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2289
|
+
getNode(key) {
|
|
2290
|
+
const node = this.allNodes.get(key);
|
|
2291
|
+
if (!node) throw new Error(`node not found: ${key}`);
|
|
2292
|
+
return node;
|
|
2293
|
+
}
|
|
2294
|
+
update() {
|
|
2067
2295
|
let bx0 = Infinity, by0 = Infinity;
|
|
2068
2296
|
let bx1 = -Infinity, by1 = -Infinity;
|
|
2069
|
-
for (const node of this.
|
|
2070
|
-
const
|
|
2071
|
-
const
|
|
2297
|
+
for (const node of this.curNodes.values()) {
|
|
2298
|
+
const { x, y } = node.pos;
|
|
2299
|
+
const { w, h } = node.data.dims;
|
|
2300
|
+
const nx0 = x, nx1 = x + w;
|
|
2301
|
+
const ny0 = y, ny1 = y + h;
|
|
2072
2302
|
bx0 = Math.min(bx0, nx0);
|
|
2073
2303
|
by0 = Math.min(by0, ny0);
|
|
2074
2304
|
bx1 = Math.max(bx1, nx1);
|
|
2075
2305
|
by1 = Math.max(by1, ny1);
|
|
2076
2306
|
}
|
|
2077
2307
|
this.bounds = { min: { x: bx0, y: by0 }, max: { x: bx1, y: by1 } };
|
|
2078
|
-
console.log("bounds", this.bounds);
|
|
2079
2308
|
this.root.setAttribute("viewBox", this.viewBox());
|
|
2080
2309
|
}
|
|
2081
|
-
addNode(
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
node
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
this.
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2310
|
+
addNode(gnode) {
|
|
2311
|
+
if (this.curNodes.has(gnode.id))
|
|
2312
|
+
throw new Error("node already exists");
|
|
2313
|
+
const { key } = gnode;
|
|
2314
|
+
let node;
|
|
2315
|
+
if (gnode.isDummy) {
|
|
2316
|
+
node = new Node2(this, gnode, true);
|
|
2317
|
+
node.renderContainer();
|
|
2318
|
+
node.setPos(gnode.pos);
|
|
2319
|
+
this.allNodes.set(key, node);
|
|
2320
|
+
} else {
|
|
2321
|
+
if (!this.allNodes.has(key))
|
|
2322
|
+
throw new Error("node has not been measured");
|
|
2323
|
+
node = this.getNode(key);
|
|
2324
|
+
}
|
|
2325
|
+
this.curNodes.set(gnode.id, node);
|
|
2326
|
+
node.append();
|
|
2327
|
+
}
|
|
2328
|
+
updateNode(gnode) {
|
|
2329
|
+
if (gnode.isDummy) throw new Error("dummy node cannot be updated");
|
|
2330
|
+
const node = this.getNode(gnode.key);
|
|
2331
|
+
const cur = this.curNodes.get(gnode.id);
|
|
2332
|
+
if (cur) cur.remove();
|
|
2333
|
+
this.curNodes.set(gnode.id, node);
|
|
2334
|
+
node.append();
|
|
2335
|
+
}
|
|
2336
|
+
deleteNode(gnode) {
|
|
2337
|
+
const node = this.getNode(gnode.key);
|
|
2338
|
+
this.curNodes.delete(gnode.id);
|
|
2339
|
+
node.remove();
|
|
2340
|
+
}
|
|
2341
|
+
addSeg(gseg, g) {
|
|
2342
|
+
if (this.curSegs.has(gseg.id))
|
|
2343
|
+
throw new Error("seg already exists");
|
|
2344
|
+
const seg = new Seg2(this, gseg, g);
|
|
2345
|
+
this.curSegs.set(gseg.id, seg);
|
|
2346
|
+
seg.append();
|
|
2347
|
+
}
|
|
2348
|
+
updateSeg(gseg) {
|
|
2349
|
+
const seg = this.curSegs.get(gseg.id);
|
|
2106
2350
|
if (!seg) throw new Error("seg not found");
|
|
2107
|
-
seg.
|
|
2351
|
+
seg.update(gseg);
|
|
2108
2352
|
}
|
|
2109
|
-
deleteSeg(
|
|
2110
|
-
const seg = this.
|
|
2353
|
+
deleteSeg(gseg) {
|
|
2354
|
+
const seg = this.curSegs.get(gseg.id);
|
|
2111
2355
|
if (!seg) throw new Error("seg not found");
|
|
2112
|
-
|
|
2113
|
-
|
|
2356
|
+
this.curSegs.delete(gseg.id);
|
|
2357
|
+
seg.remove();
|
|
2114
2358
|
}
|
|
2115
|
-
async
|
|
2116
|
-
const newNodes =
|
|
2359
|
+
async measureNodes(nodes) {
|
|
2360
|
+
const newNodes = /* @__PURE__ */ new Map();
|
|
2117
2361
|
for (const data of nodes) {
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
renderNode: this.renderNode,
|
|
2122
|
-
classPrefix: this.classPrefix,
|
|
2123
|
-
isDummy: false
|
|
2124
|
-
});
|
|
2125
|
-
this.nodes.set(node.data, node);
|
|
2126
|
-
if (!node.measured) {
|
|
2127
|
-
this.measurement.appendChild(node.content);
|
|
2128
|
-
newNodes.push(node);
|
|
2129
|
-
}
|
|
2362
|
+
const node = new Node2(this, data);
|
|
2363
|
+
newNodes.set(data.data, node);
|
|
2364
|
+
this.measurement.appendChild(node.content);
|
|
2130
2365
|
}
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
return
|
|
2366
|
+
await new Promise(requestAnimationFrame);
|
|
2367
|
+
const isVertical = this.orientation === "TB" || this.orientation === "BT";
|
|
2368
|
+
for (const node of newNodes.values()) {
|
|
2369
|
+
node.measure(isVertical);
|
|
2370
|
+
const { id, version } = node.data;
|
|
2371
|
+
const key = `${id}:${version}`;
|
|
2372
|
+
this.allNodes.set(key, node);
|
|
2373
|
+
node.renderContainer();
|
|
2374
|
+
}
|
|
2375
|
+
this.measurement.innerHTML = "";
|
|
2376
|
+
return newNodes;
|
|
2142
2377
|
}
|
|
2143
2378
|
onClick(e) {
|
|
2144
2379
|
console.log("click", e);
|
|
@@ -2150,17 +2385,47 @@ var Canvas = class {
|
|
|
2150
2385
|
return `translate(${this.transform.x}, ${this.transform.y}) scale(${this.transform.scale})`;
|
|
2151
2386
|
}
|
|
2152
2387
|
viewBox() {
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
const
|
|
2157
|
-
|
|
2388
|
+
const p = this.padding;
|
|
2389
|
+
const x = this.bounds.min.x - p;
|
|
2390
|
+
const y = this.bounds.min.y - p;
|
|
2391
|
+
const w = this.bounds.max.x - this.bounds.min.x + p * 2;
|
|
2392
|
+
const h = this.bounds.max.y - this.bounds.min.y + p * 2;
|
|
2393
|
+
return `${x} ${y} ${w} ${h}`;
|
|
2394
|
+
}
|
|
2395
|
+
generateDynamicStyles() {
|
|
2396
|
+
let css = "";
|
|
2397
|
+
const prefix = this.classPrefix;
|
|
2398
|
+
css += themeToCSS(this.theme, `.${prefix}-canvas-container`);
|
|
2399
|
+
for (const [type, vars] of Object.entries(this.nodeTypes)) {
|
|
2400
|
+
css += themeToCSS(vars, `.${prefix}-node-type-${type}`, "node");
|
|
2401
|
+
}
|
|
2402
|
+
for (const [type, vars] of Object.entries(this.edgeTypes)) {
|
|
2403
|
+
css += themeToCSS(vars, `.${prefix}-edge-type-${type}`);
|
|
2404
|
+
}
|
|
2405
|
+
return css;
|
|
2406
|
+
}
|
|
2407
|
+
createCanvasContainer() {
|
|
2408
|
+
const markerStyleEl = document.createElement("style");
|
|
2409
|
+
markerStyleEl.textContent = default2;
|
|
2410
|
+
document.head.appendChild(markerStyleEl);
|
|
2411
|
+
const zoomStyleEl = document.createElement("style");
|
|
2412
|
+
zoomStyleEl.textContent = zoomStyles;
|
|
2413
|
+
document.head.appendChild(zoomStyleEl);
|
|
2414
|
+
const dynamicStyles = this.generateDynamicStyles();
|
|
2415
|
+
if (dynamicStyles) {
|
|
2416
|
+
const themeStyleEl = document.createElement("style");
|
|
2417
|
+
themeStyleEl.textContent = dynamicStyles;
|
|
2418
|
+
document.head.appendChild(themeStyleEl);
|
|
2419
|
+
}
|
|
2420
|
+
const c = styler("canvas", styles3, this.classPrefix);
|
|
2421
|
+
const colorModeClass = this.colorMode !== "system" ? `g3p-${this.colorMode}` : "";
|
|
2422
|
+
this.container = /* @__PURE__ */ jsx4(
|
|
2158
2423
|
"div",
|
|
2159
2424
|
{
|
|
2160
|
-
className: c("container"),
|
|
2425
|
+
className: `${c("container")} ${colorModeClass}`.trim(),
|
|
2161
2426
|
ref: (el) => this.container = el,
|
|
2162
2427
|
onContextMenu: this.onContextMenu.bind(this),
|
|
2163
|
-
children: /* @__PURE__ */
|
|
2428
|
+
children: /* @__PURE__ */ jsxs3(
|
|
2164
2429
|
"svg",
|
|
2165
2430
|
{
|
|
2166
2431
|
ref: (el) => this.root = el,
|
|
@@ -2170,70 +2435,308 @@ var Canvas = class {
|
|
|
2170
2435
|
viewBox: this.viewBox(),
|
|
2171
2436
|
preserveAspectRatio: "xMidYMid meet",
|
|
2172
2437
|
onClick: this.onClick.bind(this),
|
|
2173
|
-
children:
|
|
2174
|
-
"
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2438
|
+
children: [
|
|
2439
|
+
/* @__PURE__ */ jsxs3("defs", { children: [
|
|
2440
|
+
Object.values(markerDefs).map((marker) => marker(this.markerSize, this.classPrefix, false)),
|
|
2441
|
+
Object.values(markerDefs).map((marker) => marker(this.markerSize, this.classPrefix, true))
|
|
2442
|
+
] }),
|
|
2443
|
+
/* @__PURE__ */ jsx4(
|
|
2444
|
+
"g",
|
|
2445
|
+
{
|
|
2446
|
+
ref: (el) => this.group = el,
|
|
2447
|
+
transform: this.groupTransform()
|
|
2448
|
+
}
|
|
2449
|
+
)
|
|
2450
|
+
]
|
|
2180
2451
|
}
|
|
2181
2452
|
)
|
|
2182
2453
|
}
|
|
2183
2454
|
);
|
|
2184
2455
|
}
|
|
2456
|
+
// ==================== Pan-Zoom ====================
|
|
2457
|
+
setupPanZoom() {
|
|
2458
|
+
this.container.addEventListener("wheel", this.onWheel.bind(this), { passive: false });
|
|
2459
|
+
this.container.addEventListener("mousedown", this.onMouseDown.bind(this));
|
|
2460
|
+
document.addEventListener("mousemove", this.onMouseMove.bind(this));
|
|
2461
|
+
document.addEventListener("mouseup", this.onMouseUp.bind(this));
|
|
2462
|
+
this.createZoomControls();
|
|
2463
|
+
}
|
|
2464
|
+
/** Convert screen coordinates to canvas-relative coordinates */
|
|
2465
|
+
screenToCanvas(screen) {
|
|
2466
|
+
const rect = this.container.getBoundingClientRect();
|
|
2467
|
+
return canvasPos(screen.x - rect.left, screen.y - rect.top);
|
|
2468
|
+
}
|
|
2469
|
+
/**
|
|
2470
|
+
* Get the effective scale from canvas pixels to graph units,
|
|
2471
|
+
* accounting for preserveAspectRatio="xMidYMid meet" which uses
|
|
2472
|
+
* the smaller scale (to fit) and centers the content.
|
|
2473
|
+
*/
|
|
2474
|
+
getEffectiveScale() {
|
|
2475
|
+
const vb = this.currentViewBox();
|
|
2476
|
+
const rect = this.container.getBoundingClientRect();
|
|
2477
|
+
const scaleX = vb.w / rect.width;
|
|
2478
|
+
const scaleY = vb.h / rect.height;
|
|
2479
|
+
const scale = Math.max(scaleX, scaleY);
|
|
2480
|
+
const actualW = rect.width * scale;
|
|
2481
|
+
const actualH = rect.height * scale;
|
|
2482
|
+
const offsetX = (actualW - vb.w) / 2;
|
|
2483
|
+
const offsetY = (actualH - vb.h) / 2;
|
|
2484
|
+
return { scale, offsetX, offsetY };
|
|
2485
|
+
}
|
|
2486
|
+
/** Convert canvas coordinates to graph coordinates */
|
|
2487
|
+
canvasToGraph(canvas) {
|
|
2488
|
+
const vb = this.currentViewBox();
|
|
2489
|
+
const { scale, offsetX, offsetY } = this.getEffectiveScale();
|
|
2490
|
+
return graphPos(
|
|
2491
|
+
vb.x - offsetX + canvas.x * scale,
|
|
2492
|
+
vb.y - offsetY + canvas.y * scale
|
|
2493
|
+
);
|
|
2494
|
+
}
|
|
2495
|
+
/** Get current viewBox as an object */
|
|
2496
|
+
currentViewBox() {
|
|
2497
|
+
const p = this.padding;
|
|
2498
|
+
const t = this.transform;
|
|
2499
|
+
const baseX = this.bounds.min.x - p;
|
|
2500
|
+
const baseY = this.bounds.min.y - p;
|
|
2501
|
+
const baseW = this.bounds.max.x - this.bounds.min.x + p * 2;
|
|
2502
|
+
const baseH = this.bounds.max.y - this.bounds.min.y + p * 2;
|
|
2503
|
+
const cx = baseX + baseW / 2;
|
|
2504
|
+
const cy = baseY + baseH / 2;
|
|
2505
|
+
const w = baseW / t.scale;
|
|
2506
|
+
const h = baseH / t.scale;
|
|
2507
|
+
const x = cx - w / 2 - t.x;
|
|
2508
|
+
const y = cy - h / 2 - t.y;
|
|
2509
|
+
return { x, y, w, h };
|
|
2510
|
+
}
|
|
2511
|
+
onWheel(e) {
|
|
2512
|
+
e.preventDefault();
|
|
2513
|
+
const zoomFactor = 1.1;
|
|
2514
|
+
const delta = e.deltaY > 0 ? 1 / zoomFactor : zoomFactor;
|
|
2515
|
+
const screenCursor = screenPos(e.clientX, e.clientY);
|
|
2516
|
+
const canvasCursor = this.screenToCanvas(screenCursor);
|
|
2517
|
+
const graphCursor = this.canvasToGraph(canvasCursor);
|
|
2518
|
+
const oldScale = this.transform.scale;
|
|
2519
|
+
const newScale = Math.max(0.1, Math.min(10, oldScale * delta));
|
|
2520
|
+
this.transform.scale = newScale;
|
|
2521
|
+
const newGraphCursor = this.canvasToGraph(canvasCursor);
|
|
2522
|
+
this.transform.x += newGraphCursor.x - graphCursor.x;
|
|
2523
|
+
this.transform.y += newGraphCursor.y - graphCursor.y;
|
|
2524
|
+
this.applyTransform();
|
|
2525
|
+
}
|
|
2526
|
+
onMouseDown(e) {
|
|
2527
|
+
if (e.button !== 0) return;
|
|
2528
|
+
if (e.target.closest(".g3p-zoom-controls")) return;
|
|
2529
|
+
this.isPanning = true;
|
|
2530
|
+
this.panStart = this.screenToCanvas(screenPos(e.clientX, e.clientY));
|
|
2531
|
+
this.transformStart = { ...this.transform };
|
|
2532
|
+
const { scale } = this.getEffectiveScale();
|
|
2533
|
+
this.panScale = { x: scale, y: scale };
|
|
2534
|
+
this.container.style.cursor = "grabbing";
|
|
2535
|
+
e.preventDefault();
|
|
2536
|
+
}
|
|
2537
|
+
onMouseMove(e) {
|
|
2538
|
+
if (!this.isPanning || !this.panStart || !this.transformStart || !this.panScale) return;
|
|
2539
|
+
const current = this.screenToCanvas(screenPos(e.clientX, e.clientY));
|
|
2540
|
+
const dx = current.x - this.panStart.x;
|
|
2541
|
+
const dy = current.y - this.panStart.y;
|
|
2542
|
+
this.transform.x = this.transformStart.x + dx * this.panScale.x;
|
|
2543
|
+
this.transform.y = this.transformStart.y + dy * this.panScale.y;
|
|
2544
|
+
this.applyTransform();
|
|
2545
|
+
}
|
|
2546
|
+
onMouseUp(e) {
|
|
2547
|
+
if (!this.isPanning) return;
|
|
2548
|
+
this.isPanning = false;
|
|
2549
|
+
this.panStart = null;
|
|
2550
|
+
this.transformStart = null;
|
|
2551
|
+
this.panScale = null;
|
|
2552
|
+
this.container.style.cursor = "";
|
|
2553
|
+
}
|
|
2554
|
+
applyTransform() {
|
|
2555
|
+
const vb = this.currentViewBox();
|
|
2556
|
+
this.root.setAttribute("viewBox", `${vb.x} ${vb.y} ${vb.w} ${vb.h}`);
|
|
2557
|
+
this.updateZoomLevel();
|
|
2558
|
+
}
|
|
2559
|
+
createZoomControls() {
|
|
2560
|
+
const c = styler("zoom", zoomStyles, this.classPrefix);
|
|
2561
|
+
this.zoomControls = /* @__PURE__ */ jsxs3("div", { className: c("controls"), children: [
|
|
2562
|
+
/* @__PURE__ */ jsx4("button", { className: c("btn"), onClick: () => this.zoomIn(), children: "+" }),
|
|
2563
|
+
/* @__PURE__ */ jsx4("div", { className: c("level"), id: "g3p-zoom-level", children: "100%" }),
|
|
2564
|
+
/* @__PURE__ */ jsx4("button", { className: c("btn"), onClick: () => this.zoomOut(), children: "\u2212" }),
|
|
2565
|
+
/* @__PURE__ */ jsx4("button", { className: `${c("btn")} ${c("reset")}`, onClick: () => this.zoomReset(), children: "\u27F2" })
|
|
2566
|
+
] });
|
|
2567
|
+
this.container.appendChild(this.zoomControls);
|
|
2568
|
+
}
|
|
2569
|
+
updateZoomLevel() {
|
|
2570
|
+
const level = this.container.querySelector("#g3p-zoom-level");
|
|
2571
|
+
if (level) {
|
|
2572
|
+
level.textContent = `${Math.round(this.transform.scale * 100)}%`;
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2575
|
+
zoomIn() {
|
|
2576
|
+
this.transform.scale = Math.min(10, this.transform.scale * 1.2);
|
|
2577
|
+
this.applyTransform();
|
|
2578
|
+
}
|
|
2579
|
+
zoomOut() {
|
|
2580
|
+
this.transform.scale = Math.max(0.1, this.transform.scale / 1.2);
|
|
2581
|
+
this.applyTransform();
|
|
2582
|
+
}
|
|
2583
|
+
zoomReset() {
|
|
2584
|
+
this.transform = { x: 0, y: 0, scale: 1 };
|
|
2585
|
+
this.applyTransform();
|
|
2586
|
+
}
|
|
2185
2587
|
};
|
|
2186
2588
|
|
|
2187
|
-
// src/
|
|
2188
|
-
import {
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2589
|
+
// src/canvas/render-node.tsx
|
|
2590
|
+
import { jsx as jsx5, jsxs as jsxs4 } from "jsx-dom/jsx-runtime";
|
|
2591
|
+
function renderNode(node) {
|
|
2592
|
+
if (typeof node == "string") node = { id: node };
|
|
2593
|
+
const title = node?.title ?? node?.label ?? node?.name ?? node?.text ?? node?.id ?? "?";
|
|
2594
|
+
const detail = node?.detail ?? node?.description ?? node?.subtitle;
|
|
2595
|
+
return /* @__PURE__ */ jsxs4("div", { className: "g3p-node-default", children: [
|
|
2596
|
+
/* @__PURE__ */ jsx5("div", { className: "g3p-node-title", children: title }),
|
|
2597
|
+
detail && /* @__PURE__ */ jsx5("div", { className: "g3p-node-detail", children: detail })
|
|
2598
|
+
] });
|
|
2599
|
+
}
|
|
2600
|
+
|
|
2601
|
+
// src/api/defaults.ts
|
|
2602
|
+
function applyDefaults(options) {
|
|
2603
|
+
const { graph: graph2, canvas, props } = defaults();
|
|
2604
|
+
return {
|
|
2605
|
+
graph: { ...graph2, ...options?.graph },
|
|
2606
|
+
canvas: { ...canvas, ...options?.canvas },
|
|
2607
|
+
props: { ...props, ...options?.props }
|
|
2608
|
+
};
|
|
2609
|
+
}
|
|
2610
|
+
function defaults() {
|
|
2611
|
+
return {
|
|
2612
|
+
graph: {
|
|
2613
|
+
mergeOrder: ["target", "source"],
|
|
2614
|
+
nodeMargin: 15,
|
|
2615
|
+
dummyNodeSize: 15,
|
|
2616
|
+
nodeAlign: "natural",
|
|
2617
|
+
edgeSpacing: 10,
|
|
2618
|
+
turnRadius: 10,
|
|
2619
|
+
orientation: "TB",
|
|
2620
|
+
layerMargin: 5,
|
|
2621
|
+
alignIterations: 5,
|
|
2622
|
+
alignThreshold: 10,
|
|
2623
|
+
separateTrackSets: true,
|
|
2624
|
+
markerSize: 10,
|
|
2625
|
+
layoutSteps: null
|
|
2626
|
+
},
|
|
2627
|
+
canvas: {
|
|
2628
|
+
renderNode,
|
|
2629
|
+
classPrefix: "g3p",
|
|
2630
|
+
width: "100%",
|
|
2631
|
+
height: "100%",
|
|
2632
|
+
padding: 20,
|
|
2633
|
+
editable: false,
|
|
2634
|
+
panZoom: true,
|
|
2635
|
+
markerSize: 10,
|
|
2636
|
+
colorMode: "system",
|
|
2637
|
+
theme: {},
|
|
2638
|
+
nodeTypes: {},
|
|
2639
|
+
edgeTypes: {}
|
|
2640
|
+
},
|
|
2641
|
+
props: {}
|
|
2642
|
+
};
|
|
2643
|
+
}
|
|
2644
|
+
|
|
2645
|
+
// src/api/updater.ts
|
|
2646
|
+
var Updater = class _Updater {
|
|
2647
|
+
update;
|
|
2648
|
+
constructor() {
|
|
2649
|
+
this.update = {
|
|
2650
|
+
addNodes: [],
|
|
2651
|
+
removeNodes: [],
|
|
2652
|
+
updateNodes: [],
|
|
2653
|
+
addEdges: [],
|
|
2654
|
+
removeEdges: [],
|
|
2655
|
+
updateEdges: []
|
|
2656
|
+
};
|
|
2657
|
+
}
|
|
2658
|
+
describe(desc) {
|
|
2659
|
+
this.update.description = desc;
|
|
2660
|
+
return this;
|
|
2661
|
+
}
|
|
2662
|
+
addNode(node) {
|
|
2663
|
+
this.update.addNodes.push(node);
|
|
2664
|
+
return this;
|
|
2665
|
+
}
|
|
2666
|
+
deleteNode(node) {
|
|
2667
|
+
this.update.removeNodes.push(node);
|
|
2668
|
+
return this;
|
|
2669
|
+
}
|
|
2670
|
+
updateNode(node) {
|
|
2671
|
+
this.update.updateNodes.push(node);
|
|
2672
|
+
return this;
|
|
2673
|
+
}
|
|
2674
|
+
addEdge(edge) {
|
|
2675
|
+
this.update.addEdges.push(edge);
|
|
2676
|
+
return this;
|
|
2677
|
+
}
|
|
2678
|
+
deleteEdge(edge) {
|
|
2679
|
+
this.update.removeEdges.push(edge);
|
|
2680
|
+
return this;
|
|
2681
|
+
}
|
|
2682
|
+
updateEdge(edge) {
|
|
2683
|
+
this.update.updateEdges.push(edge);
|
|
2684
|
+
return this;
|
|
2685
|
+
}
|
|
2686
|
+
static add(nodes, edges) {
|
|
2687
|
+
const updater = new _Updater();
|
|
2688
|
+
updater.update.addNodes = nodes;
|
|
2689
|
+
updater.update.addEdges = edges;
|
|
2690
|
+
return updater;
|
|
2691
|
+
}
|
|
2692
|
+
};
|
|
2693
|
+
|
|
2694
|
+
// src/api/api.ts
|
|
2695
|
+
var log11 = logger("api");
|
|
2203
2696
|
var API = class {
|
|
2204
2697
|
state;
|
|
2205
2698
|
seq;
|
|
2206
2699
|
index;
|
|
2207
2700
|
canvas;
|
|
2208
|
-
_options;
|
|
2209
2701
|
options;
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
}
|
|
2222
|
-
this.state = {
|
|
2223
|
-
nodes: IMap2(),
|
|
2224
|
-
edges: IMap2(),
|
|
2225
|
-
ports: IMap2(),
|
|
2226
|
-
segs: IMap2()
|
|
2227
|
-
};
|
|
2228
|
-
let graph2 = new Graph({ options: this._options });
|
|
2229
|
-
this.state.graph = graph2;
|
|
2702
|
+
history;
|
|
2703
|
+
nodeIds;
|
|
2704
|
+
edgeIds;
|
|
2705
|
+
nodeVersions;
|
|
2706
|
+
nextNodeId;
|
|
2707
|
+
nextEdgeId;
|
|
2708
|
+
root;
|
|
2709
|
+
constructor(args) {
|
|
2710
|
+
this.root = args.root;
|
|
2711
|
+
this.options = applyDefaults(args.options);
|
|
2712
|
+
let graph2 = new Graph({ options: this.options.graph });
|
|
2713
|
+
this.state = { graph: graph2, update: null };
|
|
2230
2714
|
this.seq = [this.state];
|
|
2231
2715
|
this.index = 0;
|
|
2232
|
-
this.
|
|
2233
|
-
this.
|
|
2716
|
+
this.nodeIds = /* @__PURE__ */ new Map();
|
|
2717
|
+
this.edgeIds = /* @__PURE__ */ new Map();
|
|
2718
|
+
this.nodeVersions = /* @__PURE__ */ new Map();
|
|
2719
|
+
this.nextNodeId = 1;
|
|
2720
|
+
this.nextEdgeId = 1;
|
|
2721
|
+
this.canvas = new Canvas({
|
|
2722
|
+
...this.options.canvas,
|
|
2723
|
+
dummyNodeSize: this.options.graph.dummyNodeSize,
|
|
2724
|
+
orientation: this.options.graph.orientation
|
|
2725
|
+
});
|
|
2726
|
+
if (args.history) {
|
|
2727
|
+
this.history = args.history;
|
|
2728
|
+
} else if (args.nodes) {
|
|
2729
|
+
this.history = [Updater.add(args.nodes, args.edges || []).update];
|
|
2730
|
+
} else {
|
|
2731
|
+
this.history = [];
|
|
2732
|
+
}
|
|
2234
2733
|
}
|
|
2235
|
-
|
|
2236
|
-
|
|
2734
|
+
async init() {
|
|
2735
|
+
const root = document.getElementById(this.root);
|
|
2736
|
+
if (!root) throw new Error("root element not found");
|
|
2737
|
+
root.appendChild(this.canvas.container);
|
|
2738
|
+
for (const update of this.history)
|
|
2739
|
+
await this.applyUpdate(update);
|
|
2237
2740
|
}
|
|
2238
2741
|
nav(nav) {
|
|
2239
2742
|
let newIndex;
|
|
@@ -2258,36 +2761,27 @@ var API = class {
|
|
|
2258
2761
|
this.state = this.seq[this.index];
|
|
2259
2762
|
}
|
|
2260
2763
|
applyDiff(oldIndex, newIndex) {
|
|
2261
|
-
const
|
|
2262
|
-
const
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
this.canvas.deleteSeg(oldSeg);
|
|
2283
|
-
else if (oldSeg.svg != newSeg.svg)
|
|
2284
|
-
this.canvas.updateSeg(newSeg);
|
|
2285
|
-
}
|
|
2286
|
-
for (const newSeg of newState.segs.values()) {
|
|
2287
|
-
if (!oldState.segs.has(newSeg.segId))
|
|
2288
|
-
this.canvas.addSeg(newSeg);
|
|
2289
|
-
}
|
|
2290
|
-
});
|
|
2764
|
+
const oldGraph = this.seq[oldIndex].graph;
|
|
2765
|
+
const newGraph = this.seq[newIndex].graph;
|
|
2766
|
+
for (const oldNode of oldGraph.nodes.values()) {
|
|
2767
|
+
const newNode = newGraph.nodes.get(oldNode.id);
|
|
2768
|
+
if (!newNode) this.canvas.deleteNode(oldNode);
|
|
2769
|
+
}
|
|
2770
|
+
for (const newNode of newGraph.nodes.values()) {
|
|
2771
|
+
if (!oldGraph.nodes.has(newNode.id)) this.canvas.addNode(newNode);
|
|
2772
|
+
}
|
|
2773
|
+
for (const oldSeg of oldGraph.segs.values()) {
|
|
2774
|
+
const newSeg = newGraph.segs.get(oldSeg.id);
|
|
2775
|
+
if (!newSeg)
|
|
2776
|
+
this.canvas.deleteSeg(oldSeg);
|
|
2777
|
+
else if (oldSeg.svg != newSeg.svg)
|
|
2778
|
+
this.canvas.updateSeg(newSeg);
|
|
2779
|
+
}
|
|
2780
|
+
for (const newSeg of newGraph.segs.values()) {
|
|
2781
|
+
if (!oldGraph.segs.has(newSeg.id))
|
|
2782
|
+
this.canvas.addSeg(newSeg, newGraph);
|
|
2783
|
+
}
|
|
2784
|
+
this.canvas.update();
|
|
2291
2785
|
}
|
|
2292
2786
|
async addNode(node) {
|
|
2293
2787
|
await this.update((update) => update.addNode(node));
|
|
@@ -2304,190 +2798,175 @@ var API = class {
|
|
|
2304
2798
|
async deleteEdge(edge) {
|
|
2305
2799
|
await this.update((update) => update.deleteEdge(edge));
|
|
2306
2800
|
}
|
|
2307
|
-
async update
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
edges: this.state.edges.asMutable(),
|
|
2315
|
-
ports: this.state.ports.asMutable(),
|
|
2316
|
-
segs: this.state.segs.asMutable()
|
|
2317
|
-
};
|
|
2318
|
-
for (const node of update.updatedNodes)
|
|
2319
|
-
this._updateNode(node, mut);
|
|
2320
|
-
for (const edge of update.updatedEdges)
|
|
2321
|
-
this._updateEdge(edge, mut);
|
|
2322
|
-
for (const node of update.addedNodes)
|
|
2323
|
-
this._addNode(node, mut);
|
|
2324
|
-
for (const node of update.removedNodes)
|
|
2801
|
+
async applyUpdate(update) {
|
|
2802
|
+
log11.info("applyUpdate", update);
|
|
2803
|
+
const nodes = await this.measureNodes(update);
|
|
2804
|
+
const graph2 = this.state.graph.withMutations((mut) => {
|
|
2805
|
+
for (const edge of update.removeEdges ?? [])
|
|
2806
|
+
this._removeEdge(edge, mut);
|
|
2807
|
+
for (const node of update.removeNodes ?? [])
|
|
2325
2808
|
this._removeNode(node, mut);
|
|
2326
|
-
for (const
|
|
2809
|
+
for (const node of update.addNodes ?? [])
|
|
2810
|
+
this._addNode(nodes.get(node), mut);
|
|
2811
|
+
for (const node of update.updateNodes ?? [])
|
|
2812
|
+
this._updateNode(nodes.get(node), mut);
|
|
2813
|
+
for (const edge of update.addEdges ?? [])
|
|
2327
2814
|
this._addEdge(edge, mut);
|
|
2328
|
-
for (const edge of update.
|
|
2329
|
-
this.
|
|
2815
|
+
for (const edge of update.updateEdges ?? [])
|
|
2816
|
+
this._updateEdge(edge, mut);
|
|
2330
2817
|
});
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
const node = newGraph.getNode(nodeId);
|
|
2334
|
-
console.log(`got pos of node ${nodeId}:`, node.pos);
|
|
2335
|
-
if (node.isDummy) {
|
|
2336
|
-
this.state.nodes.set(nodeId, { id: nodeId, pos: node.pos, isDummy: true });
|
|
2337
|
-
} else {
|
|
2338
|
-
const myNode = this.state.nodes.get(nodeId);
|
|
2339
|
-
this.state.nodes.set(nodeId, { ...myNode, pos: node.pos });
|
|
2340
|
-
}
|
|
2341
|
-
}
|
|
2342
|
-
for (const nodeId of newGraph.delNodes)
|
|
2343
|
-
this.state.nodes.delete(nodeId);
|
|
2344
|
-
for (const segId of newGraph.delSegs)
|
|
2345
|
-
this.state.segs.delete(segId);
|
|
2346
|
-
for (const segId of newGraph.dirtySegs) {
|
|
2347
|
-
const seg = newGraph.getSeg(segId);
|
|
2348
|
-
const edge = this.state.edges.get(seg.edgeIds.values().next().value);
|
|
2349
|
-
const target = seg.targetNode(newGraph);
|
|
2350
|
-
this.state.segs.set(seg.id, {
|
|
2351
|
-
segId: seg.id,
|
|
2352
|
-
edgeId: edge.id,
|
|
2353
|
-
svg: seg.svg,
|
|
2354
|
-
attrs: edge.attrs,
|
|
2355
|
-
targetDummy: target.isDummy,
|
|
2356
|
-
edgeData: edge.data
|
|
2357
|
-
});
|
|
2358
|
-
}
|
|
2359
|
-
this.state = {
|
|
2360
|
-
nodes: this.state.nodes.asImmutable(),
|
|
2361
|
-
edges: this.state.edges.asImmutable(),
|
|
2362
|
-
ports: this.state.ports.asImmutable(),
|
|
2363
|
-
segs: this.state.segs.asImmutable(),
|
|
2364
|
-
graph: newGraph,
|
|
2365
|
-
update
|
|
2366
|
-
};
|
|
2818
|
+
this.state = { graph: graph2, update };
|
|
2819
|
+
this.setNodePositions();
|
|
2367
2820
|
this.seq.splice(this.index + 1);
|
|
2368
2821
|
this.seq.push(this.state);
|
|
2369
2822
|
this.nav("last");
|
|
2370
2823
|
}
|
|
2371
|
-
|
|
2372
|
-
const
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
}
|
|
2378
|
-
_updateNode(node, mut) {
|
|
2379
|
-
const props = this._options.nodeProps(node);
|
|
2380
|
-
const oldData = this.state.nodes.get(props.id);
|
|
2381
|
-
if (oldData === void 0)
|
|
2382
|
-
throw new Error(`updating node ${props.id} which does not exist`);
|
|
2383
|
-
const attrs = this._options.nodeStyle(node);
|
|
2384
|
-
mut.updateNode({ id: props.id, dims: this.getDims(node) });
|
|
2385
|
-
const data = { id: props.id, attrs, orig: node, node: props };
|
|
2386
|
-
this.state.nodes.set(props.id, data);
|
|
2387
|
-
}
|
|
2388
|
-
_updateEdge(edge, mut) {
|
|
2389
|
-
const props = this._options.edgeProps(edge);
|
|
2390
|
-
const id = Edge.id(props), str = Edge.str(props);
|
|
2391
|
-
const oldData = this.state.edges.get(id);
|
|
2392
|
-
if (oldData === void 0)
|
|
2393
|
-
throw new Error(`updating edge ${str} which does not exist`);
|
|
2394
|
-
const attrs = props.type ? this._options.edgeStyle(props.type) : void 0;
|
|
2395
|
-
const data = { id, attrs, data: edge, edge: props };
|
|
2396
|
-
this.state.edges.set(id, data);
|
|
2397
|
-
if (props.type !== oldData.edge.type) {
|
|
2398
|
-
mut.removeEdge(oldData.edge);
|
|
2399
|
-
mut.addEdge(props);
|
|
2400
|
-
} else {
|
|
2824
|
+
setNodePositions() {
|
|
2825
|
+
const { graph: graph2 } = this.state;
|
|
2826
|
+
for (const nodeId of graph2.dirtyNodes) {
|
|
2827
|
+
const node = graph2.getNode(nodeId);
|
|
2828
|
+
if (!node.isDummy)
|
|
2829
|
+
this.canvas.getNode(node.key).setPos(node.pos);
|
|
2401
2830
|
}
|
|
2402
2831
|
}
|
|
2403
|
-
|
|
2404
|
-
const
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
props.ports = { in: [], out: [], ...props.ports };
|
|
2408
|
-
const attrs = this._options.nodeStyle(node);
|
|
2409
|
-
const data = { id: props.id, attrs, data: node, node: props };
|
|
2410
|
-
this.state.nodes.set(props.id, data);
|
|
2411
|
-
console.log("adding node:", { ...props, dims: this.getDims(node) });
|
|
2412
|
-
mut.addNode({ ...props, dims: this.getDims(node) });
|
|
2832
|
+
async update(callback) {
|
|
2833
|
+
const updater = new Updater();
|
|
2834
|
+
callback(updater);
|
|
2835
|
+
await this.applyUpdate(updater.update);
|
|
2413
2836
|
}
|
|
2414
|
-
|
|
2415
|
-
const
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2837
|
+
async measureNodes(update) {
|
|
2838
|
+
const data = [];
|
|
2839
|
+
for (const set of [update.updateNodes, update.addNodes])
|
|
2840
|
+
for (const node of set ?? [])
|
|
2841
|
+
data.push(this.parseNode(node, true));
|
|
2842
|
+
return await this.canvas.measureNodes(data);
|
|
2843
|
+
}
|
|
2844
|
+
parseNode(data, bumpVersion = false) {
|
|
2845
|
+
const get = this.options.props.node;
|
|
2846
|
+
let props;
|
|
2847
|
+
if (get) props = get(data);
|
|
2848
|
+
else if (!data) throw new Error(`invalid node ${data}`);
|
|
2849
|
+
else if (typeof data == "string") props = { id: data };
|
|
2850
|
+
else if (typeof data == "object") props = data;
|
|
2851
|
+
else throw new Error(`invalid node ${data}`);
|
|
2852
|
+
let { id, title, text, type, render } = props;
|
|
2853
|
+
id ??= this.getNodeId(data);
|
|
2854
|
+
const ports = this.parsePorts(props.ports);
|
|
2855
|
+
let version = this.nodeVersions.get(data);
|
|
2856
|
+
if (!version) version = 1;
|
|
2857
|
+
else if (bumpVersion) version++;
|
|
2858
|
+
this.nodeVersions.set(data, version);
|
|
2859
|
+
return { id, data, ports, title, text, type, render, version };
|
|
2860
|
+
}
|
|
2861
|
+
parseEdge(data) {
|
|
2862
|
+
const get = this.options.props.edge;
|
|
2863
|
+
let props;
|
|
2864
|
+
if (get) props = get(data);
|
|
2865
|
+
else if (!data) throw new Error(`invalid edge ${data}`);
|
|
2866
|
+
else if (typeof data == "string") props = this.parseStringEdge(data);
|
|
2867
|
+
else if (typeof data == "object") props = data;
|
|
2868
|
+
else throw new Error(`invalid edge ${data}`);
|
|
2869
|
+
let { id, source, target, type } = props;
|
|
2870
|
+
id ??= this.getEdgeId(data);
|
|
2871
|
+
source = this.parseEdgeEnd(source);
|
|
2872
|
+
target = this.parseEdgeEnd(target);
|
|
2873
|
+
const edge = { id, source, target, type, data };
|
|
2874
|
+
return edge;
|
|
2420
2875
|
}
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
if (
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2876
|
+
parseEdgeEnd(end) {
|
|
2877
|
+
if (!end) throw new Error(`edge has an undefined source or target`);
|
|
2878
|
+
if (typeof end == "string") return { id: end };
|
|
2879
|
+
if (typeof end == "object") {
|
|
2880
|
+
const keys = Object.keys(end);
|
|
2881
|
+
const pidx = keys.indexOf("port");
|
|
2882
|
+
if (pidx != -1) {
|
|
2883
|
+
if (typeof end.port != "string") return end;
|
|
2884
|
+
keys.splice(pidx, 1);
|
|
2885
|
+
}
|
|
2886
|
+
if (keys.length != 1) return end;
|
|
2887
|
+
if (keys[0] == "id") return end;
|
|
2888
|
+
if (keys[0] != "node") return end;
|
|
2889
|
+
const id = this.nodeIds.get(end.node);
|
|
2890
|
+
if (!id) throw new Error(`edge end ${end} references unknown node ${end.node}`);
|
|
2891
|
+
return { id, port: end.port };
|
|
2892
|
+
}
|
|
2893
|
+
throw new Error(`invalid edge end ${end}`);
|
|
2430
2894
|
}
|
|
2431
|
-
|
|
2432
|
-
const
|
|
2433
|
-
|
|
2434
|
-
if (!this.state.edges.has(id))
|
|
2435
|
-
throw new Error(`removing edge ${str} which does not exist`);
|
|
2436
|
-
this.state.edges.delete(id);
|
|
2437
|
-
mut.removeEdge(props);
|
|
2895
|
+
parseStringEdge(str) {
|
|
2896
|
+
const [source, target] = str.split(/\s*(?::|-+>?)\s*/);
|
|
2897
|
+
return { source, target };
|
|
2438
2898
|
}
|
|
2439
|
-
|
|
2899
|
+
parsePorts(ports) {
|
|
2900
|
+
const fixed = { in: null, out: null };
|
|
2901
|
+
for (const key of ["in", "out"]) {
|
|
2902
|
+
if (ports?.[key] && ports[key].length > 0)
|
|
2903
|
+
fixed[key] = ports[key].map((port) => typeof port == "string" ? { id: port } : port);
|
|
2904
|
+
}
|
|
2905
|
+
return fixed;
|
|
2440
2906
|
}
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
updatedEdges;
|
|
2449
|
-
desc;
|
|
2450
|
-
constructor() {
|
|
2451
|
-
this.addedNodes = [];
|
|
2452
|
-
this.removedNodes = [];
|
|
2453
|
-
this.updatedNodes = [];
|
|
2454
|
-
this.addedEdges = [];
|
|
2455
|
-
this.removedEdges = [];
|
|
2456
|
-
this.updatedEdges = [];
|
|
2907
|
+
getNodeId(node) {
|
|
2908
|
+
let id = this.nodeIds.get(node);
|
|
2909
|
+
if (!id) {
|
|
2910
|
+
id = `n${this.nextNodeId++}`;
|
|
2911
|
+
this.nodeIds.set(node, id);
|
|
2912
|
+
}
|
|
2913
|
+
return id;
|
|
2457
2914
|
}
|
|
2458
|
-
|
|
2459
|
-
|
|
2915
|
+
getEdgeId(edge) {
|
|
2916
|
+
let id = this.edgeIds.get(edge);
|
|
2917
|
+
if (!id) {
|
|
2918
|
+
id = `e${this.nextEdgeId++}`;
|
|
2919
|
+
this.edgeIds.set(edge, id);
|
|
2920
|
+
}
|
|
2921
|
+
return id;
|
|
2460
2922
|
}
|
|
2461
|
-
|
|
2462
|
-
|
|
2923
|
+
_addNode(node, mut) {
|
|
2924
|
+
const { data, id: newId } = node.data;
|
|
2925
|
+
const oldId = this.nodeIds.get(data);
|
|
2926
|
+
console.log("addNode", node, oldId, newId);
|
|
2927
|
+
if (oldId && oldId != newId)
|
|
2928
|
+
throw new Error(`node id of ${data} changed from ${oldId} to ${newId}`);
|
|
2929
|
+
this.nodeIds.set(data, newId);
|
|
2930
|
+
mut.addNode(node.data);
|
|
2463
2931
|
}
|
|
2464
|
-
|
|
2465
|
-
this.
|
|
2932
|
+
_removeNode(node, mut) {
|
|
2933
|
+
const id = this.nodeIds.get(node);
|
|
2934
|
+
if (!id) throw new Error(`removing node ${node} which does not exist`);
|
|
2935
|
+
mut.removeNode({ id });
|
|
2466
2936
|
}
|
|
2467
|
-
|
|
2468
|
-
|
|
2937
|
+
_updateNode(node, mut) {
|
|
2938
|
+
const { data, id: newId } = node.data;
|
|
2939
|
+
const oldId = this.nodeIds.get(data);
|
|
2940
|
+
if (!oldId) throw new Error(`updating unknown node ${node}`);
|
|
2941
|
+
if (oldId != newId) throw new Error(`node id changed from ${oldId} to ${newId}`);
|
|
2942
|
+
mut.updateNode(node.data);
|
|
2469
2943
|
}
|
|
2470
|
-
|
|
2471
|
-
this.
|
|
2944
|
+
_addEdge(edge, mut) {
|
|
2945
|
+
const data = this.parseEdge(edge);
|
|
2946
|
+
const id = this.edgeIds.get(edge);
|
|
2947
|
+
if (id && id != data.id)
|
|
2948
|
+
throw new Error(`edge id changed from ${id} to ${data.id}`);
|
|
2949
|
+
this.edgeIds.set(edge, data.id);
|
|
2950
|
+
mut.addEdge(data);
|
|
2472
2951
|
}
|
|
2473
|
-
|
|
2474
|
-
this.
|
|
2952
|
+
_removeEdge(edge, mut) {
|
|
2953
|
+
const id = this.edgeIds.get(edge);
|
|
2954
|
+
if (!id) throw new Error(`removing edge ${edge} which does not exist`);
|
|
2955
|
+
mut.removeEdge(this.parseEdge(edge));
|
|
2475
2956
|
}
|
|
2476
|
-
|
|
2477
|
-
this.
|
|
2957
|
+
_updateEdge(edge, mut) {
|
|
2958
|
+
const id = this.edgeIds.get(edge);
|
|
2959
|
+
if (!id) throw new Error(`updating unknown edge ${edge}`);
|
|
2960
|
+
const data = this.parseEdge(edge);
|
|
2961
|
+
if (data.id !== id) throw new Error(`edge id changed from ${id} to ${data.id}`);
|
|
2962
|
+
mut.updateEdge(data);
|
|
2478
2963
|
}
|
|
2479
2964
|
};
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
for (const node of nodes)
|
|
2486
|
-
update.addNode(node);
|
|
2487
|
-
for (const edge of edges)
|
|
2488
|
-
update.addEdge(edge);
|
|
2489
|
-
});
|
|
2490
|
-
}
|
|
2965
|
+
|
|
2966
|
+
// src/index.ts
|
|
2967
|
+
async function graph(args = { root: "app" }) {
|
|
2968
|
+
const api = new API(args);
|
|
2969
|
+
await api.init();
|
|
2491
2970
|
return api;
|
|
2492
2971
|
}
|
|
2493
2972
|
var index_default = graph;
|