@weasel-js/diagram 1.10.0 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +470 -19
- package/dist/{layout.entry-CIdRpG2i.d.ts → body-2lC7IiIA.d.ts} +248 -261
- package/dist/chunk-C4NAI6NV.js +2041 -0
- package/dist/chunk-C4NAI6NV.js.map +1 -0
- package/dist/chunk-NUMMW5BI.js +2865 -0
- package/dist/chunk-NUMMW5BI.js.map +1 -0
- package/dist/effect.d.ts +417 -0
- package/dist/effect.js +1283 -0
- package/dist/effect.js.map +1 -0
- package/dist/fromData-GKqRgPFs.d.ts +379 -0
- package/dist/index.d.ts +217 -297
- package/dist/index.js +2 -1299
- package/dist/index.js.map +1 -1
- package/dist/layout.d.ts +2 -1
- package/dist/layout.entry-BoWFcR4G.d.ts +285 -0
- package/dist/layout.js +1 -1
- package/package.json +10 -6
- package/dist/chunk-BWQJYEA3.js +0 -1240
- package/dist/chunk-BWQJYEA3.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,1301 +1,4 @@
|
|
|
1
|
-
|
|
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, untilSettled } from './chunk-
|
|
3
|
-
import { dragPanContribution, WeaselProvider, useScene, useSelection, useLatest, reconcileSpecs, SceneCanvas, fitViewToBounds, AUTO_POSE_DESCRIPTOR, translatePoseViaDescriptor, pointAlongPath, polylineFromPoints, registerNodeShape, effectivePose, usePoseRun, RECT_POSE_DESCRIPTOR, commandFill, commandStroke, composeAffordanceLayer } from '@weasel-js/core';
|
|
4
|
-
import { portCurvePoints } from '@weasel-js/geom';
|
|
5
|
-
import { AUTO_POSE_DESCRIPTOR as AUTO_POSE_DESCRIPTOR$1 } from '@weasel-js/core/math';
|
|
6
|
-
import { forwardRef, useEffect, useMemo, useRef, useState, useImperativeHandle, useCallback } from 'react';
|
|
7
|
-
import '@weasel-js/animator';
|
|
8
|
-
import { jsx } from 'react/jsx-runtime';
|
|
9
|
-
|
|
10
|
-
function traitOf(node) {
|
|
11
|
-
const data = node.data;
|
|
12
|
-
if (data === null || typeof data !== "object") return null;
|
|
13
|
-
const trait = data[DIAGRAM_TRAIT_KEY];
|
|
14
|
-
return trait !== null && typeof trait === "object" ? trait : null;
|
|
15
|
-
}
|
|
16
|
-
function diagramGroupOf(node) {
|
|
17
|
-
const group = traitOf(node)?.group;
|
|
18
|
-
return group !== null && typeof group === "object" ? group : null;
|
|
19
|
-
}
|
|
20
|
-
function diagramAnchorOf(node) {
|
|
21
|
-
const anchor = traitOf(node)?.anchor;
|
|
22
|
-
return anchor !== null && typeof anchor === "object" && "at" in anchor ? anchor : null;
|
|
23
|
-
}
|
|
24
|
-
function groupDerivePose(opts = {}) {
|
|
25
|
-
const geometry = opts.geometry ?? AUTO_POSE_DESCRIPTOR;
|
|
26
|
-
return (node, deps) => {
|
|
27
|
-
const group = diagramGroupOf(node);
|
|
28
|
-
if (group === null) return null;
|
|
29
|
-
let x0 = Infinity;
|
|
30
|
-
let y0 = Infinity;
|
|
31
|
-
let x1 = -Infinity;
|
|
32
|
-
let y1 = -Infinity;
|
|
33
|
-
for (const dep of deps) {
|
|
34
|
-
if (dep === void 0) continue;
|
|
35
|
-
const b = geometry.getBounds(dep.pose);
|
|
36
|
-
x0 = Math.min(x0, b.x);
|
|
37
|
-
y0 = Math.min(y0, b.y);
|
|
38
|
-
x1 = Math.max(x1, b.x + b.width);
|
|
39
|
-
y1 = Math.max(y1, b.y + b.height);
|
|
40
|
-
}
|
|
41
|
-
if (!Number.isFinite(x0)) return null;
|
|
42
|
-
const inset = insetOf(group.inset);
|
|
43
|
-
return geometry.fromBounds(
|
|
44
|
-
{
|
|
45
|
-
x: x0 - inset.left,
|
|
46
|
-
y: y0 - inset.top,
|
|
47
|
-
width: x1 - x0 + inset.left + inset.right,
|
|
48
|
-
height: y1 - y0 + inset.top + inset.bottom
|
|
49
|
-
},
|
|
50
|
-
node.pose
|
|
51
|
-
);
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
function anchorDerivePose(opts = {}) {
|
|
55
|
-
const geometry = opts.geometry ?? AUTO_POSE_DESCRIPTOR;
|
|
56
|
-
return (node, deps) => {
|
|
57
|
-
const anchor = diagramAnchorOf(node);
|
|
58
|
-
const dep = deps[0];
|
|
59
|
-
if (anchor === null || dep === void 0) return null;
|
|
60
|
-
const target = geometry.getBounds(dep.pose);
|
|
61
|
-
const own = geometry.getBounds(node.pose);
|
|
62
|
-
const self = anchor.self ?? anchor.at;
|
|
63
|
-
const x = target.x + anchor.at.u * target.width + (anchor.offset?.x ?? 0) - self.u * own.width;
|
|
64
|
-
const y = target.y + anchor.at.v * target.height + (anchor.offset?.y ?? 0) - self.v * own.height;
|
|
65
|
-
return translatePoseViaDescriptor(node.pose, x - own.x, y - own.y, geometry);
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
var DIAGRAM_GROUP = "diagram:group";
|
|
69
|
-
var DIAGRAM_ANCHOR = "diagram:anchor";
|
|
70
|
-
var GROUP_DERIVE_POSE = groupDerivePose();
|
|
71
|
-
var ANCHOR_DERIVE_POSE = anchorDerivePose();
|
|
72
|
-
var NAMED = { start: 0, mid: 0.5, end: 1 };
|
|
73
|
-
function diagramLabelOf(node) {
|
|
74
|
-
const data = node.data;
|
|
75
|
-
if (data === null || typeof data !== "object") return null;
|
|
76
|
-
const trait = data.diagram;
|
|
77
|
-
if (trait === null || typeof trait !== "object") return null;
|
|
78
|
-
const label = trait.label;
|
|
79
|
-
if (label === null || typeof label !== "object") return null;
|
|
80
|
-
return "at" in label ? label : null;
|
|
81
|
-
}
|
|
82
|
-
function labelDerivePose(opts = {}) {
|
|
83
|
-
const geometry = opts.geometry ?? AUTO_POSE_DESCRIPTOR;
|
|
84
|
-
return (node, deps) => {
|
|
85
|
-
const label = diagramLabelOf(node);
|
|
86
|
-
if (label === null) return null;
|
|
87
|
-
const path = deps[0]?.path ?? null;
|
|
88
|
-
if (path === null) return null;
|
|
89
|
-
const t = typeof label.at === "number" ? label.at : NAMED[label.at] ?? 0.5;
|
|
90
|
-
const station = pointAlongPath(path, t);
|
|
91
|
-
if (station === null) return null;
|
|
92
|
-
const offset = label.offset ?? 0;
|
|
93
|
-
const x = station.point.x + station.tangent.y * offset;
|
|
94
|
-
const y = station.point.y - station.tangent.x * offset;
|
|
95
|
-
const bounds = geometry.getBounds(node.pose);
|
|
96
|
-
return translatePoseViaDescriptor(
|
|
97
|
-
node.pose,
|
|
98
|
-
x - (bounds.x + bounds.width / 2),
|
|
99
|
-
y - (bounds.y + bounds.height / 2),
|
|
100
|
-
geometry
|
|
101
|
-
);
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
var DIAGRAM_LABEL = "diagram:label";
|
|
105
|
-
var LABEL_DERIVE_POSE = labelDerivePose();
|
|
106
|
-
|
|
107
|
-
// src/selfLoop.ts
|
|
108
|
-
var dot = (a, b) => a.x * b.x + a.y * b.y;
|
|
109
|
-
function selfLoop(ports, standoff) {
|
|
110
|
-
const facing = ports.filter((p) => p.normal !== null);
|
|
111
|
-
const most = (dir, among) => among.reduce(
|
|
112
|
-
(best, p) => best === void 0 || dot(p.normal, dir) > dot(best.normal, dir) ? p : best,
|
|
113
|
-
void 0
|
|
114
|
-
);
|
|
115
|
-
const from = most({ x: 1, y: 0 }, facing);
|
|
116
|
-
if (from === void 0) return null;
|
|
117
|
-
const to = most({ x: 0, y: -1 }, facing.filter((p) => Math.abs(dot(p.normal, from.normal)) < 0.5));
|
|
118
|
-
if (to === void 0) return null;
|
|
119
|
-
const out = { x: from.point.x + from.normal.x * standoff, y: from.point.y + from.normal.y * standoff };
|
|
120
|
-
const reach = dot({ x: to.point.x - out.x, y: to.point.y - out.y }, to.normal) + standoff;
|
|
121
|
-
return { from, to, via: { x: out.x + to.normal.x * reach, y: out.y + to.normal.y * reach } };
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// src/edge.ts
|
|
125
|
-
var straight = (req) => [req.from.point, ...req.waypoints, req.to.point];
|
|
126
|
-
var orthogonal = (req) => {
|
|
127
|
-
const stops = [req.from.point, ...req.waypoints, req.to.point];
|
|
128
|
-
const out = [stops[0]];
|
|
129
|
-
for (let i = 1; i < stops.length; i++) {
|
|
130
|
-
const a = out[out.length - 1];
|
|
131
|
-
const b = stops[i];
|
|
132
|
-
if (a.x === b.x || a.y === b.y) {
|
|
133
|
-
out.push(b);
|
|
134
|
-
continue;
|
|
135
|
-
}
|
|
136
|
-
const leaveHorizontal = i === 1 ? isHorizontal(req.from.normal) : out.length > 1 && out[out.length - 2].y === a.y;
|
|
137
|
-
const arriving = i === stops.length - 1 ? req.to.normal : null;
|
|
138
|
-
const arriveHorizontal = arriving == null ? !leaveHorizontal : isHorizontal(arriving);
|
|
139
|
-
if (leaveHorizontal !== arriveHorizontal) {
|
|
140
|
-
out.push(leaveHorizontal ? { x: b.x, y: a.y } : { x: a.x, y: b.y });
|
|
141
|
-
out.push(b);
|
|
142
|
-
continue;
|
|
143
|
-
}
|
|
144
|
-
const prev = out.length > 1 ? out[out.length - 2] : null;
|
|
145
|
-
if (leaveHorizontal) {
|
|
146
|
-
const mid = (a.x + b.x) / 2;
|
|
147
|
-
if (prev !== null && (mid - a.x) * (a.x - prev.x) < 0) out.push({ x: a.x, y: b.y }, b);
|
|
148
|
-
else out.push({ x: mid, y: a.y }, { x: mid, y: b.y }, b);
|
|
149
|
-
} else {
|
|
150
|
-
const mid = (a.y + b.y) / 2;
|
|
151
|
-
if (prev !== null && (mid - a.y) * (a.y - prev.y) < 0) out.push({ x: b.x, y: a.y }, b);
|
|
152
|
-
else out.push({ x: a.x, y: mid }, { x: b.x, y: mid }, b);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
return out;
|
|
156
|
-
};
|
|
157
|
-
function isHorizontal(normal) {
|
|
158
|
-
return Math.abs(normal?.x ?? 1) >= Math.abs(normal?.y ?? 0);
|
|
159
|
-
}
|
|
160
|
-
var bezier = (req) => {
|
|
161
|
-
const stops = [req.from.point, ...req.waypoints, req.to.point];
|
|
162
|
-
const out = [stops[0]];
|
|
163
|
-
for (let i = 1; i < stops.length; i++) {
|
|
164
|
-
const a = stops[i - 1];
|
|
165
|
-
const b = stops[i];
|
|
166
|
-
const outN = i === 1 ? req.from.normal : null;
|
|
167
|
-
const inN = i === stops.length - 1 ? req.to.normal : null;
|
|
168
|
-
const leg = portCurvePoints(
|
|
169
|
-
a.x,
|
|
170
|
-
a.y,
|
|
171
|
-
b.x,
|
|
172
|
-
b.y,
|
|
173
|
-
outN == null ? null : [outN.x, outN.y],
|
|
174
|
-
inN == null ? null : [inN.x, inN.y],
|
|
175
|
-
BEZIER_SAMPLES
|
|
176
|
-
);
|
|
177
|
-
for (const [x, y] of leg) out.push({ x, y });
|
|
178
|
-
}
|
|
179
|
-
return out;
|
|
180
|
-
};
|
|
181
|
-
var BEZIER_SAMPLES = 16;
|
|
182
|
-
var ROUTERS = Object.freeze({
|
|
183
|
-
straight,
|
|
184
|
-
orthogonal,
|
|
185
|
-
bezier
|
|
186
|
-
});
|
|
187
|
-
function diagramEdgeOf(node) {
|
|
188
|
-
const data = node.data;
|
|
189
|
-
if (data === null || typeof data !== "object") return null;
|
|
190
|
-
const edge = data.diagram;
|
|
191
|
-
if (edge === null || typeof edge !== "object") return null;
|
|
192
|
-
return "from" in edge && "to" in edge ? edge : null;
|
|
193
|
-
}
|
|
194
|
-
function resolveEnd(end, dep, toward, opts, allPorts) {
|
|
195
|
-
if (end.port !== void 0) {
|
|
196
|
-
const named = portOf(dep.node, dep.pose, end.port, opts);
|
|
197
|
-
if (named !== void 0) return named;
|
|
198
|
-
}
|
|
199
|
-
let best;
|
|
200
|
-
let bestScore = -Infinity;
|
|
201
|
-
for (const port of allPorts(dep)) {
|
|
202
|
-
const dx = toward.x - port.point.x;
|
|
203
|
-
const dy = toward.y - port.point.y;
|
|
204
|
-
const len = Math.hypot(dx, dy) || 1;
|
|
205
|
-
const facing = port.normal === null ? 0 : (port.normal.x * dx + port.normal.y * dy) / len;
|
|
206
|
-
const score = facing - len / 1e6;
|
|
207
|
-
if (score > bestScore) {
|
|
208
|
-
bestScore = score;
|
|
209
|
-
best = port;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
return best;
|
|
213
|
-
}
|
|
214
|
-
function edgeDerivePath(opts = {}) {
|
|
215
|
-
const geometry = opts.geometry ?? AUTO_POSE_DESCRIPTOR;
|
|
216
|
-
const portOpts = { geometry, ...opts.read ? { read: opts.read } : {} };
|
|
217
|
-
const allPorts = (d) => portsOf(d.node, d.pose, portOpts);
|
|
218
|
-
const centerOf = (d) => {
|
|
219
|
-
const b = geometry.getBounds(d.pose);
|
|
220
|
-
return { x: b.x + b.width / 2, y: b.y + b.height / 2 };
|
|
221
|
-
};
|
|
222
|
-
const standoffOf = (d) => {
|
|
223
|
-
const b = geometry.getBounds(d.pose);
|
|
224
|
-
return Math.min(b.width, b.height) / 2;
|
|
225
|
-
};
|
|
226
|
-
return (node, deps) => {
|
|
227
|
-
const edge = diagramEdgeOf(node);
|
|
228
|
-
if (edge === null) return null;
|
|
229
|
-
const [fromDep, toDep] = deps;
|
|
230
|
-
if (fromDep === void 0 || toDep === void 0) return null;
|
|
231
|
-
const loop = fromDep.node.id === toDep.node.id && edge.from.port === void 0 && edge.to.port === void 0 ? selfLoop(allPorts(fromDep), opts.selfLoopStandoff ?? standoffOf(fromDep)) : null;
|
|
232
|
-
const from = loop?.from ?? resolveEnd(edge.from, fromDep, centerOf(toDep), portOpts, allPorts);
|
|
233
|
-
const to = loop?.to ?? resolveEnd(edge.to, toDep, centerOf(fromDep), portOpts, allPorts);
|
|
234
|
-
if (from === void 0 || to === void 0) return null;
|
|
235
|
-
const authored = edge.waypoints ?? [];
|
|
236
|
-
const waypoints = loop !== null && authored.length === 0 ? [loop.via] : authored;
|
|
237
|
-
const routers = opts.routers ?? ROUTERS;
|
|
238
|
-
const router = edge.router !== void 0 && routers[edge.router] || straight;
|
|
239
|
-
const points = router({ from, to, waypoints });
|
|
240
|
-
return points.length < 2 ? null : polylineFromPoints(points);
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
var DIAGRAM_EDGE = "diagram:edge";
|
|
244
|
-
var EDGE_DERIVE_PATH = edgeDerivePath();
|
|
245
|
-
function withDiagramRegistry(registry = {}) {
|
|
246
|
-
return {
|
|
247
|
-
...registry,
|
|
248
|
-
derivePath: {
|
|
249
|
-
[DIAGRAM_EDGE]: EDGE_DERIVE_PATH,
|
|
250
|
-
...registry.derivePath
|
|
251
|
-
},
|
|
252
|
-
derivePose: {
|
|
253
|
-
[DIAGRAM_LABEL]: LABEL_DERIVE_POSE,
|
|
254
|
-
[DIAGRAM_GROUP]: GROUP_DERIVE_POSE,
|
|
255
|
-
[DIAGRAM_ANCHOR]: ANCHOR_DERIVE_POSE,
|
|
256
|
-
...registry.derivePose
|
|
257
|
-
}
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// src/portAffordance.ts
|
|
262
|
-
var PORT_LAYER_ID = "diagram-ports";
|
|
263
|
-
var PORT_AFFORDANCE_KIND = `layer:${PORT_LAYER_ID}`;
|
|
264
|
-
var DEFAULT_HIT_PX = 8;
|
|
265
|
-
var DEFAULT_SIZE_PX = 7;
|
|
266
|
-
function portScratchOf(hit) {
|
|
267
|
-
const payload = hit?.payload;
|
|
268
|
-
return payload !== void 0 && typeof payload.portId === "string" ? payload : null;
|
|
269
|
-
}
|
|
270
|
-
function createPortAffordance(participants, opts = {}) {
|
|
271
|
-
const {
|
|
272
|
-
id = PORT_LAYER_ID,
|
|
273
|
-
hitRadiusPx = DEFAULT_HIT_PX,
|
|
274
|
-
sizePx = DEFAULT_SIZE_PX,
|
|
275
|
-
strength = "exclusive"
|
|
276
|
-
} = opts;
|
|
277
|
-
const portOpts = {
|
|
278
|
-
...opts.geometry ? { geometry: opts.geometry } : {},
|
|
279
|
-
...opts.read ? { read: opts.read } : {}
|
|
280
|
-
};
|
|
281
|
-
const paint = sizePx <= 0 ? void 0 : {
|
|
282
|
-
kind: "square",
|
|
283
|
-
sizePx,
|
|
284
|
-
...opts.fill ? { fill: opts.fill } : {},
|
|
285
|
-
...opts.stroke ? { stroke: opts.stroke } : {}
|
|
286
|
-
};
|
|
287
|
-
return {
|
|
288
|
-
id,
|
|
289
|
-
regions(state) {
|
|
290
|
-
const out = [];
|
|
291
|
-
for (const { node, pose } of participants()) {
|
|
292
|
-
if (opts.shows !== void 0 && !opts.shows(node, state)) continue;
|
|
293
|
-
for (const port of portsOf(node, pose, portOpts)) {
|
|
294
|
-
out.push({
|
|
295
|
-
id: `${node.id}:${port.id}`,
|
|
296
|
-
targetId: null,
|
|
297
|
-
shape: { kind: "point", x: port.point.x, y: port.point.y, hitRadiusPx },
|
|
298
|
-
...paint ? { paint } : {},
|
|
299
|
-
...opts.cursor !== void 0 ? { cursor: opts.cursor } : {},
|
|
300
|
-
strength,
|
|
301
|
-
claimedKinds: ["pointer"],
|
|
302
|
-
bind: () => ({
|
|
303
|
-
initialScratch: {
|
|
304
|
-
targetId: node.id,
|
|
305
|
-
nodeId: node.id,
|
|
306
|
-
portId: port.id,
|
|
307
|
-
port
|
|
308
|
-
}
|
|
309
|
-
})
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
return out;
|
|
314
|
-
}
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// src/connect.ts
|
|
319
|
-
var CONNECT_ACTION_ID = "diagram.connect";
|
|
320
|
-
var defaultCanConnect = (from, to) => {
|
|
321
|
-
if (from.nodeId === to.nodeId && from.id === to.id) return false;
|
|
322
|
-
if (from.type === void 0 || to.type === void 0) return true;
|
|
323
|
-
return from.type === to.type;
|
|
324
|
-
};
|
|
325
|
-
var DEFAULT_SNAP = 16;
|
|
326
|
-
var DEFAULT_EDGE_STROKE = {
|
|
327
|
-
paint: { color: "#7ba7c7" },
|
|
328
|
-
width: 2,
|
|
329
|
-
markerEnd: "arrow"
|
|
330
|
-
};
|
|
331
|
-
var DEFAULT_ROUTER = "straight";
|
|
332
|
-
function connectBinding(actionId = CONNECT_ACTION_ID) {
|
|
333
|
-
return {
|
|
334
|
-
spec: { kind: "drag", target: `affordance:${PORT_AFFORDANCE_KIND}` },
|
|
335
|
-
actionId
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
|
-
var GRAB_PORT_ACTION_ID = "diagram.grabPort";
|
|
339
|
-
function grabPortAction(id = GRAB_PORT_ACTION_ID) {
|
|
340
|
-
return {
|
|
341
|
-
id,
|
|
342
|
-
label: "Grab port",
|
|
343
|
-
group: "diagram",
|
|
344
|
-
invoker: { timing: "immediate", run: () => {
|
|
345
|
-
} },
|
|
346
|
-
enabled: () => true
|
|
347
|
-
};
|
|
348
|
-
}
|
|
349
|
-
function grabPortBindings(actionId = GRAB_PORT_ACTION_ID) {
|
|
350
|
-
return [
|
|
351
|
-
{ spec: { kind: "pointerDown", target: `affordance:${PORT_AFFORDANCE_KIND}` }, actionId },
|
|
352
|
-
{ spec: { kind: "click", target: `affordance:${PORT_AFFORDANCE_KIND}` }, actionId }
|
|
353
|
-
];
|
|
354
|
-
}
|
|
355
|
-
function commitEdgeToScene(edge, ctx) {
|
|
356
|
-
const scene = ctx.deps.scene;
|
|
357
|
-
if (scene === void 0) return;
|
|
358
|
-
const trait = {
|
|
359
|
-
from: { port: edge.from.id },
|
|
360
|
-
to: { port: edge.to.id },
|
|
361
|
-
router: edge.router
|
|
362
|
-
};
|
|
363
|
-
const source = scene.get(edge.from.nodeId);
|
|
364
|
-
scene.add({
|
|
365
|
-
kind: "leaf",
|
|
366
|
-
layer: source?.layer ?? scene.layers[0].id,
|
|
367
|
-
pose: { x: 0, y: 0, width: 0, height: 0 },
|
|
368
|
-
data: { diagram: trait, stroke: DEFAULT_EDGE_STROKE },
|
|
369
|
-
dependsOn: [edge.from.nodeId, edge.to.nodeId],
|
|
370
|
-
// Without this the edge re-resolves on every dependency move and paints
|
|
371
|
-
// nothing. `EDGE_DERIVE_PATH` rather than a fresh `edgeDerivePath()` so the
|
|
372
|
-
// scene can find its registry key and the edge survives `toJSON`.
|
|
373
|
-
derivePath: EDGE_DERIVE_PATH
|
|
374
|
-
});
|
|
375
|
-
}
|
|
376
|
-
function createConnectAction(opts) {
|
|
377
|
-
const {
|
|
378
|
-
id = CONNECT_ACTION_ID,
|
|
379
|
-
canConnect = defaultCanConnect,
|
|
380
|
-
snapRadius = DEFAULT_SNAP,
|
|
381
|
-
router: routerKey = DEFAULT_ROUTER,
|
|
382
|
-
commit = commitEdgeToScene
|
|
383
|
-
} = opts;
|
|
384
|
-
const routers = opts.routers ?? ROUTERS;
|
|
385
|
-
const portOpts = {
|
|
386
|
-
...opts.geometry ? { geometry: opts.geometry } : {},
|
|
387
|
-
...opts.read ? { read: opts.read } : {}
|
|
388
|
-
};
|
|
389
|
-
const route = routers[routerKey] ?? routers["straight"] ?? ROUTERS["straight"];
|
|
390
|
-
function targetPort(from, at) {
|
|
391
|
-
let best = null;
|
|
392
|
-
let bestD2 = snapRadius * snapRadius;
|
|
393
|
-
for (const { node, pose } of opts.participants()) {
|
|
394
|
-
for (const port of portsOf(node, pose, portOpts)) {
|
|
395
|
-
if (!canConnect(from, port)) continue;
|
|
396
|
-
const d2 = (port.point.x - at.x) ** 2 + (port.point.y - at.y) ** 2;
|
|
397
|
-
if (d2 <= bestD2) {
|
|
398
|
-
bestD2 = d2;
|
|
399
|
-
best = port;
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
return best;
|
|
404
|
-
}
|
|
405
|
-
function endAt(from, at) {
|
|
406
|
-
const port = targetPort(from, at);
|
|
407
|
-
return {
|
|
408
|
-
port,
|
|
409
|
-
end: port ?? { id: "", nodeId: "", point: at, normal: null }
|
|
410
|
-
};
|
|
411
|
-
}
|
|
412
|
-
return {
|
|
413
|
-
id,
|
|
414
|
-
label: "Connect",
|
|
415
|
-
group: "diagram",
|
|
416
|
-
requires: ["scene"],
|
|
417
|
-
invoker: {
|
|
418
|
-
timing: "ongoing",
|
|
419
|
-
start(ctx) {
|
|
420
|
-
const pressed = portScratchOf(ctx.drag?.affordance);
|
|
421
|
-
if (pressed === null) return {};
|
|
422
|
-
const scratch = {
|
|
423
|
-
from: pressed.port,
|
|
424
|
-
current: { x: ctx.world.x, y: ctx.world.y },
|
|
425
|
-
open: true,
|
|
426
|
-
deps: ctx.deps
|
|
427
|
-
};
|
|
428
|
-
return {
|
|
429
|
-
kind: "diagram-connect",
|
|
430
|
-
onMove(moveCtx) {
|
|
431
|
-
scratch.current = { x: moveCtx.world.x, y: moveCtx.world.y };
|
|
432
|
-
},
|
|
433
|
-
overlay() {
|
|
434
|
-
if (!scratch.open) return null;
|
|
435
|
-
const { end } = endAt(scratch.from, scratch.current);
|
|
436
|
-
const points = route({ from: scratch.from, to: end, waypoints: [] });
|
|
437
|
-
if (points.length < 2) return null;
|
|
438
|
-
return { kind: "polyline", points, role: "connector" };
|
|
439
|
-
},
|
|
440
|
-
onEnd(endCtx, reason) {
|
|
441
|
-
scratch.open = false;
|
|
442
|
-
if (reason === "cancel") return;
|
|
443
|
-
const { port } = endAt(scratch.from, { x: endCtx.world.x, y: endCtx.world.y });
|
|
444
|
-
if (port === null) return;
|
|
445
|
-
commit(
|
|
446
|
-
{ from: scratch.from, to: port, router: routerKey },
|
|
447
|
-
{ ...endCtx, deps: scratch.deps }
|
|
448
|
-
);
|
|
449
|
-
}
|
|
450
|
-
};
|
|
451
|
-
}
|
|
452
|
-
},
|
|
453
|
-
enabled: () => true
|
|
454
|
-
};
|
|
455
|
-
}
|
|
456
|
-
function sceneParticipants(scene) {
|
|
457
|
-
return function* () {
|
|
458
|
-
for (const node of scene.renderOrderNodes()) {
|
|
459
|
-
yield {
|
|
460
|
-
node,
|
|
461
|
-
pose: effectivePose(scene, node)
|
|
462
|
-
};
|
|
463
|
-
}
|
|
464
|
-
};
|
|
465
|
-
}
|
|
466
|
-
function portLayer(participants, opts = {}) {
|
|
467
|
-
return composeAffordanceLayer(
|
|
468
|
-
opts.id ?? PORT_LAYER_ID,
|
|
469
|
-
"Diagram ports",
|
|
470
|
-
[createPortAffordance(participants, opts)]
|
|
471
|
-
);
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
// src/contribution.ts
|
|
475
|
-
function createDiagramContribution(opts) {
|
|
476
|
-
const actionId = opts.id ?? CONNECT_ACTION_ID;
|
|
477
|
-
return {
|
|
478
|
-
id: "weasel-diagram",
|
|
479
|
-
eligibility: { claimed: true },
|
|
480
|
-
actions: [createConnectAction({ ...opts, id: actionId }), grabPortAction()],
|
|
481
|
-
bindings: [connectBinding(actionId), ...grabPortBindings()]
|
|
482
|
-
};
|
|
483
|
-
}
|
|
484
|
-
function diagramPorts(opts) {
|
|
485
|
-
return {
|
|
486
|
-
layer: portLayer(opts.participants, opts),
|
|
487
|
-
contribution: createDiagramContribution(opts)
|
|
488
|
-
};
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
// src/groupTree.ts
|
|
492
|
-
function nestGroups(declared, isNode) {
|
|
493
|
-
const groupIds = new Set(declared.map((g) => g.id));
|
|
494
|
-
const parentOf = /* @__PURE__ */ new Map();
|
|
495
|
-
const claimedNodes = /* @__PURE__ */ new Set();
|
|
496
|
-
const direct = /* @__PURE__ */ new Map();
|
|
497
|
-
const isAncestorOrSelf = (candidate, of) => {
|
|
498
|
-
for (let at = of; at !== void 0; at = parentOf.get(at)) {
|
|
499
|
-
if (at === candidate) return true;
|
|
500
|
-
}
|
|
501
|
-
return false;
|
|
502
|
-
};
|
|
503
|
-
for (const group of declared) {
|
|
504
|
-
const own = [];
|
|
505
|
-
for (const id of group.members) {
|
|
506
|
-
if (isNode(id)) {
|
|
507
|
-
if (claimedNodes.has(id)) continue;
|
|
508
|
-
claimedNodes.add(id);
|
|
509
|
-
own.push(id);
|
|
510
|
-
} else if (groupIds.has(id) && !parentOf.has(id) && !isAncestorOrSelf(id, group.id)) {
|
|
511
|
-
parentOf.set(id, group.id);
|
|
512
|
-
own.push(id);
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
direct.set(group.id, own);
|
|
516
|
-
}
|
|
517
|
-
const flat = /* @__PURE__ */ new Map();
|
|
518
|
-
const flatten = (id) => {
|
|
519
|
-
const known = flat.get(id);
|
|
520
|
-
if (known !== void 0) return known;
|
|
521
|
-
const out = [];
|
|
522
|
-
for (const member of direct.get(id)) {
|
|
523
|
-
if (groupIds.has(member)) out.push(...flatten(member));
|
|
524
|
-
else out.push(member);
|
|
525
|
-
}
|
|
526
|
-
flat.set(id, out);
|
|
527
|
-
return out;
|
|
528
|
-
};
|
|
529
|
-
const live = new Set(declared.filter((g) => flatten(g.id).length > 0).map((g) => g.id));
|
|
530
|
-
return declared.filter((g) => live.has(g.id)).map((g) => {
|
|
531
|
-
const parent = parentOf.get(g.id);
|
|
532
|
-
return {
|
|
533
|
-
id: g.id,
|
|
534
|
-
direct: direct.get(g.id).filter((id) => !groupIds.has(id) || live.has(id)),
|
|
535
|
-
members: flat.get(g.id),
|
|
536
|
-
...parent !== void 0 ? { parent } : {}
|
|
537
|
-
};
|
|
538
|
-
});
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
// src/graph.ts
|
|
542
|
-
function buildGraph(source, opts = {}) {
|
|
543
|
-
const geometry = opts.geometry ?? AUTO_POSE_DESCRIPTOR$1;
|
|
544
|
-
const nodes = [];
|
|
545
|
-
const byId = /* @__PURE__ */ new Map();
|
|
546
|
-
const candidates = [];
|
|
547
|
-
const groupNodes = [];
|
|
548
|
-
for (const { node, pose } of source()) {
|
|
549
|
-
if (diagramEdgeOf(node) !== null) {
|
|
550
|
-
const deps = node.dependsOn;
|
|
551
|
-
if (Array.isArray(deps) && deps.length === 2) {
|
|
552
|
-
candidates.push({ id: node.id, from: deps[0], to: deps[1] });
|
|
553
|
-
}
|
|
554
|
-
continue;
|
|
555
|
-
}
|
|
556
|
-
const trait = diagramNodeOf(node, opts.read);
|
|
557
|
-
if (trait === null) continue;
|
|
558
|
-
if (trait.group !== void 0) {
|
|
559
|
-
const deps = node.dependsOn;
|
|
560
|
-
groupNodes.push({
|
|
561
|
-
id: node.id,
|
|
562
|
-
members: Array.isArray(deps) ? deps : [],
|
|
563
|
-
inset: insetOf(trait.group.inset)
|
|
564
|
-
});
|
|
565
|
-
continue;
|
|
566
|
-
}
|
|
567
|
-
const entry = {
|
|
568
|
-
id: node.id,
|
|
569
|
-
bounds: geometry.getBounds(pose),
|
|
570
|
-
pinned: trait.pinned === true
|
|
571
|
-
};
|
|
572
|
-
nodes.push(entry);
|
|
573
|
-
byId.set(entry.id, entry);
|
|
574
|
-
}
|
|
575
|
-
const insets = new Map(groupNodes.map((g) => [g.id, g.inset]));
|
|
576
|
-
const live = nestGroups(groupNodes, (id) => byId.has(id)).map((g) => {
|
|
577
|
-
for (const id of g.direct) {
|
|
578
|
-
const node = byId.get(id);
|
|
579
|
-
if (node !== void 0) node.group = g.id;
|
|
580
|
-
}
|
|
581
|
-
return {
|
|
582
|
-
id: g.id,
|
|
583
|
-
members: g.members,
|
|
584
|
-
inset: insets.get(g.id),
|
|
585
|
-
...g.parent !== void 0 ? { parent: g.parent } : {}
|
|
586
|
-
};
|
|
587
|
-
});
|
|
588
|
-
const known = (id) => byId.has(id) || live.some((g) => g.id === id);
|
|
589
|
-
const edges = expandGroupEdges(candidates.filter((e) => known(e.from) && known(e.to)), live);
|
|
590
|
-
const out = /* @__PURE__ */ new Map();
|
|
591
|
-
const into = /* @__PURE__ */ new Map();
|
|
592
|
-
for (const edge of edges) {
|
|
593
|
-
push(out, edge.from, edge);
|
|
594
|
-
push(into, edge.to, edge);
|
|
595
|
-
}
|
|
596
|
-
const NONE = Object.freeze([]);
|
|
597
|
-
return {
|
|
598
|
-
nodes,
|
|
599
|
-
edges,
|
|
600
|
-
groups: live,
|
|
601
|
-
node: (id) => byId.get(id),
|
|
602
|
-
outgoing: (id) => out.get(id) ?? NONE,
|
|
603
|
-
incoming: (id) => into.get(id) ?? NONE
|
|
604
|
-
};
|
|
605
|
-
}
|
|
606
|
-
function push(index, key, edge) {
|
|
607
|
-
const list = index.get(key);
|
|
608
|
-
if (list === void 0) index.set(key, [edge]);
|
|
609
|
-
else list.push(edge);
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
// src/dataGroups.ts
|
|
613
|
-
function resolveGroups(groups, ids, nodeIds, opts) {
|
|
614
|
-
for (const group of groups) {
|
|
615
|
-
if (ids.has(group.id)) throw new Error(`diagramScene: group id "${group.id}" is already a node or group id`);
|
|
616
|
-
ids.add(group.id);
|
|
617
|
-
}
|
|
618
|
-
const declared = new Map(groups.map((g) => [g.id, g]));
|
|
619
|
-
const nested = nestGroups(groups, (id) => nodeIds.has(id));
|
|
620
|
-
const depthOf = /* @__PURE__ */ new Map();
|
|
621
|
-
const byId = new Map(nested.map((g) => [g.id, g]));
|
|
622
|
-
const depth = (id) => {
|
|
623
|
-
const parent = byId.get(id).parent;
|
|
624
|
-
let d = depthOf.get(id);
|
|
625
|
-
if (d === void 0) depthOf.set(id, d = parent === void 0 ? 0 : depth(parent) + 1);
|
|
626
|
-
return d;
|
|
627
|
-
};
|
|
628
|
-
return nested.map((g, i) => ({ g, i })).sort((a, b) => depth(a.g.id) - depth(b.g.id) || a.i - b.i).map(({ g }) => {
|
|
629
|
-
const group = declared.get(g.id);
|
|
630
|
-
const pad = group.padding ?? opts.padding;
|
|
631
|
-
const title = group.label ? opts.title(group.label) : null;
|
|
632
|
-
const inset = { top: pad + (title ? title.height + pad / 2 : 0), right: pad, bottom: pad, left: pad };
|
|
633
|
-
return {
|
|
634
|
-
group,
|
|
635
|
-
direct: g.direct,
|
|
636
|
-
members: g.members,
|
|
637
|
-
inset,
|
|
638
|
-
pad,
|
|
639
|
-
title,
|
|
640
|
-
...g.parent !== void 0 ? { parent: g.parent } : {}
|
|
641
|
-
};
|
|
642
|
-
});
|
|
643
|
-
}
|
|
644
|
-
function groupSpecs(groups, placedAt, styleOf) {
|
|
645
|
-
const byId = new Map(groups.map((g) => [g.group.id, g]));
|
|
646
|
-
const boxes = /* @__PURE__ */ new Map();
|
|
647
|
-
const boxOf = (id) => {
|
|
648
|
-
const known = boxes.get(id);
|
|
649
|
-
if (known !== void 0) return known;
|
|
650
|
-
const g = byId.get(id);
|
|
651
|
-
const rs = g.direct.map((m) => byId.has(m) ? boxOf(m) : placedAt.get(m));
|
|
652
|
-
const x = Math.min(...rs.map((r) => r.x)) - g.inset.left;
|
|
653
|
-
const y = Math.min(...rs.map((r) => r.y)) - g.inset.top;
|
|
654
|
-
const box = {
|
|
655
|
-
x,
|
|
656
|
-
y,
|
|
657
|
-
width: Math.max(...rs.map((r) => r.x + r.width)) + g.inset.right - x,
|
|
658
|
-
height: Math.max(...rs.map((r) => r.y + r.height)) + g.inset.bottom - y
|
|
659
|
-
};
|
|
660
|
-
boxes.set(id, box);
|
|
661
|
-
return box;
|
|
662
|
-
};
|
|
663
|
-
const specs = [];
|
|
664
|
-
for (const g of groups) {
|
|
665
|
-
const { style, text } = styleOf(g.group);
|
|
666
|
-
const { x, y, width, height } = boxOf(g.group.id);
|
|
667
|
-
specs.push({
|
|
668
|
-
id: g.group.id,
|
|
669
|
-
kind: "leaf",
|
|
670
|
-
layer: "main",
|
|
671
|
-
pose: { x, y, width, height },
|
|
672
|
-
data: {
|
|
673
|
-
diagram: { outline: "rect", group: { inset: g.inset } },
|
|
674
|
-
fill: { paint: { color: style.fill } },
|
|
675
|
-
stroke: {
|
|
676
|
-
paint: { color: style.stroke },
|
|
677
|
-
width: style.strokeWidth ?? 1,
|
|
678
|
-
...style.dash ? { dash: style.dash } : {}
|
|
679
|
-
}
|
|
680
|
-
},
|
|
681
|
-
dependsOn: g.direct,
|
|
682
|
-
derivePose: GROUP_DERIVE_POSE
|
|
683
|
-
});
|
|
684
|
-
if (g.title === null || !g.group.label) continue;
|
|
685
|
-
const anchor = { at: { u: 0, v: 0 }, offset: { x: g.pad, y: g.pad } };
|
|
686
|
-
specs.push({
|
|
687
|
-
id: `${g.group.id}/label`,
|
|
688
|
-
kind: "leaf",
|
|
689
|
-
layer: "main",
|
|
690
|
-
pickable: false,
|
|
691
|
-
pose: { x: x + g.pad, y: y + g.pad, width: g.title.width, height: g.title.height },
|
|
692
|
-
data: { diagram: { anchor }, text: g.group.label, style: text, fill: { paint: { color: style.text } } },
|
|
693
|
-
dependsOn: [g.group.id],
|
|
694
|
-
derivePose: ANCHOR_DERIVE_POSE
|
|
695
|
-
});
|
|
696
|
-
}
|
|
697
|
-
return specs;
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
// src/fromData.ts
|
|
701
|
-
var FONT = { fontFamily: "sans-serif", fontSize: 12 };
|
|
702
|
-
var NODE = { fill: "#16222c", stroke: "#7ba7c7", text: "#dbe7f2" };
|
|
703
|
-
var EDGE = { stroke: "#7ba7c7" };
|
|
704
|
-
var GROUP = { fill: "rgba(123, 167, 199, 0.08)", stroke: "#7ba7c7", text: "#9fbdd6", strokeWidth: 1, dash: [4, 3] };
|
|
705
|
-
var LABEL_PLACEMENT = { at: "mid", offset: 9 };
|
|
706
|
-
var LINE = 1.4;
|
|
707
|
-
var GLYPH = 0.6;
|
|
708
|
-
var MIN_WIDTH = 64;
|
|
709
|
-
var LAYOUTS = { layered, tree, force };
|
|
710
|
-
var edgeIdOf = (e) => `edge:${e.from}${e.fromPort ? `.${e.fromPort}` : ""}->${e.to}${e.toPort ? `.${e.toPort}` : ""}${e.label ? `:${e.label}` : ""}`;
|
|
711
|
-
function graphOf(nodes, edges, groups = []) {
|
|
712
|
-
const byId = new Map(nodes.map((n) => [n.id, n]));
|
|
713
|
-
const out = /* @__PURE__ */ new Map();
|
|
714
|
-
const inc = /* @__PURE__ */ new Map();
|
|
715
|
-
for (const e of edges) {
|
|
716
|
-
out.set(e.from, [...out.get(e.from) ?? [], e]);
|
|
717
|
-
inc.set(e.to, [...inc.get(e.to) ?? [], e]);
|
|
718
|
-
}
|
|
719
|
-
return {
|
|
720
|
-
nodes,
|
|
721
|
-
edges,
|
|
722
|
-
groups,
|
|
723
|
-
node: (id) => byId.get(id),
|
|
724
|
-
outgoing: (id) => out.get(id) ?? [],
|
|
725
|
-
incoming: (id) => inc.get(id) ?? []
|
|
726
|
-
};
|
|
727
|
-
}
|
|
728
|
-
function diagramScene(data, opts = {}) {
|
|
729
|
-
const font = opts.font ?? FONT;
|
|
730
|
-
const styleOf = (s) => ({
|
|
731
|
-
fontFamily: s?.fontFamily ?? font.fontFamily,
|
|
732
|
-
fontSize: s?.fontSize ?? font.fontSize,
|
|
733
|
-
...s?.bold === true ? { fontWeight: 700 } : {}
|
|
734
|
-
});
|
|
735
|
-
const measure = opts.measure ? (text, s) => opts.measure(text, { ...s, fontFamily: s.fontFamily ?? font.fontFamily, fontSize: s.fontSize ?? font.fontSize }) : (text, s) => {
|
|
736
|
-
const size = s.fontSize ?? font.fontSize;
|
|
737
|
-
return { width: text.length * GLYPH * size, height: Math.ceil(LINE * size) };
|
|
738
|
-
};
|
|
739
|
-
const ids = /* @__PURE__ */ new Set();
|
|
740
|
-
for (const n of data.nodes) {
|
|
741
|
-
if (ids.has(n.id)) throw new Error(`diagramScene: node id "${n.id}" appears more than once`);
|
|
742
|
-
ids.add(n.id);
|
|
743
|
-
}
|
|
744
|
-
const nodeIds = new Set(ids);
|
|
745
|
-
const titleStyle = { ...font, fontSize: font.fontSize - 1 };
|
|
746
|
-
const groups = resolveGroups(data.groups ?? [], ids, nodeIds, {
|
|
747
|
-
padding: opts.groupPadding ?? DEFAULT_GROUP_INSET,
|
|
748
|
-
title: (text) => measure(text, titleStyle)
|
|
749
|
-
});
|
|
750
|
-
const seen = /* @__PURE__ */ new Set();
|
|
751
|
-
const edges = data.edges.filter((e) => {
|
|
752
|
-
const id = edgeIdOf(e);
|
|
753
|
-
if (seen.has(id) || !ids.has(e.from) || !ids.has(e.to)) return false;
|
|
754
|
-
seen.add(id);
|
|
755
|
-
return true;
|
|
756
|
-
});
|
|
757
|
-
const bodies = data.nodes.map((n) => ({
|
|
758
|
-
outline: n.outline ?? opts.outline ?? "rect",
|
|
759
|
-
rows: n.rows ?? (n.lines ?? []).map((text) => ({ kind: "label", text })),
|
|
760
|
-
padding: n.padding ?? opts.padding ?? 8,
|
|
761
|
-
gap: n.gap ?? opts.gap ?? 4,
|
|
762
|
-
verticalAlign: n.verticalAlign ?? opts.verticalAlign ?? "center",
|
|
763
|
-
...n.ports ? { ports: n.ports } : {}
|
|
764
|
-
}));
|
|
765
|
-
const seeds = data.nodes.map((n, i) => {
|
|
766
|
-
const pose = sizeToBody(
|
|
767
|
-
{ ...n.at ?? { x: i, y: i }, width: n.width ?? opts.minWidth ?? MIN_WIDTH, height: n.height ?? 0 },
|
|
768
|
-
measureBody(bodies[i], measure)
|
|
769
|
-
);
|
|
770
|
-
return { id: n.id, bounds: pose, pinned: n.pinned === true };
|
|
771
|
-
});
|
|
772
|
-
const graphGroups = groups.map((g) => ({
|
|
773
|
-
id: g.group.id,
|
|
774
|
-
members: g.members,
|
|
775
|
-
inset: g.inset,
|
|
776
|
-
...g.parent !== void 0 ? { parent: g.parent } : {}
|
|
777
|
-
}));
|
|
778
|
-
const seedOf = new Map(seeds.map((n) => [n.id, n]));
|
|
779
|
-
for (const g of groups) {
|
|
780
|
-
for (const m of g.direct) {
|
|
781
|
-
const seed = seedOf.get(m);
|
|
782
|
-
if (seed !== void 0) seed.group = g.group.id;
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
const graph = graphOf(
|
|
786
|
-
seeds,
|
|
787
|
-
expandGroupEdges(edges.map((e) => ({ id: edgeIdOf(e), from: e.from, to: e.to })), graphGroups),
|
|
788
|
-
graphGroups
|
|
789
|
-
);
|
|
790
|
-
const layout = typeof opts.layout === "function" ? opts.layout : opts.layout === "none" ? null : LAYOUTS[opts.layout ?? "layered"];
|
|
791
|
-
const moved = layout === null ? /* @__PURE__ */ new Map() : layout(graph, { order: "barycenter", ...opts.layoutOptions });
|
|
792
|
-
const specs = [];
|
|
793
|
-
const placedAt = new Map(data.nodes.map((n, i) => {
|
|
794
|
-
const seed = seeds[i].bounds;
|
|
795
|
-
const at = moved.get(n.id) ?? seed;
|
|
796
|
-
return [n.id, { x: at.x, y: at.y, width: seed.width, height: seed.height }];
|
|
797
|
-
}));
|
|
798
|
-
specs.push(...groupSpecs(groups, placedAt, (g) => ({
|
|
799
|
-
style: opts.groupStyle?.(g) ?? GROUP,
|
|
800
|
-
text: styleOf(titleStyle)
|
|
801
|
-
})));
|
|
802
|
-
for (const [i, n] of data.nodes.entries()) {
|
|
803
|
-
const seed = seeds[i].bounds;
|
|
804
|
-
const at = placedAt.get(n.id);
|
|
805
|
-
const style = opts.nodeStyle?.(n) ?? NODE;
|
|
806
|
-
const textData = (text, s) => ({ text, style: styleOf(s), fill: { paint: { color: style.text } } });
|
|
807
|
-
const built = buildBody(
|
|
808
|
-
bodies[i],
|
|
809
|
-
{ x: at.x, y: at.y, width: seed.width, height: seed.height },
|
|
810
|
-
{
|
|
811
|
-
id: n.id,
|
|
812
|
-
layer: "main",
|
|
813
|
-
measure,
|
|
814
|
-
body: (trait) => ({
|
|
815
|
-
diagram: n.pinned === true ? { ...trait, pinned: true } : trait,
|
|
816
|
-
fill: { paint: { color: style.fill } },
|
|
817
|
-
stroke: {
|
|
818
|
-
paint: { color: style.stroke },
|
|
819
|
-
width: style.strokeWidth ?? 2,
|
|
820
|
-
...style.dash ? { dash: style.dash } : {}
|
|
821
|
-
}
|
|
822
|
-
}),
|
|
823
|
-
row: (text, box) => text === "" ? null : textData(text, box.row.kind === "label" || box.row.kind === "field" ? box.row.style : void 0),
|
|
824
|
-
portLabel: (text) => textData(text, void 0)
|
|
825
|
-
}
|
|
826
|
-
);
|
|
827
|
-
for (const [k, s] of built.specs.entries()) {
|
|
828
|
-
specs.push({ ...s, ...k === 0 ? {} : { id: `${n.id}/${k}` } });
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
for (const e of edges) {
|
|
832
|
-
const id = edgeIdOf(e);
|
|
833
|
-
const style = opts.edgeStyle?.(e) ?? EDGE;
|
|
834
|
-
const markerStart = style.markerStart ?? null;
|
|
835
|
-
const markerEnd = style.markerEnd === void 0 ? "arrow" : style.markerEnd;
|
|
836
|
-
specs.push({
|
|
837
|
-
id,
|
|
838
|
-
kind: "leaf",
|
|
839
|
-
layer: "main",
|
|
840
|
-
pickable: false,
|
|
841
|
-
pose: { x: 0, y: 0, width: 0, height: 0 },
|
|
842
|
-
data: {
|
|
843
|
-
diagram: {
|
|
844
|
-
from: e.fromPort ? { port: e.fromPort } : {},
|
|
845
|
-
to: e.toPort ? { port: e.toPort } : {},
|
|
846
|
-
router: e.router ?? opts.router ?? "bezier",
|
|
847
|
-
...e.waypoints ? { waypoints: e.waypoints } : {}
|
|
848
|
-
},
|
|
849
|
-
stroke: {
|
|
850
|
-
paint: { color: style.stroke },
|
|
851
|
-
width: style.width ?? 1.5,
|
|
852
|
-
...style.dash ? { dash: style.dash } : {},
|
|
853
|
-
...markerStart !== null ? { markerStart } : {},
|
|
854
|
-
...markerEnd !== null ? { markerEnd } : {}
|
|
855
|
-
}
|
|
856
|
-
},
|
|
857
|
-
dependsOn: [e.from, e.to],
|
|
858
|
-
derivePath: EDGE_DERIVE_PATH
|
|
859
|
-
});
|
|
860
|
-
if (e.label) {
|
|
861
|
-
const labelStyle = { ...font, fontSize: font.fontSize - 2 };
|
|
862
|
-
specs.push({
|
|
863
|
-
id: `${id}/label`,
|
|
864
|
-
kind: "leaf",
|
|
865
|
-
layer: "main",
|
|
866
|
-
pickable: false,
|
|
867
|
-
pose: { x: 0, y: 0, width: measure(e.label, labelStyle).width, height: labelStyle.fontSize },
|
|
868
|
-
data: {
|
|
869
|
-
diagram: { label: e.labelPlacement ?? opts.labelPlacement ?? LABEL_PLACEMENT },
|
|
870
|
-
text: e.label,
|
|
871
|
-
style: labelStyle,
|
|
872
|
-
fill: { paint: { color: style.label ?? style.stroke } }
|
|
873
|
-
},
|
|
874
|
-
dependsOn: [id],
|
|
875
|
-
derivePose: LABEL_DERIVE_POSE
|
|
876
|
-
});
|
|
877
|
-
}
|
|
878
|
-
}
|
|
879
|
-
return specs;
|
|
880
|
-
}
|
|
881
|
-
var LAYOUTS2 = Object.freeze({
|
|
882
|
-
layered,
|
|
883
|
-
tree,
|
|
884
|
-
force
|
|
885
|
-
});
|
|
886
|
-
function applyLayout(scene, result, opts = {}) {
|
|
887
|
-
const poses = layoutPoses(scene, result, opts.geometry);
|
|
888
|
-
if (poses.size === 0) return 0;
|
|
889
|
-
scene.batch(opts.label ?? "Layout", () => {
|
|
890
|
-
for (const [id, pose] of poses) scene.setPose(id, pose);
|
|
891
|
-
});
|
|
892
|
-
let moved = 0;
|
|
893
|
-
for (const id of result.keys()) if (poses.has(id)) moved++;
|
|
894
|
-
return moved;
|
|
895
|
-
}
|
|
896
|
-
function layoutPoses(scene, result, geometry = AUTO_POSE_DESCRIPTOR) {
|
|
897
|
-
const out = /* @__PURE__ */ new Map();
|
|
898
|
-
for (const [id, at] of result) {
|
|
899
|
-
const node = scene.get(id);
|
|
900
|
-
if (node === void 0) continue;
|
|
901
|
-
const bounds = geometry.getBounds(node.pose);
|
|
902
|
-
const dx = at.x - bounds.x;
|
|
903
|
-
const dy = at.y - bounds.y;
|
|
904
|
-
if (dx === 0 && dy === 0) continue;
|
|
905
|
-
out.set(id, translatePoseViaDescriptor(node.pose, dx, dy, geometry));
|
|
906
|
-
for (const descendant of subtreeOf(scene, id)) {
|
|
907
|
-
const child = scene.get(descendant);
|
|
908
|
-
if (child === void 0) continue;
|
|
909
|
-
out.set(descendant, translatePoseViaDescriptor(child.pose, dx, dy, geometry));
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
return out;
|
|
913
|
-
}
|
|
914
|
-
function subtreeOf(scene, id) {
|
|
915
|
-
const out = [];
|
|
916
|
-
const stack = [...scene.childrenOf(id)];
|
|
917
|
-
while (stack.length > 0) {
|
|
918
|
-
const next = stack.pop();
|
|
919
|
-
out.push(next);
|
|
920
|
-
stack.push(...scene.childrenOf(next));
|
|
921
|
-
}
|
|
922
|
-
return out;
|
|
923
|
-
}
|
|
924
|
-
var LAYOUT_ACTION_ID = "diagram.layout";
|
|
925
|
-
function createLayoutAction(opts) {
|
|
926
|
-
const algorithm = typeof opts.algorithm === "function" ? opts.algorithm : LAYOUTS2[opts.algorithm ?? "layered"] ?? layered;
|
|
927
|
-
const build = {
|
|
928
|
-
...opts.read ? { read: opts.read } : {},
|
|
929
|
-
...opts.geometry ? { geometry: opts.geometry } : {}
|
|
930
|
-
};
|
|
931
|
-
return {
|
|
932
|
-
id: opts.id ?? LAYOUT_ACTION_ID,
|
|
933
|
-
label: opts.label ?? "Lay out diagram",
|
|
934
|
-
group: opts.group ?? "diagram",
|
|
935
|
-
requires: ["scene"],
|
|
936
|
-
invoker: {
|
|
937
|
-
timing: "immediate",
|
|
938
|
-
run: (deps) => {
|
|
939
|
-
const scene = deps["scene"];
|
|
940
|
-
if (scene === void 0) return;
|
|
941
|
-
const graph = buildGraph(opts.source, build);
|
|
942
|
-
applyLayout(scene, algorithm(graph, opts.layout), {
|
|
943
|
-
...opts.geometry ? { geometry: opts.geometry } : {},
|
|
944
|
-
...opts.label ? { label: opts.label } : {}
|
|
945
|
-
});
|
|
946
|
-
}
|
|
947
|
-
},
|
|
948
|
-
enabled: () => true
|
|
949
|
-
};
|
|
950
|
-
}
|
|
951
|
-
|
|
952
|
-
// src/live.ts
|
|
953
|
-
var EASE_FRAMES = 24;
|
|
954
|
-
var easeInOut = (t) => t < 0.5 ? 2 * t * t : 1 - (-2 * t + 2) ** 2 / 2;
|
|
955
|
-
var REST_DISTANCE = 0.1;
|
|
956
|
-
var REST_TICKS = 5;
|
|
957
|
-
function forceProducer(graph, opts = {}) {
|
|
958
|
-
const geometry = opts.geometry ?? AUTO_POSE_DESCRIPTOR;
|
|
959
|
-
const relaxation = forceRelaxation(graph, opts);
|
|
960
|
-
const dragAlpha = opts.dragAlpha ?? 0.3;
|
|
961
|
-
const restDistance = opts.restDistance ?? REST_DISTANCE;
|
|
962
|
-
const restTicks = opts.restTicks ?? REST_TICKS;
|
|
963
|
-
const was = relaxation.bodies.map((b) => ({ x: b.x, y: b.y }));
|
|
964
|
-
let still = 0;
|
|
965
|
-
return ({ pinned }) => {
|
|
966
|
-
for (const node of graph.nodes) {
|
|
967
|
-
const body = relaxation.byId.get(node.id);
|
|
968
|
-
if (body === void 0) continue;
|
|
969
|
-
const held = pinned.get(node.id);
|
|
970
|
-
if (held !== void 0) {
|
|
971
|
-
const bounds = geometry.getBounds(held);
|
|
972
|
-
body.fx = center(bounds, "x");
|
|
973
|
-
body.fy = center(bounds, "y");
|
|
974
|
-
} else if (!relaxation.pinned.has(node.id)) {
|
|
975
|
-
delete body.fx;
|
|
976
|
-
delete body.fy;
|
|
977
|
-
}
|
|
978
|
-
}
|
|
979
|
-
relaxation.sim.alphaTarget(pinned.size > 0 ? dragAlpha : 0);
|
|
980
|
-
relaxation.sim.tick();
|
|
981
|
-
let moved = 0;
|
|
982
|
-
relaxation.bodies.forEach((body, i) => {
|
|
983
|
-
const at = was[i];
|
|
984
|
-
moved = Math.max(moved, Math.hypot(body.x - at.x, body.y - at.y));
|
|
985
|
-
at.x = body.x;
|
|
986
|
-
at.y = body.y;
|
|
987
|
-
});
|
|
988
|
-
still = pinned.size === 0 && moved < restDistance ? still + 1 : 0;
|
|
989
|
-
const done = still >= restTicks || relaxation.sim.isSettled();
|
|
990
|
-
return { result: relaxation.placed(), done };
|
|
991
|
-
};
|
|
992
|
-
}
|
|
993
|
-
function easedProducer(graph, algorithm, opts = {}) {
|
|
994
|
-
const frames = opts.frames ?? EASE_FRAMES;
|
|
995
|
-
const easing = opts.easing ?? easeInOut;
|
|
996
|
-
const target = algorithm(graph, opts.layout);
|
|
997
|
-
const from = /* @__PURE__ */ new Map();
|
|
998
|
-
for (const node of graph.nodes) from.set(node.id, { x: node.bounds.x, y: node.bounds.y });
|
|
999
|
-
return ({ pinned, frame }) => {
|
|
1000
|
-
const t = Math.min((frame + 1) / frames, 1);
|
|
1001
|
-
const u = easing(t);
|
|
1002
|
-
const result = /* @__PURE__ */ new Map();
|
|
1003
|
-
for (const [id, to] of target) {
|
|
1004
|
-
if (pinned.has(id)) continue;
|
|
1005
|
-
const start = from.get(id);
|
|
1006
|
-
if (start === void 0) continue;
|
|
1007
|
-
result.set(id, {
|
|
1008
|
-
x: start.x + (to.x - start.x) * u,
|
|
1009
|
-
y: start.y + (to.y - start.y) * u
|
|
1010
|
-
});
|
|
1011
|
-
}
|
|
1012
|
-
return { result, done: t >= 1 };
|
|
1013
|
-
};
|
|
1014
|
-
}
|
|
1015
|
-
function shapeOf(graph) {
|
|
1016
|
-
const nodes = graph.nodes.map((n) => n.id).sort().join(",");
|
|
1017
|
-
const edges = graph.edges.map((e) => `${e.from}>${e.to}`).sort().join(",");
|
|
1018
|
-
return `${nodes}|${edges}`;
|
|
1019
|
-
}
|
|
1020
|
-
function useLiveLayout(opts) {
|
|
1021
|
-
const optsRef = useLatest(opts);
|
|
1022
|
-
const producer = useRef(null);
|
|
1023
|
-
const producerFrame = useRef(0);
|
|
1024
|
-
const shape = useRef("");
|
|
1025
|
-
const build = useCallback(() => {
|
|
1026
|
-
const o = optsRef.current;
|
|
1027
|
-
const graph = buildGraph(o.source, {
|
|
1028
|
-
...o.read ? { read: o.read } : {},
|
|
1029
|
-
...o.geometry ? { geometry: o.geometry } : {}
|
|
1030
|
-
});
|
|
1031
|
-
shape.current = shapeOf(graph);
|
|
1032
|
-
producerFrame.current = 0;
|
|
1033
|
-
const algorithm = o.algorithm ?? "force";
|
|
1034
|
-
if (algorithm === "force") {
|
|
1035
|
-
producer.current = forceProducer(graph, {
|
|
1036
|
-
...o.layout,
|
|
1037
|
-
...o.force,
|
|
1038
|
-
...o.geometry ? { geometry: o.geometry } : {}
|
|
1039
|
-
});
|
|
1040
|
-
return;
|
|
1041
|
-
}
|
|
1042
|
-
const fn = typeof algorithm === "function" ? algorithm : LAYOUTS2[algorithm] ?? layered;
|
|
1043
|
-
producer.current = easedProducer(graph, fn, {
|
|
1044
|
-
...o.layout ? { layout: o.layout } : {},
|
|
1045
|
-
...o.frames === void 0 ? {} : { frames: o.frames },
|
|
1046
|
-
...o.easing ? { easing: o.easing } : {}
|
|
1047
|
-
});
|
|
1048
|
-
}, [optsRef]);
|
|
1049
|
-
const step = useCallback((ctx) => {
|
|
1050
|
-
const o = optsRef.current;
|
|
1051
|
-
if (producer.current === null) return { poses: [], done: true };
|
|
1052
|
-
const { result, done } = producer.current({
|
|
1053
|
-
pinned: ctx.pinned,
|
|
1054
|
-
frame: producerFrame.current++
|
|
1055
|
-
});
|
|
1056
|
-
let placed = result;
|
|
1057
|
-
if (ctx.pinned.size > 0) {
|
|
1058
|
-
const held = new Map(result);
|
|
1059
|
-
for (const id of ctx.pinned.keys()) held.delete(id);
|
|
1060
|
-
placed = held;
|
|
1061
|
-
}
|
|
1062
|
-
const poses = layoutPoses(o.scene, placed, o.geometry);
|
|
1063
|
-
return { poses: [...poses], done };
|
|
1064
|
-
}, [optsRef]);
|
|
1065
|
-
const run = usePoseRun({
|
|
1066
|
-
scene: opts.scene,
|
|
1067
|
-
step,
|
|
1068
|
-
onCommit: (moved) => optsRef.current.onSettle?.(moved),
|
|
1069
|
-
...opts.label ? { label: opts.label } : {},
|
|
1070
|
-
...opts.requestFrame ? { requestFrame: opts.requestFrame } : {},
|
|
1071
|
-
...opts.cancelFrame ? { cancelFrame: opts.cancelFrame } : {}
|
|
1072
|
-
});
|
|
1073
|
-
useEffect(() => {
|
|
1074
|
-
if (opts.reheatOnGraphChange === false) return;
|
|
1075
|
-
return opts.scene.subscribe(() => {
|
|
1076
|
-
if (!run.isRunning()) return;
|
|
1077
|
-
const o = optsRef.current;
|
|
1078
|
-
const graph = buildGraph(o.source, {
|
|
1079
|
-
...o.read ? { read: o.read } : {},
|
|
1080
|
-
...o.geometry ? { geometry: o.geometry } : {}
|
|
1081
|
-
});
|
|
1082
|
-
if (shapeOf(graph) === shape.current) return;
|
|
1083
|
-
build();
|
|
1084
|
-
});
|
|
1085
|
-
}, [opts.scene, opts.reheatOnGraphChange, run, build, optsRef]);
|
|
1086
|
-
return useMemo(() => ({
|
|
1087
|
-
start() {
|
|
1088
|
-
if (run.isRunning()) return;
|
|
1089
|
-
build();
|
|
1090
|
-
run.start();
|
|
1091
|
-
},
|
|
1092
|
-
stop: () => {
|
|
1093
|
-
run.stop();
|
|
1094
|
-
},
|
|
1095
|
-
cancel: () => {
|
|
1096
|
-
run.cancel();
|
|
1097
|
-
},
|
|
1098
|
-
restart() {
|
|
1099
|
-
run.cancel();
|
|
1100
|
-
build();
|
|
1101
|
-
run.start();
|
|
1102
|
-
},
|
|
1103
|
-
isRunning: () => run.isRunning()
|
|
1104
|
-
}), [run, build]);
|
|
1105
|
-
}
|
|
1106
|
-
var EASED_LAYOUTS = Object.freeze({
|
|
1107
|
-
layered,
|
|
1108
|
-
tree
|
|
1109
|
-
});
|
|
1110
|
-
function diagramShape(opts = {}) {
|
|
1111
|
-
const geometry = opts.geometry ?? RECT_POSE_DESCRIPTOR;
|
|
1112
|
-
const outlineOf = (node) => diagramNodeOf(node, opts.read)?.outline;
|
|
1113
|
-
return {
|
|
1114
|
-
id: opts.id ?? "diagram:outline",
|
|
1115
|
-
matches: (node) => outlineOf(node) !== void 0,
|
|
1116
|
-
paint: (node, pose) => {
|
|
1117
|
-
const outline = outlineOf(node);
|
|
1118
|
-
if (outline === void 0) return [];
|
|
1119
|
-
const d = node.data;
|
|
1120
|
-
const box = geometry.getBounds(pose);
|
|
1121
|
-
const declaredFill = d.fill === void 0 ? opts.defaultFill : d.fill;
|
|
1122
|
-
const declaredStroke = d.stroke === void 0 ? opts.defaultStroke : d.stroke;
|
|
1123
|
-
const fill = declaredFill && commandFill(declaredFill, box);
|
|
1124
|
-
const stroke = declaredStroke && commandStroke(declaredStroke, box);
|
|
1125
|
-
return [{
|
|
1126
|
-
kind: "path",
|
|
1127
|
-
path: outlinePath(outline, box),
|
|
1128
|
-
...fill ? { fill } : {},
|
|
1129
|
-
...stroke ? { stroke } : {}
|
|
1130
|
-
}];
|
|
1131
|
-
},
|
|
1132
|
-
// The drawn boundary, so picking and clipping follow the diamond rather
|
|
1133
|
-
// than its bounding box.
|
|
1134
|
-
silhouette: (node, pose) => {
|
|
1135
|
-
const outline = outlineOf(node);
|
|
1136
|
-
return outline === void 0 ? null : outlinePath(outline, geometry.getBounds(pose));
|
|
1137
|
-
}
|
|
1138
|
-
};
|
|
1139
|
-
}
|
|
1140
|
-
function registerDiagramShape(opts = {}) {
|
|
1141
|
-
return registerNodeShape(diagramShape(opts), { priority: "high" });
|
|
1142
|
-
}
|
|
1143
|
-
function useMirroredSelection(selection, selected, onSelect) {
|
|
1144
|
-
const want = selected ?? null;
|
|
1145
|
-
const pushed = useRef(void 0);
|
|
1146
|
-
useEffect(() => {
|
|
1147
|
-
if (pushed.current === want) return;
|
|
1148
|
-
pushed.current = want;
|
|
1149
|
-
if ((selection.get()[0] ?? null) !== want) selection.set(want === null ? [] : [want]);
|
|
1150
|
-
}, [want]);
|
|
1151
|
-
const onSelectRef = useLatest(onSelect);
|
|
1152
|
-
return useCallback(() => {
|
|
1153
|
-
onSelectRef.current?.(selection.get()[0] ?? null);
|
|
1154
|
-
}, [selection, onSelectRef]);
|
|
1155
|
-
}
|
|
1156
|
-
var AMBIENT = [dragPanContribution()];
|
|
1157
|
-
var DiagramView = forwardRef(function DiagramView2(props, ref) {
|
|
1158
|
-
return /* @__PURE__ */ jsx(WeaselProvider, { children: /* @__PURE__ */ jsx(Inner, { ...props, apiRef: ref }) });
|
|
1159
|
-
});
|
|
1160
|
-
function Inner({
|
|
1161
|
-
specs,
|
|
1162
|
-
width,
|
|
1163
|
-
height,
|
|
1164
|
-
selected,
|
|
1165
|
-
onSelect,
|
|
1166
|
-
onMove,
|
|
1167
|
-
onConnect,
|
|
1168
|
-
enableKeybindings,
|
|
1169
|
-
canConnect,
|
|
1170
|
-
portOptions,
|
|
1171
|
-
live: liveOpts,
|
|
1172
|
-
minScale,
|
|
1173
|
-
maxScale,
|
|
1174
|
-
anchor,
|
|
1175
|
-
fitPadding,
|
|
1176
|
-
background,
|
|
1177
|
-
view,
|
|
1178
|
-
onViewChange,
|
|
1179
|
-
className,
|
|
1180
|
-
apiRef
|
|
1181
|
-
}) {
|
|
1182
|
-
useEffect(() => registerDiagramShape(), []);
|
|
1183
|
-
const registry = useMemo(() => withDiagramRegistry(), []);
|
|
1184
|
-
const scene = useScene({
|
|
1185
|
-
systemLayers: [{ id: "main" }],
|
|
1186
|
-
initial: specs,
|
|
1187
|
-
registry
|
|
1188
|
-
});
|
|
1189
|
-
const selection = useSelection({ scene, mode: "single" });
|
|
1190
|
-
const onClick = useMirroredSelection(selection, selected, onSelect);
|
|
1191
|
-
const onMoveRef = useLatest(onMove);
|
|
1192
|
-
const syncing = useRef(false);
|
|
1193
|
-
const applied = useRef(specs);
|
|
1194
|
-
const known = useRef(/* @__PURE__ */ new Map());
|
|
1195
|
-
useEffect(() => {
|
|
1196
|
-
const read = () => {
|
|
1197
|
-
const out = /* @__PURE__ */ new Map();
|
|
1198
|
-
for (const n of scene.renderOrderNodes()) {
|
|
1199
|
-
if (n.kind === "container") out.set(n.id, { x: n.pose.x, y: n.pose.y });
|
|
1200
|
-
}
|
|
1201
|
-
return out;
|
|
1202
|
-
};
|
|
1203
|
-
known.current = read();
|
|
1204
|
-
return scene.subscribe(() => {
|
|
1205
|
-
const now = read();
|
|
1206
|
-
const moves = [];
|
|
1207
|
-
if (!syncing.current) {
|
|
1208
|
-
for (const [id, at] of now) {
|
|
1209
|
-
const was = known.current.get(id);
|
|
1210
|
-
if (was !== void 0 && (was.x !== at.x || was.y !== at.y)) moves.push({ id, ...at });
|
|
1211
|
-
}
|
|
1212
|
-
}
|
|
1213
|
-
known.current = now;
|
|
1214
|
-
if (moves.length > 0) onMoveRef.current?.(moves);
|
|
1215
|
-
});
|
|
1216
|
-
}, [scene, onMoveRef]);
|
|
1217
|
-
useEffect(() => {
|
|
1218
|
-
if (applied.current === specs) return;
|
|
1219
|
-
syncing.current = true;
|
|
1220
|
-
try {
|
|
1221
|
-
reconcileSpecs(scene, applied.current, specs);
|
|
1222
|
-
} finally {
|
|
1223
|
-
syncing.current = false;
|
|
1224
|
-
}
|
|
1225
|
-
applied.current = specs;
|
|
1226
|
-
}, [scene, specs]);
|
|
1227
|
-
const source = useMemo(() => sceneParticipants(scene), [scene]);
|
|
1228
|
-
const [run, setRun] = useState(null);
|
|
1229
|
-
const live = useLiveLayout({ ...liveOpts, scene, source, algorithm: run?.algorithm ?? "force" });
|
|
1230
|
-
useEffect(() => {
|
|
1231
|
-
if (run !== null) live.restart();
|
|
1232
|
-
}, [run, live]);
|
|
1233
|
-
useImperativeHandle(apiRef, () => ({
|
|
1234
|
-
layout: (algorithm = "force") => setRun({ algorithm }),
|
|
1235
|
-
live
|
|
1236
|
-
}), [live]);
|
|
1237
|
-
const onConnectRef = useLatest(onConnect);
|
|
1238
|
-
const connecting = onConnect !== void 0;
|
|
1239
|
-
const ports = useMemo(() => connecting ? diagramPorts({
|
|
1240
|
-
...portOptions,
|
|
1241
|
-
participants: source,
|
|
1242
|
-
...canConnect ? { canConnect } : {},
|
|
1243
|
-
commit: ({ from, to }) => onConnectRef.current?.({
|
|
1244
|
-
from: from.nodeId,
|
|
1245
|
-
fromPort: from.id,
|
|
1246
|
-
to: to.nodeId,
|
|
1247
|
-
toPort: to.id
|
|
1248
|
-
})
|
|
1249
|
-
}) : null, [connecting, source, canConnect, portOptions, onConnectRef]);
|
|
1250
|
-
const canvasRef = useRef(null);
|
|
1251
|
-
useEffect(() => ports ? canvasRef.current?.registerLayer(ports.layer) : void 0, [ports]);
|
|
1252
|
-
const ambient = useMemo(() => ports ? [...AMBIENT, ports.contribution] : AMBIENT, [ports]);
|
|
1253
|
-
const controlled = view !== void 0;
|
|
1254
|
-
const defaultView = useMemo(
|
|
1255
|
-
() => controlled ? void 0 : fitDiagram(specs, { width, height }, { minScale, maxScale, anchor, padding: fitPadding }),
|
|
1256
|
-
// The fit is where the view opens; a later `specs` keeps the view it has.
|
|
1257
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1258
|
-
[controlled, width, height, minScale, maxScale, anchor, fitPadding]
|
|
1259
|
-
);
|
|
1260
|
-
return /* @__PURE__ */ jsx(
|
|
1261
|
-
SceneCanvas,
|
|
1262
|
-
{
|
|
1263
|
-
ref: canvasRef,
|
|
1264
|
-
scene,
|
|
1265
|
-
selection,
|
|
1266
|
-
features: onMove ? ["view", "pick", "move"] : ["view", "pick"],
|
|
1267
|
-
enableKeybindings: enableKeybindings ?? (onMove !== void 0 || onConnect !== void 0),
|
|
1268
|
-
ambient,
|
|
1269
|
-
onClick,
|
|
1270
|
-
width,
|
|
1271
|
-
height,
|
|
1272
|
-
className,
|
|
1273
|
-
...defaultView ? { defaultView } : {},
|
|
1274
|
-
...view ? { view } : {},
|
|
1275
|
-
...onViewChange ? { onViewChange } : {},
|
|
1276
|
-
...background ? { backgroundFill: background } : {}
|
|
1277
|
-
}
|
|
1278
|
-
);
|
|
1279
|
-
}
|
|
1280
|
-
function fitDiagram(specs, size, fit = {}) {
|
|
1281
|
-
const boxes = specs.filter((s) => s.kind === "container" || diagramGroupOf(s) !== null).map((s) => s.pose);
|
|
1282
|
-
if (boxes.length === 0) return void 0;
|
|
1283
|
-
const x = Math.min(...boxes.map((b) => b.x));
|
|
1284
|
-
const y = Math.min(...boxes.map((b) => b.y));
|
|
1285
|
-
const bounds = {
|
|
1286
|
-
x,
|
|
1287
|
-
y,
|
|
1288
|
-
width: Math.max(...boxes.map((b) => b.x + b.width)) - x,
|
|
1289
|
-
height: Math.max(...boxes.map((b) => b.y + b.height)) - y
|
|
1290
|
-
};
|
|
1291
|
-
return fitViewToBounds(bounds, size, { x: 0, y: 0, scale: { x: 1, y: 1 } }, {
|
|
1292
|
-
maxScale: fit.maxScale ?? 1,
|
|
1293
|
-
...fit.minScale !== void 0 ? { minScale: fit.minScale } : {},
|
|
1294
|
-
...fit.anchor !== void 0 ? { anchor: fit.anchor } : {},
|
|
1295
|
-
...fit.padding !== void 0 ? { padding: fit.padding } : {}
|
|
1296
|
-
});
|
|
1297
|
-
}
|
|
1298
|
-
|
|
1299
|
-
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, selfLoop, straight, useLiveLayout, useMirroredSelection, withDiagramRegistry };
|
|
1
|
+
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, LAYOUTS, LAYOUT_ACTION_ID, PORT_AFFORDANCE_KIND, PORT_LAYER_ID, ROUTERS, anchorDerivePose, applyLayout, bezier, boxAround, buildGraph, commitEdgeToScene, connectBinding, createConnectAction, createDiagramContribution, createLayoutAction, createPortAffordance, defaultCanConnect, diagramAnchorOf, diagramEdgeOf, diagramGroupOf, diagramLabelOf, diagramLayoutOf, diagramMarkOf, diagramPorts, diagramScene, diagramShape, easedProducer, edgeDerivePath, edgeIdOf, forceProducer, grabPortAction, grabPortBindings, groupDerivePose, labelDerivePose, layoutOwnsPort, layoutOwnsRoute, layoutPoses, markShape, orthogonal, portLayer, portScratchOf, registerDiagramShape, registerMarkShape, resolveEnd, routeWrites, routesOf, sceneParticipants, selfLoop, straight, useLiveLayout, useMirroredSelection, withDiagramRegistry } from './chunk-C4NAI6NV.js';
|
|
2
|
+
export { COMPASS, DEFAULT_GROUP_INSET, DEFAULT_NODE_GAP, DEFAULT_PORTS, DEFAULT_RANK_GAP, DIAGRAM_TRAIT_KEY, PORT_LABEL_GAP, anchorOffset, axesFor, backEdges, bodyOutline, bodyTrait, boxForContent, buildBody, canvasMeasure, chain, contentBox, createDiagramNodes, dataKeyReader, diagramNodeOf, expandGroupEdges, footprintNode, force, forceRelaxation, forestOf, graphOrder, insetOf, isPinned, layered, layeredPlan, layeredRoutes, layoutBody, layoutRowPorts, marginOf, measureBody, outlinePath, outlinePolyline, packAcross, packClusters, pinnedSet, planOf, plannerPlan, portLabelBox, portLabelsOf, portOf, portSitesOf, portsOf, portsOnOutline, rankGaps, ranksByLength, ranksOf, rayHit, seededOrder, settle, sizeToBody, translated, tree, untilSettled } from './chunk-NUMMW5BI.js';
|
|
1300
3
|
//# sourceMappingURL=index.js.map
|
|
1301
4
|
//# sourceMappingURL=index.js.map
|