@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.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
@@ -25,23 +35,23 @@ __export(index_exports, {
|
|
|
25
35
|
});
|
|
26
36
|
module.exports = __toCommonJS(index_exports);
|
|
27
37
|
|
|
28
|
-
// src/
|
|
29
|
-
var import_jsx_runtime = require("jsx-dom/jsx-runtime");
|
|
30
|
-
function renderNode(node) {
|
|
31
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: node?.id || "" });
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// src/graph/types/graph.ts
|
|
38
|
+
// src/graph/graph.ts
|
|
35
39
|
var import_immutable8 = require("immutable");
|
|
36
40
|
|
|
37
|
-
// src/graph/
|
|
41
|
+
// src/graph/node.ts
|
|
38
42
|
var import_immutable = require("immutable");
|
|
39
|
-
var
|
|
43
|
+
var defNodeData = {
|
|
40
44
|
id: "",
|
|
45
|
+
data: void 0,
|
|
46
|
+
version: 0,
|
|
47
|
+
title: void 0,
|
|
48
|
+
text: void 0,
|
|
49
|
+
type: void 0,
|
|
50
|
+
render: void 0,
|
|
51
|
+
ports: { in: null, out: null },
|
|
41
52
|
aligned: {},
|
|
42
53
|
edges: { in: (0, import_immutable.Set)(), out: (0, import_immutable.Set)() },
|
|
43
54
|
segs: { in: (0, import_immutable.Set)(), out: (0, import_immutable.Set)() },
|
|
44
|
-
ports: { in: [], out: [] },
|
|
45
55
|
layerId: "",
|
|
46
56
|
isDummy: false,
|
|
47
57
|
isMerged: false,
|
|
@@ -52,29 +62,35 @@ var defaultNodeProps = {
|
|
|
52
62
|
dims: void 0,
|
|
53
63
|
mutable: false
|
|
54
64
|
};
|
|
55
|
-
var Node = class _Node extends (0, import_immutable.Record)(
|
|
65
|
+
var Node = class _Node extends (0, import_immutable.Record)(defNodeData) {
|
|
56
66
|
static dummyPrefix = "d:";
|
|
57
|
-
get edgeId() {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
get edgeIds() {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
// get edgeId(): EdgeId {
|
|
68
|
+
// if (!this.isDummy)
|
|
69
|
+
// throw new Error(`node ${this.id} is not a dummy`)
|
|
70
|
+
// if (this.isMerged)
|
|
71
|
+
// throw new Error(`node ${this.id} is merged`)
|
|
72
|
+
// return this.get('edgeIds')[0]
|
|
73
|
+
// }
|
|
74
|
+
// get edgeIds(): EdgeId[] {
|
|
75
|
+
// if (!this.isDummy)
|
|
76
|
+
// throw new Error(`node ${this.id} is not a dummy`)
|
|
77
|
+
// if (!this.isMerged)
|
|
78
|
+
// throw new Error(`node ${this.id} is not merged`)
|
|
79
|
+
// return this.get('edgeIds')
|
|
80
|
+
// }
|
|
81
|
+
get key() {
|
|
82
|
+
return this.isDummy ? this.id : _Node.key(this);
|
|
83
|
+
}
|
|
84
|
+
static key(node) {
|
|
85
|
+
return `${node.id}:${node.version}`;
|
|
70
86
|
}
|
|
71
87
|
static isDummyId(nodeId) {
|
|
72
88
|
return nodeId.startsWith(_Node.dummyPrefix);
|
|
73
89
|
}
|
|
74
|
-
static addNormal(g,
|
|
90
|
+
static addNormal(g, data) {
|
|
75
91
|
const layer = g.layerAt(0);
|
|
76
92
|
const node = new _Node({
|
|
77
|
-
...
|
|
93
|
+
...data,
|
|
78
94
|
edges: { in: (0, import_immutable.Set)(), out: (0, import_immutable.Set)() },
|
|
79
95
|
segs: { in: (0, import_immutable.Set)(), out: (0, import_immutable.Set)() },
|
|
80
96
|
aligned: {},
|
|
@@ -86,10 +102,10 @@ var Node = class _Node extends (0, import_immutable.Record)(defaultNodeProps) {
|
|
|
86
102
|
g.dirtyNodes.add(node.id);
|
|
87
103
|
return node;
|
|
88
104
|
}
|
|
89
|
-
static addDummy(g,
|
|
90
|
-
const layer = g.getLayer(
|
|
105
|
+
static addDummy(g, data) {
|
|
106
|
+
const layer = g.getLayer(data.layerId);
|
|
91
107
|
const node = new _Node({
|
|
92
|
-
...
|
|
108
|
+
...data,
|
|
93
109
|
id: `${_Node.dummyPrefix}${g.nextDummyId++}`,
|
|
94
110
|
edges: { in: (0, import_immutable.Set)(), out: (0, import_immutable.Set)() },
|
|
95
111
|
segs: { in: (0, import_immutable.Set)(), out: (0, import_immutable.Set)() },
|
|
@@ -105,6 +121,12 @@ var Node = class _Node extends (0, import_immutable.Record)(defaultNodeProps) {
|
|
|
105
121
|
g.dirtyNodes.add(node.id);
|
|
106
122
|
return node;
|
|
107
123
|
}
|
|
124
|
+
static del(g, node) {
|
|
125
|
+
return g.getNode(node.id).delSelf(g);
|
|
126
|
+
}
|
|
127
|
+
static update(g, data) {
|
|
128
|
+
return g.getNode(data.id).mut(g).merge(data);
|
|
129
|
+
}
|
|
108
130
|
mut(g) {
|
|
109
131
|
if (this.mutable) return this;
|
|
110
132
|
return g.mutateNode(this);
|
|
@@ -127,6 +149,9 @@ var Node = class _Node extends (0, import_immutable.Record)(defaultNodeProps) {
|
|
|
127
149
|
isUnlinked() {
|
|
128
150
|
return this.edges.in.size == 0 && this.edges.out.size == 0 && this.segs.in.size == 0 && this.segs.out.size == 0;
|
|
129
151
|
}
|
|
152
|
+
hasPorts() {
|
|
153
|
+
return !!this.ports?.in?.length || !!this.ports?.out?.length;
|
|
154
|
+
}
|
|
130
155
|
layerIndex(g) {
|
|
131
156
|
return this.getLayer(g).index;
|
|
132
157
|
}
|
|
@@ -135,11 +160,9 @@ var Node = class _Node extends (0, import_immutable.Record)(defaultNodeProps) {
|
|
|
135
160
|
}
|
|
136
161
|
setIndex(g, index) {
|
|
137
162
|
if (this.index == index) return this;
|
|
138
|
-
console.log(`set index of ${this.id} to ${index}`);
|
|
139
163
|
return this.mut(g).set("index", index);
|
|
140
164
|
}
|
|
141
165
|
setLayerPos(g, lpos) {
|
|
142
|
-
console.log("setLayerPos", this.id, lpos);
|
|
143
166
|
if (this.lpos == lpos) return this;
|
|
144
167
|
return this.mut(g).set("lpos", lpos);
|
|
145
168
|
}
|
|
@@ -153,7 +176,6 @@ var Node = class _Node extends (0, import_immutable.Record)(defaultNodeProps) {
|
|
|
153
176
|
return this;
|
|
154
177
|
}
|
|
155
178
|
moveToLayer(g, layer) {
|
|
156
|
-
console.log("moveToLayer", this, this.getLayer(g), layer);
|
|
157
179
|
this.getLayer(g).delNode(g, this.id);
|
|
158
180
|
layer.addNode(g, this.id);
|
|
159
181
|
return this.setLayer(g, layer.id);
|
|
@@ -275,16 +297,44 @@ var Node = class _Node extends (0, import_immutable.Record)(defaultNodeProps) {
|
|
|
275
297
|
}
|
|
276
298
|
};
|
|
277
299
|
|
|
278
|
-
// src/graph/
|
|
300
|
+
// src/graph/edge.ts
|
|
279
301
|
var import_immutable2 = require("immutable");
|
|
280
|
-
|
|
302
|
+
|
|
303
|
+
// src/log.ts
|
|
304
|
+
var levels = {
|
|
305
|
+
error: 0,
|
|
306
|
+
warn: 1,
|
|
307
|
+
info: 2,
|
|
308
|
+
debug: 3
|
|
309
|
+
};
|
|
310
|
+
var currentLevel = "debug";
|
|
311
|
+
function shouldLog(level) {
|
|
312
|
+
return levels[level] <= levels[currentLevel];
|
|
313
|
+
}
|
|
314
|
+
function logger(module2) {
|
|
315
|
+
return {
|
|
316
|
+
error: (msg, ...args) => shouldLog("error") && console.error(`[${module2}] ${msg}`, ...args),
|
|
317
|
+
warn: (msg, ...args) => shouldLog("warn") && console.warn(`[${module2}] ${msg}`, ...args),
|
|
318
|
+
info: (msg, ...args) => shouldLog("info") && console.info(`[${module2}] ${msg}`, ...args),
|
|
319
|
+
debug: (msg, ...args) => shouldLog("debug") && console.debug(`[${module2}] ${msg}`, ...args)
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
var log = logger("core");
|
|
323
|
+
|
|
324
|
+
// src/graph/edge.ts
|
|
325
|
+
var log2 = logger("edge");
|
|
326
|
+
var defEdgeData = {
|
|
281
327
|
id: "",
|
|
328
|
+
data: null,
|
|
329
|
+
label: void 0,
|
|
282
330
|
source: { id: "" },
|
|
283
331
|
target: { id: "" },
|
|
332
|
+
type: void 0,
|
|
333
|
+
style: void 0,
|
|
284
334
|
mutable: false,
|
|
285
335
|
segIds: []
|
|
286
336
|
};
|
|
287
|
-
var Edge = class _Edge extends (0, import_immutable2.Record)(
|
|
337
|
+
var Edge = class _Edge extends (0, import_immutable2.Record)(defEdgeData) {
|
|
288
338
|
static prefix = "e:";
|
|
289
339
|
mut(g) {
|
|
290
340
|
if (this.mutable) return this;
|
|
@@ -342,37 +392,44 @@ var Edge = class _Edge extends (0, import_immutable2.Record)(defaultEdgeProps) {
|
|
|
342
392
|
return _Edge.str(this);
|
|
343
393
|
}
|
|
344
394
|
static str(edge) {
|
|
345
|
-
let source = edge.source
|
|
346
|
-
if (edge
|
|
395
|
+
let source = edge.source?.id;
|
|
396
|
+
if (!source) throw new Error("edge source is undefined");
|
|
397
|
+
if (edge.source?.port)
|
|
347
398
|
source = `${source} (port ${edge.source.port})`;
|
|
348
|
-
let target = edge.target
|
|
349
|
-
if (edge
|
|
399
|
+
let target = edge.target?.id;
|
|
400
|
+
if (!target) throw new Error("edge target is undefined");
|
|
401
|
+
if (edge.target?.port)
|
|
350
402
|
target = `${target} (port ${edge.target.port})`;
|
|
351
403
|
let str = `edge from ${source} to ${target}`;
|
|
352
404
|
if (edge.type) str += ` of type ${edge.type}`;
|
|
353
405
|
return str;
|
|
354
406
|
}
|
|
355
|
-
static
|
|
407
|
+
static key(edge, prefix = _Edge.prefix, side = "both") {
|
|
356
408
|
let source = "", target = "";
|
|
357
409
|
if (side == "source" || side == "both") {
|
|
410
|
+
if (!edge.source?.id) throw new Error("edge source is undefined");
|
|
358
411
|
source = edge.source.id;
|
|
359
|
-
if (edge.source
|
|
412
|
+
if (edge.source?.port)
|
|
360
413
|
source = `${source}.${edge.source.port}`;
|
|
414
|
+
const marker = edge.source?.marker ?? edge.style?.marker?.source;
|
|
415
|
+
if (marker && marker != "none") source += `[${marker}]`;
|
|
361
416
|
source += "-";
|
|
362
417
|
}
|
|
363
418
|
if (side == "target" || side == "both") {
|
|
419
|
+
if (!edge.target?.id) throw new Error("edge target is undefined");
|
|
364
420
|
target = edge.target.id;
|
|
365
421
|
if (edge.target.port)
|
|
366
422
|
target = `${target}.${edge.target.port}`;
|
|
367
423
|
target = "-" + target;
|
|
424
|
+
const marker = edge.target?.marker ?? edge.style?.marker?.target ?? "arrow";
|
|
425
|
+
if (marker && marker != "none") target += `[${marker}]`;
|
|
368
426
|
}
|
|
369
427
|
const type = edge.type || "";
|
|
370
428
|
return `${prefix}${source}${type}${target}`;
|
|
371
429
|
}
|
|
372
|
-
static add(g,
|
|
430
|
+
static add(g, data) {
|
|
373
431
|
const edge = new _Edge({
|
|
374
|
-
...
|
|
375
|
-
id: _Edge.id(props),
|
|
432
|
+
...data,
|
|
376
433
|
segIds: []
|
|
377
434
|
});
|
|
378
435
|
edge.link(g);
|
|
@@ -380,18 +437,39 @@ var Edge = class _Edge extends (0, import_immutable2.Record)(defaultEdgeProps) {
|
|
|
380
437
|
g.dirtyEdges.add(edge.id);
|
|
381
438
|
return edge;
|
|
382
439
|
}
|
|
440
|
+
static del(g, data) {
|
|
441
|
+
return g.getEdge(data.id).delSelf(g);
|
|
442
|
+
}
|
|
443
|
+
static update(g, data) {
|
|
444
|
+
let edge = g.getEdge(data.id);
|
|
445
|
+
let relink = false;
|
|
446
|
+
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) {
|
|
447
|
+
for (const seg of edge.segs(g))
|
|
448
|
+
seg.delEdgeId(g, edge.id);
|
|
449
|
+
edge.unlink(g);
|
|
450
|
+
relink = true;
|
|
451
|
+
}
|
|
452
|
+
edge = edge.mut(g).merge(data);
|
|
453
|
+
if (relink)
|
|
454
|
+
edge.link(g);
|
|
455
|
+
return edge;
|
|
456
|
+
}
|
|
383
457
|
};
|
|
384
458
|
|
|
385
|
-
// src/graph/
|
|
459
|
+
// src/graph/seg.ts
|
|
386
460
|
var import_immutable3 = require("immutable");
|
|
387
|
-
var
|
|
461
|
+
var defSegData = {
|
|
388
462
|
id: "",
|
|
389
463
|
source: { id: "" },
|
|
390
464
|
target: { id: "" },
|
|
465
|
+
type: void 0,
|
|
466
|
+
style: void 0,
|
|
391
467
|
edgeIds: (0, import_immutable3.Set)(),
|
|
468
|
+
trackPos: void 0,
|
|
469
|
+
svg: void 0,
|
|
392
470
|
mutable: false
|
|
393
471
|
};
|
|
394
|
-
var Seg = class _Seg extends (0, import_immutable3.Record)(
|
|
472
|
+
var Seg = class _Seg extends (0, import_immutable3.Record)(defSegData) {
|
|
395
473
|
static prefix = "s:";
|
|
396
474
|
mut(g) {
|
|
397
475
|
if (this.mutable) return this;
|
|
@@ -416,7 +494,7 @@ var Seg = class _Seg extends (0, import_immutable3.Record)(defaultSegProps) {
|
|
|
416
494
|
sameEnd(other, side) {
|
|
417
495
|
const mine = this[side];
|
|
418
496
|
const yours = other[side];
|
|
419
|
-
return mine.id === yours.id && mine.port === yours.port;
|
|
497
|
+
return mine.id === yours.id && mine.port === yours.port && mine.marker === yours.marker;
|
|
420
498
|
}
|
|
421
499
|
setPos(g, source, target) {
|
|
422
500
|
return this.mut(g).merge({
|
|
@@ -474,10 +552,10 @@ var Seg = class _Seg extends (0, import_immutable3.Record)(defaultSegProps) {
|
|
|
474
552
|
}
|
|
475
553
|
return this.mut(g).set("edgeIds", this.edgeIds.asMutable().remove(edgeId));
|
|
476
554
|
}
|
|
477
|
-
static add(g,
|
|
555
|
+
static add(g, data) {
|
|
478
556
|
const seg = new _Seg({
|
|
479
|
-
...
|
|
480
|
-
id: Edge.
|
|
557
|
+
...data,
|
|
558
|
+
id: Edge.key(data, _Seg.prefix)
|
|
481
559
|
});
|
|
482
560
|
seg.link(g);
|
|
483
561
|
g.segs.set(seg.id, seg);
|
|
@@ -486,33 +564,10 @@ var Seg = class _Seg extends (0, import_immutable3.Record)(defaultSegProps) {
|
|
|
486
564
|
}
|
|
487
565
|
};
|
|
488
566
|
|
|
489
|
-
// src/graph/
|
|
567
|
+
// src/graph/layer.ts
|
|
490
568
|
var import_immutable4 = require("immutable");
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
var levels = {
|
|
494
|
-
error: 0,
|
|
495
|
-
warn: 1,
|
|
496
|
-
info: 2,
|
|
497
|
-
debug: 3
|
|
498
|
-
};
|
|
499
|
-
var currentLevel = "debug";
|
|
500
|
-
function shouldLog(level) {
|
|
501
|
-
return levels[level] <= levels[currentLevel];
|
|
502
|
-
}
|
|
503
|
-
function logger(module2) {
|
|
504
|
-
return {
|
|
505
|
-
error: (msg, ...args) => shouldLog("error") && console.error(`[${module2}] ${msg}`, ...args),
|
|
506
|
-
warn: (msg, ...args) => shouldLog("warn") && console.warn(`[${module2}] ${msg}`, ...args),
|
|
507
|
-
info: (msg, ...args) => shouldLog("info") && console.info(`[${module2}] ${msg}`, ...args),
|
|
508
|
-
debug: (msg, ...args) => shouldLog("debug") && console.debug(`[${module2}] ${msg}`, ...args)
|
|
509
|
-
};
|
|
510
|
-
}
|
|
511
|
-
var log = logger("core");
|
|
512
|
-
|
|
513
|
-
// src/graph/types/layer.ts
|
|
514
|
-
var log2 = logger("layer");
|
|
515
|
-
var defaultLayerProps = {
|
|
569
|
+
var log3 = logger("layer");
|
|
570
|
+
var defLayerData = {
|
|
516
571
|
id: "",
|
|
517
572
|
index: 0,
|
|
518
573
|
nodeIds: (0, import_immutable4.Set)(),
|
|
@@ -523,7 +578,7 @@ var defaultLayerProps = {
|
|
|
523
578
|
isSorted: false,
|
|
524
579
|
mutable: false
|
|
525
580
|
};
|
|
526
|
-
var Layer = class extends (0, import_immutable4.Record)(
|
|
581
|
+
var Layer = class extends (0, import_immutable4.Record)(defLayerData) {
|
|
527
582
|
static prefix = "l:";
|
|
528
583
|
mut(g) {
|
|
529
584
|
if (this.mutable) return this;
|
|
@@ -605,7 +660,6 @@ var Layer = class extends (0, import_immutable4.Record)(defaultLayerProps) {
|
|
|
605
660
|
}
|
|
606
661
|
setSorted(g, nodeIds) {
|
|
607
662
|
if (this.hasSortOrder(nodeIds)) return this;
|
|
608
|
-
console.log(`setting sorted for layer ${this.id}`);
|
|
609
663
|
nodeIds.forEach((nodeId, i) => g.getNode(nodeId).setIndex(g, i));
|
|
610
664
|
return this.mut(g).merge({ sorted: nodeIds, isSorted: true });
|
|
611
665
|
}
|
|
@@ -615,7 +669,7 @@ var Layer = class extends (0, import_immutable4.Record)(defaultLayerProps) {
|
|
|
615
669
|
}
|
|
616
670
|
};
|
|
617
671
|
|
|
618
|
-
// src/graph/
|
|
672
|
+
// src/graph/mutator.ts
|
|
619
673
|
var Mutator = class {
|
|
620
674
|
changes;
|
|
621
675
|
constructor() {
|
|
@@ -624,20 +678,27 @@ var Mutator = class {
|
|
|
624
678
|
removedNodes: [],
|
|
625
679
|
updatedNodes: [],
|
|
626
680
|
addedEdges: [],
|
|
627
|
-
removedEdges: []
|
|
681
|
+
removedEdges: [],
|
|
682
|
+
updatedEdges: []
|
|
628
683
|
};
|
|
629
684
|
}
|
|
685
|
+
describe(description) {
|
|
686
|
+
this.changes.description = description;
|
|
687
|
+
}
|
|
630
688
|
addNode(node) {
|
|
631
689
|
this.changes.addedNodes.push(node);
|
|
632
690
|
}
|
|
633
691
|
addNodes(...nodes) {
|
|
634
692
|
nodes.forEach((node) => this.addNode(node));
|
|
635
693
|
}
|
|
694
|
+
removeNode(node) {
|
|
695
|
+
this.changes.removedNodes.push(node);
|
|
696
|
+
}
|
|
697
|
+
removeNodes(...nodes) {
|
|
698
|
+
nodes.forEach((node) => this.removeNode(node));
|
|
699
|
+
}
|
|
636
700
|
updateNode(node) {
|
|
637
|
-
|
|
638
|
-
this.changes.updatedNodes.push({ id: node });
|
|
639
|
-
else
|
|
640
|
-
this.changes.updatedNodes.push(node);
|
|
701
|
+
this.changes.updatedNodes.push(node);
|
|
641
702
|
}
|
|
642
703
|
updateNodes(...nodes) {
|
|
643
704
|
nodes.forEach((node) => this.updateNode(node));
|
|
@@ -648,21 +709,18 @@ var Mutator = class {
|
|
|
648
709
|
addEdges(...edges) {
|
|
649
710
|
edges.forEach((edge) => this.addEdge(edge));
|
|
650
711
|
}
|
|
651
|
-
removeNode(node) {
|
|
652
|
-
if (typeof node === "string")
|
|
653
|
-
this.changes.removedNodes.push({ id: node });
|
|
654
|
-
else
|
|
655
|
-
this.changes.removedNodes.push(node);
|
|
656
|
-
}
|
|
657
|
-
removeNodes(...nodes) {
|
|
658
|
-
nodes.forEach((node) => this.removeNode(node));
|
|
659
|
-
}
|
|
660
712
|
removeEdge(edge) {
|
|
661
713
|
this.changes.removedEdges.push(edge);
|
|
662
714
|
}
|
|
663
715
|
removeEdges(...edges) {
|
|
664
716
|
edges.forEach((edge) => this.removeEdge(edge));
|
|
665
717
|
}
|
|
718
|
+
updateEdge(edge) {
|
|
719
|
+
this.changes.updatedEdges.push(edge);
|
|
720
|
+
}
|
|
721
|
+
updateEdges(...edges) {
|
|
722
|
+
edges.forEach((edge) => this.updateEdge(edge));
|
|
723
|
+
}
|
|
666
724
|
};
|
|
667
725
|
|
|
668
726
|
// src/graph/services/cycles.ts
|
|
@@ -762,14 +820,12 @@ var Cycles = class _Cycles {
|
|
|
762
820
|
|
|
763
821
|
// src/graph/services/dummy.ts
|
|
764
822
|
var import_immutable5 = require("immutable");
|
|
765
|
-
var
|
|
823
|
+
var log4 = logger("dummy");
|
|
766
824
|
var Dummy = class _Dummy {
|
|
767
825
|
static updateDummies(g) {
|
|
768
|
-
log3.debug(`updating dummies:`, [...g.dirtyEdges]);
|
|
769
826
|
for (const edgeId of g.dirtyEdges) {
|
|
770
|
-
log3.debug(`updating dummies of edge ${edgeId}`);
|
|
771
827
|
const edge = g.getEdge(edgeId);
|
|
772
|
-
const { type } = edge;
|
|
828
|
+
const { type, style } = edge;
|
|
773
829
|
const sourceLayer = edge.sourceNode(g).layerIndex(g);
|
|
774
830
|
const targetLayer = edge.targetNode(g).layerIndex(g);
|
|
775
831
|
let segIndex = 0;
|
|
@@ -793,12 +849,10 @@ var Dummy = class _Dummy {
|
|
|
793
849
|
});
|
|
794
850
|
target = { id: dummy.id };
|
|
795
851
|
}
|
|
796
|
-
seg = Seg.add(g, { source, target, type, edgeIds: (0, import_immutable5.Set)([edgeId]) });
|
|
797
|
-
log3.debug(`edge ${edgeId}: adding segment ${seg.id} from ${source.id} at layer ${layerIndex - 1} to ${target.id} at layer ${layerIndex}`);
|
|
852
|
+
seg = Seg.add(g, { source, target, type, style, edgeIds: (0, import_immutable5.Set)([edgeId]) });
|
|
798
853
|
segs.splice(segIndex, 0, seg.id);
|
|
799
854
|
changed = true;
|
|
800
855
|
} 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)) {
|
|
801
|
-
log3.debug(`edge ${edgeId}: removing segment ${seg.id} from layer ${layerIndex - 1} to layer ${layerIndex}`);
|
|
802
856
|
seg = seg.delEdgeId(g, edgeId);
|
|
803
857
|
segs.splice(segIndex, 1);
|
|
804
858
|
changed = true;
|
|
@@ -810,14 +864,12 @@ var Dummy = class _Dummy {
|
|
|
810
864
|
}
|
|
811
865
|
}
|
|
812
866
|
while (segIndex < segs.length) {
|
|
813
|
-
log3.debug(`edge ${edgeId}: removing trailing segment ${segs[segIndex]}`);
|
|
814
867
|
g.getSeg(segs[segIndex]).delEdgeId(g, edgeId);
|
|
815
868
|
segs.splice(segIndex, 1);
|
|
816
869
|
changed = true;
|
|
817
870
|
segIndex++;
|
|
818
871
|
}
|
|
819
872
|
if (changed) {
|
|
820
|
-
log3.debug(`edge ${edgeId}: updated segments to ${segs.join(", ")}`);
|
|
821
873
|
edge.setSegIds(g, segs);
|
|
822
874
|
}
|
|
823
875
|
}
|
|
@@ -832,7 +884,6 @@ var Dummy = class _Dummy {
|
|
|
832
884
|
const dir = side == "source" ? "in" : "out";
|
|
833
885
|
const altSide = side == "source" ? "target" : "source";
|
|
834
886
|
const altDir = altSide == "source" ? "in" : "out";
|
|
835
|
-
log3.debug(`merging dummies by ${side}`);
|
|
836
887
|
for (const layerId of layerIds) {
|
|
837
888
|
let layer = g.getLayer(layerId);
|
|
838
889
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -841,7 +892,7 @@ var Dummy = class _Dummy {
|
|
|
841
892
|
const node = g.getNode(nodeId);
|
|
842
893
|
if (node.isMerged) continue;
|
|
843
894
|
const edge = g.getEdge(node.edgeIds[0]);
|
|
844
|
-
const key = Edge.
|
|
895
|
+
const key = Edge.key(edge, "k:", side);
|
|
845
896
|
if (!groups.has(key)) groups.set(key, /* @__PURE__ */ new Set());
|
|
846
897
|
groups.get(key).add(node);
|
|
847
898
|
}
|
|
@@ -883,7 +934,7 @@ var Dummy = class _Dummy {
|
|
|
883
934
|
|
|
884
935
|
// src/graph/services/layers.ts
|
|
885
936
|
var import_immutable6 = require("immutable");
|
|
886
|
-
var
|
|
937
|
+
var log5 = logger("layers");
|
|
887
938
|
var Layers = class {
|
|
888
939
|
static updateLayers(g) {
|
|
889
940
|
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);
|
|
@@ -946,13 +997,11 @@ var Layers = class {
|
|
|
946
997
|
|
|
947
998
|
// src/graph/services/layout.ts
|
|
948
999
|
var import_immutable7 = require("immutable");
|
|
949
|
-
var
|
|
1000
|
+
var log6 = logger("layout");
|
|
950
1001
|
var Layout = class _Layout {
|
|
951
1002
|
static parentIndex(g, node) {
|
|
952
1003
|
const parents = (0, import_immutable7.Seq)([...node.adjs(g, "segs", "in")]);
|
|
953
|
-
console.log(`parents of ${node.id}:`, [...parents], [...parents.map((p) => p.index)]);
|
|
954
1004
|
const pidx = parents.map((p) => p.index).min();
|
|
955
|
-
log5.debug(`node ${node.id}: parent index ${pidx}`);
|
|
956
1005
|
if (pidx !== void 0) return pidx;
|
|
957
1006
|
return node.isDummy ? -Infinity : Infinity;
|
|
958
1007
|
}
|
|
@@ -970,7 +1019,6 @@ var Layout = class _Layout {
|
|
|
970
1019
|
return minA.localeCompare(minB);
|
|
971
1020
|
}
|
|
972
1021
|
static positionNodes(g) {
|
|
973
|
-
console.log("positionNodes", g.dirtyNodes);
|
|
974
1022
|
for (const nodeId of g.dirtyNodes)
|
|
975
1023
|
g.dirtyLayers.add(g.getNode(nodeId).layerId);
|
|
976
1024
|
let adjustNext = false;
|
|
@@ -978,15 +1026,12 @@ var Layout = class _Layout {
|
|
|
978
1026
|
if (!adjustNext && !g.dirtyLayers.has(layerId)) continue;
|
|
979
1027
|
adjustNext = false;
|
|
980
1028
|
let layer = g.getLayer(layerId);
|
|
981
|
-
console.log(`positioning layer ${layerId} at ${layer.index}`);
|
|
982
1029
|
const pidxs = /* @__PURE__ */ new Map();
|
|
983
1030
|
for (const nodeId of layer.nodeIds)
|
|
984
1031
|
pidxs.set(nodeId, _Layout.parentIndex(g, g.getNode(nodeId)));
|
|
985
|
-
console.log("pidxs", pidxs);
|
|
986
1032
|
const sorted = [...layer.nodeIds].sort(
|
|
987
1033
|
(aId, bId) => _Layout.compareNodes(g, aId, bId, pidxs)
|
|
988
1034
|
);
|
|
989
|
-
console.log(`sorted:`, sorted);
|
|
990
1035
|
if (layer.hasSortOrder(sorted)) continue;
|
|
991
1036
|
g.dirtyLayers.add(layerId);
|
|
992
1037
|
layer = layer.setSorted(g, sorted);
|
|
@@ -994,7 +1039,6 @@ var Layout = class _Layout {
|
|
|
994
1039
|
let lpos = 0;
|
|
995
1040
|
for (let i = 0; i < sorted.length; i++) {
|
|
996
1041
|
let node = g.getNode(sorted[i]);
|
|
997
|
-
log5.debug(`node ${node.id}: final index ${i}`);
|
|
998
1042
|
node = node.setIndex(g, i).setLayerPos(g, lpos);
|
|
999
1043
|
const size = node.dims?.[g.w] ?? 0;
|
|
1000
1044
|
lpos += size + g.options.nodeMargin;
|
|
@@ -1026,7 +1070,12 @@ var Layout = class _Layout {
|
|
|
1026
1070
|
for (const layerId of layerIds) {
|
|
1027
1071
|
if (!adjustNext && !g.dirtyLayers.has(layerId)) continue;
|
|
1028
1072
|
adjustNext = false;
|
|
1073
|
+
let iterations = 0;
|
|
1029
1074
|
while (true) {
|
|
1075
|
+
if (++iterations > 10) {
|
|
1076
|
+
log6.error(`alignNodes: infinite loop detected in layer ${layerId}`);
|
|
1077
|
+
break;
|
|
1078
|
+
}
|
|
1030
1079
|
let changed = false;
|
|
1031
1080
|
const nodeIds = _Layout.sortLayer(g, layerId, reverseNodes);
|
|
1032
1081
|
for (const nodeId of nodeIds) {
|
|
@@ -1092,17 +1141,45 @@ var Layout = class _Layout {
|
|
|
1092
1141
|
[g.x]: p[g.x] + w / 2,
|
|
1093
1142
|
[g.y]: p[g.y] + h / 2
|
|
1094
1143
|
};
|
|
1095
|
-
p[g.x] += _Layout.nodePortOffset(g, nodeId, seg
|
|
1096
|
-
if (side == "
|
|
1144
|
+
p[g.x] += _Layout.nodePortOffset(g, nodeId, seg, side);
|
|
1145
|
+
if (side == "target" == g.r)
|
|
1097
1146
|
p[g.y] += h;
|
|
1098
1147
|
return p;
|
|
1099
1148
|
}
|
|
1100
|
-
static nodePortOffset(g, nodeId,
|
|
1101
|
-
|
|
1102
|
-
|
|
1149
|
+
static nodePortOffset(g, nodeId, seg, side) {
|
|
1150
|
+
const node = g.getNode(nodeId);
|
|
1151
|
+
const dir = side == "source" ? "out" : "in";
|
|
1152
|
+
const portId = seg[side].port;
|
|
1153
|
+
let min = 0, size = node.dims?.[g.w] ?? 0;
|
|
1154
|
+
if (portId) {
|
|
1155
|
+
const ports = node.ports?.[dir];
|
|
1156
|
+
const port = ports?.find((p) => p.id === portId);
|
|
1157
|
+
if (port?.offset !== void 0) {
|
|
1158
|
+
min = port.offset;
|
|
1159
|
+
size = port.size ?? 0;
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
const alt = side == "source" ? "target" : "source";
|
|
1163
|
+
let segs = [];
|
|
1164
|
+
const keyOf = (seg2) => `${seg2.type ?? ""}:${seg2[side].marker ?? ""}`;
|
|
1165
|
+
for (const segId of node.segs[dir])
|
|
1166
|
+
segs.push(g.getSeg(segId));
|
|
1167
|
+
if (portId) segs = segs.filter((s) => s[side].port == portId);
|
|
1168
|
+
const groups = Object.groupBy(segs, (s) => keyOf(s));
|
|
1169
|
+
const posMap = /* @__PURE__ */ new Map();
|
|
1170
|
+
for (const [key, segs2] of Object.entries(groups)) {
|
|
1171
|
+
let pos = Infinity;
|
|
1172
|
+
for (const seg2 of segs2) pos = Math.min(pos, seg2.node(g, alt).lpos);
|
|
1173
|
+
posMap.set(key, pos);
|
|
1174
|
+
}
|
|
1175
|
+
const keys = [...posMap.keys()].sort((a, b) => posMap.get(a) - posMap.get(b));
|
|
1176
|
+
const gap = size / (keys.length + 1);
|
|
1177
|
+
const index = keys.indexOf(keyOf(seg));
|
|
1178
|
+
return min + (index + 1) * gap;
|
|
1103
1179
|
}
|
|
1104
1180
|
static shiftNode(g, nodeId, alignId, dir, lpos, reverseMove, conservative) {
|
|
1105
1181
|
const node = g.getNode(nodeId);
|
|
1182
|
+
log6.debug(`shift ${nodeId} (at ${node.lpos}) to ${alignId} (at ${lpos})`);
|
|
1106
1183
|
if (!conservative)
|
|
1107
1184
|
_Layout.markAligned(g, nodeId, alignId, dir, lpos);
|
|
1108
1185
|
const space = g.options.nodeMargin;
|
|
@@ -1133,7 +1210,7 @@ var Layout = class _Layout {
|
|
|
1133
1210
|
g.getNode(node.aligned[dir]).setAligned(g, alt, void 0);
|
|
1134
1211
|
if (otherId)
|
|
1135
1212
|
g.getNode(otherId).setAligned(g, alt, nodeId);
|
|
1136
|
-
node.setAligned(g, dir, otherId);
|
|
1213
|
+
node.setAligned(g, dir, otherId).setLayerPos(g, lpos);
|
|
1137
1214
|
}
|
|
1138
1215
|
static *aligned(g, nodeId, dir) {
|
|
1139
1216
|
const visit = function* (node2, dir2) {
|
|
@@ -1193,39 +1270,141 @@ var Layout = class _Layout {
|
|
|
1193
1270
|
let pos = 0;
|
|
1194
1271
|
const dir = g.r ? -1 : 1;
|
|
1195
1272
|
const trackSep = Math.max(
|
|
1196
|
-
g.options.layerMargin,
|
|
1197
1273
|
g.options.edgeSpacing,
|
|
1198
1274
|
g.options.turnRadius
|
|
1199
1275
|
);
|
|
1276
|
+
const marginSep = Math.max(
|
|
1277
|
+
g.options.edgeSpacing,
|
|
1278
|
+
g.options.layerMargin,
|
|
1279
|
+
g.options.turnRadius + g.options.markerSize
|
|
1280
|
+
);
|
|
1200
1281
|
for (const layerId of g.layerList) {
|
|
1201
1282
|
let layer = g.getLayer(layerId);
|
|
1202
1283
|
let height;
|
|
1203
|
-
console.log(`getCoords: layer = ${layerId} at ${layer.index}`);
|
|
1204
1284
|
if (g.dirtyLayers.has(layerId)) {
|
|
1205
1285
|
height = (0, import_immutable7.Seq)(layer.nodes(g)).map((node) => node.dims?.[g.h] ?? 0).max() ?? 0;
|
|
1206
1286
|
layer = layer.setSize(g, height);
|
|
1207
1287
|
} else height = layer.size;
|
|
1208
|
-
console.log(`getCoords: layer = ${layerId}: pos = ${pos}, height = ${height}`);
|
|
1209
1288
|
for (const node of layer.nodes(g)) {
|
|
1210
1289
|
if (!g.dirtyNodes.has(node.id) && pos == layer.pos) continue;
|
|
1211
1290
|
const npos = { [g.x]: node.lpos, [g.y]: pos };
|
|
1212
1291
|
if (!g.n) npos[g.y] += dir * height;
|
|
1213
1292
|
if (g.r == g.n) npos[g.y] -= node.dims?.[g.h] ?? 0;
|
|
1214
|
-
console.log(`getCoords: node = ${node.id}: pos:`, npos);
|
|
1215
1293
|
node.setPos(g, npos);
|
|
1216
1294
|
}
|
|
1217
1295
|
layer = layer.setPos(g, pos);
|
|
1218
|
-
pos += dir * (height +
|
|
1296
|
+
pos += dir * (height + marginSep);
|
|
1219
1297
|
for (const track of layer.tracks) {
|
|
1220
1298
|
for (const segId of track)
|
|
1221
1299
|
g.getSeg(segId).setTrackPos(g, pos);
|
|
1222
|
-
pos += dir *
|
|
1300
|
+
pos += dir * trackSep;
|
|
1223
1301
|
}
|
|
1302
|
+
pos += dir * (marginSep - trackSep);
|
|
1224
1303
|
}
|
|
1225
1304
|
}
|
|
1226
1305
|
};
|
|
1227
1306
|
|
|
1307
|
+
// src/canvas/marker.tsx
|
|
1308
|
+
var import_marker = __toESM(require("./marker.css?raw"), 1);
|
|
1309
|
+
var import_jsx_runtime = require("jsx-dom/jsx-runtime");
|
|
1310
|
+
function arrow(size, classPrefix, reverse = false) {
|
|
1311
|
+
const h = size / 1.5;
|
|
1312
|
+
const w = size;
|
|
1313
|
+
const ry = h / 2;
|
|
1314
|
+
const suffix = reverse ? "-reverse" : "";
|
|
1315
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1316
|
+
"marker",
|
|
1317
|
+
{
|
|
1318
|
+
id: `g3p-marker-arrow${suffix}`,
|
|
1319
|
+
className: `${classPrefix}-marker ${classPrefix}-marker-arrow`,
|
|
1320
|
+
markerWidth: size,
|
|
1321
|
+
markerHeight: size,
|
|
1322
|
+
refX: "2",
|
|
1323
|
+
refY: ry,
|
|
1324
|
+
orient: reverse ? "auto-start-reverse" : "auto",
|
|
1325
|
+
markerUnits: "userSpaceOnUse",
|
|
1326
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: `M0,0 L0,${h} L${w},${ry} z` })
|
|
1327
|
+
}
|
|
1328
|
+
);
|
|
1329
|
+
}
|
|
1330
|
+
function circle(size, classPrefix, reverse = false) {
|
|
1331
|
+
const r = size / 3;
|
|
1332
|
+
const cy = size / 2;
|
|
1333
|
+
const suffix = reverse ? "-reverse" : "";
|
|
1334
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1335
|
+
"marker",
|
|
1336
|
+
{
|
|
1337
|
+
id: `g3p-marker-circle${suffix}`,
|
|
1338
|
+
className: `${classPrefix}-marker ${classPrefix}-marker-circle`,
|
|
1339
|
+
markerWidth: size,
|
|
1340
|
+
markerHeight: size,
|
|
1341
|
+
refX: "2",
|
|
1342
|
+
refY: cy,
|
|
1343
|
+
orient: reverse ? "auto-start-reverse" : "auto",
|
|
1344
|
+
markerUnits: "userSpaceOnUse",
|
|
1345
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: r + 2, cy, r })
|
|
1346
|
+
}
|
|
1347
|
+
);
|
|
1348
|
+
}
|
|
1349
|
+
function diamond(size, classPrefix, reverse = false) {
|
|
1350
|
+
const w = size * 0.7;
|
|
1351
|
+
const h = size / 2;
|
|
1352
|
+
const cy = size / 2;
|
|
1353
|
+
const suffix = reverse ? "-reverse" : "";
|
|
1354
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1355
|
+
"marker",
|
|
1356
|
+
{
|
|
1357
|
+
id: `g3p-marker-diamond${suffix}`,
|
|
1358
|
+
className: `${classPrefix}-marker ${classPrefix}-marker-diamond`,
|
|
1359
|
+
markerWidth: size,
|
|
1360
|
+
markerHeight: size,
|
|
1361
|
+
refX: "2",
|
|
1362
|
+
refY: cy,
|
|
1363
|
+
orient: reverse ? "auto-start-reverse" : "auto",
|
|
1364
|
+
markerUnits: "userSpaceOnUse",
|
|
1365
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: `M2,${cy} L${2 + w / 2},${cy - h / 2} L${2 + w},${cy} L${2 + w / 2},${cy + h / 2} z` })
|
|
1366
|
+
}
|
|
1367
|
+
);
|
|
1368
|
+
}
|
|
1369
|
+
function bar(size, classPrefix, reverse = false) {
|
|
1370
|
+
const h = size * 0.6;
|
|
1371
|
+
const cy = size / 2;
|
|
1372
|
+
const suffix = reverse ? "-reverse" : "";
|
|
1373
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1374
|
+
"marker",
|
|
1375
|
+
{
|
|
1376
|
+
id: `g3p-marker-bar${suffix}`,
|
|
1377
|
+
className: `${classPrefix}-marker ${classPrefix}-marker-bar`,
|
|
1378
|
+
markerWidth: size,
|
|
1379
|
+
markerHeight: size,
|
|
1380
|
+
refX: "2",
|
|
1381
|
+
refY: cy,
|
|
1382
|
+
orient: reverse ? "auto-start-reverse" : "auto",
|
|
1383
|
+
markerUnits: "userSpaceOnUse",
|
|
1384
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "2", y1: cy - h / 2, x2: "2", y2: cy + h / 2, "stroke-width": "2" })
|
|
1385
|
+
}
|
|
1386
|
+
);
|
|
1387
|
+
}
|
|
1388
|
+
function none(size, classPrefix, reverse = false) {
|
|
1389
|
+
return void 0;
|
|
1390
|
+
}
|
|
1391
|
+
function normalize(data) {
|
|
1392
|
+
let source = data.source?.marker ?? data.style?.marker?.source;
|
|
1393
|
+
let target = data.target?.marker ?? data.style?.marker?.target ?? "arrow";
|
|
1394
|
+
if (source == "none") source = void 0;
|
|
1395
|
+
if (target == "none") target = void 0;
|
|
1396
|
+
return { source, target };
|
|
1397
|
+
}
|
|
1398
|
+
var markerDefs = {
|
|
1399
|
+
arrow,
|
|
1400
|
+
circle,
|
|
1401
|
+
diamond,
|
|
1402
|
+
bar,
|
|
1403
|
+
none
|
|
1404
|
+
};
|
|
1405
|
+
|
|
1228
1406
|
// src/graph/services/lines.ts
|
|
1407
|
+
var log7 = logger("lines");
|
|
1229
1408
|
var Lines = class _Lines {
|
|
1230
1409
|
static layoutSeg(g, seg) {
|
|
1231
1410
|
const sourcePos = Layout.anchorPos(g, seg, "source");
|
|
@@ -1300,7 +1479,12 @@ var Lines = class _Lines {
|
|
|
1300
1479
|
const radius = g.options.turnRadius;
|
|
1301
1480
|
const p1 = Layout.anchorPos(g, seg, "source");
|
|
1302
1481
|
const p2 = Layout.anchorPos(g, seg, "target");
|
|
1303
|
-
const
|
|
1482
|
+
const source = seg.sourceNode(g);
|
|
1483
|
+
const target = seg.targetNode(g);
|
|
1484
|
+
const marker = normalize(seg);
|
|
1485
|
+
if (source.isDummy) marker.source = void 0;
|
|
1486
|
+
if (target.isDummy) marker.target = void 0;
|
|
1487
|
+
const path = seg.trackPos !== void 0 ? _Lines.createRailroadPath(g, p1, p2, seg.trackPos, radius, marker) : _Lines.createDirectPath(g, p1, p2, radius, marker);
|
|
1304
1488
|
const svg = _Lines.pathToSVG(path);
|
|
1305
1489
|
seg.setSVG(g, svg);
|
|
1306
1490
|
}
|
|
@@ -1326,7 +1510,7 @@ var Lines = class _Lines {
|
|
|
1326
1510
|
}
|
|
1327
1511
|
return line;
|
|
1328
1512
|
}
|
|
1329
|
-
static pathBuilder(g, start, end, trackPos, radius) {
|
|
1513
|
+
static pathBuilder(g, start, end, trackPos, radius, marker) {
|
|
1330
1514
|
const { x, y } = g;
|
|
1331
1515
|
const lr = end[x] > start[x];
|
|
1332
1516
|
const d = lr ? 1 : -1;
|
|
@@ -1338,6 +1522,8 @@ var Lines = class _Lines {
|
|
|
1338
1522
|
if (g.r) s = 1 - s;
|
|
1339
1523
|
if (!lr) s = 1 - s;
|
|
1340
1524
|
if (!g.v) s = 1 - s;
|
|
1525
|
+
if (marker.source) start[y] += o * (g.options.markerSize - 1);
|
|
1526
|
+
if (marker.target) end[y] -= o * (g.options.markerSize - 1);
|
|
1341
1527
|
const p = { ...start, s };
|
|
1342
1528
|
const path = [];
|
|
1343
1529
|
const advance = (p2, type) => {
|
|
@@ -1346,8 +1532,8 @@ var Lines = class _Lines {
|
|
|
1346
1532
|
return { x, y, lr, d, o, rd, ro, t, s, p, path, advance };
|
|
1347
1533
|
}
|
|
1348
1534
|
// Create a railroad-style path with two 90-degree turns
|
|
1349
|
-
static createRailroadPath(g, start, end, trackPos, radius) {
|
|
1350
|
-
const { x, y, rd, ro, t, s, p, path, advance } = this.pathBuilder(g, start, end, trackPos, radius);
|
|
1535
|
+
static createRailroadPath(g, start, end, trackPos, radius, marker) {
|
|
1536
|
+
const { x, y, rd, ro, t, s, p, path, advance } = this.pathBuilder(g, start, end, trackPos, radius, marker);
|
|
1351
1537
|
advance({ [y]: t - ro }, "line");
|
|
1352
1538
|
advance({ [x]: p[x] + rd, [y]: t }, "arc");
|
|
1353
1539
|
advance({ [x]: end[x] - rd }, "line");
|
|
@@ -1356,8 +1542,8 @@ var Lines = class _Lines {
|
|
|
1356
1542
|
return path;
|
|
1357
1543
|
}
|
|
1358
1544
|
// Create a mostly-vertical path with optional S-curve
|
|
1359
|
-
static createDirectPath(g, start, end, radius) {
|
|
1360
|
-
const { x, y, d, o, s, p, path, advance } = this.pathBuilder(g, start, end, 0, radius);
|
|
1545
|
+
static createDirectPath(g, start, end, radius, marker) {
|
|
1546
|
+
const { x, y, d, o, s, p, path, advance } = this.pathBuilder(g, start, end, 0, radius, marker);
|
|
1361
1547
|
const dx = Math.abs(end.x - start.x);
|
|
1362
1548
|
const dy = Math.abs(end.y - start.y);
|
|
1363
1549
|
const d_ = { x: dx, y: dy };
|
|
@@ -1448,21 +1634,14 @@ var Lines = class _Lines {
|
|
|
1448
1634
|
}
|
|
1449
1635
|
};
|
|
1450
1636
|
|
|
1451
|
-
// src/graph/
|
|
1452
|
-
var
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
turnRadius: 10,
|
|
1460
|
-
orientation: "TB",
|
|
1461
|
-
layerMargin: 5,
|
|
1462
|
-
alignIterations: 5,
|
|
1463
|
-
alignThreshold: 10,
|
|
1464
|
-
separateTrackSets: true,
|
|
1465
|
-
layoutSteps: null
|
|
1637
|
+
// src/graph/graph.ts
|
|
1638
|
+
var emptyChanges = {
|
|
1639
|
+
addedNodes: [],
|
|
1640
|
+
removedNodes: [],
|
|
1641
|
+
updatedNodes: [],
|
|
1642
|
+
addedEdges: [],
|
|
1643
|
+
removedEdges: [],
|
|
1644
|
+
updatedEdges: []
|
|
1466
1645
|
};
|
|
1467
1646
|
var Graph = class _Graph {
|
|
1468
1647
|
prior;
|
|
@@ -1491,30 +1670,10 @@ var Graph = class _Graph {
|
|
|
1491
1670
|
x;
|
|
1492
1671
|
y;
|
|
1493
1672
|
d;
|
|
1494
|
-
constructor({ prior, changes, options
|
|
1673
|
+
constructor({ prior, changes, options }) {
|
|
1674
|
+
this.options = prior?.options ?? options;
|
|
1675
|
+
this.changes = changes ?? emptyChanges;
|
|
1495
1676
|
this.initFromPrior(prior);
|
|
1496
|
-
this.dirtyNodes = /* @__PURE__ */ new Set();
|
|
1497
|
-
this.dirtyEdges = /* @__PURE__ */ new Set();
|
|
1498
|
-
this.dirtyLayers = /* @__PURE__ */ new Set();
|
|
1499
|
-
this.dirtySegs = /* @__PURE__ */ new Set();
|
|
1500
|
-
this.delNodes = /* @__PURE__ */ new Set();
|
|
1501
|
-
this.delEdges = /* @__PURE__ */ new Set();
|
|
1502
|
-
this.delSegs = /* @__PURE__ */ new Set();
|
|
1503
|
-
this.options = {
|
|
1504
|
-
...defaultOptions,
|
|
1505
|
-
...prior?.options,
|
|
1506
|
-
...options
|
|
1507
|
-
};
|
|
1508
|
-
this.changes = changes ?? {
|
|
1509
|
-
addedNodes: [],
|
|
1510
|
-
removedNodes: [],
|
|
1511
|
-
updatedNodes: [],
|
|
1512
|
-
addedEdges: [],
|
|
1513
|
-
removedEdges: []
|
|
1514
|
-
};
|
|
1515
|
-
this.changes.addedNodes.push(...nodes || []);
|
|
1516
|
-
this.changes.addedEdges.push(...edges || []);
|
|
1517
|
-
this.dirty = this.changes.addedNodes.length > 0 || this.changes.removedNodes.length > 0 || this.changes.addedEdges.length > 0 || this.changes.removedEdges.length > 0;
|
|
1518
1677
|
this.r = this.options.orientation === "BT" || this.options.orientation === "RL";
|
|
1519
1678
|
this.v = this.options.orientation === "TB" || this.options.orientation === "BT";
|
|
1520
1679
|
this.h = this.v ? "h" : "w";
|
|
@@ -1530,38 +1689,41 @@ var Graph = class _Graph {
|
|
|
1530
1689
|
this.n = true;
|
|
1531
1690
|
else
|
|
1532
1691
|
this.n = natAligns[this.options.orientation] == this.options.nodeAlign;
|
|
1533
|
-
if (this.dirty)
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1692
|
+
if (this.dirty) this.processUpdate();
|
|
1693
|
+
}
|
|
1694
|
+
processUpdate() {
|
|
1695
|
+
try {
|
|
1696
|
+
this.beginMutate();
|
|
1697
|
+
this.applyChanges();
|
|
1698
|
+
Cycles.checkCycles(this);
|
|
1699
|
+
Layers.updateLayers(this);
|
|
1700
|
+
Dummy.updateDummies(this);
|
|
1701
|
+
Dummy.mergeDummies(this);
|
|
1702
|
+
Layout.positionNodes(this);
|
|
1703
|
+
Layout.alignAll(this);
|
|
1704
|
+
Lines.trackEdges(this);
|
|
1705
|
+
Layout.getCoords(this);
|
|
1706
|
+
Lines.pathEdges(this);
|
|
1707
|
+
} catch (e) {
|
|
1708
|
+
this.initFromPrior(this.prior);
|
|
1709
|
+
throw e;
|
|
1710
|
+
} finally {
|
|
1711
|
+
this.endMutate();
|
|
1552
1712
|
}
|
|
1553
1713
|
}
|
|
1554
1714
|
applyChanges() {
|
|
1555
1715
|
for (const edge of this.changes.removedEdges)
|
|
1556
|
-
|
|
1716
|
+
Edge.del(this, edge);
|
|
1557
1717
|
for (const node of this.changes.removedNodes)
|
|
1558
|
-
|
|
1718
|
+
Node.del(this, node);
|
|
1559
1719
|
for (const node of this.changes.addedNodes)
|
|
1560
1720
|
Node.addNormal(this, node);
|
|
1561
1721
|
for (const edge of this.changes.addedEdges)
|
|
1562
1722
|
Edge.add(this, edge);
|
|
1563
1723
|
for (const node of this.changes.updatedNodes)
|
|
1564
|
-
|
|
1724
|
+
Node.update(this, node);
|
|
1725
|
+
for (const edge of this.changes.updatedEdges)
|
|
1726
|
+
Edge.update(this, edge);
|
|
1565
1727
|
}
|
|
1566
1728
|
layerAt(index) {
|
|
1567
1729
|
while (index >= this.layerList.size)
|
|
@@ -1638,24 +1800,24 @@ var Graph = class _Graph {
|
|
|
1638
1800
|
nodes.forEach((node) => mutator.addNode(node));
|
|
1639
1801
|
});
|
|
1640
1802
|
}
|
|
1641
|
-
|
|
1803
|
+
removeNodes(...nodes) {
|
|
1642
1804
|
return this.withMutations((mutator) => {
|
|
1643
|
-
|
|
1805
|
+
nodes.forEach((node) => mutator.removeNode(node));
|
|
1644
1806
|
});
|
|
1645
1807
|
}
|
|
1646
|
-
|
|
1808
|
+
removeNode(node) {
|
|
1647
1809
|
return this.withMutations((mutator) => {
|
|
1648
|
-
mutator.
|
|
1810
|
+
mutator.removeNode(node);
|
|
1649
1811
|
});
|
|
1650
1812
|
}
|
|
1651
|
-
|
|
1813
|
+
addEdges(...edges) {
|
|
1652
1814
|
return this.withMutations((mutator) => {
|
|
1653
|
-
|
|
1815
|
+
edges.forEach((edge) => mutator.addEdge(edge));
|
|
1654
1816
|
});
|
|
1655
1817
|
}
|
|
1656
|
-
|
|
1818
|
+
addEdge(edge) {
|
|
1657
1819
|
return this.withMutations((mutator) => {
|
|
1658
|
-
mutator.
|
|
1820
|
+
mutator.addEdge(edge);
|
|
1659
1821
|
});
|
|
1660
1822
|
}
|
|
1661
1823
|
removeEdges(...edges) {
|
|
@@ -1705,6 +1867,14 @@ var Graph = class _Graph {
|
|
|
1705
1867
|
this.nextLayerId = prior?.nextLayerId ?? 0;
|
|
1706
1868
|
this.nextDummyId = prior?.nextDummyId ?? 0;
|
|
1707
1869
|
this.prior = prior;
|
|
1870
|
+
this.dirtyNodes = /* @__PURE__ */ new Set();
|
|
1871
|
+
this.dirtyEdges = /* @__PURE__ */ new Set();
|
|
1872
|
+
this.dirtyLayers = /* @__PURE__ */ new Set();
|
|
1873
|
+
this.dirtySegs = /* @__PURE__ */ new Set();
|
|
1874
|
+
this.delNodes = /* @__PURE__ */ new Set();
|
|
1875
|
+
this.delEdges = /* @__PURE__ */ new Set();
|
|
1876
|
+
this.delSegs = /* @__PURE__ */ new Set();
|
|
1877
|
+
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;
|
|
1708
1878
|
}
|
|
1709
1879
|
beginMutate() {
|
|
1710
1880
|
this.nodes = this.nodes.asMutable();
|
|
@@ -1734,15 +1904,20 @@ var Graph = class _Graph {
|
|
|
1734
1904
|
}
|
|
1735
1905
|
};
|
|
1736
1906
|
|
|
1737
|
-
// src/
|
|
1738
|
-
var
|
|
1907
|
+
// src/common.ts
|
|
1908
|
+
var screenPos = (x, y) => ({ x, y });
|
|
1909
|
+
var canvasPos = (x, y) => ({ x, y });
|
|
1910
|
+
var graphPos = (x, y) => ({ x, y });
|
|
1911
|
+
|
|
1912
|
+
// src/canvas/node.tsx
|
|
1913
|
+
var import_node3 = __toESM(require("./node.css?raw"), 1);
|
|
1739
1914
|
|
|
1740
1915
|
// src/canvas/styler.ts
|
|
1741
1916
|
var injected = {};
|
|
1742
|
-
function styler(name,
|
|
1917
|
+
function styler(name, styles4, prefix) {
|
|
1743
1918
|
if (prefix === "g3p" && !injected[name]) {
|
|
1744
1919
|
const style = document.createElement("style");
|
|
1745
|
-
style.textContent =
|
|
1920
|
+
style.textContent = styles4;
|
|
1746
1921
|
document.head.appendChild(style);
|
|
1747
1922
|
injected[name] = true;
|
|
1748
1923
|
}
|
|
@@ -1754,98 +1929,106 @@ function styler(name, styles, prefix) {
|
|
|
1754
1929
|
};
|
|
1755
1930
|
}
|
|
1756
1931
|
|
|
1757
|
-
// src/canvas/node.css
|
|
1758
|
-
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}";
|
|
1759
|
-
|
|
1760
1932
|
// src/canvas/node.tsx
|
|
1761
1933
|
var import_jsx_runtime2 = require("jsx-dom/jsx-runtime");
|
|
1934
|
+
var log8 = logger("canvas");
|
|
1762
1935
|
var Node2 = class {
|
|
1763
1936
|
selected;
|
|
1764
1937
|
hovered;
|
|
1765
1938
|
container;
|
|
1766
|
-
dims;
|
|
1767
1939
|
content;
|
|
1768
|
-
|
|
1940
|
+
canvas;
|
|
1941
|
+
data;
|
|
1942
|
+
classPrefix;
|
|
1769
1943
|
isDummy;
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
onMouseDown: () => null,
|
|
1781
|
-
onMouseUp: () => null,
|
|
1782
|
-
classPrefix: "g3p",
|
|
1783
|
-
...options
|
|
1784
|
-
});
|
|
1785
|
-
if (!this.isDummy) {
|
|
1786
|
-
this.content = this.renderNode(this.data);
|
|
1787
|
-
this.measured = false;
|
|
1944
|
+
pos;
|
|
1945
|
+
constructor(canvas, data, isDummy = false) {
|
|
1946
|
+
this.canvas = canvas;
|
|
1947
|
+
this.data = data;
|
|
1948
|
+
this.selected = false;
|
|
1949
|
+
this.hovered = false;
|
|
1950
|
+
this.classPrefix = canvas.classPrefix;
|
|
1951
|
+
this.isDummy = isDummy;
|
|
1952
|
+
if (this.isDummy) {
|
|
1953
|
+
const size = canvas.dummyNodeSize;
|
|
1788
1954
|
} else {
|
|
1789
|
-
|
|
1955
|
+
const render = data.render ?? canvas.renderNode;
|
|
1956
|
+
this.content = this.renderContent(render(data.data));
|
|
1790
1957
|
}
|
|
1791
1958
|
}
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1959
|
+
remove() {
|
|
1960
|
+
this.container.remove();
|
|
1961
|
+
}
|
|
1962
|
+
append() {
|
|
1963
|
+
console.log("append", this);
|
|
1964
|
+
this.canvas.group.appendChild(this.container);
|
|
1965
|
+
}
|
|
1966
|
+
needsContentSize() {
|
|
1967
|
+
return !this.isDummy && this.content instanceof HTMLElement;
|
|
1968
|
+
}
|
|
1969
|
+
needsContainerSize() {
|
|
1970
|
+
return !this.isDummy;
|
|
1796
1971
|
}
|
|
1797
1972
|
handleClick(e) {
|
|
1798
1973
|
e.stopPropagation();
|
|
1799
|
-
this.onClick?.(this.data, e);
|
|
1800
1974
|
}
|
|
1801
1975
|
handleMouseEnter(e) {
|
|
1802
|
-
this.onMouseEnter?.(this.data, e);
|
|
1803
1976
|
}
|
|
1804
1977
|
handleMouseLeave(e) {
|
|
1805
|
-
this.onMouseLeave?.(this.data, e);
|
|
1806
1978
|
}
|
|
1807
1979
|
handleContextMenu(e) {
|
|
1808
|
-
if (this.onContextMenu) {
|
|
1809
|
-
e.stopPropagation();
|
|
1810
|
-
this.onContextMenu(this.data, e);
|
|
1811
|
-
}
|
|
1812
1980
|
}
|
|
1813
1981
|
handleMouseDown(e) {
|
|
1814
|
-
this.onMouseDown?.(this.data, e);
|
|
1815
1982
|
}
|
|
1816
1983
|
handleMouseUp(e) {
|
|
1817
|
-
this.onMouseUp?.(this.data, e);
|
|
1818
1984
|
}
|
|
1819
1985
|
setPos(pos) {
|
|
1820
|
-
console.log(`setPos:`, this, pos);
|
|
1821
1986
|
this.pos = pos;
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1987
|
+
const { x, y } = pos;
|
|
1988
|
+
this.container.setAttribute("transform", `translate(${x}, ${y})`);
|
|
1989
|
+
}
|
|
1990
|
+
hasPorts() {
|
|
1991
|
+
return !!this.data?.ports?.in?.length || !!this.data?.ports?.out?.length;
|
|
1992
|
+
}
|
|
1993
|
+
renderContent(el) {
|
|
1994
|
+
const hasPorts = this.hasPorts();
|
|
1995
|
+
el = this.renderBorder(el);
|
|
1996
|
+
if (hasPorts)
|
|
1997
|
+
el = this.renderOutsidePorts(el);
|
|
1998
|
+
return el;
|
|
1999
|
+
}
|
|
2000
|
+
renderContainer() {
|
|
2001
|
+
const c = styler("node", import_node3.default, this.classPrefix);
|
|
2002
|
+
const hasPorts = this.hasPorts();
|
|
2003
|
+
const inner = this.isDummy ? this.renderDummy() : this.renderForeign();
|
|
2004
|
+
const nodeType = this.data?.type;
|
|
2005
|
+
const typeClass = nodeType ? `g3p-node-type-${nodeType}` : "";
|
|
2006
|
+
this.container = /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1828
2007
|
"g",
|
|
1829
2008
|
{
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
onMouseUp: this.handleMouseUp.bind(this),
|
|
2009
|
+
className: `${c("container")} ${c("dummy", this.isDummy)} ${typeClass}`.trim(),
|
|
2010
|
+
onClick: (e) => this.handleClick(e),
|
|
2011
|
+
onMouseEnter: (e) => this.handleMouseEnter(e),
|
|
2012
|
+
onMouseLeave: (e) => this.handleMouseLeave(e),
|
|
2013
|
+
onContextMenu: (e) => this.handleContextMenu(e),
|
|
2014
|
+
onMouseDown: (e) => this.handleMouseDown(e),
|
|
2015
|
+
onMouseUp: (e) => this.handleMouseUp(e),
|
|
1838
2016
|
style: { cursor: "pointer" },
|
|
1839
|
-
children:
|
|
2017
|
+
children: inner
|
|
1840
2018
|
}
|
|
1841
2019
|
);
|
|
1842
2020
|
}
|
|
2021
|
+
renderForeign() {
|
|
2022
|
+
const { w, h } = this.data.dims;
|
|
2023
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("foreignObject", { width: w, height: h, children: this.content });
|
|
2024
|
+
}
|
|
1843
2025
|
renderDummy() {
|
|
1844
|
-
const c = styler("node",
|
|
1845
|
-
let
|
|
2026
|
+
const c = styler("node", import_node3.default, this.classPrefix);
|
|
2027
|
+
let w = this.canvas.dummyNodeSize;
|
|
2028
|
+
let h = this.canvas.dummyNodeSize;
|
|
1846
2029
|
w /= 2;
|
|
1847
2030
|
h /= 2;
|
|
1848
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
2031
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { children: [
|
|
1849
2032
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1850
2033
|
"ellipse",
|
|
1851
2034
|
{
|
|
@@ -1864,157 +2047,190 @@ var Node2 = class {
|
|
|
1864
2047
|
rx: w,
|
|
1865
2048
|
ry: h,
|
|
1866
2049
|
fill: "none",
|
|
1867
|
-
className: c("border")
|
|
1868
|
-
strokeWidth: "2"
|
|
2050
|
+
className: c("border")
|
|
1869
2051
|
}
|
|
1870
2052
|
)
|
|
1871
2053
|
] });
|
|
1872
2054
|
}
|
|
1873
|
-
|
|
1874
|
-
const
|
|
1875
|
-
const
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
className: c("border"),
|
|
1891
|
-
width: w,
|
|
1892
|
-
height: h,
|
|
1893
|
-
rx: 8,
|
|
1894
|
-
ry: 8,
|
|
1895
|
-
fill: "none",
|
|
1896
|
-
strokeWidth: "2"
|
|
1897
|
-
}
|
|
1898
|
-
),
|
|
1899
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1900
|
-
"foreignObject",
|
|
1901
|
-
{
|
|
1902
|
-
width: w,
|
|
1903
|
-
height: h,
|
|
1904
|
-
className: c("content-wrapper"),
|
|
1905
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1906
|
-
"div",
|
|
1907
|
-
{
|
|
1908
|
-
className: c("content"),
|
|
1909
|
-
style: {
|
|
1910
|
-
width: `${w}px`,
|
|
1911
|
-
height: `${h}px`,
|
|
1912
|
-
overflow: "hidden"
|
|
1913
|
-
},
|
|
1914
|
-
children: this.content
|
|
1915
|
-
}
|
|
1916
|
-
)
|
|
2055
|
+
measure(isVertical) {
|
|
2056
|
+
const rect = this.content.getBoundingClientRect();
|
|
2057
|
+
const data = this.data;
|
|
2058
|
+
data.dims = { w: rect.width, h: rect.height };
|
|
2059
|
+
for (const dir of ["in", "out"]) {
|
|
2060
|
+
const ports = data.ports?.[dir];
|
|
2061
|
+
if (!ports) continue;
|
|
2062
|
+
for (const port of ports) {
|
|
2063
|
+
const el = this.content.querySelector(`#g3p-port-${data.id}-${port.id}`);
|
|
2064
|
+
if (!el) continue;
|
|
2065
|
+
const portRect = el.getBoundingClientRect();
|
|
2066
|
+
if (isVertical) {
|
|
2067
|
+
port.offset = portRect.left - rect.left;
|
|
2068
|
+
port.size = portRect.width;
|
|
2069
|
+
} else {
|
|
2070
|
+
port.offset = portRect.top - rect.top;
|
|
2071
|
+
port.size = portRect.height;
|
|
1917
2072
|
}
|
|
1918
|
-
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
getPortPosition(dir) {
|
|
2077
|
+
const o = this.canvas.orientation;
|
|
2078
|
+
if (dir === "in") {
|
|
2079
|
+
if (o === "TB") return "top";
|
|
2080
|
+
if (o === "BT") return "bottom";
|
|
2081
|
+
if (o === "LR") return "left";
|
|
2082
|
+
return "right";
|
|
2083
|
+
} else {
|
|
2084
|
+
if (o === "TB") return "bottom";
|
|
2085
|
+
if (o === "BT") return "top";
|
|
2086
|
+
if (o === "LR") return "right";
|
|
2087
|
+
return "left";
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
isVerticalOrientation() {
|
|
2091
|
+
const o = this.canvas.orientation;
|
|
2092
|
+
return o === "TB" || o === "BT";
|
|
2093
|
+
}
|
|
2094
|
+
isReversedOrientation() {
|
|
2095
|
+
const o = this.canvas.orientation;
|
|
2096
|
+
return o === "BT" || o === "RL";
|
|
2097
|
+
}
|
|
2098
|
+
renderPortRow(dir, inout) {
|
|
2099
|
+
const ports = this.data?.ports?.[dir];
|
|
2100
|
+
if (!ports?.length) return null;
|
|
2101
|
+
const c = styler("node", import_node3.default, this.classPrefix);
|
|
2102
|
+
const pos = this.getPortPosition(dir);
|
|
2103
|
+
const isVertical = this.isVerticalOrientation();
|
|
2104
|
+
const layoutClass = isVertical ? "row" : "col";
|
|
2105
|
+
const rotateLabels = false;
|
|
2106
|
+
const rotateClass = rotateLabels ? `port-rotated-${pos}` : "";
|
|
2107
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: `${c("ports")} ${c(`ports-${layoutClass}`)}`, children: ports.map((port) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
2108
|
+
"div",
|
|
2109
|
+
{
|
|
2110
|
+
id: `g3p-port-${this.data.id}-${port.id}`,
|
|
2111
|
+
className: `${c("port")} ${c(`port-${inout}-${pos}`)} ${c(rotateClass)}`,
|
|
2112
|
+
children: port.label ?? port.id
|
|
2113
|
+
}
|
|
2114
|
+
)) });
|
|
2115
|
+
}
|
|
2116
|
+
renderInsidePorts(el) {
|
|
2117
|
+
const c = styler("node", import_node3.default, this.classPrefix);
|
|
2118
|
+
const isVertical = this.isVerticalOrientation();
|
|
2119
|
+
const isReversed = this.isReversedOrientation();
|
|
2120
|
+
let inPorts = this.renderPortRow("in", "in");
|
|
2121
|
+
let outPorts = this.renderPortRow("out", "in");
|
|
2122
|
+
if (!inPorts && !outPorts) return el;
|
|
2123
|
+
if (isReversed) [inPorts, outPorts] = [outPorts, inPorts];
|
|
2124
|
+
const wrapperClass = isVertical ? "v" : "h";
|
|
2125
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `${c("with-ports")} ${c(`with-ports-${wrapperClass}`)}`, children: [
|
|
2126
|
+
inPorts,
|
|
2127
|
+
el,
|
|
2128
|
+
outPorts
|
|
1919
2129
|
] });
|
|
1920
2130
|
}
|
|
2131
|
+
renderOutsidePorts(el) {
|
|
2132
|
+
const c = styler("node", import_node3.default, this.classPrefix);
|
|
2133
|
+
const isVertical = this.isVerticalOrientation();
|
|
2134
|
+
const isReversed = this.isReversedOrientation();
|
|
2135
|
+
let inPorts = this.renderPortRow("in", "out");
|
|
2136
|
+
let outPorts = this.renderPortRow("out", "out");
|
|
2137
|
+
if (!inPorts && !outPorts) return el;
|
|
2138
|
+
if (isReversed) [inPorts, outPorts] = [outPorts, inPorts];
|
|
2139
|
+
const wrapperClass = isVertical ? "v" : "h";
|
|
2140
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `${c("with-ports")} ${c(`with-ports-${wrapperClass}`)}`, children: [
|
|
2141
|
+
inPorts,
|
|
2142
|
+
el,
|
|
2143
|
+
outPorts
|
|
2144
|
+
] });
|
|
2145
|
+
}
|
|
2146
|
+
renderBorder(el) {
|
|
2147
|
+
const c = styler("node", import_node3.default, this.classPrefix);
|
|
2148
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: c("border"), children: el });
|
|
2149
|
+
}
|
|
1921
2150
|
};
|
|
1922
2151
|
|
|
1923
|
-
// src/canvas/seg.css
|
|
1924
|
-
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}";
|
|
1925
|
-
|
|
1926
2152
|
// src/canvas/seg.tsx
|
|
2153
|
+
var import_seg3 = __toESM(require("./seg.css?raw"), 1);
|
|
1927
2154
|
var import_jsx_runtime3 = require("jsx-dom/jsx-runtime");
|
|
2155
|
+
var log9 = logger("canvas");
|
|
1928
2156
|
var Seg2 = class {
|
|
2157
|
+
id;
|
|
1929
2158
|
selected;
|
|
1930
2159
|
hovered;
|
|
1931
|
-
|
|
2160
|
+
canvas;
|
|
2161
|
+
classPrefix;
|
|
2162
|
+
type;
|
|
2163
|
+
svg;
|
|
2164
|
+
el;
|
|
2165
|
+
source;
|
|
2166
|
+
target;
|
|
2167
|
+
constructor(canvas, data, g) {
|
|
2168
|
+
this.id = data.id;
|
|
2169
|
+
this.canvas = canvas;
|
|
1932
2170
|
this.selected = false;
|
|
1933
2171
|
this.hovered = false;
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
},
|
|
1941
|
-
onContextMenu: () => {
|
|
1942
|
-
},
|
|
1943
|
-
classPrefix: "g3p",
|
|
1944
|
-
...options
|
|
1945
|
-
});
|
|
1946
|
-
this.attrs ??= {};
|
|
1947
|
-
this.attrs.targetTerminal ??= "arrow";
|
|
2172
|
+
this.svg = data.svg;
|
|
2173
|
+
this.classPrefix = canvas.classPrefix;
|
|
2174
|
+
this.source = { ...data.source, isDummy: data.sourceNode(g).isDummy };
|
|
2175
|
+
this.target = { ...data.target, isDummy: data.targetNode(g).isDummy };
|
|
2176
|
+
this.type = data.type;
|
|
2177
|
+
this.el = this.render();
|
|
1948
2178
|
}
|
|
1949
2179
|
handleClick(e) {
|
|
1950
2180
|
e.stopPropagation();
|
|
1951
|
-
this.onClick?.(this.edgeData, e);
|
|
1952
2181
|
}
|
|
1953
2182
|
handleMouseEnter(e) {
|
|
1954
|
-
this.onMouseEnter?.(this.edgeData, e);
|
|
1955
2183
|
}
|
|
1956
2184
|
handleMouseLeave(e) {
|
|
1957
|
-
this.onMouseLeave?.(this.edgeData, e);
|
|
1958
2185
|
}
|
|
1959
2186
|
handleContextMenu(e) {
|
|
1960
|
-
if (this.onContextMenu) {
|
|
1961
|
-
e.stopPropagation();
|
|
1962
|
-
this.onContextMenu(this.edgeData, e);
|
|
1963
|
-
}
|
|
1964
2187
|
}
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
2188
|
+
append() {
|
|
2189
|
+
this.canvas.group.appendChild(this.el);
|
|
2190
|
+
}
|
|
2191
|
+
remove() {
|
|
2192
|
+
this.el.remove();
|
|
1970
2193
|
}
|
|
1971
|
-
|
|
1972
|
-
this.svg = svg;
|
|
1973
|
-
|
|
1974
|
-
this.
|
|
1975
|
-
this.
|
|
2194
|
+
update(data) {
|
|
2195
|
+
this.svg = data.svg;
|
|
2196
|
+
this.type = data.type;
|
|
2197
|
+
this.source = data.source;
|
|
2198
|
+
this.target = data.target;
|
|
2199
|
+
this.remove();
|
|
2200
|
+
this.el = this.render();
|
|
2201
|
+
this.append();
|
|
1976
2202
|
}
|
|
1977
2203
|
render() {
|
|
1978
|
-
const c = styler("
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
};
|
|
1984
|
-
const hoverAttrs = {
|
|
1985
|
-
...styleAttrs,
|
|
1986
|
-
strokeWidth: styleAttrs.strokeWidth ? Math.max(styleAttrs.strokeWidth * 3, 10) : void 0
|
|
1987
|
-
};
|
|
1988
|
-
const { source, target } = this.renderTerminals();
|
|
2204
|
+
const c = styler("seg", import_seg3.default, this.classPrefix);
|
|
2205
|
+
let { source, target } = normalize(this);
|
|
2206
|
+
if (this.source.isDummy) source = void 0;
|
|
2207
|
+
if (this.target.isDummy) target = void 0;
|
|
2208
|
+
const typeClass = this.type ? `g3p-edge-type-${this.type}` : "";
|
|
1989
2209
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1990
2210
|
"g",
|
|
1991
2211
|
{
|
|
1992
2212
|
ref: (el) => this.el = el,
|
|
1993
|
-
id: `g3p-seg-${this.
|
|
1994
|
-
className: c("container"),
|
|
2213
|
+
id: `g3p-seg-${this.id}`,
|
|
2214
|
+
className: `${c("container")} ${typeClass}`.trim(),
|
|
1995
2215
|
onClick: this.handleClick.bind(this),
|
|
1996
2216
|
onMouseEnter: this.handleMouseEnter.bind(this),
|
|
1997
2217
|
onMouseLeave: this.handleMouseLeave.bind(this),
|
|
1998
2218
|
onContextMenu: this.handleContextMenu.bind(this),
|
|
1999
2219
|
children: [
|
|
2000
|
-
source?.defs,
|
|
2001
|
-
target?.defs,
|
|
2002
2220
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2003
2221
|
"path",
|
|
2004
2222
|
{
|
|
2005
2223
|
d: this.svg,
|
|
2006
|
-
...styleAttrs,
|
|
2007
2224
|
fill: "none",
|
|
2008
2225
|
className: c("line"),
|
|
2009
|
-
markerStart: source ? `url(
|
|
2010
|
-
markerEnd: target ? `url(
|
|
2226
|
+
markerStart: source ? `url(#g3p-marker-${source}-reverse)` : void 0,
|
|
2227
|
+
markerEnd: target ? `url(#g3p-marker-${target})` : void 0
|
|
2011
2228
|
}
|
|
2012
2229
|
),
|
|
2013
2230
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2014
2231
|
"path",
|
|
2015
2232
|
{
|
|
2016
2233
|
d: this.svg,
|
|
2017
|
-
...hoverAttrs,
|
|
2018
2234
|
stroke: "transparent",
|
|
2019
2235
|
fill: "none",
|
|
2020
2236
|
className: c("hitbox"),
|
|
@@ -2025,29 +2241,48 @@ var Seg2 = class {
|
|
|
2025
2241
|
}
|
|
2026
2242
|
);
|
|
2027
2243
|
}
|
|
2028
|
-
renderTerminal(type, side) {
|
|
2029
|
-
if (!type)
|
|
2030
|
-
return null;
|
|
2031
|
-
const id = `g3p-seg-${this.segId}-${side}-${type}`;
|
|
2032
|
-
const defs = /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2033
|
-
"marker",
|
|
2034
|
-
{
|
|
2035
|
-
id,
|
|
2036
|
-
markerWidth: "10",
|
|
2037
|
-
markerHeight: "10",
|
|
2038
|
-
refX: "9",
|
|
2039
|
-
refY: "3",
|
|
2040
|
-
orient: "auto",
|
|
2041
|
-
markerUnits: "userSpaceOnUse",
|
|
2042
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M0,0 L0,6 L9,3 z" })
|
|
2043
|
-
}
|
|
2044
|
-
) });
|
|
2045
|
-
return { id, defs };
|
|
2046
|
-
}
|
|
2047
2244
|
};
|
|
2048
2245
|
|
|
2049
2246
|
// src/canvas/canvas.tsx
|
|
2247
|
+
var import_canvas = __toESM(require("./canvas.css?raw"), 1);
|
|
2248
|
+
var import_zoom = __toESM(require("./zoom.css?raw"), 1);
|
|
2050
2249
|
var import_jsx_runtime4 = require("jsx-dom/jsx-runtime");
|
|
2250
|
+
var log10 = logger("canvas");
|
|
2251
|
+
var themeVarMap = {
|
|
2252
|
+
// Canvas
|
|
2253
|
+
bg: "--g3p-bg",
|
|
2254
|
+
shadow: "--g3p-shadow",
|
|
2255
|
+
// Node
|
|
2256
|
+
border: "--g3p-border",
|
|
2257
|
+
borderHover: "--g3p-border-hover",
|
|
2258
|
+
borderSelected: "--g3p-border-selected",
|
|
2259
|
+
text: "--g3p-text",
|
|
2260
|
+
textMuted: "--g3p-text-muted",
|
|
2261
|
+
// Port
|
|
2262
|
+
bgHover: "--g3p-port-bg-hover",
|
|
2263
|
+
// Edge
|
|
2264
|
+
color: "--g3p-edge-color"
|
|
2265
|
+
};
|
|
2266
|
+
function themeToCSS(theme, selector, prefix = "") {
|
|
2267
|
+
const entries = Object.entries(theme).filter(([_, v]) => v !== void 0);
|
|
2268
|
+
if (!entries.length) return "";
|
|
2269
|
+
let css = `${selector} {
|
|
2270
|
+
`;
|
|
2271
|
+
for (const [key, value] of entries) {
|
|
2272
|
+
let cssVar = themeVarMap[key];
|
|
2273
|
+
if (key === "bg" && prefix === "node") {
|
|
2274
|
+
cssVar = "--g3p-bg-node";
|
|
2275
|
+
} else if (key === "bg" && prefix === "port") {
|
|
2276
|
+
cssVar = "--g3p-port-bg";
|
|
2277
|
+
}
|
|
2278
|
+
if (cssVar) {
|
|
2279
|
+
css += ` ${cssVar}: ${value};
|
|
2280
|
+
`;
|
|
2281
|
+
}
|
|
2282
|
+
}
|
|
2283
|
+
css += "}\n";
|
|
2284
|
+
return css;
|
|
2285
|
+
}
|
|
2051
2286
|
var Canvas = class {
|
|
2052
2287
|
container;
|
|
2053
2288
|
root;
|
|
@@ -2055,26 +2290,27 @@ var Canvas = class {
|
|
|
2055
2290
|
transform;
|
|
2056
2291
|
bounds;
|
|
2057
2292
|
measurement;
|
|
2058
|
-
|
|
2059
|
-
|
|
2293
|
+
allNodes;
|
|
2294
|
+
curNodes;
|
|
2295
|
+
curSegs;
|
|
2060
2296
|
updating;
|
|
2297
|
+
// Pan-zoom state
|
|
2298
|
+
isPanning = false;
|
|
2299
|
+
panStart = null;
|
|
2300
|
+
transformStart = null;
|
|
2301
|
+
panScale = null;
|
|
2302
|
+
zoomControls;
|
|
2061
2303
|
constructor(options) {
|
|
2062
|
-
Object.assign(this,
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
portStyle: "outside",
|
|
2067
|
-
classPrefix: "g3p",
|
|
2068
|
-
width: "100%",
|
|
2069
|
-
height: "100%",
|
|
2070
|
-
transform: { x: 0, y: 0, scale: 1 },
|
|
2071
|
-
bounds: { min: { x: 0, y: 0 }, max: { x: 1, y: 1 } },
|
|
2072
|
-
...options
|
|
2073
|
-
});
|
|
2074
|
-
this.nodes = /* @__PURE__ */ new Map();
|
|
2075
|
-
this.segs = /* @__PURE__ */ new Map();
|
|
2304
|
+
Object.assign(this, options);
|
|
2305
|
+
this.allNodes = /* @__PURE__ */ new Map();
|
|
2306
|
+
this.curNodes = /* @__PURE__ */ new Map();
|
|
2307
|
+
this.curSegs = /* @__PURE__ */ new Map();
|
|
2076
2308
|
this.updating = false;
|
|
2309
|
+
this.bounds = { min: { x: 0, y: 0 }, max: { x: 0, y: 0 } };
|
|
2310
|
+
this.transform = { x: 0, y: 0, scale: 1 };
|
|
2077
2311
|
this.createMeasurementContainer();
|
|
2312
|
+
this.createCanvasContainer();
|
|
2313
|
+
if (this.panZoom) this.setupPanZoom();
|
|
2078
2314
|
}
|
|
2079
2315
|
createMeasurementContainer() {
|
|
2080
2316
|
this.measurement = document.createElement("div");
|
|
@@ -2087,85 +2323,94 @@ var Canvas = class {
|
|
|
2087
2323
|
`;
|
|
2088
2324
|
document.body.appendChild(this.measurement);
|
|
2089
2325
|
}
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2326
|
+
getNode(key) {
|
|
2327
|
+
const node = this.allNodes.get(key);
|
|
2328
|
+
if (!node) throw new Error(`node not found: ${key}`);
|
|
2329
|
+
return node;
|
|
2330
|
+
}
|
|
2331
|
+
update() {
|
|
2094
2332
|
let bx0 = Infinity, by0 = Infinity;
|
|
2095
2333
|
let bx1 = -Infinity, by1 = -Infinity;
|
|
2096
|
-
for (const node of this.
|
|
2097
|
-
const
|
|
2098
|
-
const
|
|
2334
|
+
for (const node of this.curNodes.values()) {
|
|
2335
|
+
const { x, y } = node.pos;
|
|
2336
|
+
const { w, h } = node.data.dims;
|
|
2337
|
+
const nx0 = x, nx1 = x + w;
|
|
2338
|
+
const ny0 = y, ny1 = y + h;
|
|
2099
2339
|
bx0 = Math.min(bx0, nx0);
|
|
2100
2340
|
by0 = Math.min(by0, ny0);
|
|
2101
2341
|
bx1 = Math.max(bx1, nx1);
|
|
2102
2342
|
by1 = Math.max(by1, ny1);
|
|
2103
2343
|
}
|
|
2104
2344
|
this.bounds = { min: { x: bx0, y: by0 }, max: { x: bx1, y: by1 } };
|
|
2105
|
-
console.log("bounds", this.bounds);
|
|
2106
2345
|
this.root.setAttribute("viewBox", this.viewBox());
|
|
2107
2346
|
}
|
|
2108
|
-
addNode(
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
node
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
this.
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2347
|
+
addNode(gnode) {
|
|
2348
|
+
if (this.curNodes.has(gnode.id))
|
|
2349
|
+
throw new Error("node already exists");
|
|
2350
|
+
const { key } = gnode;
|
|
2351
|
+
let node;
|
|
2352
|
+
if (gnode.isDummy) {
|
|
2353
|
+
node = new Node2(this, gnode, true);
|
|
2354
|
+
node.renderContainer();
|
|
2355
|
+
node.setPos(gnode.pos);
|
|
2356
|
+
this.allNodes.set(key, node);
|
|
2357
|
+
} else {
|
|
2358
|
+
if (!this.allNodes.has(key))
|
|
2359
|
+
throw new Error("node has not been measured");
|
|
2360
|
+
node = this.getNode(key);
|
|
2361
|
+
}
|
|
2362
|
+
this.curNodes.set(gnode.id, node);
|
|
2363
|
+
node.append();
|
|
2364
|
+
}
|
|
2365
|
+
updateNode(gnode) {
|
|
2366
|
+
if (gnode.isDummy) throw new Error("dummy node cannot be updated");
|
|
2367
|
+
const node = this.getNode(gnode.key);
|
|
2368
|
+
const cur = this.curNodes.get(gnode.id);
|
|
2369
|
+
if (cur) cur.remove();
|
|
2370
|
+
this.curNodes.set(gnode.id, node);
|
|
2371
|
+
node.append();
|
|
2372
|
+
}
|
|
2373
|
+
deleteNode(gnode) {
|
|
2374
|
+
const node = this.getNode(gnode.key);
|
|
2375
|
+
this.curNodes.delete(gnode.id);
|
|
2376
|
+
node.remove();
|
|
2377
|
+
}
|
|
2378
|
+
addSeg(gseg, g) {
|
|
2379
|
+
if (this.curSegs.has(gseg.id))
|
|
2380
|
+
throw new Error("seg already exists");
|
|
2381
|
+
const seg = new Seg2(this, gseg, g);
|
|
2382
|
+
this.curSegs.set(gseg.id, seg);
|
|
2383
|
+
seg.append();
|
|
2384
|
+
}
|
|
2385
|
+
updateSeg(gseg) {
|
|
2386
|
+
const seg = this.curSegs.get(gseg.id);
|
|
2133
2387
|
if (!seg) throw new Error("seg not found");
|
|
2134
|
-
seg.
|
|
2388
|
+
seg.update(gseg);
|
|
2135
2389
|
}
|
|
2136
|
-
deleteSeg(
|
|
2137
|
-
const seg = this.
|
|
2390
|
+
deleteSeg(gseg) {
|
|
2391
|
+
const seg = this.curSegs.get(gseg.id);
|
|
2138
2392
|
if (!seg) throw new Error("seg not found");
|
|
2139
|
-
|
|
2140
|
-
|
|
2393
|
+
this.curSegs.delete(gseg.id);
|
|
2394
|
+
seg.remove();
|
|
2141
2395
|
}
|
|
2142
|
-
async
|
|
2143
|
-
const newNodes =
|
|
2396
|
+
async measureNodes(nodes) {
|
|
2397
|
+
const newNodes = /* @__PURE__ */ new Map();
|
|
2144
2398
|
for (const data of nodes) {
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
renderNode: this.renderNode,
|
|
2149
|
-
classPrefix: this.classPrefix,
|
|
2150
|
-
isDummy: false
|
|
2151
|
-
});
|
|
2152
|
-
this.nodes.set(node.data, node);
|
|
2153
|
-
if (!node.measured) {
|
|
2154
|
-
this.measurement.appendChild(node.content);
|
|
2155
|
-
newNodes.push(node);
|
|
2156
|
-
}
|
|
2399
|
+
const node = new Node2(this, data);
|
|
2400
|
+
newNodes.set(data.data, node);
|
|
2401
|
+
this.measurement.appendChild(node.content);
|
|
2157
2402
|
}
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
return
|
|
2403
|
+
await new Promise(requestAnimationFrame);
|
|
2404
|
+
const isVertical = this.orientation === "TB" || this.orientation === "BT";
|
|
2405
|
+
for (const node of newNodes.values()) {
|
|
2406
|
+
node.measure(isVertical);
|
|
2407
|
+
const { id, version } = node.data;
|
|
2408
|
+
const key = `${id}:${version}`;
|
|
2409
|
+
this.allNodes.set(key, node);
|
|
2410
|
+
node.renderContainer();
|
|
2411
|
+
}
|
|
2412
|
+
this.measurement.innerHTML = "";
|
|
2413
|
+
return newNodes;
|
|
2169
2414
|
}
|
|
2170
2415
|
onClick(e) {
|
|
2171
2416
|
console.log("click", e);
|
|
@@ -2177,17 +2422,47 @@ var Canvas = class {
|
|
|
2177
2422
|
return `translate(${this.transform.x}, ${this.transform.y}) scale(${this.transform.scale})`;
|
|
2178
2423
|
}
|
|
2179
2424
|
viewBox() {
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
const
|
|
2184
|
-
|
|
2425
|
+
const p = this.padding;
|
|
2426
|
+
const x = this.bounds.min.x - p;
|
|
2427
|
+
const y = this.bounds.min.y - p;
|
|
2428
|
+
const w = this.bounds.max.x - this.bounds.min.x + p * 2;
|
|
2429
|
+
const h = this.bounds.max.y - this.bounds.min.y + p * 2;
|
|
2430
|
+
return `${x} ${y} ${w} ${h}`;
|
|
2431
|
+
}
|
|
2432
|
+
generateDynamicStyles() {
|
|
2433
|
+
let css = "";
|
|
2434
|
+
const prefix = this.classPrefix;
|
|
2435
|
+
css += themeToCSS(this.theme, `.${prefix}-canvas-container`);
|
|
2436
|
+
for (const [type, vars] of Object.entries(this.nodeTypes)) {
|
|
2437
|
+
css += themeToCSS(vars, `.${prefix}-node-type-${type}`, "node");
|
|
2438
|
+
}
|
|
2439
|
+
for (const [type, vars] of Object.entries(this.edgeTypes)) {
|
|
2440
|
+
css += themeToCSS(vars, `.${prefix}-edge-type-${type}`);
|
|
2441
|
+
}
|
|
2442
|
+
return css;
|
|
2443
|
+
}
|
|
2444
|
+
createCanvasContainer() {
|
|
2445
|
+
const markerStyleEl = document.createElement("style");
|
|
2446
|
+
markerStyleEl.textContent = import_marker.default;
|
|
2447
|
+
document.head.appendChild(markerStyleEl);
|
|
2448
|
+
const zoomStyleEl = document.createElement("style");
|
|
2449
|
+
zoomStyleEl.textContent = import_zoom.default;
|
|
2450
|
+
document.head.appendChild(zoomStyleEl);
|
|
2451
|
+
const dynamicStyles = this.generateDynamicStyles();
|
|
2452
|
+
if (dynamicStyles) {
|
|
2453
|
+
const themeStyleEl = document.createElement("style");
|
|
2454
|
+
themeStyleEl.textContent = dynamicStyles;
|
|
2455
|
+
document.head.appendChild(themeStyleEl);
|
|
2456
|
+
}
|
|
2457
|
+
const c = styler("canvas", import_canvas.default, this.classPrefix);
|
|
2458
|
+
const colorModeClass = this.colorMode !== "system" ? `g3p-${this.colorMode}` : "";
|
|
2459
|
+
this.container = /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2185
2460
|
"div",
|
|
2186
2461
|
{
|
|
2187
|
-
className: c("container"),
|
|
2462
|
+
className: `${c("container")} ${colorModeClass}`.trim(),
|
|
2188
2463
|
ref: (el) => this.container = el,
|
|
2189
2464
|
onContextMenu: this.onContextMenu.bind(this),
|
|
2190
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime4.
|
|
2465
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2191
2466
|
"svg",
|
|
2192
2467
|
{
|
|
2193
2468
|
ref: (el) => this.root = el,
|
|
@@ -2197,70 +2472,308 @@ var Canvas = class {
|
|
|
2197
2472
|
viewBox: this.viewBox(),
|
|
2198
2473
|
preserveAspectRatio: "xMidYMid meet",
|
|
2199
2474
|
onClick: this.onClick.bind(this),
|
|
2200
|
-
children:
|
|
2201
|
-
"
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2475
|
+
children: [
|
|
2476
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("defs", { children: [
|
|
2477
|
+
Object.values(markerDefs).map((marker) => marker(this.markerSize, this.classPrefix, false)),
|
|
2478
|
+
Object.values(markerDefs).map((marker) => marker(this.markerSize, this.classPrefix, true))
|
|
2479
|
+
] }),
|
|
2480
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2481
|
+
"g",
|
|
2482
|
+
{
|
|
2483
|
+
ref: (el) => this.group = el,
|
|
2484
|
+
transform: this.groupTransform()
|
|
2485
|
+
}
|
|
2486
|
+
)
|
|
2487
|
+
]
|
|
2207
2488
|
}
|
|
2208
2489
|
)
|
|
2209
2490
|
}
|
|
2210
2491
|
);
|
|
2211
2492
|
}
|
|
2493
|
+
// ==================== Pan-Zoom ====================
|
|
2494
|
+
setupPanZoom() {
|
|
2495
|
+
this.container.addEventListener("wheel", this.onWheel.bind(this), { passive: false });
|
|
2496
|
+
this.container.addEventListener("mousedown", this.onMouseDown.bind(this));
|
|
2497
|
+
document.addEventListener("mousemove", this.onMouseMove.bind(this));
|
|
2498
|
+
document.addEventListener("mouseup", this.onMouseUp.bind(this));
|
|
2499
|
+
this.createZoomControls();
|
|
2500
|
+
}
|
|
2501
|
+
/** Convert screen coordinates to canvas-relative coordinates */
|
|
2502
|
+
screenToCanvas(screen) {
|
|
2503
|
+
const rect = this.container.getBoundingClientRect();
|
|
2504
|
+
return canvasPos(screen.x - rect.left, screen.y - rect.top);
|
|
2505
|
+
}
|
|
2506
|
+
/**
|
|
2507
|
+
* Get the effective scale from canvas pixels to graph units,
|
|
2508
|
+
* accounting for preserveAspectRatio="xMidYMid meet" which uses
|
|
2509
|
+
* the smaller scale (to fit) and centers the content.
|
|
2510
|
+
*/
|
|
2511
|
+
getEffectiveScale() {
|
|
2512
|
+
const vb = this.currentViewBox();
|
|
2513
|
+
const rect = this.container.getBoundingClientRect();
|
|
2514
|
+
const scaleX = vb.w / rect.width;
|
|
2515
|
+
const scaleY = vb.h / rect.height;
|
|
2516
|
+
const scale = Math.max(scaleX, scaleY);
|
|
2517
|
+
const actualW = rect.width * scale;
|
|
2518
|
+
const actualH = rect.height * scale;
|
|
2519
|
+
const offsetX = (actualW - vb.w) / 2;
|
|
2520
|
+
const offsetY = (actualH - vb.h) / 2;
|
|
2521
|
+
return { scale, offsetX, offsetY };
|
|
2522
|
+
}
|
|
2523
|
+
/** Convert canvas coordinates to graph coordinates */
|
|
2524
|
+
canvasToGraph(canvas) {
|
|
2525
|
+
const vb = this.currentViewBox();
|
|
2526
|
+
const { scale, offsetX, offsetY } = this.getEffectiveScale();
|
|
2527
|
+
return graphPos(
|
|
2528
|
+
vb.x - offsetX + canvas.x * scale,
|
|
2529
|
+
vb.y - offsetY + canvas.y * scale
|
|
2530
|
+
);
|
|
2531
|
+
}
|
|
2532
|
+
/** Get current viewBox as an object */
|
|
2533
|
+
currentViewBox() {
|
|
2534
|
+
const p = this.padding;
|
|
2535
|
+
const t = this.transform;
|
|
2536
|
+
const baseX = this.bounds.min.x - p;
|
|
2537
|
+
const baseY = this.bounds.min.y - p;
|
|
2538
|
+
const baseW = this.bounds.max.x - this.bounds.min.x + p * 2;
|
|
2539
|
+
const baseH = this.bounds.max.y - this.bounds.min.y + p * 2;
|
|
2540
|
+
const cx = baseX + baseW / 2;
|
|
2541
|
+
const cy = baseY + baseH / 2;
|
|
2542
|
+
const w = baseW / t.scale;
|
|
2543
|
+
const h = baseH / t.scale;
|
|
2544
|
+
const x = cx - w / 2 - t.x;
|
|
2545
|
+
const y = cy - h / 2 - t.y;
|
|
2546
|
+
return { x, y, w, h };
|
|
2547
|
+
}
|
|
2548
|
+
onWheel(e) {
|
|
2549
|
+
e.preventDefault();
|
|
2550
|
+
const zoomFactor = 1.1;
|
|
2551
|
+
const delta = e.deltaY > 0 ? 1 / zoomFactor : zoomFactor;
|
|
2552
|
+
const screenCursor = screenPos(e.clientX, e.clientY);
|
|
2553
|
+
const canvasCursor = this.screenToCanvas(screenCursor);
|
|
2554
|
+
const graphCursor = this.canvasToGraph(canvasCursor);
|
|
2555
|
+
const oldScale = this.transform.scale;
|
|
2556
|
+
const newScale = Math.max(0.1, Math.min(10, oldScale * delta));
|
|
2557
|
+
this.transform.scale = newScale;
|
|
2558
|
+
const newGraphCursor = this.canvasToGraph(canvasCursor);
|
|
2559
|
+
this.transform.x += newGraphCursor.x - graphCursor.x;
|
|
2560
|
+
this.transform.y += newGraphCursor.y - graphCursor.y;
|
|
2561
|
+
this.applyTransform();
|
|
2562
|
+
}
|
|
2563
|
+
onMouseDown(e) {
|
|
2564
|
+
if (e.button !== 0) return;
|
|
2565
|
+
if (e.target.closest(".g3p-zoom-controls")) return;
|
|
2566
|
+
this.isPanning = true;
|
|
2567
|
+
this.panStart = this.screenToCanvas(screenPos(e.clientX, e.clientY));
|
|
2568
|
+
this.transformStart = { ...this.transform };
|
|
2569
|
+
const { scale } = this.getEffectiveScale();
|
|
2570
|
+
this.panScale = { x: scale, y: scale };
|
|
2571
|
+
this.container.style.cursor = "grabbing";
|
|
2572
|
+
e.preventDefault();
|
|
2573
|
+
}
|
|
2574
|
+
onMouseMove(e) {
|
|
2575
|
+
if (!this.isPanning || !this.panStart || !this.transformStart || !this.panScale) return;
|
|
2576
|
+
const current = this.screenToCanvas(screenPos(e.clientX, e.clientY));
|
|
2577
|
+
const dx = current.x - this.panStart.x;
|
|
2578
|
+
const dy = current.y - this.panStart.y;
|
|
2579
|
+
this.transform.x = this.transformStart.x + dx * this.panScale.x;
|
|
2580
|
+
this.transform.y = this.transformStart.y + dy * this.panScale.y;
|
|
2581
|
+
this.applyTransform();
|
|
2582
|
+
}
|
|
2583
|
+
onMouseUp(e) {
|
|
2584
|
+
if (!this.isPanning) return;
|
|
2585
|
+
this.isPanning = false;
|
|
2586
|
+
this.panStart = null;
|
|
2587
|
+
this.transformStart = null;
|
|
2588
|
+
this.panScale = null;
|
|
2589
|
+
this.container.style.cursor = "";
|
|
2590
|
+
}
|
|
2591
|
+
applyTransform() {
|
|
2592
|
+
const vb = this.currentViewBox();
|
|
2593
|
+
this.root.setAttribute("viewBox", `${vb.x} ${vb.y} ${vb.w} ${vb.h}`);
|
|
2594
|
+
this.updateZoomLevel();
|
|
2595
|
+
}
|
|
2596
|
+
createZoomControls() {
|
|
2597
|
+
const c = styler("zoom", import_zoom.default, this.classPrefix);
|
|
2598
|
+
this.zoomControls = /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: c("controls"), children: [
|
|
2599
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: c("btn"), onClick: () => this.zoomIn(), children: "+" }),
|
|
2600
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: c("level"), id: "g3p-zoom-level", children: "100%" }),
|
|
2601
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: c("btn"), onClick: () => this.zoomOut(), children: "\u2212" }),
|
|
2602
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: `${c("btn")} ${c("reset")}`, onClick: () => this.zoomReset(), children: "\u27F2" })
|
|
2603
|
+
] });
|
|
2604
|
+
this.container.appendChild(this.zoomControls);
|
|
2605
|
+
}
|
|
2606
|
+
updateZoomLevel() {
|
|
2607
|
+
const level = this.container.querySelector("#g3p-zoom-level");
|
|
2608
|
+
if (level) {
|
|
2609
|
+
level.textContent = `${Math.round(this.transform.scale * 100)}%`;
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2612
|
+
zoomIn() {
|
|
2613
|
+
this.transform.scale = Math.min(10, this.transform.scale * 1.2);
|
|
2614
|
+
this.applyTransform();
|
|
2615
|
+
}
|
|
2616
|
+
zoomOut() {
|
|
2617
|
+
this.transform.scale = Math.max(0.1, this.transform.scale / 1.2);
|
|
2618
|
+
this.applyTransform();
|
|
2619
|
+
}
|
|
2620
|
+
zoomReset() {
|
|
2621
|
+
this.transform = { x: 0, y: 0, scale: 1 };
|
|
2622
|
+
this.applyTransform();
|
|
2623
|
+
}
|
|
2212
2624
|
};
|
|
2213
2625
|
|
|
2214
|
-
// src/
|
|
2215
|
-
var
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2626
|
+
// src/canvas/render-node.tsx
|
|
2627
|
+
var import_jsx_runtime5 = require("jsx-dom/jsx-runtime");
|
|
2628
|
+
function renderNode(node) {
|
|
2629
|
+
if (typeof node == "string") node = { id: node };
|
|
2630
|
+
const title = node?.title ?? node?.label ?? node?.name ?? node?.text ?? node?.id ?? "?";
|
|
2631
|
+
const detail = node?.detail ?? node?.description ?? node?.subtitle;
|
|
2632
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "g3p-node-default", children: [
|
|
2633
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "g3p-node-title", children: title }),
|
|
2634
|
+
detail && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "g3p-node-detail", children: detail })
|
|
2635
|
+
] });
|
|
2636
|
+
}
|
|
2637
|
+
|
|
2638
|
+
// src/api/defaults.ts
|
|
2639
|
+
function applyDefaults(options) {
|
|
2640
|
+
const { graph: graph2, canvas, props } = defaults();
|
|
2641
|
+
return {
|
|
2642
|
+
graph: { ...graph2, ...options?.graph },
|
|
2643
|
+
canvas: { ...canvas, ...options?.canvas },
|
|
2644
|
+
props: { ...props, ...options?.props }
|
|
2645
|
+
};
|
|
2646
|
+
}
|
|
2647
|
+
function defaults() {
|
|
2648
|
+
return {
|
|
2649
|
+
graph: {
|
|
2650
|
+
mergeOrder: ["target", "source"],
|
|
2651
|
+
nodeMargin: 15,
|
|
2652
|
+
dummyNodeSize: 15,
|
|
2653
|
+
nodeAlign: "natural",
|
|
2654
|
+
edgeSpacing: 10,
|
|
2655
|
+
turnRadius: 10,
|
|
2656
|
+
orientation: "TB",
|
|
2657
|
+
layerMargin: 5,
|
|
2658
|
+
alignIterations: 5,
|
|
2659
|
+
alignThreshold: 10,
|
|
2660
|
+
separateTrackSets: true,
|
|
2661
|
+
markerSize: 10,
|
|
2662
|
+
layoutSteps: null
|
|
2663
|
+
},
|
|
2664
|
+
canvas: {
|
|
2665
|
+
renderNode,
|
|
2666
|
+
classPrefix: "g3p",
|
|
2667
|
+
width: "100%",
|
|
2668
|
+
height: "100%",
|
|
2669
|
+
padding: 20,
|
|
2670
|
+
editable: false,
|
|
2671
|
+
panZoom: true,
|
|
2672
|
+
markerSize: 10,
|
|
2673
|
+
colorMode: "system",
|
|
2674
|
+
theme: {},
|
|
2675
|
+
nodeTypes: {},
|
|
2676
|
+
edgeTypes: {}
|
|
2677
|
+
},
|
|
2678
|
+
props: {}
|
|
2679
|
+
};
|
|
2680
|
+
}
|
|
2681
|
+
|
|
2682
|
+
// src/api/updater.ts
|
|
2683
|
+
var Updater = class _Updater {
|
|
2684
|
+
update;
|
|
2685
|
+
constructor() {
|
|
2686
|
+
this.update = {
|
|
2687
|
+
addNodes: [],
|
|
2688
|
+
removeNodes: [],
|
|
2689
|
+
updateNodes: [],
|
|
2690
|
+
addEdges: [],
|
|
2691
|
+
removeEdges: [],
|
|
2692
|
+
updateEdges: []
|
|
2693
|
+
};
|
|
2694
|
+
}
|
|
2695
|
+
describe(desc) {
|
|
2696
|
+
this.update.description = desc;
|
|
2697
|
+
return this;
|
|
2698
|
+
}
|
|
2699
|
+
addNode(node) {
|
|
2700
|
+
this.update.addNodes.push(node);
|
|
2701
|
+
return this;
|
|
2702
|
+
}
|
|
2703
|
+
deleteNode(node) {
|
|
2704
|
+
this.update.removeNodes.push(node);
|
|
2705
|
+
return this;
|
|
2706
|
+
}
|
|
2707
|
+
updateNode(node) {
|
|
2708
|
+
this.update.updateNodes.push(node);
|
|
2709
|
+
return this;
|
|
2710
|
+
}
|
|
2711
|
+
addEdge(edge) {
|
|
2712
|
+
this.update.addEdges.push(edge);
|
|
2713
|
+
return this;
|
|
2714
|
+
}
|
|
2715
|
+
deleteEdge(edge) {
|
|
2716
|
+
this.update.removeEdges.push(edge);
|
|
2717
|
+
return this;
|
|
2718
|
+
}
|
|
2719
|
+
updateEdge(edge) {
|
|
2720
|
+
this.update.updateEdges.push(edge);
|
|
2721
|
+
return this;
|
|
2722
|
+
}
|
|
2723
|
+
static add(nodes, edges) {
|
|
2724
|
+
const updater = new _Updater();
|
|
2725
|
+
updater.update.addNodes = nodes;
|
|
2726
|
+
updater.update.addEdges = edges;
|
|
2727
|
+
return updater;
|
|
2728
|
+
}
|
|
2729
|
+
};
|
|
2730
|
+
|
|
2731
|
+
// src/api/api.ts
|
|
2732
|
+
var log11 = logger("api");
|
|
2230
2733
|
var API = class {
|
|
2231
2734
|
state;
|
|
2232
2735
|
seq;
|
|
2233
2736
|
index;
|
|
2234
2737
|
canvas;
|
|
2235
|
-
_options;
|
|
2236
2738
|
options;
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
}
|
|
2249
|
-
this.state = {
|
|
2250
|
-
nodes: (0, import_immutable9.Map)(),
|
|
2251
|
-
edges: (0, import_immutable9.Map)(),
|
|
2252
|
-
ports: (0, import_immutable9.Map)(),
|
|
2253
|
-
segs: (0, import_immutable9.Map)()
|
|
2254
|
-
};
|
|
2255
|
-
let graph2 = new Graph({ options: this._options });
|
|
2256
|
-
this.state.graph = graph2;
|
|
2739
|
+
history;
|
|
2740
|
+
nodeIds;
|
|
2741
|
+
edgeIds;
|
|
2742
|
+
nodeVersions;
|
|
2743
|
+
nextNodeId;
|
|
2744
|
+
nextEdgeId;
|
|
2745
|
+
root;
|
|
2746
|
+
constructor(args) {
|
|
2747
|
+
this.root = args.root;
|
|
2748
|
+
this.options = applyDefaults(args.options);
|
|
2749
|
+
let graph2 = new Graph({ options: this.options.graph });
|
|
2750
|
+
this.state = { graph: graph2, update: null };
|
|
2257
2751
|
this.seq = [this.state];
|
|
2258
2752
|
this.index = 0;
|
|
2259
|
-
this.
|
|
2260
|
-
this.
|
|
2753
|
+
this.nodeIds = /* @__PURE__ */ new Map();
|
|
2754
|
+
this.edgeIds = /* @__PURE__ */ new Map();
|
|
2755
|
+
this.nodeVersions = /* @__PURE__ */ new Map();
|
|
2756
|
+
this.nextNodeId = 1;
|
|
2757
|
+
this.nextEdgeId = 1;
|
|
2758
|
+
this.canvas = new Canvas({
|
|
2759
|
+
...this.options.canvas,
|
|
2760
|
+
dummyNodeSize: this.options.graph.dummyNodeSize,
|
|
2761
|
+
orientation: this.options.graph.orientation
|
|
2762
|
+
});
|
|
2763
|
+
if (args.history) {
|
|
2764
|
+
this.history = args.history;
|
|
2765
|
+
} else if (args.nodes) {
|
|
2766
|
+
this.history = [Updater.add(args.nodes, args.edges || []).update];
|
|
2767
|
+
} else {
|
|
2768
|
+
this.history = [];
|
|
2769
|
+
}
|
|
2261
2770
|
}
|
|
2262
|
-
|
|
2263
|
-
|
|
2771
|
+
async init() {
|
|
2772
|
+
const root = document.getElementById(this.root);
|
|
2773
|
+
if (!root) throw new Error("root element not found");
|
|
2774
|
+
root.appendChild(this.canvas.container);
|
|
2775
|
+
for (const update of this.history)
|
|
2776
|
+
await this.applyUpdate(update);
|
|
2264
2777
|
}
|
|
2265
2778
|
nav(nav) {
|
|
2266
2779
|
let newIndex;
|
|
@@ -2285,36 +2798,27 @@ var API = class {
|
|
|
2285
2798
|
this.state = this.seq[this.index];
|
|
2286
2799
|
}
|
|
2287
2800
|
applyDiff(oldIndex, newIndex) {
|
|
2288
|
-
const
|
|
2289
|
-
const
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
this.canvas.deleteSeg(oldSeg);
|
|
2310
|
-
else if (oldSeg.svg != newSeg.svg)
|
|
2311
|
-
this.canvas.updateSeg(newSeg);
|
|
2312
|
-
}
|
|
2313
|
-
for (const newSeg of newState.segs.values()) {
|
|
2314
|
-
if (!oldState.segs.has(newSeg.segId))
|
|
2315
|
-
this.canvas.addSeg(newSeg);
|
|
2316
|
-
}
|
|
2317
|
-
});
|
|
2801
|
+
const oldGraph = this.seq[oldIndex].graph;
|
|
2802
|
+
const newGraph = this.seq[newIndex].graph;
|
|
2803
|
+
for (const oldNode of oldGraph.nodes.values()) {
|
|
2804
|
+
const newNode = newGraph.nodes.get(oldNode.id);
|
|
2805
|
+
if (!newNode) this.canvas.deleteNode(oldNode);
|
|
2806
|
+
}
|
|
2807
|
+
for (const newNode of newGraph.nodes.values()) {
|
|
2808
|
+
if (!oldGraph.nodes.has(newNode.id)) this.canvas.addNode(newNode);
|
|
2809
|
+
}
|
|
2810
|
+
for (const oldSeg of oldGraph.segs.values()) {
|
|
2811
|
+
const newSeg = newGraph.segs.get(oldSeg.id);
|
|
2812
|
+
if (!newSeg)
|
|
2813
|
+
this.canvas.deleteSeg(oldSeg);
|
|
2814
|
+
else if (oldSeg.svg != newSeg.svg)
|
|
2815
|
+
this.canvas.updateSeg(newSeg);
|
|
2816
|
+
}
|
|
2817
|
+
for (const newSeg of newGraph.segs.values()) {
|
|
2818
|
+
if (!oldGraph.segs.has(newSeg.id))
|
|
2819
|
+
this.canvas.addSeg(newSeg, newGraph);
|
|
2820
|
+
}
|
|
2821
|
+
this.canvas.update();
|
|
2318
2822
|
}
|
|
2319
2823
|
async addNode(node) {
|
|
2320
2824
|
await this.update((update) => update.addNode(node));
|
|
@@ -2331,190 +2835,175 @@ var API = class {
|
|
|
2331
2835
|
async deleteEdge(edge) {
|
|
2332
2836
|
await this.update((update) => update.deleteEdge(edge));
|
|
2333
2837
|
}
|
|
2334
|
-
async update
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
edges: this.state.edges.asMutable(),
|
|
2342
|
-
ports: this.state.ports.asMutable(),
|
|
2343
|
-
segs: this.state.segs.asMutable()
|
|
2344
|
-
};
|
|
2345
|
-
for (const node of update.updatedNodes)
|
|
2346
|
-
this._updateNode(node, mut);
|
|
2347
|
-
for (const edge of update.updatedEdges)
|
|
2348
|
-
this._updateEdge(edge, mut);
|
|
2349
|
-
for (const node of update.addedNodes)
|
|
2350
|
-
this._addNode(node, mut);
|
|
2351
|
-
for (const node of update.removedNodes)
|
|
2838
|
+
async applyUpdate(update) {
|
|
2839
|
+
log11.info("applyUpdate", update);
|
|
2840
|
+
const nodes = await this.measureNodes(update);
|
|
2841
|
+
const graph2 = this.state.graph.withMutations((mut) => {
|
|
2842
|
+
for (const edge of update.removeEdges ?? [])
|
|
2843
|
+
this._removeEdge(edge, mut);
|
|
2844
|
+
for (const node of update.removeNodes ?? [])
|
|
2352
2845
|
this._removeNode(node, mut);
|
|
2353
|
-
for (const
|
|
2846
|
+
for (const node of update.addNodes ?? [])
|
|
2847
|
+
this._addNode(nodes.get(node), mut);
|
|
2848
|
+
for (const node of update.updateNodes ?? [])
|
|
2849
|
+
this._updateNode(nodes.get(node), mut);
|
|
2850
|
+
for (const edge of update.addEdges ?? [])
|
|
2354
2851
|
this._addEdge(edge, mut);
|
|
2355
|
-
for (const edge of update.
|
|
2356
|
-
this.
|
|
2852
|
+
for (const edge of update.updateEdges ?? [])
|
|
2853
|
+
this._updateEdge(edge, mut);
|
|
2357
2854
|
});
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
const node = newGraph.getNode(nodeId);
|
|
2361
|
-
console.log(`got pos of node ${nodeId}:`, node.pos);
|
|
2362
|
-
if (node.isDummy) {
|
|
2363
|
-
this.state.nodes.set(nodeId, { id: nodeId, pos: node.pos, isDummy: true });
|
|
2364
|
-
} else {
|
|
2365
|
-
const myNode = this.state.nodes.get(nodeId);
|
|
2366
|
-
this.state.nodes.set(nodeId, { ...myNode, pos: node.pos });
|
|
2367
|
-
}
|
|
2368
|
-
}
|
|
2369
|
-
for (const nodeId of newGraph.delNodes)
|
|
2370
|
-
this.state.nodes.delete(nodeId);
|
|
2371
|
-
for (const segId of newGraph.delSegs)
|
|
2372
|
-
this.state.segs.delete(segId);
|
|
2373
|
-
for (const segId of newGraph.dirtySegs) {
|
|
2374
|
-
const seg = newGraph.getSeg(segId);
|
|
2375
|
-
const edge = this.state.edges.get(seg.edgeIds.values().next().value);
|
|
2376
|
-
const target = seg.targetNode(newGraph);
|
|
2377
|
-
this.state.segs.set(seg.id, {
|
|
2378
|
-
segId: seg.id,
|
|
2379
|
-
edgeId: edge.id,
|
|
2380
|
-
svg: seg.svg,
|
|
2381
|
-
attrs: edge.attrs,
|
|
2382
|
-
targetDummy: target.isDummy,
|
|
2383
|
-
edgeData: edge.data
|
|
2384
|
-
});
|
|
2385
|
-
}
|
|
2386
|
-
this.state = {
|
|
2387
|
-
nodes: this.state.nodes.asImmutable(),
|
|
2388
|
-
edges: this.state.edges.asImmutable(),
|
|
2389
|
-
ports: this.state.ports.asImmutable(),
|
|
2390
|
-
segs: this.state.segs.asImmutable(),
|
|
2391
|
-
graph: newGraph,
|
|
2392
|
-
update
|
|
2393
|
-
};
|
|
2855
|
+
this.state = { graph: graph2, update };
|
|
2856
|
+
this.setNodePositions();
|
|
2394
2857
|
this.seq.splice(this.index + 1);
|
|
2395
2858
|
this.seq.push(this.state);
|
|
2396
2859
|
this.nav("last");
|
|
2397
2860
|
}
|
|
2398
|
-
|
|
2399
|
-
const
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
}
|
|
2405
|
-
_updateNode(node, mut) {
|
|
2406
|
-
const props = this._options.nodeProps(node);
|
|
2407
|
-
const oldData = this.state.nodes.get(props.id);
|
|
2408
|
-
if (oldData === void 0)
|
|
2409
|
-
throw new Error(`updating node ${props.id} which does not exist`);
|
|
2410
|
-
const attrs = this._options.nodeStyle(node);
|
|
2411
|
-
mut.updateNode({ id: props.id, dims: this.getDims(node) });
|
|
2412
|
-
const data = { id: props.id, attrs, orig: node, node: props };
|
|
2413
|
-
this.state.nodes.set(props.id, data);
|
|
2414
|
-
}
|
|
2415
|
-
_updateEdge(edge, mut) {
|
|
2416
|
-
const props = this._options.edgeProps(edge);
|
|
2417
|
-
const id = Edge.id(props), str = Edge.str(props);
|
|
2418
|
-
const oldData = this.state.edges.get(id);
|
|
2419
|
-
if (oldData === void 0)
|
|
2420
|
-
throw new Error(`updating edge ${str} which does not exist`);
|
|
2421
|
-
const attrs = props.type ? this._options.edgeStyle(props.type) : void 0;
|
|
2422
|
-
const data = { id, attrs, data: edge, edge: props };
|
|
2423
|
-
this.state.edges.set(id, data);
|
|
2424
|
-
if (props.type !== oldData.edge.type) {
|
|
2425
|
-
mut.removeEdge(oldData.edge);
|
|
2426
|
-
mut.addEdge(props);
|
|
2427
|
-
} else {
|
|
2861
|
+
setNodePositions() {
|
|
2862
|
+
const { graph: graph2 } = this.state;
|
|
2863
|
+
for (const nodeId of graph2.dirtyNodes) {
|
|
2864
|
+
const node = graph2.getNode(nodeId);
|
|
2865
|
+
if (!node.isDummy)
|
|
2866
|
+
this.canvas.getNode(node.key).setPos(node.pos);
|
|
2428
2867
|
}
|
|
2429
2868
|
}
|
|
2430
|
-
|
|
2431
|
-
const
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
props.ports = { in: [], out: [], ...props.ports };
|
|
2435
|
-
const attrs = this._options.nodeStyle(node);
|
|
2436
|
-
const data = { id: props.id, attrs, data: node, node: props };
|
|
2437
|
-
this.state.nodes.set(props.id, data);
|
|
2438
|
-
console.log("adding node:", { ...props, dims: this.getDims(node) });
|
|
2439
|
-
mut.addNode({ ...props, dims: this.getDims(node) });
|
|
2869
|
+
async update(callback) {
|
|
2870
|
+
const updater = new Updater();
|
|
2871
|
+
callback(updater);
|
|
2872
|
+
await this.applyUpdate(updater.update);
|
|
2440
2873
|
}
|
|
2441
|
-
|
|
2442
|
-
const
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2874
|
+
async measureNodes(update) {
|
|
2875
|
+
const data = [];
|
|
2876
|
+
for (const set of [update.updateNodes, update.addNodes])
|
|
2877
|
+
for (const node of set ?? [])
|
|
2878
|
+
data.push(this.parseNode(node, true));
|
|
2879
|
+
return await this.canvas.measureNodes(data);
|
|
2880
|
+
}
|
|
2881
|
+
parseNode(data, bumpVersion = false) {
|
|
2882
|
+
const get = this.options.props.node;
|
|
2883
|
+
let props;
|
|
2884
|
+
if (get) props = get(data);
|
|
2885
|
+
else if (!data) throw new Error(`invalid node ${data}`);
|
|
2886
|
+
else if (typeof data == "string") props = { id: data };
|
|
2887
|
+
else if (typeof data == "object") props = data;
|
|
2888
|
+
else throw new Error(`invalid node ${data}`);
|
|
2889
|
+
let { id, title, text, type, render } = props;
|
|
2890
|
+
id ??= this.getNodeId(data);
|
|
2891
|
+
const ports = this.parsePorts(props.ports);
|
|
2892
|
+
let version = this.nodeVersions.get(data);
|
|
2893
|
+
if (!version) version = 1;
|
|
2894
|
+
else if (bumpVersion) version++;
|
|
2895
|
+
this.nodeVersions.set(data, version);
|
|
2896
|
+
return { id, data, ports, title, text, type, render, version };
|
|
2897
|
+
}
|
|
2898
|
+
parseEdge(data) {
|
|
2899
|
+
const get = this.options.props.edge;
|
|
2900
|
+
let props;
|
|
2901
|
+
if (get) props = get(data);
|
|
2902
|
+
else if (!data) throw new Error(`invalid edge ${data}`);
|
|
2903
|
+
else if (typeof data == "string") props = this.parseStringEdge(data);
|
|
2904
|
+
else if (typeof data == "object") props = data;
|
|
2905
|
+
else throw new Error(`invalid edge ${data}`);
|
|
2906
|
+
let { id, source, target, type } = props;
|
|
2907
|
+
id ??= this.getEdgeId(data);
|
|
2908
|
+
source = this.parseEdgeEnd(source);
|
|
2909
|
+
target = this.parseEdgeEnd(target);
|
|
2910
|
+
const edge = { id, source, target, type, data };
|
|
2911
|
+
return edge;
|
|
2447
2912
|
}
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
if (
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2913
|
+
parseEdgeEnd(end) {
|
|
2914
|
+
if (!end) throw new Error(`edge has an undefined source or target`);
|
|
2915
|
+
if (typeof end == "string") return { id: end };
|
|
2916
|
+
if (typeof end == "object") {
|
|
2917
|
+
const keys = Object.keys(end);
|
|
2918
|
+
const pidx = keys.indexOf("port");
|
|
2919
|
+
if (pidx != -1) {
|
|
2920
|
+
if (typeof end.port != "string") return end;
|
|
2921
|
+
keys.splice(pidx, 1);
|
|
2922
|
+
}
|
|
2923
|
+
if (keys.length != 1) return end;
|
|
2924
|
+
if (keys[0] == "id") return end;
|
|
2925
|
+
if (keys[0] != "node") return end;
|
|
2926
|
+
const id = this.nodeIds.get(end.node);
|
|
2927
|
+
if (!id) throw new Error(`edge end ${end} references unknown node ${end.node}`);
|
|
2928
|
+
return { id, port: end.port };
|
|
2929
|
+
}
|
|
2930
|
+
throw new Error(`invalid edge end ${end}`);
|
|
2457
2931
|
}
|
|
2458
|
-
|
|
2459
|
-
const
|
|
2460
|
-
|
|
2461
|
-
if (!this.state.edges.has(id))
|
|
2462
|
-
throw new Error(`removing edge ${str} which does not exist`);
|
|
2463
|
-
this.state.edges.delete(id);
|
|
2464
|
-
mut.removeEdge(props);
|
|
2932
|
+
parseStringEdge(str) {
|
|
2933
|
+
const [source, target] = str.split(/\s*(?::|-+>?)\s*/);
|
|
2934
|
+
return { source, target };
|
|
2465
2935
|
}
|
|
2466
|
-
|
|
2936
|
+
parsePorts(ports) {
|
|
2937
|
+
const fixed = { in: null, out: null };
|
|
2938
|
+
for (const key of ["in", "out"]) {
|
|
2939
|
+
if (ports?.[key] && ports[key].length > 0)
|
|
2940
|
+
fixed[key] = ports[key].map((port) => typeof port == "string" ? { id: port } : port);
|
|
2941
|
+
}
|
|
2942
|
+
return fixed;
|
|
2467
2943
|
}
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
updatedEdges;
|
|
2476
|
-
desc;
|
|
2477
|
-
constructor() {
|
|
2478
|
-
this.addedNodes = [];
|
|
2479
|
-
this.removedNodes = [];
|
|
2480
|
-
this.updatedNodes = [];
|
|
2481
|
-
this.addedEdges = [];
|
|
2482
|
-
this.removedEdges = [];
|
|
2483
|
-
this.updatedEdges = [];
|
|
2944
|
+
getNodeId(node) {
|
|
2945
|
+
let id = this.nodeIds.get(node);
|
|
2946
|
+
if (!id) {
|
|
2947
|
+
id = `n${this.nextNodeId++}`;
|
|
2948
|
+
this.nodeIds.set(node, id);
|
|
2949
|
+
}
|
|
2950
|
+
return id;
|
|
2484
2951
|
}
|
|
2485
|
-
|
|
2486
|
-
|
|
2952
|
+
getEdgeId(edge) {
|
|
2953
|
+
let id = this.edgeIds.get(edge);
|
|
2954
|
+
if (!id) {
|
|
2955
|
+
id = `e${this.nextEdgeId++}`;
|
|
2956
|
+
this.edgeIds.set(edge, id);
|
|
2957
|
+
}
|
|
2958
|
+
return id;
|
|
2487
2959
|
}
|
|
2488
|
-
|
|
2489
|
-
|
|
2960
|
+
_addNode(node, mut) {
|
|
2961
|
+
const { data, id: newId } = node.data;
|
|
2962
|
+
const oldId = this.nodeIds.get(data);
|
|
2963
|
+
console.log("addNode", node, oldId, newId);
|
|
2964
|
+
if (oldId && oldId != newId)
|
|
2965
|
+
throw new Error(`node id of ${data} changed from ${oldId} to ${newId}`);
|
|
2966
|
+
this.nodeIds.set(data, newId);
|
|
2967
|
+
mut.addNode(node.data);
|
|
2490
2968
|
}
|
|
2491
|
-
|
|
2492
|
-
this.
|
|
2969
|
+
_removeNode(node, mut) {
|
|
2970
|
+
const id = this.nodeIds.get(node);
|
|
2971
|
+
if (!id) throw new Error(`removing node ${node} which does not exist`);
|
|
2972
|
+
mut.removeNode({ id });
|
|
2493
2973
|
}
|
|
2494
|
-
|
|
2495
|
-
|
|
2974
|
+
_updateNode(node, mut) {
|
|
2975
|
+
const { data, id: newId } = node.data;
|
|
2976
|
+
const oldId = this.nodeIds.get(data);
|
|
2977
|
+
if (!oldId) throw new Error(`updating unknown node ${node}`);
|
|
2978
|
+
if (oldId != newId) throw new Error(`node id changed from ${oldId} to ${newId}`);
|
|
2979
|
+
mut.updateNode(node.data);
|
|
2496
2980
|
}
|
|
2497
|
-
|
|
2498
|
-
this.
|
|
2981
|
+
_addEdge(edge, mut) {
|
|
2982
|
+
const data = this.parseEdge(edge);
|
|
2983
|
+
const id = this.edgeIds.get(edge);
|
|
2984
|
+
if (id && id != data.id)
|
|
2985
|
+
throw new Error(`edge id changed from ${id} to ${data.id}`);
|
|
2986
|
+
this.edgeIds.set(edge, data.id);
|
|
2987
|
+
mut.addEdge(data);
|
|
2499
2988
|
}
|
|
2500
|
-
|
|
2501
|
-
this.
|
|
2989
|
+
_removeEdge(edge, mut) {
|
|
2990
|
+
const id = this.edgeIds.get(edge);
|
|
2991
|
+
if (!id) throw new Error(`removing edge ${edge} which does not exist`);
|
|
2992
|
+
mut.removeEdge(this.parseEdge(edge));
|
|
2502
2993
|
}
|
|
2503
|
-
|
|
2504
|
-
this.
|
|
2994
|
+
_updateEdge(edge, mut) {
|
|
2995
|
+
const id = this.edgeIds.get(edge);
|
|
2996
|
+
if (!id) throw new Error(`updating unknown edge ${edge}`);
|
|
2997
|
+
const data = this.parseEdge(edge);
|
|
2998
|
+
if (data.id !== id) throw new Error(`edge id changed from ${id} to ${data.id}`);
|
|
2999
|
+
mut.updateEdge(data);
|
|
2505
3000
|
}
|
|
2506
3001
|
};
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
for (const node of nodes)
|
|
2513
|
-
update.addNode(node);
|
|
2514
|
-
for (const edge of edges)
|
|
2515
|
-
update.addEdge(edge);
|
|
2516
|
-
});
|
|
2517
|
-
}
|
|
3002
|
+
|
|
3003
|
+
// src/index.ts
|
|
3004
|
+
async function graph(args = { root: "app" }) {
|
|
3005
|
+
const api = new API(args);
|
|
3006
|
+
await api.init();
|
|
2518
3007
|
return api;
|
|
2519
3008
|
}
|
|
2520
3009
|
var index_default = graph;
|