@weasel-js/diagram 1.9.1 → 1.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -7
- package/dist/{chunk-6VYT4WCM.js → chunk-ERT3XWBS.js} +286 -110
- package/dist/chunk-ERT3XWBS.js.map +1 -0
- package/dist/index.d.ts +229 -28
- package/dist/index.js +363 -78
- package/dist/index.js.map +1 -1
- package/dist/layout.d.ts +1 -1
- package/dist/{layout.entry-ClWUwH16.d.ts → layout.entry-KHbx5uGC.d.ts} +338 -275
- package/dist/layout.js +1 -1
- package/package.json +3 -3
- package/dist/chunk-6VYT4WCM.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,11 +1,73 @@
|
|
|
1
|
-
import { portOf,
|
|
2
|
-
export { COMPASS, DEFAULT_NODE_GAP, DEFAULT_PORTS, DEFAULT_RANK_GAP, DIAGRAM_TRAIT_KEY, axesFor, backEdges, bodyOutline, bodyTrait, boxForContent, buildBody, canvasMeasure, contentBox, createDiagramNodes, dataKeyReader, diagramNodeOf, force, forceRelaxation, forestOf, graphOrder, isPinned, layered, layoutBody, layoutRowPorts, measureBody, outlinePath, outlinePolyline, packAcross, pinnedSet, portOf, portsOf, portsOnOutline, ranksOf, rayHit, seededOrder, settle, sizeToBody, translated, tree } from './chunk-
|
|
3
|
-
import { dragPanContribution,
|
|
1
|
+
import { insetOf, layered, portOf, outlinePath, diagramNodeOf, expandGroupEdges, forceRelaxation, center, force, tree, DIAGRAM_TRAIT_KEY, portsOf, DEFAULT_GROUP_INSET, sizeToBody, measureBody, buildBody } from './chunk-ERT3XWBS.js';
|
|
2
|
+
export { COMPASS, DEFAULT_GROUP_INSET, DEFAULT_NODE_GAP, DEFAULT_PORTS, DEFAULT_RANK_GAP, DIAGRAM_TRAIT_KEY, axesFor, backEdges, bodyOutline, bodyTrait, boxForContent, buildBody, canvasMeasure, contentBox, createDiagramNodes, dataKeyReader, diagramNodeOf, expandGroupEdges, force, forceRelaxation, forestOf, graphOrder, insetOf, isPinned, layered, layoutBody, layoutRowPorts, measureBody, outlinePath, outlinePolyline, packAcross, packClusters, pinnedSet, portOf, portsOf, portsOnOutline, rankGaps, ranksOf, rayHit, seededOrder, settle, sizeToBody, translated, tree } from './chunk-ERT3XWBS.js';
|
|
3
|
+
import { dragPanContribution, WeaselProvider, useScene, useSelection, useLatest, reconcileSpecs, SceneCanvas, fitViewToBounds, AUTO_POSE_DESCRIPTOR, translatePoseViaDescriptor, pointAlongPath, polylineFromPoints, registerNodeShape, effectivePose, usePoseRun, RECT_POSE_DESCRIPTOR, composeAffordanceLayer } from '@weasel-js/core';
|
|
4
4
|
import { portCurvePoints } from '@weasel-js/geom';
|
|
5
5
|
import { AUTO_POSE_DESCRIPTOR as AUTO_POSE_DESCRIPTOR$1 } from '@weasel-js/core/math';
|
|
6
|
-
import { useRef,
|
|
6
|
+
import { forwardRef, useEffect, useMemo, useRef, useState, useImperativeHandle, useCallback } from 'react';
|
|
7
7
|
import { jsx } from 'react/jsx-runtime';
|
|
8
8
|
|
|
9
|
+
function traitOf(node) {
|
|
10
|
+
const data = node.data;
|
|
11
|
+
if (data === null || typeof data !== "object") return null;
|
|
12
|
+
const trait = data[DIAGRAM_TRAIT_KEY];
|
|
13
|
+
return trait !== null && typeof trait === "object" ? trait : null;
|
|
14
|
+
}
|
|
15
|
+
function diagramGroupOf(node) {
|
|
16
|
+
const group = traitOf(node)?.group;
|
|
17
|
+
return group !== null && typeof group === "object" ? group : null;
|
|
18
|
+
}
|
|
19
|
+
function diagramAnchorOf(node) {
|
|
20
|
+
const anchor = traitOf(node)?.anchor;
|
|
21
|
+
return anchor !== null && typeof anchor === "object" && "at" in anchor ? anchor : null;
|
|
22
|
+
}
|
|
23
|
+
function groupDerivePose(opts = {}) {
|
|
24
|
+
const geometry = opts.geometry ?? AUTO_POSE_DESCRIPTOR;
|
|
25
|
+
return (node, deps) => {
|
|
26
|
+
const group = diagramGroupOf(node);
|
|
27
|
+
if (group === null) return null;
|
|
28
|
+
let x0 = Infinity;
|
|
29
|
+
let y0 = Infinity;
|
|
30
|
+
let x1 = -Infinity;
|
|
31
|
+
let y1 = -Infinity;
|
|
32
|
+
for (const dep of deps) {
|
|
33
|
+
if (dep === void 0) continue;
|
|
34
|
+
const b = geometry.getBounds(dep.pose);
|
|
35
|
+
x0 = Math.min(x0, b.x);
|
|
36
|
+
y0 = Math.min(y0, b.y);
|
|
37
|
+
x1 = Math.max(x1, b.x + b.width);
|
|
38
|
+
y1 = Math.max(y1, b.y + b.height);
|
|
39
|
+
}
|
|
40
|
+
if (!Number.isFinite(x0)) return null;
|
|
41
|
+
const inset = insetOf(group.inset);
|
|
42
|
+
return geometry.fromBounds(
|
|
43
|
+
{
|
|
44
|
+
x: x0 - inset.left,
|
|
45
|
+
y: y0 - inset.top,
|
|
46
|
+
width: x1 - x0 + inset.left + inset.right,
|
|
47
|
+
height: y1 - y0 + inset.top + inset.bottom
|
|
48
|
+
},
|
|
49
|
+
node.pose
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function anchorDerivePose(opts = {}) {
|
|
54
|
+
const geometry = opts.geometry ?? AUTO_POSE_DESCRIPTOR;
|
|
55
|
+
return (node, deps) => {
|
|
56
|
+
const anchor = diagramAnchorOf(node);
|
|
57
|
+
const dep = deps[0];
|
|
58
|
+
if (anchor === null || dep === void 0) return null;
|
|
59
|
+
const target = geometry.getBounds(dep.pose);
|
|
60
|
+
const own = geometry.getBounds(node.pose);
|
|
61
|
+
const self = anchor.self ?? anchor.at;
|
|
62
|
+
const x = target.x + anchor.at.u * target.width + (anchor.offset?.x ?? 0) - self.u * own.width;
|
|
63
|
+
const y = target.y + anchor.at.v * target.height + (anchor.offset?.y ?? 0) - self.v * own.height;
|
|
64
|
+
return translatePoseViaDescriptor(node.pose, x - own.x, y - own.y, geometry);
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
var DIAGRAM_GROUP = "diagram:group";
|
|
68
|
+
var DIAGRAM_ANCHOR = "diagram:anchor";
|
|
69
|
+
var GROUP_DERIVE_POSE = groupDerivePose();
|
|
70
|
+
var ANCHOR_DERIVE_POSE = anchorDerivePose();
|
|
9
71
|
var NAMED = { start: 0, mid: 0.5, end: 1 };
|
|
10
72
|
function diagramLabelOf(node) {
|
|
11
73
|
const data = node.data;
|
|
@@ -161,6 +223,8 @@ function withDiagramRegistry(registry = {}) {
|
|
|
161
223
|
},
|
|
162
224
|
derivePose: {
|
|
163
225
|
[DIAGRAM_LABEL]: LABEL_DERIVE_POSE,
|
|
226
|
+
[DIAGRAM_GROUP]: GROUP_DERIVE_POSE,
|
|
227
|
+
[DIAGRAM_ANCHOR]: ANCHOR_DERIVE_POSE,
|
|
164
228
|
...registry.derivePose
|
|
165
229
|
}
|
|
166
230
|
};
|
|
@@ -403,6 +467,7 @@ function buildGraph(source, opts = {}) {
|
|
|
403
467
|
const nodes = [];
|
|
404
468
|
const byId = /* @__PURE__ */ new Map();
|
|
405
469
|
const candidates = [];
|
|
470
|
+
const groupNodes = [];
|
|
406
471
|
for (const { node, pose } of source()) {
|
|
407
472
|
if (diagramEdgeOf(node) !== null) {
|
|
408
473
|
const deps = node.dependsOn;
|
|
@@ -413,6 +478,15 @@ function buildGraph(source, opts = {}) {
|
|
|
413
478
|
}
|
|
414
479
|
const trait = diagramNodeOf(node, opts.read);
|
|
415
480
|
if (trait === null) continue;
|
|
481
|
+
if (trait.group !== void 0) {
|
|
482
|
+
const deps = node.dependsOn;
|
|
483
|
+
groupNodes.push({
|
|
484
|
+
id: node.id,
|
|
485
|
+
members: Array.isArray(deps) ? deps : [],
|
|
486
|
+
inset: insetOf(trait.group.inset)
|
|
487
|
+
});
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
416
490
|
const entry = {
|
|
417
491
|
id: node.id,
|
|
418
492
|
bounds: geometry.getBounds(pose),
|
|
@@ -421,7 +495,19 @@ function buildGraph(source, opts = {}) {
|
|
|
421
495
|
nodes.push(entry);
|
|
422
496
|
byId.set(entry.id, entry);
|
|
423
497
|
}
|
|
424
|
-
const
|
|
498
|
+
const groups = [];
|
|
499
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
500
|
+
for (const g of groupNodes) {
|
|
501
|
+
const members = g.members.filter((id) => byId.has(id) && !claimed.has(id));
|
|
502
|
+
for (const id of members) {
|
|
503
|
+
claimed.add(id);
|
|
504
|
+
byId.get(id).group = g.id;
|
|
505
|
+
}
|
|
506
|
+
groups.push({ ...g, members });
|
|
507
|
+
}
|
|
508
|
+
const live = groups.filter((g) => g.members.length > 0);
|
|
509
|
+
const known = (id) => byId.has(id) || live.some((g) => g.id === id);
|
|
510
|
+
const edges = expandGroupEdges(candidates.filter((e) => known(e.from) && known(e.to)), live);
|
|
425
511
|
const out = /* @__PURE__ */ new Map();
|
|
426
512
|
const into = /* @__PURE__ */ new Map();
|
|
427
513
|
for (const edge of edges) {
|
|
@@ -432,6 +518,7 @@ function buildGraph(source, opts = {}) {
|
|
|
432
518
|
return {
|
|
433
519
|
nodes,
|
|
434
520
|
edges,
|
|
521
|
+
groups: live,
|
|
435
522
|
node: (id) => byId.get(id),
|
|
436
523
|
outgoing: (id) => out.get(id) ?? NONE,
|
|
437
524
|
incoming: (id) => into.get(id) ?? NONE
|
|
@@ -443,23 +530,81 @@ function push(index, key, edge) {
|
|
|
443
530
|
else list.push(edge);
|
|
444
531
|
}
|
|
445
532
|
|
|
533
|
+
// src/dataGroups.ts
|
|
534
|
+
function resolveGroups(groups, ids, nodeIds, opts) {
|
|
535
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
536
|
+
const out = [];
|
|
537
|
+
for (const group of groups) {
|
|
538
|
+
if (ids.has(group.id)) throw new Error(`diagramScene: group id "${group.id}" is already a node or group id`);
|
|
539
|
+
ids.add(group.id);
|
|
540
|
+
const members = group.members.filter((m) => nodeIds.has(m) && !claimed.has(m));
|
|
541
|
+
for (const m of members) claimed.add(m);
|
|
542
|
+
if (members.length === 0) continue;
|
|
543
|
+
const pad = group.padding ?? opts.padding;
|
|
544
|
+
const title = group.label ? opts.title(group.label) : null;
|
|
545
|
+
const inset = { top: pad + (title ? title.height + pad / 2 : 0), right: pad, bottom: pad, left: pad };
|
|
546
|
+
out.push({ group, members, inset, pad, title });
|
|
547
|
+
}
|
|
548
|
+
return out;
|
|
549
|
+
}
|
|
550
|
+
function groupSpecs(groups, placedAt, styleOf) {
|
|
551
|
+
const specs = [];
|
|
552
|
+
for (const g of groups) {
|
|
553
|
+
const { style, text } = styleOf(g.group);
|
|
554
|
+
const rs = g.members.map((m) => placedAt.get(m));
|
|
555
|
+
const x = Math.min(...rs.map((r) => r.x)) - g.inset.left;
|
|
556
|
+
const y = Math.min(...rs.map((r) => r.y)) - g.inset.top;
|
|
557
|
+
specs.push({
|
|
558
|
+
id: g.group.id,
|
|
559
|
+
kind: "leaf",
|
|
560
|
+
layer: "main",
|
|
561
|
+
pickable: false,
|
|
562
|
+
pose: {
|
|
563
|
+
x,
|
|
564
|
+
y,
|
|
565
|
+
width: Math.max(...rs.map((r) => r.x + r.width)) + g.inset.right - x,
|
|
566
|
+
height: Math.max(...rs.map((r) => r.y + r.height)) + g.inset.bottom - y
|
|
567
|
+
},
|
|
568
|
+
data: {
|
|
569
|
+
diagram: { outline: "rect", group: { inset: g.inset } },
|
|
570
|
+
fill: { color: style.fill },
|
|
571
|
+
stroke: {
|
|
572
|
+
paint: { color: style.stroke },
|
|
573
|
+
width: style.strokeWidth ?? 1,
|
|
574
|
+
...style.dash ? { dash: style.dash } : {}
|
|
575
|
+
}
|
|
576
|
+
},
|
|
577
|
+
dependsOn: g.members,
|
|
578
|
+
derivePose: GROUP_DERIVE_POSE
|
|
579
|
+
});
|
|
580
|
+
if (g.title === null || !g.group.label) continue;
|
|
581
|
+
const anchor = { at: { u: 0, v: 0 }, offset: { x: g.pad, y: g.pad } };
|
|
582
|
+
specs.push({
|
|
583
|
+
id: `${g.group.id}/label`,
|
|
584
|
+
kind: "leaf",
|
|
585
|
+
layer: "main",
|
|
586
|
+
pickable: false,
|
|
587
|
+
pose: { x: x + g.pad, y: y + g.pad, width: g.title.width, height: g.title.height },
|
|
588
|
+
data: { diagram: { anchor }, text: g.group.label, style: text, fill: { color: style.text } },
|
|
589
|
+
dependsOn: [g.group.id],
|
|
590
|
+
derivePose: ANCHOR_DERIVE_POSE
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
return specs;
|
|
594
|
+
}
|
|
595
|
+
|
|
446
596
|
// src/fromData.ts
|
|
447
597
|
var FONT = { fontFamily: "sans-serif", fontSize: 12 };
|
|
448
598
|
var NODE = { fill: "#16222c", stroke: "#7ba7c7", text: "#dbe7f2" };
|
|
449
599
|
var EDGE = { stroke: "#7ba7c7" };
|
|
450
|
-
var
|
|
600
|
+
var GROUP = { fill: "rgba(123, 167, 199, 0.08)", stroke: "#7ba7c7", text: "#9fbdd6", strokeWidth: 1, dash: [4, 3] };
|
|
601
|
+
var LABEL_PLACEMENT = { at: "mid", offset: 9 };
|
|
451
602
|
var LINE = 1.4;
|
|
452
603
|
var GLYPH = 0.6;
|
|
453
604
|
var MIN_WIDTH = 64;
|
|
454
|
-
var
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
return {
|
|
458
|
-
width: Math.max(MIN_WIDTH, Math.ceil(longest * GLYPH * fontSize + 2 * PAD.x)),
|
|
459
|
-
height: Math.ceil(Math.max(1, node.lines.length) * LINE * fontSize + 2 * PAD.y)
|
|
460
|
-
};
|
|
461
|
-
}
|
|
462
|
-
function graphOf(nodes, edges) {
|
|
605
|
+
var LAYOUTS = { layered, tree, force };
|
|
606
|
+
var edgeIdOf = (e) => `edge:${e.from}${e.fromPort ? `.${e.fromPort}` : ""}->${e.to}${e.toPort ? `.${e.toPort}` : ""}${e.label ? `:${e.label}` : ""}`;
|
|
607
|
+
function graphOf(nodes, edges, groups = []) {
|
|
463
608
|
const byId = new Map(nodes.map((n) => [n.id, n]));
|
|
464
609
|
const out = /* @__PURE__ */ new Map();
|
|
465
610
|
const inc = /* @__PURE__ */ new Map();
|
|
@@ -470,6 +615,7 @@ function graphOf(nodes, edges) {
|
|
|
470
615
|
return {
|
|
471
616
|
nodes,
|
|
472
617
|
edges,
|
|
618
|
+
groups,
|
|
473
619
|
node: (id) => byId.get(id),
|
|
474
620
|
outgoing: (id) => out.get(id) ?? [],
|
|
475
621
|
incoming: (id) => inc.get(id) ?? []
|
|
@@ -477,11 +623,26 @@ function graphOf(nodes, edges) {
|
|
|
477
623
|
}
|
|
478
624
|
function diagramScene(data, opts = {}) {
|
|
479
625
|
const font = opts.font ?? FONT;
|
|
626
|
+
const styleOf = (s) => ({
|
|
627
|
+
fontFamily: s?.fontFamily ?? font.fontFamily,
|
|
628
|
+
fontSize: s?.fontSize ?? font.fontSize,
|
|
629
|
+
...s?.bold === true ? { fontWeight: 700 } : {}
|
|
630
|
+
});
|
|
631
|
+
const measure = opts.measure ? (text, s) => opts.measure(text, { ...s, fontFamily: s.fontFamily ?? font.fontFamily, fontSize: s.fontSize ?? font.fontSize }) : (text, s) => {
|
|
632
|
+
const size = s.fontSize ?? font.fontSize;
|
|
633
|
+
return { width: text.length * GLYPH * size, height: Math.ceil(LINE * size) };
|
|
634
|
+
};
|
|
480
635
|
const ids = /* @__PURE__ */ new Set();
|
|
481
636
|
for (const n of data.nodes) {
|
|
482
637
|
if (ids.has(n.id)) throw new Error(`diagramScene: node id "${n.id}" appears more than once`);
|
|
483
638
|
ids.add(n.id);
|
|
484
639
|
}
|
|
640
|
+
const nodeIds = new Set(ids);
|
|
641
|
+
const titleStyle = { ...font, fontSize: font.fontSize - 1 };
|
|
642
|
+
const groups = resolveGroups(data.groups ?? [], ids, nodeIds, {
|
|
643
|
+
padding: opts.groupPadding ?? DEFAULT_GROUP_INSET,
|
|
644
|
+
title: (text) => measure(text, titleStyle)
|
|
645
|
+
});
|
|
485
646
|
const seen = /* @__PURE__ */ new Set();
|
|
486
647
|
const edges = data.edges.filter((e) => {
|
|
487
648
|
const id = edgeIdOf(e);
|
|
@@ -489,49 +650,75 @@ function diagramScene(data, opts = {}) {
|
|
|
489
650
|
seen.add(id);
|
|
490
651
|
return true;
|
|
491
652
|
});
|
|
492
|
-
const
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
653
|
+
const bodies = data.nodes.map((n) => ({
|
|
654
|
+
outline: n.outline ?? opts.outline ?? "rect",
|
|
655
|
+
rows: n.rows ?? (n.lines ?? []).map((text) => ({ kind: "label", text })),
|
|
656
|
+
padding: n.padding ?? opts.padding ?? 8,
|
|
657
|
+
gap: n.gap ?? opts.gap ?? 4,
|
|
658
|
+
verticalAlign: n.verticalAlign ?? opts.verticalAlign ?? "center",
|
|
659
|
+
...n.ports ? { ports: n.ports } : {}
|
|
496
660
|
}));
|
|
497
|
-
const
|
|
498
|
-
|
|
661
|
+
const seeds = data.nodes.map((n, i) => {
|
|
662
|
+
const pose = sizeToBody(
|
|
663
|
+
{ ...n.at ?? { x: i, y: i }, width: n.width ?? opts.minWidth ?? MIN_WIDTH, height: n.height ?? 0 },
|
|
664
|
+
measureBody(bodies[i], measure)
|
|
665
|
+
);
|
|
666
|
+
return { id: n.id, bounds: pose, pinned: n.pinned === true };
|
|
667
|
+
});
|
|
668
|
+
const graphGroups = groups.map((g) => ({ id: g.group.id, members: g.members, inset: g.inset }));
|
|
669
|
+
const seedOf = new Map(seeds.map((n) => [n.id, n]));
|
|
670
|
+
for (const g of groups) for (const m of g.members) seedOf.get(m).group = g.group.id;
|
|
671
|
+
const graph = graphOf(
|
|
672
|
+
seeds,
|
|
673
|
+
expandGroupEdges(edges.map((e) => ({ id: edgeIdOf(e), from: e.from, to: e.to })), graphGroups),
|
|
674
|
+
graphGroups
|
|
675
|
+
);
|
|
676
|
+
const layout = typeof opts.layout === "function" ? opts.layout : opts.layout === "none" ? null : LAYOUTS[opts.layout ?? "layered"];
|
|
677
|
+
const moved = layout === null ? /* @__PURE__ */ new Map() : layout(graph, { order: "barycenter", ...opts.layoutOptions });
|
|
499
678
|
const specs = [];
|
|
500
|
-
|
|
679
|
+
const placedAt = new Map(data.nodes.map((n, i) => {
|
|
501
680
|
const seed = seeds[i].bounds;
|
|
502
681
|
const at = moved.get(n.id) ?? seed;
|
|
503
|
-
|
|
682
|
+
return [n.id, { x: at.x, y: at.y, width: seed.width, height: seed.height }];
|
|
683
|
+
}));
|
|
684
|
+
specs.push(...groupSpecs(groups, placedAt, (g) => ({
|
|
685
|
+
style: opts.groupStyle?.(g) ?? GROUP,
|
|
686
|
+
text: styleOf(titleStyle)
|
|
687
|
+
})));
|
|
688
|
+
for (const [i, n] of data.nodes.entries()) {
|
|
689
|
+
const seed = seeds[i].bounds;
|
|
690
|
+
const at = placedAt.get(n.id);
|
|
504
691
|
const style = opts.nodeStyle?.(n) ?? NODE;
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
diagram: { outline: "rect" },
|
|
512
|
-
fill: { color: style.fill },
|
|
513
|
-
stroke: { paint: { color: style.stroke }, width: style.strokeWidth ?? 2 }
|
|
514
|
-
}
|
|
515
|
-
});
|
|
516
|
-
for (const [k, text] of n.lines.entries()) {
|
|
517
|
-
specs.push({
|
|
518
|
-
kind: "leaf",
|
|
692
|
+
const textData = (text, s) => ({ text, style: styleOf(s), fill: { color: style.text } });
|
|
693
|
+
const built = buildBody(
|
|
694
|
+
bodies[i],
|
|
695
|
+
{ x: at.x, y: at.y, width: seed.width, height: seed.height },
|
|
696
|
+
{
|
|
697
|
+
id: n.id,
|
|
519
698
|
layer: "main",
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
699
|
+
measure,
|
|
700
|
+
body: (trait) => ({
|
|
701
|
+
diagram: n.pinned === true ? { ...trait, pinned: true } : trait,
|
|
702
|
+
fill: { color: style.fill },
|
|
703
|
+
stroke: {
|
|
704
|
+
paint: { color: style.stroke },
|
|
705
|
+
width: style.strokeWidth ?? 2,
|
|
706
|
+
...style.dash ? { dash: style.dash } : {}
|
|
707
|
+
}
|
|
708
|
+
}),
|
|
709
|
+
row: (text, box) => text === "" ? null : textData(text, box.row.kind === "label" || box.row.kind === "field" ? box.row.style : void 0),
|
|
710
|
+
portLabel: (text) => textData(text, void 0)
|
|
711
|
+
}
|
|
712
|
+
);
|
|
713
|
+
for (const [k, s] of built.specs.entries()) {
|
|
714
|
+
specs.push({ ...s, ...k === 0 ? {} : { id: `${n.id}/${k}` } });
|
|
530
715
|
}
|
|
531
716
|
}
|
|
532
717
|
for (const e of edges) {
|
|
533
718
|
const id = edgeIdOf(e);
|
|
534
719
|
const style = opts.edgeStyle?.(e) ?? EDGE;
|
|
720
|
+
const markerStart = style.markerStart ?? null;
|
|
721
|
+
const markerEnd = style.markerEnd === void 0 ? "arrow" : style.markerEnd;
|
|
535
722
|
specs.push({
|
|
536
723
|
id,
|
|
537
724
|
kind: "leaf",
|
|
@@ -539,23 +726,36 @@ function diagramScene(data, opts = {}) {
|
|
|
539
726
|
pickable: false,
|
|
540
727
|
pose: { x: 0, y: 0, width: 0, height: 0 },
|
|
541
728
|
data: {
|
|
542
|
-
diagram: {
|
|
543
|
-
|
|
729
|
+
diagram: {
|
|
730
|
+
from: e.fromPort ? { port: e.fromPort } : {},
|
|
731
|
+
to: e.toPort ? { port: e.toPort } : {},
|
|
732
|
+
router: e.router ?? opts.router ?? "bezier",
|
|
733
|
+
...e.waypoints ? { waypoints: e.waypoints } : {}
|
|
734
|
+
},
|
|
735
|
+
stroke: {
|
|
736
|
+
paint: { color: style.stroke },
|
|
737
|
+
width: style.width ?? 1.5,
|
|
738
|
+
...style.dash ? { dash: style.dash } : {},
|
|
739
|
+
...markerStart !== null ? { markerStart } : {},
|
|
740
|
+
...markerEnd !== null ? { markerEnd } : {}
|
|
741
|
+
}
|
|
544
742
|
},
|
|
545
743
|
dependsOn: [e.from, e.to],
|
|
546
744
|
derivePath: EDGE_DERIVE_PATH
|
|
547
745
|
});
|
|
548
746
|
if (e.label) {
|
|
747
|
+
const labelStyle = { ...font, fontSize: font.fontSize - 2 };
|
|
549
748
|
specs.push({
|
|
749
|
+
id: `${id}/label`,
|
|
550
750
|
kind: "leaf",
|
|
551
751
|
layer: "main",
|
|
552
752
|
pickable: false,
|
|
553
|
-
pose: { x: 0, y: 0, width: e.label
|
|
753
|
+
pose: { x: 0, y: 0, width: measure(e.label, labelStyle).width, height: labelStyle.fontSize },
|
|
554
754
|
data: {
|
|
555
|
-
diagram: { label:
|
|
755
|
+
diagram: { label: e.labelPlacement ?? opts.labelPlacement ?? LABEL_PLACEMENT },
|
|
556
756
|
text: e.label,
|
|
557
|
-
style:
|
|
558
|
-
fill: { color: style.stroke }
|
|
757
|
+
style: labelStyle,
|
|
758
|
+
fill: { color: style.label ?? style.stroke }
|
|
559
759
|
},
|
|
560
760
|
dependsOn: [id],
|
|
561
761
|
derivePose: LABEL_DERIVE_POSE
|
|
@@ -564,7 +764,7 @@ function diagramScene(data, opts = {}) {
|
|
|
564
764
|
}
|
|
565
765
|
return specs;
|
|
566
766
|
}
|
|
567
|
-
var
|
|
767
|
+
var LAYOUTS2 = Object.freeze({
|
|
568
768
|
layered,
|
|
569
769
|
tree,
|
|
570
770
|
force
|
|
@@ -609,7 +809,7 @@ function subtreeOf(scene, id) {
|
|
|
609
809
|
}
|
|
610
810
|
var LAYOUT_ACTION_ID = "diagram.layout";
|
|
611
811
|
function createLayoutAction(opts) {
|
|
612
|
-
const algorithm = typeof opts.algorithm === "function" ? opts.algorithm :
|
|
812
|
+
const algorithm = typeof opts.algorithm === "function" ? opts.algorithm : LAYOUTS2[opts.algorithm ?? "layered"] ?? layered;
|
|
613
813
|
const build = {
|
|
614
814
|
...opts.read ? { read: opts.read } : {},
|
|
615
815
|
...opts.geometry ? { geometry: opts.geometry } : {}
|
|
@@ -725,7 +925,7 @@ function useLiveLayout(opts) {
|
|
|
725
925
|
});
|
|
726
926
|
return;
|
|
727
927
|
}
|
|
728
|
-
const fn = typeof algorithm === "function" ? algorithm :
|
|
928
|
+
const fn = typeof algorithm === "function" ? algorithm : LAYOUTS2[algorithm] ?? layered;
|
|
729
929
|
producer.current = easedProducer(graph, fn, {
|
|
730
930
|
...o.layout ? { layout: o.layout } : {},
|
|
731
931
|
...o.frames === void 0 ? {} : { frames: o.frames },
|
|
@@ -836,18 +1036,32 @@ function useMirroredSelection(selection, selected, onSelect) {
|
|
|
836
1036
|
onSelectRef.current?.(selection.get()[0] ?? null);
|
|
837
1037
|
}, [selection, onSelectRef]);
|
|
838
1038
|
}
|
|
839
|
-
var made = 0;
|
|
840
|
-
var keys = /* @__PURE__ */ new WeakMap();
|
|
841
|
-
var keyOf = (specs) => {
|
|
842
|
-
let k = keys.get(specs);
|
|
843
|
-
if (k === void 0) keys.set(specs, k = ++made);
|
|
844
|
-
return k;
|
|
845
|
-
};
|
|
846
1039
|
var AMBIENT = [dragPanContribution()];
|
|
847
|
-
|
|
848
|
-
return /* @__PURE__ */ jsx(WeaselProvider, { children: /* @__PURE__ */ jsx(Inner, { ...props }
|
|
849
|
-
}
|
|
850
|
-
function Inner({
|
|
1040
|
+
var DiagramView = forwardRef(function DiagramView2(props, ref) {
|
|
1041
|
+
return /* @__PURE__ */ jsx(WeaselProvider, { children: /* @__PURE__ */ jsx(Inner, { ...props, apiRef: ref }) });
|
|
1042
|
+
});
|
|
1043
|
+
function Inner({
|
|
1044
|
+
specs,
|
|
1045
|
+
width,
|
|
1046
|
+
height,
|
|
1047
|
+
selected,
|
|
1048
|
+
onSelect,
|
|
1049
|
+
onMove,
|
|
1050
|
+
onConnect,
|
|
1051
|
+
enableKeybindings,
|
|
1052
|
+
canConnect,
|
|
1053
|
+
portOptions,
|
|
1054
|
+
live: liveOpts,
|
|
1055
|
+
minScale,
|
|
1056
|
+
maxScale,
|
|
1057
|
+
anchor,
|
|
1058
|
+
fitPadding,
|
|
1059
|
+
background,
|
|
1060
|
+
view,
|
|
1061
|
+
onViewChange,
|
|
1062
|
+
className,
|
|
1063
|
+
apiRef
|
|
1064
|
+
}) {
|
|
851
1065
|
useEffect(() => registerDiagramShape(), []);
|
|
852
1066
|
const registry = useMemo(() => withDiagramRegistry(), []);
|
|
853
1067
|
const scene = useScene({
|
|
@@ -857,27 +1071,97 @@ function Inner({ specs, width, height, selected, onSelect, minScale, anchor, cla
|
|
|
857
1071
|
});
|
|
858
1072
|
const selection = useSelection({ scene, mode: "single" });
|
|
859
1073
|
const onClick = useMirroredSelection(selection, selected, onSelect);
|
|
1074
|
+
const onMoveRef = useLatest(onMove);
|
|
1075
|
+
const syncing = useRef(false);
|
|
1076
|
+
const applied = useRef(specs);
|
|
1077
|
+
const known = useRef(/* @__PURE__ */ new Map());
|
|
1078
|
+
useEffect(() => {
|
|
1079
|
+
const read = () => {
|
|
1080
|
+
const out = /* @__PURE__ */ new Map();
|
|
1081
|
+
for (const n of scene.renderOrderNodes()) {
|
|
1082
|
+
if (n.kind === "container") out.set(n.id, { x: n.pose.x, y: n.pose.y });
|
|
1083
|
+
}
|
|
1084
|
+
return out;
|
|
1085
|
+
};
|
|
1086
|
+
known.current = read();
|
|
1087
|
+
return scene.subscribe(() => {
|
|
1088
|
+
const now = read();
|
|
1089
|
+
const moves = [];
|
|
1090
|
+
if (!syncing.current) {
|
|
1091
|
+
for (const [id, at] of now) {
|
|
1092
|
+
const was = known.current.get(id);
|
|
1093
|
+
if (was !== void 0 && (was.x !== at.x || was.y !== at.y)) moves.push({ id, ...at });
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
known.current = now;
|
|
1097
|
+
if (moves.length > 0) onMoveRef.current?.(moves);
|
|
1098
|
+
});
|
|
1099
|
+
}, [scene, onMoveRef]);
|
|
1100
|
+
useEffect(() => {
|
|
1101
|
+
if (applied.current === specs) return;
|
|
1102
|
+
syncing.current = true;
|
|
1103
|
+
try {
|
|
1104
|
+
reconcileSpecs(scene, applied.current, specs);
|
|
1105
|
+
} finally {
|
|
1106
|
+
syncing.current = false;
|
|
1107
|
+
}
|
|
1108
|
+
applied.current = specs;
|
|
1109
|
+
}, [scene, specs]);
|
|
1110
|
+
const source = useMemo(() => sceneParticipants(scene), [scene]);
|
|
1111
|
+
const [run, setRun] = useState(null);
|
|
1112
|
+
const live = useLiveLayout({ ...liveOpts, scene, source, algorithm: run?.algorithm ?? "force" });
|
|
1113
|
+
useEffect(() => {
|
|
1114
|
+
if (run !== null) live.restart();
|
|
1115
|
+
}, [run, live]);
|
|
1116
|
+
useImperativeHandle(apiRef, () => ({
|
|
1117
|
+
layout: (algorithm = "force") => setRun({ algorithm }),
|
|
1118
|
+
live
|
|
1119
|
+
}), [live]);
|
|
1120
|
+
const onConnectRef = useLatest(onConnect);
|
|
1121
|
+
const connecting = onConnect !== void 0;
|
|
1122
|
+
const ports = useMemo(() => connecting ? diagramPorts({
|
|
1123
|
+
...portOptions,
|
|
1124
|
+
participants: source,
|
|
1125
|
+
...canConnect ? { canConnect } : {},
|
|
1126
|
+
commit: ({ from, to }) => onConnectRef.current?.({
|
|
1127
|
+
from: from.nodeId,
|
|
1128
|
+
fromPort: from.id,
|
|
1129
|
+
to: to.nodeId,
|
|
1130
|
+
toPort: to.id
|
|
1131
|
+
})
|
|
1132
|
+
}) : null, [connecting, source, canConnect, portOptions, onConnectRef]);
|
|
1133
|
+
const canvasRef = useRef(null);
|
|
1134
|
+
useEffect(() => ports ? canvasRef.current?.registerLayer(ports.layer) : void 0, [ports]);
|
|
1135
|
+
const ambient = useMemo(() => ports ? [...AMBIENT, ports.contribution] : AMBIENT, [ports]);
|
|
1136
|
+
const controlled = view !== void 0;
|
|
860
1137
|
const defaultView = useMemo(
|
|
861
|
-
() => fitDiagram(specs, { width, height }, minScale, anchor),
|
|
862
|
-
|
|
1138
|
+
() => controlled ? void 0 : fitDiagram(specs, { width, height }, { minScale, maxScale, anchor, padding: fitPadding }),
|
|
1139
|
+
// The fit is where the view opens; a later `specs` keeps the view it has.
|
|
1140
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1141
|
+
[controlled, width, height, minScale, maxScale, anchor, fitPadding]
|
|
863
1142
|
);
|
|
864
1143
|
return /* @__PURE__ */ jsx(
|
|
865
1144
|
SceneCanvas,
|
|
866
1145
|
{
|
|
1146
|
+
ref: canvasRef,
|
|
867
1147
|
scene,
|
|
868
1148
|
selection,
|
|
869
|
-
features: ["view", "pick"],
|
|
870
|
-
|
|
1149
|
+
features: onMove ? ["view", "pick", "move"] : ["view", "pick"],
|
|
1150
|
+
enableKeybindings: enableKeybindings ?? (onMove !== void 0 || onConnect !== void 0),
|
|
1151
|
+
ambient,
|
|
871
1152
|
onClick,
|
|
872
1153
|
width,
|
|
873
1154
|
height,
|
|
874
1155
|
className,
|
|
875
|
-
...defaultView ? { defaultView } : {}
|
|
1156
|
+
...defaultView ? { defaultView } : {},
|
|
1157
|
+
...view ? { view } : {},
|
|
1158
|
+
...onViewChange ? { onViewChange } : {},
|
|
1159
|
+
...background ? { backgroundFill: background } : {}
|
|
876
1160
|
}
|
|
877
1161
|
);
|
|
878
1162
|
}
|
|
879
|
-
function fitDiagram(specs, size,
|
|
880
|
-
const boxes = specs.filter((s) => s.kind === "container").map((s) => s.pose);
|
|
1163
|
+
function fitDiagram(specs, size, fit = {}) {
|
|
1164
|
+
const boxes = specs.filter((s) => s.kind === "container" || diagramGroupOf(s) !== null).map((s) => s.pose);
|
|
881
1165
|
if (boxes.length === 0) return void 0;
|
|
882
1166
|
const x = Math.min(...boxes.map((b) => b.x));
|
|
883
1167
|
const y = Math.min(...boxes.map((b) => b.y));
|
|
@@ -888,12 +1172,13 @@ function fitDiagram(specs, size, minScale, anchor) {
|
|
|
888
1172
|
height: Math.max(...boxes.map((b) => b.y + b.height)) - y
|
|
889
1173
|
};
|
|
890
1174
|
return fitViewToBounds(bounds, size, { x: 0, y: 0, scale: { x: 1, y: 1 } }, {
|
|
891
|
-
maxScale: 1,
|
|
892
|
-
...minScale !== void 0 ? { minScale } : {},
|
|
893
|
-
...anchor !== void 0 ? { anchor } : {}
|
|
1175
|
+
maxScale: fit.maxScale ?? 1,
|
|
1176
|
+
...fit.minScale !== void 0 ? { minScale: fit.minScale } : {},
|
|
1177
|
+
...fit.anchor !== void 0 ? { anchor: fit.anchor } : {},
|
|
1178
|
+
...fit.padding !== void 0 ? { padding: fit.padding } : {}
|
|
894
1179
|
});
|
|
895
1180
|
}
|
|
896
1181
|
|
|
897
|
-
export { CONNECT_ACTION_ID, DEFAULT_EDGE_STROKE, DIAGRAM_EDGE, DIAGRAM_LABEL, DiagramView, EASED_LAYOUTS, EDGE_DERIVE_PATH, GRAB_PORT_ACTION_ID, LABEL_DERIVE_POSE, LAYOUTS, LAYOUT_ACTION_ID, PORT_AFFORDANCE_KIND, PORT_LAYER_ID, ROUTERS, applyLayout, bezier, buildGraph, commitEdgeToScene, connectBinding, createConnectAction, createDiagramContribution, createLayoutAction, createPortAffordance, defaultCanConnect, diagramEdgeOf, diagramLabelOf, diagramPorts, diagramScene, diagramShape, easedProducer, edgeDerivePath, edgeIdOf, forceProducer, grabPortAction, grabPortBindings, labelDerivePose, layoutPoses, orthogonal, portLayer, portScratchOf, registerDiagramShape, resolveEnd, sceneParticipants, straight, useLiveLayout, useMirroredSelection, withDiagramRegistry };
|
|
1182
|
+
export { ANCHOR_DERIVE_POSE, CONNECT_ACTION_ID, DEFAULT_EDGE_STROKE, DIAGRAM_ANCHOR, DIAGRAM_EDGE, DIAGRAM_GROUP, DIAGRAM_LABEL, DiagramView, EASED_LAYOUTS, EDGE_DERIVE_PATH, GRAB_PORT_ACTION_ID, GROUP_DERIVE_POSE, LABEL_DERIVE_POSE, LAYOUTS2 as LAYOUTS, LAYOUT_ACTION_ID, PORT_AFFORDANCE_KIND, PORT_LAYER_ID, ROUTERS, anchorDerivePose, applyLayout, bezier, buildGraph, commitEdgeToScene, connectBinding, createConnectAction, createDiagramContribution, createLayoutAction, createPortAffordance, defaultCanConnect, diagramAnchorOf, diagramEdgeOf, diagramGroupOf, diagramLabelOf, diagramPorts, diagramScene, diagramShape, easedProducer, edgeDerivePath, edgeIdOf, forceProducer, grabPortAction, grabPortBindings, groupDerivePose, labelDerivePose, layoutPoses, orthogonal, portLayer, portScratchOf, registerDiagramShape, resolveEnd, sceneParticipants, straight, useLiveLayout, useMirroredSelection, withDiagramRegistry };
|
|
898
1183
|
//# sourceMappingURL=index.js.map
|
|
899
1184
|
//# sourceMappingURL=index.js.map
|