@weasel-js/diagram 1.13.0 → 1.14.1
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 +122 -17
- package/dist/{chunk-Y72FK3Y7.js → chunk-5VNPLOIX.js} +94 -6
- package/dist/chunk-5VNPLOIX.js.map +1 -0
- package/dist/{chunk-ESFIS7BB.js → chunk-62SMY45E.js} +918 -574
- package/dist/chunk-62SMY45E.js.map +1 -0
- package/dist/{chunk-FAQXYGMX.js → chunk-TBG6DFB2.js} +3 -3
- package/dist/{chunk-FAQXYGMX.js.map → chunk-TBG6DFB2.js.map} +1 -1
- package/dist/{chunk-L4H444QI.js → chunk-VB6HUKNG.js} +659 -185
- package/dist/chunk-VB6HUKNG.js.map +1 -0
- package/dist/{chunk-QU4QZUC6.js → chunk-ZWXM5F4I.js} +141 -39
- package/dist/chunk-ZWXM5F4I.js.map +1 -0
- package/dist/{dataTypes-DW-MljuN.d.ts → dataTypes-BIkhAOc4.d.ts} +13 -2
- package/dist/effect-audio.d.ts +2 -1
- package/dist/effect-audio.js +6 -5
- package/dist/effect-audio.js.map +1 -1
- package/dist/effect.d.ts +46 -16
- package/dist/effect.js +257 -102
- package/dist/effect.js.map +1 -1
- package/dist/icons.table.js +262 -0
- package/dist/icons.table.js.map +1 -1
- package/dist/icons.table.json +262 -0
- package/dist/index.d.ts +491 -167
- package/dist/index.js +586 -36
- package/dist/index.js.map +1 -1
- package/dist/labelTrait-BBjRgxbz.d.ts +44 -0
- package/dist/layout.d.ts +4 -3
- package/dist/{layout.entry-CQoPNfzA.d.ts → layout.entry-Ca6pVvqo.d.ts} +23 -2
- package/dist/layout.js +2 -2
- package/dist/{stageTypes-DvnR8cKQ.d.ts → stageTypes-UiO25hcn.d.ts} +7 -0
- package/dist/{types-vbW21fHm.d.ts → types-B0n2CNTW.d.ts} +108 -81
- package/package.json +11 -9
- package/dist/chunk-ESFIS7BB.js.map +0 -1
- package/dist/chunk-L4H444QI.js.map +0 -1
- package/dist/chunk-QU4QZUC6.js.map +0 -1
- package/dist/chunk-Y72FK3Y7.js.map +0 -1
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { createAvoidRouter, placeLabels, edgeHops, DEFAULT_HOP_SIZE, HOPS_DOC_KEY, simplifyStops } from './chunk-
|
|
2
|
-
import { edgeRouter, registerMarkShape, withDiagramRegistry, diagramLabelOf, editEdgeLabelBinding, editEdgeLabelAction, diagramGroupOf, diagramContainerOf, headerBox, labelFollowsLayout, buildGraph, LABEL_DRAG_AFFORDANCE_KIND, EDIT_EDGE_LABEL_ACTION_ID, labelDragLayer, laneOutlineOf, routerOf, labelScratchOf, ROUTERS, avoid, LABEL_PLACEMENT, editableLabels, titleIdOf, orthogonal, diagramAnchorOf, MIN_LANE_SIZE,
|
|
3
|
-
import { diagramEdgeOf, isPromise,
|
|
4
|
-
import { bodyPredicate, dragPanContribution, WeaselProvider, useScene, useSelection, useLatest, SceneCanvas, useDepSource, registerNodeShape, reconcileSpecs,
|
|
1
|
+
import { createAvoidRouter, placeLabels, edgeHops, radial, DEFAULT_HOP_SIZE, HOPS_DOC_KEY, simplifyStops } from './chunk-5VNPLOIX.js';
|
|
2
|
+
import { edgeRouter, registerMarkShape, withDiagramRegistry, diagramLabelOf, editEdgeLabelBinding, editEdgeLabelAction, diagramGroupOf, diagramContainerOf, headerBox, labelFollowsLayout, buildGraph, LABEL_DRAG_AFFORDANCE_KIND, EDIT_EDGE_LABEL_ACTION_ID, labelDragLayer, laneOutlineOf, recordedRows, keyedIndex, boxIndex, boxesMeeting, routerOf, labelScratchOf, ROUTERS, avoid, LABEL_PLACEMENT, editableLabels, titleIdOf, orthogonal, diagramAnchorOf, MIN_LANE_SIZE, EDGE_DERIVE_PATH, titleInFrame, regionSpecs, DIAGRAM_THEME_KEY, diagramThemeOf, endAims, resolveEnd, themeLabelFamily, bodyBox, boxCenter, roleEdgeStyle, roleStyle, themeLabelFont, firstLanes, DEFAULT_LANE_SIZE } from './chunk-ZWXM5F4I.js';
|
|
3
|
+
import { diagramEdgeOf, isPromise, diagramNodeOf, portsOf, portSitesOf, outlinePath, outlineDetail, samePoints, layoutWritesRoute, plannerPlan, planOf, layered, dataKeyReader, layoutBody, forceRelaxation, center, layoutOwnsPort, chain, force, tree, portOf, labelCenter, labelSpotAt, isPinned, layoutOwnsRoute } from './chunk-VB6HUKNG.js';
|
|
4
|
+
import { bodyPredicate, dragPanContribution, WeaselProvider, useScene, useSelection, useLatest, SceneCanvas, useDepSource, RIGID_POSE_COMPOSITION, registerNodeShape, reconcileSpecs, pagesBounds, pagesLayer, AUTO_POSE_DESCRIPTOR, usePoseRun, drawnBoundsScene, pointAlongPath, scenePoseAdapter, drawnAABBOf, composeWorldPose, worldPosesOf, RECT_POSE_DESCRIPTOR, resolveFill, resolveStroke, commandFill, commandStroke, nodeGates, scenePoseFrame, fitViewToBounds, clampView, rebaseLocalPose, translatePoseViaDescriptor, carriedDescendants, sceneCarrySource, composeAffordanceLayer, createResizeAction, createDeleteAction, effectivePose, polylineFromPoints, SNAP_ROLES, ActionDisabledReason, refusalOf, snapMods, documentDepOf, definesFrame, paintsLayer, viewToTransform, ellipsePath, derivedDepOf, localizeWorldPoses, PathBuilder, startCarry, createInsertOp, createSetDataOp, documentPose } from '@weasel-js/core';
|
|
5
|
+
import { createSettledCache } from '@weasel-js/geom/nd';
|
|
5
6
|
import { AUTO_POSE_DESCRIPTOR as AUTO_POSE_DESCRIPTOR$1, rotatePoint } from '@weasel-js/core/math';
|
|
6
7
|
import { clampRectWithin, placeRect, pathContainsPoint } from '@weasel-js/geom';
|
|
7
|
-
import {
|
|
8
|
+
import { nudgeGap, moveItems, nearestGap, isNoopDrop } from '@weasel-js/headless';
|
|
8
9
|
import { forwardRef, useMemo, useEffect, useState, useRef, useCallback, useImperativeHandle, useLayoutEffect } from 'react';
|
|
10
|
+
import { useLatest as useLatest$1, adoptCssOnce } from '@weasel-js/react';
|
|
9
11
|
import '@weasel-js/animator';
|
|
10
12
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
11
13
|
|
|
@@ -50,34 +52,69 @@ function createPortAffordance(participants, opts = {}) {
|
|
|
50
52
|
}
|
|
51
53
|
};
|
|
52
54
|
};
|
|
55
|
+
const regionOf = (node, port) => ({
|
|
56
|
+
id: `${node.id}:${port.id}`,
|
|
57
|
+
targetId: null,
|
|
58
|
+
shape: { kind: "point", x: port.point.x, y: port.point.y, hitRadiusPx },
|
|
59
|
+
...withPaint(paintOf2(port)),
|
|
60
|
+
...opts.cursor !== void 0 ? { cursor: opts.cursor } : {},
|
|
61
|
+
strength,
|
|
62
|
+
claimedKinds: ["pointer"],
|
|
63
|
+
bind: () => ({
|
|
64
|
+
initialScratch: {
|
|
65
|
+
targetId: node.id,
|
|
66
|
+
nodeId: node.id,
|
|
67
|
+
portId: port.id,
|
|
68
|
+
port
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
});
|
|
72
|
+
const shown = (participant, state) => offersPorts(participant, state) && (opts.shows === void 0 || opts.shows(participant.node, state));
|
|
73
|
+
const showsPort = (port) => opts.showsPort === void 0 || opts.showsPort(port);
|
|
74
|
+
const generation = participants.generation;
|
|
75
|
+
const allPorts = () => {
|
|
76
|
+
const at = [];
|
|
77
|
+
for (const participant of participants()) {
|
|
78
|
+
for (const port of portsOf(participant.node, participant.pose, portOpts)) at.push({ participant, port });
|
|
79
|
+
}
|
|
80
|
+
return at;
|
|
81
|
+
};
|
|
82
|
+
const held = createSettledCache({ reads: 1 });
|
|
83
|
+
const portsAt = (key) => held.read(key, allPorts) ?? allPorts();
|
|
84
|
+
const regionAt = (entry) => entry.region ??= regionOf(entry.participant.node, entry.port);
|
|
85
|
+
const index = keyedIndex(generation ?? (() => void 0), () => {
|
|
86
|
+
const at = portsAt(generation());
|
|
87
|
+
return { at, boxes: boxIndex(at.map(({ port: { point: p } }) => [p.x, p.y, p.x, p.y])) };
|
|
88
|
+
});
|
|
89
|
+
const regions = (state) => {
|
|
90
|
+
const out = [];
|
|
91
|
+
if (generation !== void 0) {
|
|
92
|
+
for (const entry of portsAt(generation())) {
|
|
93
|
+
if (shown(entry.participant, state) && showsPort(entry.port)) out.push(regionAt(entry));
|
|
94
|
+
}
|
|
95
|
+
return out;
|
|
96
|
+
}
|
|
97
|
+
for (const participant of participants()) {
|
|
98
|
+
if (!shown(participant, state)) continue;
|
|
99
|
+
for (const port of portsOf(participant.node, participant.pose, portOpts)) {
|
|
100
|
+
if (showsPort(port)) out.push(regionOf(participant.node, port));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return out;
|
|
104
|
+
};
|
|
53
105
|
return {
|
|
54
106
|
id,
|
|
55
|
-
regions
|
|
107
|
+
regions,
|
|
108
|
+
regionsAt(state, wx, wy, view) {
|
|
109
|
+
const built = index();
|
|
110
|
+
const rx = hitRadiusPx / Math.abs(view.scale.x);
|
|
111
|
+
const ry = hitRadiusPx / Math.abs(view.scale.y);
|
|
112
|
+
const near2 = built && boxesMeeting(built.boxes, wx - rx, wy - ry, wx + rx, wy + ry);
|
|
113
|
+
if (!built || !near2) return regions(state);
|
|
56
114
|
const out = [];
|
|
57
|
-
for (const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (opts.shows !== void 0 && !opts.shows(node, state)) continue;
|
|
61
|
-
for (const port of portsOf(node, pose, portOpts)) {
|
|
62
|
-
if (opts.showsPort !== void 0 && !opts.showsPort(port)) continue;
|
|
63
|
-
out.push({
|
|
64
|
-
id: `${node.id}:${port.id}`,
|
|
65
|
-
targetId: null,
|
|
66
|
-
shape: { kind: "point", x: port.point.x, y: port.point.y, hitRadiusPx },
|
|
67
|
-
...withPaint(paintOf2(port)),
|
|
68
|
-
...opts.cursor !== void 0 ? { cursor: opts.cursor } : {},
|
|
69
|
-
strength,
|
|
70
|
-
claimedKinds: ["pointer"],
|
|
71
|
-
bind: () => ({
|
|
72
|
-
initialScratch: {
|
|
73
|
-
targetId: node.id,
|
|
74
|
-
nodeId: node.id,
|
|
75
|
-
portId: port.id,
|
|
76
|
-
port
|
|
77
|
-
}
|
|
78
|
-
})
|
|
79
|
-
});
|
|
80
|
-
}
|
|
115
|
+
for (const i of near2) {
|
|
116
|
+
const entry = built.at[i];
|
|
117
|
+
if (shown(entry.participant, state) && showsPort(entry.port)) out.push(regionAt(entry));
|
|
81
118
|
}
|
|
82
119
|
return out;
|
|
83
120
|
}
|
|
@@ -104,7 +141,28 @@ function bodyAt(participants, at, opts = {}) {
|
|
|
104
141
|
}
|
|
105
142
|
return hit;
|
|
106
143
|
}
|
|
144
|
+
function refusalMarks(box, at, reason) {
|
|
145
|
+
const out = [];
|
|
146
|
+
if (box !== null) {
|
|
147
|
+
const { x, y, width, height } = box;
|
|
148
|
+
out.push({ kind: "polyline", points: [{ x, y }, { x: x + width, y }, { x: x + width, y: y + height }, { x, y: y + height }, { x, y }], role: "refused" });
|
|
149
|
+
}
|
|
150
|
+
out.push({ kind: "reason", at: { x: at.x, y: at.y }, text: reason });
|
|
151
|
+
return out;
|
|
152
|
+
}
|
|
107
153
|
function createTargets(opts) {
|
|
154
|
+
const resolver = createResolver(opts);
|
|
155
|
+
const { landing, preview, boxOf } = resolver;
|
|
156
|
+
function overlays(route, fixed, at, view, moving = "to", waypoints = []) {
|
|
157
|
+
const points = preview(route, fixed, at, view, moving, waypoints);
|
|
158
|
+
const hit = landing(fixed, at, view);
|
|
159
|
+
const run = points.length < 2 ? [] : [{ kind: "polyline", points, role: hit?.refuse != null ? "refused" : "connector" }];
|
|
160
|
+
if (hit === null || hit.refuse === null) return run;
|
|
161
|
+
return [...run, ...refusalMarks(boxOf(hit.end.nodeId), at, hit.refuse)];
|
|
162
|
+
}
|
|
163
|
+
return { ...resolver, overlays };
|
|
164
|
+
}
|
|
165
|
+
function createResolver(opts) {
|
|
108
166
|
const portOpts = {
|
|
109
167
|
...opts.geometry ? { geometry: opts.geometry } : {},
|
|
110
168
|
...opts.read ? { read: opts.read } : {}
|
|
@@ -117,22 +175,27 @@ function createTargets(opts) {
|
|
|
117
175
|
if (!offersPorts(participant, view)) continue;
|
|
118
176
|
for (const port of portsOf(participant.node, participant.pose, portOpts)) {
|
|
119
177
|
if (opts.showsPort !== void 0 && !opts.showsPort(port)) continue;
|
|
120
|
-
if (!opts.canConnect(from, port)) continue;
|
|
121
178
|
const d2 = (port.point.x - at.x) ** 2 + (port.point.y - at.y) ** 2;
|
|
122
|
-
if (d2
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
179
|
+
if (d2 > bestD2) continue;
|
|
180
|
+
const verdict = opts.canConnect(from, port);
|
|
181
|
+
if (verdict === false) continue;
|
|
182
|
+
bestD2 = d2;
|
|
183
|
+
best = { end: port, refuse: refusalOf(verdict) };
|
|
126
184
|
}
|
|
127
185
|
}
|
|
128
186
|
return best;
|
|
129
187
|
}
|
|
130
|
-
function
|
|
188
|
+
function landing(from, at, view) {
|
|
131
189
|
const port = portTarget(from, at, view);
|
|
132
190
|
if (port !== null || !opts.bodies) return port;
|
|
133
191
|
const body = bodyAt(opts.participants, at, { ...portOpts, view });
|
|
134
192
|
if (body === null || portsOf(body.participant.node, body.participant.pose, portOpts).length === 0) return null;
|
|
135
|
-
|
|
193
|
+
const verdict = opts.canConnect(from, body.end);
|
|
194
|
+
return verdict === false ? null : { end: body.end, refuse: refusalOf(verdict) };
|
|
195
|
+
}
|
|
196
|
+
function target(from, at, view) {
|
|
197
|
+
const hit = landing(from, at, view);
|
|
198
|
+
return hit === null || hit.refuse !== null ? null : hit.end;
|
|
136
199
|
}
|
|
137
200
|
function participantOf(nodeId) {
|
|
138
201
|
for (const participant of opts.participants()) {
|
|
@@ -153,7 +216,7 @@ function createTargets(opts) {
|
|
|
153
216
|
return resolveEnd({}, dep, toward2, portOpts, all) ?? null;
|
|
154
217
|
}
|
|
155
218
|
function preview(route, fixed, at, view, moving = "to", waypoints = []) {
|
|
156
|
-
const landed =
|
|
219
|
+
const landed = landing(fixed, at, view)?.end ?? null;
|
|
157
220
|
const [fixedCenter, landedCenter] = [centerOf(fixed), landed === null ? at : centerOf(landed)];
|
|
158
221
|
const aims = moving === "to" ? endAims(fixedCenter, landedCenter, waypoints) : endAims(landedCenter, fixedCenter, waypoints);
|
|
159
222
|
const [fixedAim, landedAim] = moving === "to" ? [aims.from, aims.to] : [aims.to, aims.from];
|
|
@@ -162,7 +225,11 @@ function createTargets(opts) {
|
|
|
162
225
|
if (free === null || anchored === null) return [];
|
|
163
226
|
return moving === "to" ? route({ from: anchored, to: free, waypoints }) : route({ from: free, to: anchored, waypoints });
|
|
164
227
|
}
|
|
165
|
-
|
|
228
|
+
function boxOf(nodeId) {
|
|
229
|
+
const participant = participantOf(nodeId);
|
|
230
|
+
return participant === void 0 ? null : geometry.getBounds(participant.pose);
|
|
231
|
+
}
|
|
232
|
+
return { target, landing, portFor, preview, boxOf };
|
|
166
233
|
}
|
|
167
234
|
var CONNECT_ACTION_ID = "diagram.connect";
|
|
168
235
|
var defaultCanConnect = (from, to) => {
|
|
@@ -266,66 +333,513 @@ function createConnectAction(opts) {
|
|
|
266
333
|
scratch.current = { x: moveCtx.world.x, y: moveCtx.world.y };
|
|
267
334
|
},
|
|
268
335
|
overlay() {
|
|
269
|
-
if (!scratch.open) return null;
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
336
|
+
if (!scratch.open) return null;
|
|
337
|
+
return targets.overlays(route, scratch.from, scratch.current, scratch.deps.view);
|
|
338
|
+
},
|
|
339
|
+
onEnd(endCtx, reason) {
|
|
340
|
+
scratch.open = false;
|
|
341
|
+
if (reason === "cancel") return;
|
|
342
|
+
const to = targets.target(scratch.from, { x: endCtx.world.x, y: endCtx.world.y }, scratch.deps.view);
|
|
343
|
+
if (to === null) return;
|
|
344
|
+
commit(
|
|
345
|
+
{ from: scratch.from, to, router: routerKey },
|
|
346
|
+
{ ...endCtx, deps: scratch.deps }
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
enabled: () => true
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// src/containers/canDropInto.ts
|
|
357
|
+
function diagramCanDropInto(scene, canReparent) {
|
|
358
|
+
const nodeOf = (id) => scene.get(id);
|
|
359
|
+
const roleOf = (id) => {
|
|
360
|
+
const node = nodeOf(id);
|
|
361
|
+
return node === void 0 ? null : diagramContainerOf(node)?.role ?? null;
|
|
362
|
+
};
|
|
363
|
+
const attached = (id) => {
|
|
364
|
+
const node = nodeOf(id);
|
|
365
|
+
return node !== void 0 && (diagramEdgeOf(node) !== null || diagramLabelOf(node) !== null);
|
|
366
|
+
};
|
|
367
|
+
const allowed = (parent, ids) => {
|
|
368
|
+
const node = nodeOf(parent);
|
|
369
|
+
if (node === void 0 || ids.some(attached)) return false;
|
|
370
|
+
const role = roleOf(parent);
|
|
371
|
+
if (role === "pool") return ids.every((id) => roleOf(id) === "lane");
|
|
372
|
+
if (ids.some((id) => roleOf(id) === "lane")) return false;
|
|
373
|
+
if (role !== null) return ids.every((id) => roleOf(id) !== "pool");
|
|
374
|
+
return !attached(parent) && dataKeyReader(node) === null;
|
|
375
|
+
};
|
|
376
|
+
return (parent, ids) => {
|
|
377
|
+
if (!allowed(parent, ids)) return false;
|
|
378
|
+
if (canReparent === void 0 || ids.every((id) => nodeOf(id)?.parent === parent)) return true;
|
|
379
|
+
return canReparent(ids, parent);
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
function diagramDropParentAt(scene, point, role) {
|
|
383
|
+
if (role === "pool" || role === "lane") return null;
|
|
384
|
+
let best = null;
|
|
385
|
+
const compose = scene.composition?.compose;
|
|
386
|
+
const walk = (ids, frame) => {
|
|
387
|
+
for (const id of ids) {
|
|
388
|
+
const node = scene.get(id);
|
|
389
|
+
if (node === void 0) continue;
|
|
390
|
+
const pose = node.pose;
|
|
391
|
+
const world = frame === null || compose === void 0 ? pose : compose(frame, pose);
|
|
392
|
+
const trait = diagramContainerOf(node);
|
|
393
|
+
const b = bodyBox(world, trait?.header);
|
|
394
|
+
const inside2 = point.x >= b.x && point.x < b.x + b.width && point.y >= b.y && point.y < b.y + b.height;
|
|
395
|
+
if (!inside2) continue;
|
|
396
|
+
if (trait?.role === "container" || trait?.role === "lane") best = id;
|
|
397
|
+
walk(scene.childrenOf(id), world);
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
walk(scene.roots, null);
|
|
401
|
+
return best;
|
|
402
|
+
}
|
|
403
|
+
function packed(items, spans, order) {
|
|
404
|
+
const gaps = items.slice(1).map((m, i) => spans.get(m).start - (spans.get(items[i]).start + spans.get(items[i]).size));
|
|
405
|
+
const out = /* @__PURE__ */ new Map();
|
|
406
|
+
let at = items.length > 0 ? spans.get(items[0]).start : 0;
|
|
407
|
+
order.forEach((m, i) => {
|
|
408
|
+
out.set(m, at);
|
|
409
|
+
at += spans.get(m).size + (gaps[i] ?? gaps[gaps.length - 1] ?? 0);
|
|
410
|
+
});
|
|
411
|
+
return out;
|
|
412
|
+
}
|
|
413
|
+
var carriedBy = (item) => item.carries ?? [item.id];
|
|
414
|
+
var RELEASE_MS = 500;
|
|
415
|
+
function slideOthers(scene, slots, carried) {
|
|
416
|
+
const { axis } = slots;
|
|
417
|
+
const size = axis === "x" ? "width" : "height";
|
|
418
|
+
const ids = slots.items.map((i) => i.id);
|
|
419
|
+
const spans = new Map(slots.items.map((i) => [i.id, { start: i.bounds[axis], size: i.bounds[size] }]));
|
|
420
|
+
const nodes = slots.items.map((item) => ({
|
|
421
|
+
id: item.id,
|
|
422
|
+
poses: carriedBy(item).flatMap((id) => {
|
|
423
|
+
const node = scene.get(id);
|
|
424
|
+
return node === void 0 || node.derivePose !== void 0 ? [] : [{ id, pose: effectivePose(scene, node) }];
|
|
425
|
+
})
|
|
426
|
+
}));
|
|
427
|
+
const hold = scene.overrides.hold();
|
|
428
|
+
return {
|
|
429
|
+
/** The items standing in `order`, or as they were for `null`, with the carried one `by` along. */
|
|
430
|
+
show(order, by) {
|
|
431
|
+
const starts = packed(ids, spans, order ?? ids);
|
|
432
|
+
hold.set(nodes.flatMap(({ id, poses }) => {
|
|
433
|
+
const d = id === carried ? by : starts.get(id) - spans.get(id).start;
|
|
434
|
+
return poses.map(({ id: node, pose }) => [node, { ...pose, [axis]: pose[axis] + d }]);
|
|
435
|
+
}));
|
|
436
|
+
},
|
|
437
|
+
drop: () => hold.drop(),
|
|
438
|
+
/** Held until the host's new specs land, so the items do not jump back for
|
|
439
|
+
* the frames in between; a host that keeps the old order gets it back shortly after. */
|
|
440
|
+
keep() {
|
|
441
|
+
const release = () => {
|
|
442
|
+
stop();
|
|
443
|
+
clearTimeout(timer);
|
|
444
|
+
hold.drop();
|
|
445
|
+
};
|
|
446
|
+
const stop = scene.subscribe(release);
|
|
447
|
+
const timer = setTimeout(release, RELEASE_MS);
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// src/reorder.ts
|
|
453
|
+
var REORDER_ACTION_ID = "diagram.reorder";
|
|
454
|
+
var END_GAP = 24;
|
|
455
|
+
var inside = (p, b) => p.x >= b.x && p.x <= b.x + b.width && p.y >= b.y && p.y <= b.y + b.height;
|
|
456
|
+
function sequenceSlots(axis, items) {
|
|
457
|
+
const [along2, across, size, depth] = axis === "x" ? ["x", "y", "width", "height"] : ["y", "x", "height", "width"];
|
|
458
|
+
const lo = (b) => b[along2];
|
|
459
|
+
const hi = (b) => b[along2] + b[size];
|
|
460
|
+
const bar = (at, boxes2) => {
|
|
461
|
+
const from = Math.min(...boxes2.map((b) => b[across]));
|
|
462
|
+
const to = Math.max(...boxes2.map((b) => b[across] + b[depth]));
|
|
463
|
+
const point = (c) => axis === "x" ? { x: at, y: c } : { x: c, y: at };
|
|
464
|
+
return { a: point(from), b: point(to) };
|
|
465
|
+
};
|
|
466
|
+
const boxes = items.map((i) => i.bounds);
|
|
467
|
+
const halves = boxes.slice(1).map((b, k) => (lo(b) - hi(boxes[k])) / 2);
|
|
468
|
+
const gaps = [];
|
|
469
|
+
if (boxes.length === 0) return { axis, items, gaps };
|
|
470
|
+
gaps.push({ index: 0, ...bar(lo(boxes[0]) - (halves[0] ?? END_GAP / 2), [boxes[0]]) });
|
|
471
|
+
for (let k = 1; k < boxes.length; k++) gaps.push({ index: k, ...bar(hi(boxes[k - 1]) + halves[k - 1], [boxes[k - 1], boxes[k]]) });
|
|
472
|
+
const last = boxes[boxes.length - 1];
|
|
473
|
+
gaps.push({ index: boxes.length, ...bar(hi(last) + (halves[halves.length - 1] ?? END_GAP / 2), [last]) });
|
|
474
|
+
return { axis, items, gaps };
|
|
475
|
+
}
|
|
476
|
+
function pickAt(slots, at) {
|
|
477
|
+
const from = slots?.items.findIndex((i) => i.grip !== null && inside(at, i.grip ?? i.bounds)) ?? -1;
|
|
478
|
+
return from < 0 ? null : { from, item: slots.items[from] };
|
|
479
|
+
}
|
|
480
|
+
function gapAt(held, by) {
|
|
481
|
+
const { axis, gaps } = held.slots;
|
|
482
|
+
const size = axis === "x" ? "width" : "height";
|
|
483
|
+
const mid = held.item.bounds[axis] + held.item.bounds[size] / 2 + by;
|
|
484
|
+
const best = gaps[nearestGap(gaps.map((g) => g.a[axis]), mid)];
|
|
485
|
+
return best === void 0 || isNoopDrop([held.from], best.index) ? null : best;
|
|
486
|
+
}
|
|
487
|
+
function moveOf(held, gap) {
|
|
488
|
+
const order = moveItems(held.slots.items.map((i) => i.id), [held.from], gap.index);
|
|
489
|
+
return { id: held.item.id, from: held.from, to: gap.index, order };
|
|
490
|
+
}
|
|
491
|
+
function createReorderAction(opts) {
|
|
492
|
+
const { canDrop = () => true, show = "bar" } = opts;
|
|
493
|
+
return {
|
|
494
|
+
id: opts.id ?? REORDER_ACTION_ID,
|
|
495
|
+
label: opts.label ?? "Reorder",
|
|
496
|
+
group: "diagram",
|
|
497
|
+
requires: ["scene", ...opts.requires ?? []],
|
|
498
|
+
cursor: "grab",
|
|
499
|
+
activeCursor: "grabbing",
|
|
500
|
+
enabled: (deps, at) => deps === void 0 || at === void 0 || pickAt(opts.slots(deps, at), at) !== null ? true : ActionDisabledReason.NotApplicable,
|
|
501
|
+
invoker: {
|
|
502
|
+
timing: "ongoing",
|
|
503
|
+
start(ctx) {
|
|
504
|
+
const start = { x: (ctx.drag?.start ?? ctx.world).x, y: (ctx.drag?.start ?? ctx.world).y };
|
|
505
|
+
const slots = opts.slots(ctx.deps, start);
|
|
506
|
+
const picked = pickAt(slots, start);
|
|
507
|
+
if (slots === null || picked === null) return {};
|
|
508
|
+
const held = { slots, ...picked };
|
|
509
|
+
const { axis } = slots;
|
|
510
|
+
const region = opts.region?.(ctx.deps, held.item) ?? null;
|
|
511
|
+
const leaves = (at) => region !== null && !inside(at, region);
|
|
512
|
+
let by = ctx.world[axis] - start[axis];
|
|
513
|
+
let open = true;
|
|
514
|
+
const verdicts = /* @__PURE__ */ new Map();
|
|
515
|
+
const landing = (gap) => {
|
|
516
|
+
if (gap === null) return null;
|
|
517
|
+
if (!verdicts.has(gap.index)) {
|
|
518
|
+
const move = moveOf(held, gap);
|
|
519
|
+
verdicts.set(gap.index, canDrop(move) ? move : null);
|
|
520
|
+
}
|
|
521
|
+
return verdicts.get(gap.index);
|
|
522
|
+
};
|
|
523
|
+
const finish = (endCtx, reason) => {
|
|
524
|
+
open = false;
|
|
525
|
+
by = endCtx.world[axis] - start[axis];
|
|
526
|
+
const move = reason === "commit" ? landing(gapAt(held, by)) : null;
|
|
527
|
+
if (move !== null) opts.commit(move, { ...endCtx, deps: ctx.deps });
|
|
528
|
+
return move;
|
|
529
|
+
};
|
|
530
|
+
if (show === "slide") {
|
|
531
|
+
const slide = ctx.deps.scene === void 0 ? null : slideOthers(ctx.deps.scene, held.slots, held.item.id);
|
|
532
|
+
return {
|
|
533
|
+
kind: "diagram-reorder",
|
|
534
|
+
onMove(moveCtx) {
|
|
535
|
+
if (leaves(moveCtx.world)) return "pass";
|
|
536
|
+
by = moveCtx.world[axis] - start[axis];
|
|
537
|
+
slide?.show(landing(gapAt(held, by))?.order ?? null, by);
|
|
538
|
+
},
|
|
539
|
+
onEnd(endCtx, reason) {
|
|
540
|
+
const move = finish(endCtx, reason);
|
|
541
|
+
if (move === null) slide?.drop();
|
|
542
|
+
else slide?.keep();
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
const carried = carriedBy(held.item);
|
|
547
|
+
const poses = /* @__PURE__ */ new Map();
|
|
548
|
+
for (const id of carried) {
|
|
549
|
+
const pose = ctx.deps.scene?.get(id)?.pose;
|
|
550
|
+
if (pose !== void 0) poses.set(id, pose);
|
|
551
|
+
}
|
|
552
|
+
return {
|
|
553
|
+
kind: "diagram-reorder",
|
|
554
|
+
onMove(moveCtx) {
|
|
555
|
+
if (leaves(moveCtx.world)) return "pass";
|
|
556
|
+
by = moveCtx.world[axis] - start[axis];
|
|
557
|
+
},
|
|
558
|
+
previewIds: () => open && poses.size > 0 ? [...poses.keys()] : null,
|
|
559
|
+
previewPose: (id) => {
|
|
560
|
+
const pose = poses.get(id);
|
|
561
|
+
return pose === void 0 ? null : { ...pose, [axis]: pose[axis] + by };
|
|
562
|
+
},
|
|
563
|
+
previewOpaqueIds: () => [...carried],
|
|
564
|
+
overlay() {
|
|
565
|
+
const gap = open ? gapAt(held, by) : null;
|
|
566
|
+
if (gap === null) return null;
|
|
567
|
+
return { kind: "polyline", points: [gap.a, gap.b], role: landing(gap) !== null ? "insert" : "refused" };
|
|
568
|
+
},
|
|
569
|
+
onEnd(endCtx, reason) {
|
|
570
|
+
finish(endCtx, reason);
|
|
571
|
+
}
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
var ANYWHERE = bodyPredicate(() => true);
|
|
578
|
+
function reorderBinding(actionId = REORDER_ACTION_ID, on = ANYWHERE) {
|
|
579
|
+
return { spec: { kind: "drag", target: { kindOf: on } }, actionId, opts: { id: on === ANYWHERE ? `${actionId}.drag` : `${actionId}.bodyDrag` } };
|
|
580
|
+
}
|
|
581
|
+
function reorderContribution(opts) {
|
|
582
|
+
const actionId = opts.id ?? REORDER_ACTION_ID;
|
|
583
|
+
return {
|
|
584
|
+
id: `weasel-diagram-reorder:${actionId}`,
|
|
585
|
+
eligibility: { always: true },
|
|
586
|
+
actions: [createReorderAction({ ...opts, id: actionId })],
|
|
587
|
+
bindings: [reorderBinding(actionId, opts.on)]
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
function worldRectOf(scene, id) {
|
|
591
|
+
const node = scene.get(id);
|
|
592
|
+
if (node === void 0) return null;
|
|
593
|
+
const drawn = drawnBoundsScene(scene);
|
|
594
|
+
const route = diagramEdgeOf(node) === null ? null : drawn.derivedPathOf?.(id) ?? null;
|
|
595
|
+
const mid = route === null ? null : pointAlongPath(route, 0.5);
|
|
596
|
+
if (mid != null) return { x: mid.point.x, y: mid.point.y, width: 0, height: 0 };
|
|
597
|
+
const live = { ...scenePoseAdapter(scene), getPose: (at) => effectivePose(scene, scene.get(at)) };
|
|
598
|
+
return drawnAABBOf(drawn, id, composeWorldPose(live, id, scene.composition.compose), { includeStroke: false });
|
|
599
|
+
}
|
|
600
|
+
function screenRectOf(rect, view) {
|
|
601
|
+
return {
|
|
602
|
+
x: (rect.x - view.x) * view.scale.x,
|
|
603
|
+
y: (rect.y - view.y) * view.scale.y,
|
|
604
|
+
width: rect.width * view.scale.x,
|
|
605
|
+
height: rect.height * view.scale.y
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
var CENTER = "center";
|
|
609
|
+
function place(el, overlay, anchor, view) {
|
|
610
|
+
const size = { width: el.offsetWidth, height: el.offsetHeight };
|
|
611
|
+
const placement = overlay.placement ?? CENTER;
|
|
612
|
+
const at = placement === CENTER ? { x: anchor.x + (anchor.width - size.width) / 2, y: anchor.y + (anchor.height - size.height) / 2 } : clampRectWithin(placeRect({ anchor, overlay: size, boundary: view, placement, offset: overlay.offset ?? 0 }).rect, view);
|
|
613
|
+
el.style.transform = `translate(${Math.round(at.x)}px, ${Math.round(at.y)}px)`;
|
|
614
|
+
}
|
|
615
|
+
function useOverlayPlacement(canvasRef, scene, overlays) {
|
|
616
|
+
const elements = useRef(/* @__PURE__ */ new Map());
|
|
617
|
+
const current = useLatest$1(overlays);
|
|
618
|
+
const placeAll = useCallback(() => {
|
|
619
|
+
const canvas = canvasRef.current;
|
|
620
|
+
const surface = canvas?.element;
|
|
621
|
+
if (canvas == null || surface == null) return;
|
|
622
|
+
const view = canvas.getView();
|
|
623
|
+
for (const overlay of current.current) {
|
|
624
|
+
const el = elements.current.get(overlay.key ?? overlay.target);
|
|
625
|
+
if (el === void 0) continue;
|
|
626
|
+
const world = worldRectOf(scene, overlay.target);
|
|
627
|
+
el.hidden = world === null;
|
|
628
|
+
if (world === null) continue;
|
|
629
|
+
const rect = screenRectOf(world, view);
|
|
630
|
+
const anchor = { ...rect, x: rect.x + surface.offsetLeft, y: rect.y + surface.offsetTop };
|
|
631
|
+
place(el, overlay, anchor, {
|
|
632
|
+
x: surface.offsetLeft,
|
|
633
|
+
y: surface.offsetTop,
|
|
634
|
+
width: surface.offsetWidth,
|
|
635
|
+
height: surface.offsetHeight
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
}, [canvasRef, scene, current]);
|
|
639
|
+
useLayoutEffect(() => {
|
|
640
|
+
placeAll();
|
|
641
|
+
const canvas = canvasRef.current;
|
|
642
|
+
if (canvas == null) return;
|
|
643
|
+
const stop = canvas.subscribeFrame(placeAll);
|
|
644
|
+
const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(placeAll);
|
|
645
|
+
for (const el of elements.current.values()) observer?.observe(el);
|
|
646
|
+
return () => {
|
|
647
|
+
stop();
|
|
648
|
+
observer?.disconnect();
|
|
649
|
+
};
|
|
650
|
+
}, [canvasRef, placeAll, overlays]);
|
|
651
|
+
const refs = useRef(/* @__PURE__ */ new Map());
|
|
652
|
+
return useCallback((key) => {
|
|
653
|
+
let ref = refs.current.get(key);
|
|
654
|
+
if (ref === void 0) {
|
|
655
|
+
ref = (el) => {
|
|
656
|
+
if (el !== null) {
|
|
657
|
+
elements.current.set(key, el);
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
elements.current.delete(key);
|
|
661
|
+
refs.current.delete(key);
|
|
662
|
+
};
|
|
663
|
+
refs.current.set(key, ref);
|
|
664
|
+
}
|
|
665
|
+
return ref;
|
|
666
|
+
}, []);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// src/reorderModel.ts
|
|
670
|
+
var depsOf = (node) => Array.isArray(node?.dependsOn) ? node.dependsOn : [];
|
|
671
|
+
function holders(scene) {
|
|
672
|
+
const out = /* @__PURE__ */ new Map();
|
|
673
|
+
for (const node of scene.renderOrderNodes()) {
|
|
674
|
+
if (diagramGroupOf(node) === null) continue;
|
|
675
|
+
for (const dep of depsOf(node)) {
|
|
676
|
+
const member = scene.get(dep);
|
|
677
|
+
if (member !== void 0 && diagramLabelOf(member) === null) out.set(dep, node.id);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
return out;
|
|
681
|
+
}
|
|
682
|
+
function membersOf(scene, holder) {
|
|
683
|
+
const node = scene.get(holder);
|
|
684
|
+
const ids = node !== void 0 && diagramGroupOf(node) !== null ? depsOf(node) : (scene.childrenOf?.(holder) ?? []).filter((id) => id !== titleIdOf(holder));
|
|
685
|
+
return ids.filter((id) => {
|
|
686
|
+
const member = scene.get(id);
|
|
687
|
+
return member !== void 0 && diagramLabelOf(member) === null && diagramEdgeOf(member) === null;
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
function holderOf(scene, held, id) {
|
|
691
|
+
const group = held.get(id);
|
|
692
|
+
if (group !== void 0) return group;
|
|
693
|
+
const parent = scene.get(id)?.parent;
|
|
694
|
+
const node = parent == null ? void 0 : scene.get(parent);
|
|
695
|
+
return node !== void 0 && diagramContainerOf(node)?.role === "container" ? node.id : void 0;
|
|
696
|
+
}
|
|
697
|
+
var axisOf = (node) => diagramGroupOf(node)?.reorder ?? diagramContainerOf(node)?.reorder;
|
|
698
|
+
function reorderTargetOf(scene, id, boxOf) {
|
|
699
|
+
const held = holders(scene);
|
|
700
|
+
for (let member = id, holder = holderOf(scene, held, id); holder !== void 0; member = holder, holder = holderOf(scene, held, holder)) {
|
|
701
|
+
const axis = axisOf(scene.get(holder));
|
|
702
|
+
if (axis === void 0) continue;
|
|
703
|
+
const start = (m) => {
|
|
704
|
+
const box = boxOf(m);
|
|
705
|
+
return box === null ? 0 : axis === "x" ? box.x : box.y;
|
|
706
|
+
};
|
|
707
|
+
const members2 = membersOf(scene, holder).sort((a, b) => start(a) - start(b));
|
|
708
|
+
return { parent: holder, axis, member, members: members2 };
|
|
709
|
+
}
|
|
710
|
+
return null;
|
|
711
|
+
}
|
|
712
|
+
function carriedWith(scene, member) {
|
|
713
|
+
const out = /* @__PURE__ */ new Set();
|
|
714
|
+
const visit = (id) => {
|
|
715
|
+
if (out.has(id)) return;
|
|
716
|
+
out.add(id);
|
|
717
|
+
const node = scene.get(id);
|
|
718
|
+
if (node !== void 0 && diagramGroupOf(node) !== null) depsOf(node).forEach(visit);
|
|
719
|
+
scene.childrenOf?.(id).forEach(visit);
|
|
720
|
+
};
|
|
721
|
+
visit(member);
|
|
722
|
+
for (const node of scene.renderOrderNodes()) {
|
|
723
|
+
const on = depsOf(node)[0];
|
|
724
|
+
if (on !== void 0 && out.has(on) && diagramAnchorOf(node) !== null) out.add(node.id);
|
|
725
|
+
}
|
|
726
|
+
return out;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
// src/itemDropTarget.ts
|
|
730
|
+
function placeAmong(scene, container, at) {
|
|
731
|
+
const axis = diagramContainerOf(scene.get(container))?.reorder;
|
|
732
|
+
const boxed = membersOf(scene, container).flatMap((id) => {
|
|
733
|
+
const bounds = worldRectOf(scene, id);
|
|
734
|
+
return bounds === null ? [] : [{ id, bounds }];
|
|
735
|
+
});
|
|
736
|
+
if (axis === void 0) {
|
|
737
|
+
const before = (b) => at.y > b.y + b.height || at.y >= b.y && at.x > b.x + b.width / 2;
|
|
738
|
+
return { index: boxed.filter((m) => before(m.bounds)).length, gap: null };
|
|
739
|
+
}
|
|
740
|
+
const slots = sequenceSlots(axis, [...boxed].sort((p, q) => p.bounds[axis] - q.bounds[axis]));
|
|
741
|
+
const gap = slots.gaps[nearestGap(slots.gaps.map((g) => g.a[axis]), at[axis])] ?? null;
|
|
742
|
+
return { index: gap?.index ?? 0, gap };
|
|
743
|
+
}
|
|
744
|
+
function itemDropTargetAt(scene, participants, at) {
|
|
745
|
+
const point = { x: at.x, y: at.y };
|
|
746
|
+
const hit = bodyAt(participants, point);
|
|
747
|
+
const node = hit !== null && diagramContainerOf(hit.participant.node) === null ? hit.end.nodeId : null;
|
|
748
|
+
const container = diagramDropParentAt(scene, point, null);
|
|
749
|
+
const place2 = container === null ? null : placeAmong(scene, container, point);
|
|
750
|
+
return { target: node ?? container, container, index: place2?.index ?? null, at: point, gap: place2?.gap ?? null };
|
|
751
|
+
}
|
|
752
|
+
function outlineOf(scene, id, role) {
|
|
753
|
+
const b = worldRectOf(scene, id);
|
|
754
|
+
if (b === null) return null;
|
|
755
|
+
const points = [{ x: b.x, y: b.y }, { x: b.x + b.width, y: b.y }, { x: b.x + b.width, y: b.y + b.height }, { x: b.x, y: b.y + b.height }, { x: b.x, y: b.y }];
|
|
756
|
+
return { kind: "polyline", points, role };
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
// src/itemDrop.ts
|
|
760
|
+
var DIAGRAM_ITEM_CARRY = "weasel-diagram/item";
|
|
761
|
+
var ITEM_DROP_ACTION_ID = "diagram.dropItem";
|
|
762
|
+
function startItemDrag(down, item, cb) {
|
|
763
|
+
return startCarry(down.currentTarget, down, { type: DIAGRAM_ITEM_CARRY, value: item }, cb);
|
|
764
|
+
}
|
|
765
|
+
function dragItemProps(item, cb) {
|
|
766
|
+
return {
|
|
767
|
+
onPointerDown: (e) => {
|
|
768
|
+
if (e.button !== 0) return;
|
|
769
|
+
e.preventDefault();
|
|
770
|
+
startItemDrag(e, item, cb);
|
|
771
|
+
}
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
var ANY = "optional";
|
|
775
|
+
function createItemDropAction(opts) {
|
|
776
|
+
const { participants, report, canDrop = () => true, requires = [] } = opts;
|
|
777
|
+
return {
|
|
778
|
+
id: ITEM_DROP_ACTION_ID,
|
|
779
|
+
label: "Drop an item",
|
|
780
|
+
group: "diagram",
|
|
781
|
+
requires: [.../* @__PURE__ */ new Set(["scene", ...requires])],
|
|
782
|
+
defaultBinding: [{
|
|
783
|
+
spec: { kind: "carry", types: [DIAGRAM_ITEM_CARRY], mods: { alt: ANY, ctrl: ANY, meta: ANY, shift: ANY } },
|
|
784
|
+
opts: { id: `${ITEM_DROP_ACTION_ID}.carry` }
|
|
785
|
+
}],
|
|
786
|
+
invoker: {
|
|
787
|
+
timing: "ongoing",
|
|
788
|
+
start(ctx) {
|
|
789
|
+
const scene = ctx.deps.scene;
|
|
790
|
+
if (scene === void 0 || ctx.carry === void 0) return {};
|
|
791
|
+
const item = ctx.carry.value;
|
|
792
|
+
const { deps } = ctx;
|
|
793
|
+
const verdicts = /* @__PURE__ */ new Map();
|
|
794
|
+
const over = (c) => {
|
|
795
|
+
const { gap, ...drop } = itemDropTargetAt(scene, participants, c.world);
|
|
796
|
+
const key = `${drop.target}|${drop.container}|${drop.index}`;
|
|
797
|
+
if (!verdicts.has(key)) verdicts.set(key, canDrop(item, drop));
|
|
798
|
+
return { drop, gap, verdict: verdicts.get(key) };
|
|
799
|
+
};
|
|
800
|
+
let now = over(ctx);
|
|
801
|
+
let open = true;
|
|
802
|
+
return {
|
|
803
|
+
kind: "diagram-item-drop",
|
|
804
|
+
onMove(moveCtx) {
|
|
805
|
+
now = over(moveCtx);
|
|
806
|
+
},
|
|
807
|
+
overlay() {
|
|
808
|
+
const { drop, gap, verdict } = now;
|
|
809
|
+
if (!open || drop.target === null || verdict === false) return null;
|
|
810
|
+
const reason = refusalOf(verdict);
|
|
811
|
+
const box = outlineOf(scene, drop.target, reason === null ? "target" : "refused");
|
|
812
|
+
const out = box === null ? [] : [box];
|
|
813
|
+
if (reason !== null) out.push({ kind: "reason", at: drop.at, text: reason });
|
|
814
|
+
else if (gap !== null) out.push({ kind: "polyline", points: [gap.a, gap.b], role: "insert" });
|
|
815
|
+
return out;
|
|
273
816
|
},
|
|
274
817
|
onEnd(endCtx, reason) {
|
|
275
|
-
|
|
276
|
-
if (reason
|
|
277
|
-
|
|
278
|
-
if (
|
|
279
|
-
commit(
|
|
280
|
-
{ from: scratch.from, to, router: routerKey },
|
|
281
|
-
{ ...endCtx, deps: scratch.deps }
|
|
282
|
-
);
|
|
818
|
+
open = false;
|
|
819
|
+
if (reason !== "commit") return;
|
|
820
|
+
now = over(endCtx);
|
|
821
|
+
if (now.verdict === true) report({ item, ...now.drop }, deps);
|
|
283
822
|
}
|
|
284
823
|
};
|
|
285
824
|
}
|
|
286
|
-
}
|
|
287
|
-
enabled: () => true
|
|
825
|
+
}
|
|
288
826
|
};
|
|
289
827
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
const node = nodeOf(id);
|
|
296
|
-
return node === void 0 ? null : diagramContainerOf(node)?.role ?? null;
|
|
297
|
-
};
|
|
298
|
-
const attached = (id) => {
|
|
299
|
-
const node = nodeOf(id);
|
|
300
|
-
return node !== void 0 && (diagramEdgeOf(node) !== null || diagramLabelOf(node) !== null);
|
|
301
|
-
};
|
|
302
|
-
return (parent, ids) => {
|
|
303
|
-
const node = nodeOf(parent);
|
|
304
|
-
if (node === void 0 || ids.some(attached)) return false;
|
|
305
|
-
const role = roleOf(parent);
|
|
306
|
-
if (role === "pool") return ids.every((id) => roleOf(id) === "lane");
|
|
307
|
-
if (ids.some((id) => roleOf(id) === "lane")) return false;
|
|
308
|
-
if (role !== null) return ids.every((id) => roleOf(id) !== "pool");
|
|
309
|
-
return !attached(parent) && dataKeyReader(node) === null;
|
|
828
|
+
function itemDropContribution(opts) {
|
|
829
|
+
return {
|
|
830
|
+
id: "weasel-diagram-item-drop",
|
|
831
|
+
eligibility: { always: true },
|
|
832
|
+
actions: [createItemDropAction(opts)]
|
|
310
833
|
};
|
|
311
834
|
}
|
|
312
|
-
function
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
const b = bodyBox(node.pose, trait?.header);
|
|
321
|
-
const inside2 = point.x >= b.x && point.x < b.x + b.width && point.y >= b.y && point.y < b.y + b.height;
|
|
322
|
-
if (!inside2) continue;
|
|
323
|
-
if (trait?.role === "container" || trait?.role === "lane") best = id;
|
|
324
|
-
walk(scene.childrenOf(id));
|
|
325
|
-
}
|
|
326
|
-
};
|
|
327
|
-
walk(scene.roots);
|
|
328
|
-
return best;
|
|
835
|
+
function useItemDrop(participants, props) {
|
|
836
|
+
const latest = useLatest(props);
|
|
837
|
+
const on = props.onDropItem !== void 0;
|
|
838
|
+
return useMemo(() => on ? itemDropContribution({
|
|
839
|
+
participants,
|
|
840
|
+
canDrop: (item, target) => latest.current.canDropItem?.(item, target) ?? true,
|
|
841
|
+
report: (drop) => latest.current.onDropItem?.(drop)
|
|
842
|
+
}) : null, [on, participants, latest]);
|
|
329
843
|
}
|
|
330
844
|
|
|
331
845
|
// src/theme/themeWrites.ts
|
|
@@ -439,6 +953,8 @@ function themeWrites(scene, theme, ids, from) {
|
|
|
439
953
|
}
|
|
440
954
|
return out;
|
|
441
955
|
}
|
|
956
|
+
|
|
957
|
+
// src/stencils/containers.ts
|
|
442
958
|
var CONTAINER_STENCILS = [
|
|
443
959
|
{ id: "container", label: "Container", role: "container", size: { width: 240, height: 160 }, icon: (
|
|
444
960
|
/* @icon */
|
|
@@ -453,6 +969,7 @@ var CONTAINER_STENCILS = [
|
|
|
453
969
|
"diagram:poolColumns"
|
|
454
970
|
) }
|
|
455
971
|
];
|
|
972
|
+
var CONTAINERS_GROUP = { id: "containers", label: "Containers", stencils: CONTAINER_STENCILS };
|
|
456
973
|
var NEW_LANE_SIZE = 120;
|
|
457
974
|
function placedBounds(stencil, b) {
|
|
458
975
|
if (b.width > 0 && b.height > 0) return { ...b };
|
|
@@ -475,7 +992,8 @@ function containerBuild(stencil, bounds, opts) {
|
|
|
475
992
|
...stencil.role === "pool" ? firstLanes(id, axis, at, opts) : []
|
|
476
993
|
];
|
|
477
994
|
const label = `Insert ${stencil.label}`;
|
|
478
|
-
|
|
995
|
+
const stored = localizeWorldPoses(specs, RIGID_POSE_COMPOSITION);
|
|
996
|
+
return { id, ops: stored.map((node) => createInsertOp({ node, label })) };
|
|
479
997
|
}
|
|
480
998
|
var poolAxisOf = (pool) => pool.header?.side === "top" ? "x" : "y";
|
|
481
999
|
function lanesOf(scene, poolId) {
|
|
@@ -495,19 +1013,20 @@ function addLane(scene, poolId, index, style) {
|
|
|
495
1013
|
if (pool === void 0 || trait?.role !== "pool") return null;
|
|
496
1014
|
const axis = poolAxisOf(trait);
|
|
497
1015
|
const lanes = lanesOf(scene, poolId);
|
|
498
|
-
const
|
|
1016
|
+
const { width, height } = pool.pose;
|
|
1017
|
+
const b = { x: 0, y: 0, width, height };
|
|
499
1018
|
const at = index === void 0 || index >= lanes.length ? null : scene.get(lanes[index]).pose;
|
|
500
1019
|
const boundary = at !== null ? axis === "y" ? at.y : at.x : axis === "y" ? b.y + b.height : b.x + b.width;
|
|
501
1020
|
const start = boundary - NEW_LANE_SIZE / 2;
|
|
502
1021
|
const offset = trait.header?.size ?? 0;
|
|
503
1022
|
const pose = axis === "y" ? { x: b.x + offset, y: start, width: b.width - offset, height: NEW_LANE_SIZE } : { x: start, y: b.y + offset, width: NEW_LANE_SIZE, height: b.height - offset };
|
|
504
1023
|
const id = scene.mintId();
|
|
505
|
-
const specs = regionSpecs("lane", axis, pose, `Lane ${lanes.length + 1}`, {
|
|
1024
|
+
const specs = localizeWorldPoses(regionSpecs("lane", axis, pose, `Lane ${lanes.length + 1}`, {
|
|
506
1025
|
id,
|
|
507
1026
|
layer: pool.layer,
|
|
508
1027
|
parent: poolId,
|
|
509
1028
|
...style ? { style } : {}
|
|
510
|
-
});
|
|
1029
|
+
}), scene.composition);
|
|
511
1030
|
scene.batch("Add lane", () => {
|
|
512
1031
|
for (const spec of specs) scene.add(spec);
|
|
513
1032
|
if (style === void 0 && scene.doc.get(DIAGRAM_THEME_KEY) !== void 0) {
|
|
@@ -531,11 +1050,27 @@ function releaseMembers(scene, id) {
|
|
|
531
1050
|
const beside = releasedFrom(scene, node.id);
|
|
532
1051
|
const parent = scene.get(beside).parent;
|
|
533
1052
|
const index = (parent === null ? scene.roots : scene.childrenOf(parent)).indexOf(beside);
|
|
1053
|
+
const frame = scenePoseFrame(scene);
|
|
1054
|
+
const worlds = members2.map((m) => frame.world(m));
|
|
534
1055
|
scene.batch("Release members", () => {
|
|
535
|
-
members2.forEach((m, k) =>
|
|
1056
|
+
members2.forEach((m, k) => {
|
|
1057
|
+
scene.move(m, parent, index + k);
|
|
1058
|
+
scene.setPose(m, frame.localUnder(parent, worlds[k]));
|
|
1059
|
+
});
|
|
536
1060
|
scene.remove(node.id);
|
|
537
1061
|
});
|
|
538
1062
|
}
|
|
1063
|
+
function worldWith(sc, frame = /* @__PURE__ */ new Map()) {
|
|
1064
|
+
const poses = {
|
|
1065
|
+
getPose: (id) => frame.has(id) ? frame.get(id) : sc.get(id).pose,
|
|
1066
|
+
getParent: (id) => sc.get(id)?.parent ?? null,
|
|
1067
|
+
definesFrame: (id) => {
|
|
1068
|
+
const n = sc.get(id);
|
|
1069
|
+
return n === void 0 || definesFrame(n);
|
|
1070
|
+
}
|
|
1071
|
+
};
|
|
1072
|
+
return (id) => composeWorldPose(poses, id, sc.composition.compose);
|
|
1073
|
+
}
|
|
539
1074
|
var LANE_LAYER_ID = "diagram-lanes";
|
|
540
1075
|
var LANE_AFFORDANCE_KIND = `layer:${LANE_LAYER_ID}`;
|
|
541
1076
|
function laneScratchOf(hit) {
|
|
@@ -544,6 +1079,7 @@ function laneScratchOf(hit) {
|
|
|
544
1079
|
}
|
|
545
1080
|
function poolsOf(scene) {
|
|
546
1081
|
const out = [];
|
|
1082
|
+
const world = worldWith(scene);
|
|
547
1083
|
for (const node of scene.renderOrderNodes()) {
|
|
548
1084
|
const trait = diagramContainerOf(node);
|
|
549
1085
|
if (trait?.role !== "pool" || scene.isHidden(node.id) || scene.isLocked(node.id)) continue;
|
|
@@ -551,7 +1087,8 @@ function poolsOf(scene) {
|
|
|
551
1087
|
id: node.id,
|
|
552
1088
|
axis: poolAxisOf(trait),
|
|
553
1089
|
pose: node.pose,
|
|
554
|
-
|
|
1090
|
+
at: world(node.id),
|
|
1091
|
+
lanes: lanesOf(scene, node.id).map((id) => ({ id, pose: scene.get(id).pose, at: world(id) }))
|
|
555
1092
|
});
|
|
556
1093
|
}
|
|
557
1094
|
return out;
|
|
@@ -595,7 +1132,7 @@ function createLaneAffordance(scene, opts = {}) {
|
|
|
595
1132
|
out.push({
|
|
596
1133
|
id: `${lane.id}:boundary`,
|
|
597
1134
|
targetId: null,
|
|
598
|
-
shape: { kind: "outline", path: boundaryOf(pool.axis, lane.
|
|
1135
|
+
shape: { kind: "outline", path: boundaryOf(pool.axis, lane.at), hitRadiusPx },
|
|
599
1136
|
cursor,
|
|
600
1137
|
strength: "exclusive",
|
|
601
1138
|
claimedKinds: ["pointer"],
|
|
@@ -603,7 +1140,7 @@ function createLaneAffordance(scene, opts = {}) {
|
|
|
603
1140
|
});
|
|
604
1141
|
}
|
|
605
1142
|
if (addSizePx <= 0) continue;
|
|
606
|
-
const p = pool.
|
|
1143
|
+
const p = pool.at;
|
|
607
1144
|
const end = pool.axis === "y" ? { x: p.x + p.width / 2, y: p.y + p.height, normal: { x: 0, y: 1 } } : { x: p.x + p.width, y: p.y + p.height / 2, normal: { x: 1, y: 0 } };
|
|
608
1145
|
out.push({
|
|
609
1146
|
id: `${pool.id}:add-lane`,
|
|
@@ -652,7 +1189,7 @@ function laneResizeFrame(scene, pool, index, size) {
|
|
|
652
1189
|
const placeTitle = (id, at) => {
|
|
653
1190
|
const node = scene.get(id);
|
|
654
1191
|
if (node !== void 0 && scene.get(titleIdOf(id)) !== void 0) {
|
|
655
|
-
out.set(titleIdOf(id),
|
|
1192
|
+
out.set(titleIdOf(id), titleInFrame(at, diagramContainerOf(node)?.header));
|
|
656
1193
|
}
|
|
657
1194
|
};
|
|
658
1195
|
const laneAt = grow(lane.pose, d);
|
|
@@ -722,9 +1259,10 @@ function createAddLaneAction(opts) {
|
|
|
722
1259
|
invoker: {
|
|
723
1260
|
timing: "immediate",
|
|
724
1261
|
run(deps, params) {
|
|
725
|
-
const
|
|
726
|
-
|
|
727
|
-
const world =
|
|
1262
|
+
const p = params;
|
|
1263
|
+
const pressed = laneScratchOf(p?.affordance);
|
|
1264
|
+
const world = p?.world;
|
|
1265
|
+
if (pressed === null || pressed.laneId !== void 0 || !world) return;
|
|
728
1266
|
commit(pressed.poolId, { world, modifiers: NO_MODIFIERS, deps });
|
|
729
1267
|
}
|
|
730
1268
|
},
|
|
@@ -803,7 +1341,8 @@ function editContainerTitleAction(commit) {
|
|
|
803
1341
|
invoker: {
|
|
804
1342
|
timing: "immediate",
|
|
805
1343
|
run(deps, params) {
|
|
806
|
-
const
|
|
1344
|
+
const world = params?.world;
|
|
1345
|
+
const id = world ? regionHeaderAt(deps, world) : null;
|
|
807
1346
|
if (id !== null) commit(id);
|
|
808
1347
|
}
|
|
809
1348
|
},
|
|
@@ -826,6 +1365,10 @@ function createSetHopsAction(opts = {}) {
|
|
|
826
1365
|
return {
|
|
827
1366
|
id: SET_HOPS_ACTION_ID,
|
|
828
1367
|
label: "Line jumps",
|
|
1368
|
+
icon: (
|
|
1369
|
+
/* @icon */
|
|
1370
|
+
"diagram:lineJumps"
|
|
1371
|
+
),
|
|
829
1372
|
group: "diagram",
|
|
830
1373
|
requires: ["scene"],
|
|
831
1374
|
invoker: {
|
|
@@ -960,20 +1503,27 @@ function createResetLabelAction(opts) {
|
|
|
960
1503
|
};
|
|
961
1504
|
}
|
|
962
1505
|
function sceneParticipants(scene) {
|
|
963
|
-
|
|
1506
|
+
const each = function* () {
|
|
964
1507
|
const gates = nodeGates(scene);
|
|
1508
|
+
const frame = scenePoseFrame(scene);
|
|
965
1509
|
for (const node of scene.renderOrderNodes()) {
|
|
966
1510
|
const hidden = gates.hidden(node.id);
|
|
967
1511
|
const locked = gates.locked(node.id);
|
|
968
1512
|
yield {
|
|
969
1513
|
node,
|
|
970
|
-
pose:
|
|
1514
|
+
pose: frame.world(node.id),
|
|
971
1515
|
...node.layer !== void 0 ? { layer: node.layer } : {},
|
|
972
1516
|
...hidden ? { hidden } : {},
|
|
973
1517
|
...locked ? { locked } : {}
|
|
974
1518
|
};
|
|
975
1519
|
}
|
|
976
1520
|
};
|
|
1521
|
+
const { getVersion } = scene;
|
|
1522
|
+
const getGeneration = scene.overrides.getGeneration;
|
|
1523
|
+
if (getVersion === void 0 || getGeneration === void 0) return each;
|
|
1524
|
+
return Object.assign(each, {
|
|
1525
|
+
generation: () => [getVersion.call(scene), getGeneration.call(scene.overrides)]
|
|
1526
|
+
});
|
|
977
1527
|
}
|
|
978
1528
|
function portLayer(participants, opts = {}) {
|
|
979
1529
|
return composeAffordanceLayer(
|
|
@@ -1330,7 +1880,7 @@ function neighborsOf(handle, stops) {
|
|
|
1330
1880
|
const at = handle.kind === "vertex" ? handle.index + 1 : handle.leg + 1;
|
|
1331
1881
|
return [stops[at - 1], stops[handle.kind === "vertex" ? at + 1 : at]];
|
|
1332
1882
|
}
|
|
1333
|
-
function
|
|
1883
|
+
function moveOf2(p, session, ctx) {
|
|
1334
1884
|
const d = { x: ctx.world.x - p.press.x, y: ctx.world.y - p.press.y };
|
|
1335
1885
|
if (session === null) return d;
|
|
1336
1886
|
const at = session.at({ x: p.handle.at.x + d.x, y: p.handle.at.y + d.y }, snapMods(ctx)).point;
|
|
@@ -1382,7 +1932,7 @@ function createRouteEditAction(opts) {
|
|
|
1382
1932
|
return {
|
|
1383
1933
|
kind: "diagram-edit-route",
|
|
1384
1934
|
onMove(moveCtx) {
|
|
1385
|
-
waypoints = waypointsAt(p,
|
|
1935
|
+
waypoints = waypointsAt(p, moveOf2(p, session, moveCtx));
|
|
1386
1936
|
},
|
|
1387
1937
|
previewIds: () => proposed() === null ? null : [p.edgeId],
|
|
1388
1938
|
previewPose: (id) => id === p.edgeId && proposed() !== null ? p.pose : null,
|
|
@@ -1392,7 +1942,7 @@ function createRouteEditAction(opts) {
|
|
|
1392
1942
|
},
|
|
1393
1943
|
onEnd(endCtx, reason) {
|
|
1394
1944
|
open = false;
|
|
1395
|
-
const final = reason === "commit" ? waypointsAt(p,
|
|
1945
|
+
const final = reason === "commit" ? waypointsAt(p, moveOf2(p, session, endCtx)) : null;
|
|
1396
1946
|
session?.end();
|
|
1397
1947
|
if (final !== null) commit({ edgeId: p.edgeId, waypoints: final }, { ...endCtx, deps: p.deps });
|
|
1398
1948
|
}
|
|
@@ -1439,8 +1989,10 @@ function createRemoveWaypointAction(opts) {
|
|
|
1439
1989
|
invoker: {
|
|
1440
1990
|
timing: "immediate",
|
|
1441
1991
|
run(deps, params) {
|
|
1442
|
-
const
|
|
1443
|
-
const
|
|
1992
|
+
const p = params;
|
|
1993
|
+
const world = p?.world;
|
|
1994
|
+
if (!world) return;
|
|
1995
|
+
const pressed = waypointScratchOf(p.affordance);
|
|
1444
1996
|
const route = pressed === null ? null : routeOf3(pressed.edgeId);
|
|
1445
1997
|
const waypoints = pressed === null || route === null ? null : removalOf(route, pressed.handle);
|
|
1446
1998
|
const edit = pressed !== null && waypoints !== null ? { edgeId: pressed.edgeId, waypoints } : removalAt(deps, world);
|
|
@@ -1493,6 +2045,24 @@ var ROUTER_LABELS = {
|
|
|
1493
2045
|
straight: "Straight",
|
|
1494
2046
|
bezier: "Curved"
|
|
1495
2047
|
};
|
|
2048
|
+
var ROUTER_ICONS = {
|
|
2049
|
+
avoid: (
|
|
2050
|
+
/* @icon */
|
|
2051
|
+
"diagram:routerAvoid"
|
|
2052
|
+
),
|
|
2053
|
+
orthogonal: (
|
|
2054
|
+
/* @icon */
|
|
2055
|
+
"diagram:routerOrthogonal"
|
|
2056
|
+
),
|
|
2057
|
+
straight: (
|
|
2058
|
+
/* @icon */
|
|
2059
|
+
"diagram:routerStraight"
|
|
2060
|
+
),
|
|
2061
|
+
bezier: (
|
|
2062
|
+
/* @icon */
|
|
2063
|
+
"diagram:routerCurved"
|
|
2064
|
+
)
|
|
2065
|
+
};
|
|
1496
2066
|
function withRouter(data, trait, router) {
|
|
1497
2067
|
const { route: _r, ...rest } = trait;
|
|
1498
2068
|
return { ...data, diagram: { ...rest, router } };
|
|
@@ -1520,6 +2090,7 @@ function createSetRouterAction(router, opts) {
|
|
|
1520
2090
|
return {
|
|
1521
2091
|
id: SET_ROUTER_ACTION_IDS[router],
|
|
1522
2092
|
label: ROUTER_LABELS[router],
|
|
2093
|
+
icon: ROUTER_ICONS[router],
|
|
1523
2094
|
group: "diagram",
|
|
1524
2095
|
requires: ["scene"],
|
|
1525
2096
|
invoker: {
|
|
@@ -1650,7 +2221,7 @@ function avoidSceneInput(scene, opts = {}, poseOf) {
|
|
|
1650
2221
|
const pose = (node) => {
|
|
1651
2222
|
let p = poses.get(node.id);
|
|
1652
2223
|
if (p === void 0) {
|
|
1653
|
-
p = poseOf?.(node.id) ??
|
|
2224
|
+
p = poseOf?.(node.id) ?? composeWorldPose(scenePoseAdapter(sc), node.id, sc.composition.compose);
|
|
1654
2225
|
poses.set(node.id, p);
|
|
1655
2226
|
}
|
|
1656
2227
|
return p;
|
|
@@ -1714,10 +2285,20 @@ function avoidSceneInput(scene, opts = {}, poseOf) {
|
|
|
1714
2285
|
}
|
|
1715
2286
|
return { obstacles, edges };
|
|
1716
2287
|
}
|
|
2288
|
+
var specWorldPoses = (specs, composition = RIGID_POSE_COMPOSITION) => worldPosesOf(specs, composition);
|
|
2289
|
+
function specDeps(specs, world) {
|
|
2290
|
+
const byId = new Map(specs.map((s) => [s.id, s]));
|
|
2291
|
+
return (id) => {
|
|
2292
|
+
const s = byId.get(id);
|
|
2293
|
+
if (s === void 0) return void 0;
|
|
2294
|
+
const pose = world.get(s.id) ?? s.pose;
|
|
2295
|
+
return { node: { ...s, parent: null, pose }, pose, path: null };
|
|
2296
|
+
};
|
|
2297
|
+
}
|
|
1717
2298
|
|
|
1718
2299
|
// src/avoid/specRoutes.ts
|
|
1719
2300
|
var facing = (ports) => ports.flatMap((p) => p.normal === null ? [] : [{ point: p.point, normal: p.normal }]);
|
|
1720
|
-
function obstaclesOf(specs) {
|
|
2301
|
+
function obstaclesOf(specs, world) {
|
|
1721
2302
|
const children = /* @__PURE__ */ new Map();
|
|
1722
2303
|
for (const s of specs) {
|
|
1723
2304
|
if (s.parent == null) continue;
|
|
@@ -1736,10 +2317,11 @@ function obstaclesOf(specs) {
|
|
|
1736
2317
|
if (spec.derivePose !== void 0 || diagramEdgeOf(spec) !== null) continue;
|
|
1737
2318
|
const region = diagramContainerOf(spec);
|
|
1738
2319
|
if (diagramNodeOf(node) === null && region?.role !== "lane") continue;
|
|
1739
|
-
const
|
|
2320
|
+
const box = world.get(node.id) ?? spec.pose;
|
|
2321
|
+
const header = region === null ? null : headerBox(box, region.header);
|
|
1740
2322
|
if (header !== null) out.push({ id: headerIdOf(node.id), kind: "shape", box: header });
|
|
1741
2323
|
if (region?.role === "lane") continue;
|
|
1742
|
-
out.push(region === null ? { id: node.id, kind: "shape", box
|
|
2324
|
+
out.push(region === null ? { id: node.id, kind: "shape", box } : { id: node.id, kind: "region", box, members: inside2(node.id, /* @__PURE__ */ new Set()) });
|
|
1743
2325
|
}
|
|
1744
2326
|
return out;
|
|
1745
2327
|
}
|
|
@@ -1755,12 +2337,14 @@ function labelsOf(specs) {
|
|
|
1755
2337
|
return out;
|
|
1756
2338
|
}
|
|
1757
2339
|
function createSpecRouter(options = {}) {
|
|
1758
|
-
const
|
|
2340
|
+
const { composition = RIGID_POSE_COMPOSITION, ...avoid2 } = options;
|
|
2341
|
+
const router = createAvoidRouter(avoid2);
|
|
1759
2342
|
let held = /* @__PURE__ */ new Map();
|
|
1760
2343
|
return (specs) => {
|
|
1761
2344
|
const byId = new Map(specs.map((s) => [s.id, s]));
|
|
1762
2345
|
const labels = labelsOf(specs);
|
|
1763
|
-
const
|
|
2346
|
+
const world = specWorldPoses(specs, composition);
|
|
2347
|
+
const obstacles = obstaclesOf(specs, world);
|
|
1764
2348
|
const edges = [];
|
|
1765
2349
|
for (const spec of specs) {
|
|
1766
2350
|
const edge = diagramEdgeOf(spec);
|
|
@@ -1770,7 +2354,8 @@ function createSpecRouter(options = {}) {
|
|
|
1770
2354
|
if (a === void 0 || b === void 0 || a === b) continue;
|
|
1771
2355
|
const portsAt = (s, port) => {
|
|
1772
2356
|
const n = { id: s.id, kind: s.kind, data: s.data };
|
|
1773
|
-
|
|
2357
|
+
const pose = world.get(n.id) ?? s.pose;
|
|
2358
|
+
return facing(port === void 0 ? portsOf(n, pose) : portSitesOf(n, pose, port));
|
|
1774
2359
|
};
|
|
1775
2360
|
const route = held.get(spec.id);
|
|
1776
2361
|
edges.push({
|
|
@@ -1846,11 +2431,7 @@ function hopDataOps(scene, hops) {
|
|
|
1846
2431
|
function createSpecHops(r) {
|
|
1847
2432
|
const route = edgeRouter();
|
|
1848
2433
|
return (specs) => {
|
|
1849
|
-
const
|
|
1850
|
-
const dep = (id) => {
|
|
1851
|
-
const s = byId.get(id);
|
|
1852
|
-
return s === void 0 ? void 0 : { node: s, pose: s.pose, path: null };
|
|
1853
|
-
};
|
|
2434
|
+
const dep = specDeps(specs, specWorldPoses(specs));
|
|
1854
2435
|
const polylines = /* @__PURE__ */ new Map();
|
|
1855
2436
|
const order = [];
|
|
1856
2437
|
for (const spec of specs) {
|
|
@@ -1911,390 +2492,74 @@ function sameEnd(ends, moving, target) {
|
|
|
1911
2492
|
function createReconnectAction(opts) {
|
|
1912
2493
|
const { canConnect = defaultCanConnect, commit = commitReconnectToScene } = opts;
|
|
1913
2494
|
const endsOf = createEdgeEnds(opts.participants, opts);
|
|
1914
|
-
const shared = { ...opts, snapRadius: opts.snapRadius ?? DEFAULT_SNAP2, bodies: opts.bodies ?? true };
|
|
1915
|
-
const targetsFor = (moving) => createTargets({
|
|
1916
|
-
...shared,
|
|
1917
|
-
canConnect: moving.end === "to" ? (fixed, candidate) => canConnect(fixed, candidate, moving) : (fixed, candidate) => canConnect(candidate, fixed, moving)
|
|
1918
|
-
});
|
|
1919
|
-
return {
|
|
1920
|
-
id: opts.id ?? RECONNECT_ACTION_ID,
|
|
1921
|
-
label: "Reconnect",
|
|
1922
|
-
group: "diagram",
|
|
1923
|
-
requires: ["scene", "view"],
|
|
1924
|
-
invoker: {
|
|
1925
|
-
timing: "ongoing",
|
|
1926
|
-
start(ctx) {
|
|
1927
|
-
const pressed = edgeEndScratchOf(ctx.drag?.affordance);
|
|
1928
|
-
const ends = pressed === null ? null : endsOf(pressed.edgeId);
|
|
1929
|
-
if (pressed === null || ends === null) return {};
|
|
1930
|
-
const scratch = {
|
|
1931
|
-
edgeId: pressed.edgeId,
|
|
1932
|
-
moving: pressed.end,
|
|
1933
|
-
fixed: fixedEndOf(ends, pressed.end, opts),
|
|
1934
|
-
ends,
|
|
1935
|
-
route: routerOf(ends.trait, opts.routers),
|
|
1936
|
-
current: { x: ctx.world.x, y: ctx.world.y },
|
|
1937
|
-
open: true,
|
|
1938
|
-
deps: ctx.deps
|
|
1939
|
-
};
|
|
1940
|
-
const resolver = targetsFor({ edge: scratch.edgeId, end: scratch.moving });
|
|
1941
|
-
return {
|
|
1942
|
-
kind: "diagram-reconnect",
|
|
1943
|
-
onMove(moveCtx) {
|
|
1944
|
-
scratch.current = { x: moveCtx.world.x, y: moveCtx.world.y };
|
|
1945
|
-
},
|
|
1946
|
-
overlay() {
|
|
1947
|
-
if (!scratch.open) return null;
|
|
1948
|
-
const points = resolver.preview(scratch.route, scratch.fixed, scratch.current, scratch.deps.view, scratch.moving, scratch.ends.trait.waypoints);
|
|
1949
|
-
return points.length < 2 ? null : { kind: "polyline", points, role: "connector" };
|
|
1950
|
-
},
|
|
1951
|
-
onEnd(endCtx, reason) {
|
|
1952
|
-
scratch.open = false;
|
|
1953
|
-
if (reason === "cancel") return;
|
|
1954
|
-
const target = resolver.target(scratch.fixed, { x: endCtx.world.x, y: endCtx.world.y }, scratch.deps.view);
|
|
1955
|
-
if (target === null || sameEnd(scratch.ends, scratch.moving, target)) return;
|
|
1956
|
-
commit({ edgeId: scratch.edgeId, end: scratch.moving, target }, { ...endCtx, deps: scratch.deps });
|
|
1957
|
-
}
|
|
1958
|
-
};
|
|
1959
|
-
}
|
|
1960
|
-
},
|
|
1961
|
-
enabled: () => true
|
|
1962
|
-
};
|
|
1963
|
-
}
|
|
1964
|
-
function reconnectBinding(actionId = RECONNECT_ACTION_ID) {
|
|
1965
|
-
return { spec: { kind: "drag", target: `affordance:${EDGE_END_AFFORDANCE_KIND}` }, actionId, opts: { id: `${actionId}.drag` } };
|
|
1966
|
-
}
|
|
1967
|
-
function diagramEdgeEnds(opts) {
|
|
1968
|
-
const actionId = opts.id ?? RECONNECT_ACTION_ID;
|
|
1969
|
-
return {
|
|
1970
|
-
// `opts.id` names the action; the layer keeps its own id.
|
|
1971
|
-
layer: edgeEndLayer(opts.participants, opts.selected, { ...opts, id: EDGE_END_LAYER_ID }),
|
|
1972
|
-
contribution: {
|
|
1973
|
-
id: "weasel-diagram-edge-ends",
|
|
1974
|
-
eligibility: { claimed: true },
|
|
1975
|
-
actions: [createReconnectAction({ ...opts, id: actionId }), grabPortAction(GRAB_EDGE_END_ACTION_ID)],
|
|
1976
|
-
bindings: [
|
|
1977
|
-
reconnectBinding(actionId),
|
|
1978
|
-
...grabPortBindings(GRAB_EDGE_END_ACTION_ID, EDGE_END_AFFORDANCE_KIND)
|
|
1979
|
-
]
|
|
1980
|
-
}
|
|
1981
|
-
};
|
|
1982
|
-
}
|
|
1983
|
-
function packed(items, spans, order) {
|
|
1984
|
-
const gaps = items.slice(1).map((m, i) => spans.get(m).start - (spans.get(items[i]).start + spans.get(items[i]).size));
|
|
1985
|
-
const out = /* @__PURE__ */ new Map();
|
|
1986
|
-
let at = items.length > 0 ? spans.get(items[0]).start : 0;
|
|
1987
|
-
order.forEach((m, i) => {
|
|
1988
|
-
out.set(m, at);
|
|
1989
|
-
at += spans.get(m).size + (gaps[i] ?? gaps[gaps.length - 1] ?? 0);
|
|
1990
|
-
});
|
|
1991
|
-
return out;
|
|
1992
|
-
}
|
|
1993
|
-
var carriedBy = (item) => item.carries ?? [item.id];
|
|
1994
|
-
var RELEASE_MS = 500;
|
|
1995
|
-
function slideOthers(scene, slots, carried) {
|
|
1996
|
-
const { axis } = slots;
|
|
1997
|
-
const size = axis === "x" ? "width" : "height";
|
|
1998
|
-
const ids = slots.items.map((i) => i.id);
|
|
1999
|
-
const spans = new Map(slots.items.map((i) => [i.id, { start: i.bounds[axis], size: i.bounds[size] }]));
|
|
2000
|
-
const nodes = slots.items.map((item) => ({
|
|
2001
|
-
id: item.id,
|
|
2002
|
-
poses: carriedBy(item).flatMap((id) => {
|
|
2003
|
-
const node = scene.get(id);
|
|
2004
|
-
return node === void 0 || node.derivePose !== void 0 ? [] : [{ id, pose: effectivePose(scene, node) }];
|
|
2005
|
-
})
|
|
2006
|
-
}));
|
|
2007
|
-
const hold = scene.overrides.hold();
|
|
2008
|
-
return {
|
|
2009
|
-
/** The items standing in `order`, or as they were for `null`, with the carried one `by` along. */
|
|
2010
|
-
show(order, by) {
|
|
2011
|
-
const starts = packed(ids, spans, order ?? ids);
|
|
2012
|
-
hold.set(nodes.flatMap(({ id, poses }) => {
|
|
2013
|
-
const d = id === carried ? by : starts.get(id) - spans.get(id).start;
|
|
2014
|
-
return poses.map(({ id: node, pose }) => [node, { ...pose, [axis]: pose[axis] + d }]);
|
|
2015
|
-
}));
|
|
2016
|
-
},
|
|
2017
|
-
drop: () => hold.drop(),
|
|
2018
|
-
/** Held until the host's new specs land, so the items do not jump back for
|
|
2019
|
-
* the frames in between; a host that keeps the old order gets it back shortly after. */
|
|
2020
|
-
keep() {
|
|
2021
|
-
const release = () => {
|
|
2022
|
-
stop();
|
|
2023
|
-
clearTimeout(timer);
|
|
2024
|
-
hold.drop();
|
|
2025
|
-
};
|
|
2026
|
-
const stop = scene.subscribe(release);
|
|
2027
|
-
const timer = setTimeout(release, RELEASE_MS);
|
|
2028
|
-
}
|
|
2029
|
-
};
|
|
2030
|
-
}
|
|
2031
|
-
|
|
2032
|
-
// src/reorder.ts
|
|
2033
|
-
var REORDER_ACTION_ID = "diagram.reorder";
|
|
2034
|
-
var END_GAP = 24;
|
|
2035
|
-
var inside = (p, b) => p.x >= b.x && p.x <= b.x + b.width && p.y >= b.y && p.y <= b.y + b.height;
|
|
2036
|
-
function sequenceSlots(axis, items) {
|
|
2037
|
-
const [along2, across, size, depth] = axis === "x" ? ["x", "y", "width", "height"] : ["y", "x", "height", "width"];
|
|
2038
|
-
const lo = (b) => b[along2];
|
|
2039
|
-
const hi = (b) => b[along2] + b[size];
|
|
2040
|
-
const bar = (at, boxes2) => {
|
|
2041
|
-
const from = Math.min(...boxes2.map((b) => b[across]));
|
|
2042
|
-
const to = Math.max(...boxes2.map((b) => b[across] + b[depth]));
|
|
2043
|
-
const point = (c) => axis === "x" ? { x: at, y: c } : { x: c, y: at };
|
|
2044
|
-
return { a: point(from), b: point(to) };
|
|
2045
|
-
};
|
|
2046
|
-
const boxes = items.map((i) => i.bounds);
|
|
2047
|
-
const halves = boxes.slice(1).map((b, k) => (lo(b) - hi(boxes[k])) / 2);
|
|
2048
|
-
const gaps = [];
|
|
2049
|
-
if (boxes.length === 0) return { axis, items, gaps };
|
|
2050
|
-
gaps.push({ index: 0, ...bar(lo(boxes[0]) - (halves[0] ?? END_GAP / 2), [boxes[0]]) });
|
|
2051
|
-
for (let k = 1; k < boxes.length; k++) gaps.push({ index: k, ...bar(hi(boxes[k - 1]) + halves[k - 1], [boxes[k - 1], boxes[k]]) });
|
|
2052
|
-
const last = boxes[boxes.length - 1];
|
|
2053
|
-
gaps.push({ index: boxes.length, ...bar(hi(last) + (halves[halves.length - 1] ?? END_GAP / 2), [last]) });
|
|
2054
|
-
return { axis, items, gaps };
|
|
2055
|
-
}
|
|
2056
|
-
function pickAt(slots, at) {
|
|
2057
|
-
const from = slots?.items.findIndex((i) => i.grip !== null && inside(at, i.grip ?? i.bounds)) ?? -1;
|
|
2058
|
-
return from < 0 ? null : { from, item: slots.items[from] };
|
|
2059
|
-
}
|
|
2060
|
-
function gapAt(held, by) {
|
|
2061
|
-
const { axis, gaps } = held.slots;
|
|
2062
|
-
const size = axis === "x" ? "width" : "height";
|
|
2063
|
-
const mid = held.item.bounds[axis] + held.item.bounds[size] / 2 + by;
|
|
2064
|
-
let best = null;
|
|
2065
|
-
for (const g of gaps) if (best === null || Math.abs(g.a[axis] - mid) < Math.abs(best.a[axis] - mid)) best = g;
|
|
2066
|
-
return best === null || best.index === held.from || best.index === held.from + 1 ? null : best;
|
|
2067
|
-
}
|
|
2068
|
-
function moveOf2(held, gap) {
|
|
2069
|
-
const order = held.slots.items.map((i) => i.id);
|
|
2070
|
-
order.splice(held.from, 1);
|
|
2071
|
-
order.splice(gap.index > held.from ? gap.index - 1 : gap.index, 0, held.item.id);
|
|
2072
|
-
return { id: held.item.id, from: held.from, to: gap.index, order };
|
|
2073
|
-
}
|
|
2074
|
-
function createReorderAction(opts) {
|
|
2075
|
-
const { canDrop = () => true, show = "bar" } = opts;
|
|
2076
|
-
return {
|
|
2077
|
-
id: opts.id ?? REORDER_ACTION_ID,
|
|
2078
|
-
label: opts.label ?? "Reorder",
|
|
2079
|
-
group: "diagram",
|
|
2080
|
-
requires: ["scene", ...opts.requires ?? []],
|
|
2081
|
-
cursor: "grab",
|
|
2082
|
-
activeCursor: "grabbing",
|
|
2083
|
-
enabled: (deps, at) => deps === void 0 || at === void 0 || pickAt(opts.slots(deps, at), at) !== null ? true : ActionDisabledReason.NotApplicable,
|
|
2084
|
-
invoker: {
|
|
2085
|
-
timing: "ongoing",
|
|
2086
|
-
start(ctx) {
|
|
2087
|
-
const start = { x: (ctx.drag?.start ?? ctx.world).x, y: (ctx.drag?.start ?? ctx.world).y };
|
|
2088
|
-
const slots = opts.slots(ctx.deps, start);
|
|
2089
|
-
const picked = pickAt(slots, start);
|
|
2090
|
-
if (slots === null || picked === null) return {};
|
|
2091
|
-
const held = { slots, ...picked };
|
|
2092
|
-
const { axis } = slots;
|
|
2093
|
-
let by = ctx.world[axis] - start[axis];
|
|
2094
|
-
let open = true;
|
|
2095
|
-
const verdicts = /* @__PURE__ */ new Map();
|
|
2096
|
-
const landing = (gap) => {
|
|
2097
|
-
if (gap === null) return null;
|
|
2098
|
-
if (!verdicts.has(gap.index)) {
|
|
2099
|
-
const move = moveOf2(held, gap);
|
|
2100
|
-
verdicts.set(gap.index, canDrop(move) ? move : null);
|
|
2101
|
-
}
|
|
2102
|
-
return verdicts.get(gap.index);
|
|
2103
|
-
};
|
|
2104
|
-
const finish = (endCtx, reason) => {
|
|
2105
|
-
open = false;
|
|
2106
|
-
by = endCtx.world[axis] - start[axis];
|
|
2107
|
-
const move = reason === "commit" ? landing(gapAt(held, by)) : null;
|
|
2108
|
-
if (move !== null) opts.commit(move, { ...endCtx, deps: ctx.deps });
|
|
2109
|
-
return move;
|
|
2495
|
+
const shared = { ...opts, snapRadius: opts.snapRadius ?? DEFAULT_SNAP2, bodies: opts.bodies ?? true };
|
|
2496
|
+
const targetsFor = (moving) => createTargets({
|
|
2497
|
+
...shared,
|
|
2498
|
+
canConnect: moving.end === "to" ? (fixed, candidate) => canConnect(fixed, candidate, moving) : (fixed, candidate) => canConnect(candidate, fixed, moving)
|
|
2499
|
+
});
|
|
2500
|
+
return {
|
|
2501
|
+
id: opts.id ?? RECONNECT_ACTION_ID,
|
|
2502
|
+
label: "Reconnect",
|
|
2503
|
+
group: "diagram",
|
|
2504
|
+
requires: ["scene", "view"],
|
|
2505
|
+
invoker: {
|
|
2506
|
+
timing: "ongoing",
|
|
2507
|
+
start(ctx) {
|
|
2508
|
+
const pressed = edgeEndScratchOf(ctx.drag?.affordance);
|
|
2509
|
+
const ends = pressed === null ? null : endsOf(pressed.edgeId);
|
|
2510
|
+
if (pressed === null || ends === null) return {};
|
|
2511
|
+
const scratch = {
|
|
2512
|
+
edgeId: pressed.edgeId,
|
|
2513
|
+
moving: pressed.end,
|
|
2514
|
+
fixed: fixedEndOf(ends, pressed.end, opts),
|
|
2515
|
+
ends,
|
|
2516
|
+
route: routerOf(ends.trait, opts.routers),
|
|
2517
|
+
current: { x: ctx.world.x, y: ctx.world.y },
|
|
2518
|
+
open: true,
|
|
2519
|
+
deps: ctx.deps
|
|
2110
2520
|
};
|
|
2111
|
-
|
|
2112
|
-
const slide = ctx.deps.scene === void 0 ? null : slideOthers(ctx.deps.scene, held.slots, held.item.id);
|
|
2113
|
-
return {
|
|
2114
|
-
kind: "diagram-reorder",
|
|
2115
|
-
onMove(moveCtx) {
|
|
2116
|
-
by = moveCtx.world[axis] - start[axis];
|
|
2117
|
-
slide?.show(landing(gapAt(held, by))?.order ?? null, by);
|
|
2118
|
-
},
|
|
2119
|
-
onEnd(endCtx, reason) {
|
|
2120
|
-
const move = finish(endCtx, reason);
|
|
2121
|
-
if (move === null) slide?.drop();
|
|
2122
|
-
else slide?.keep();
|
|
2123
|
-
}
|
|
2124
|
-
};
|
|
2125
|
-
}
|
|
2126
|
-
const carried = carriedBy(held.item);
|
|
2127
|
-
const poses = /* @__PURE__ */ new Map();
|
|
2128
|
-
for (const id of carried) {
|
|
2129
|
-
const pose = ctx.deps.scene?.get(id)?.pose;
|
|
2130
|
-
if (pose !== void 0) poses.set(id, pose);
|
|
2131
|
-
}
|
|
2521
|
+
const resolver = targetsFor({ edge: scratch.edgeId, end: scratch.moving });
|
|
2132
2522
|
return {
|
|
2133
|
-
kind: "diagram-
|
|
2523
|
+
kind: "diagram-reconnect",
|
|
2134
2524
|
onMove(moveCtx) {
|
|
2135
|
-
|
|
2136
|
-
},
|
|
2137
|
-
previewIds: () => open && poses.size > 0 ? [...poses.keys()] : null,
|
|
2138
|
-
previewPose: (id) => {
|
|
2139
|
-
const pose = poses.get(id);
|
|
2140
|
-
return pose === void 0 ? null : { ...pose, [axis]: pose[axis] + by };
|
|
2525
|
+
scratch.current = { x: moveCtx.world.x, y: moveCtx.world.y };
|
|
2141
2526
|
},
|
|
2142
|
-
previewOpaqueIds: () => [...carried],
|
|
2143
2527
|
overlay() {
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
return { kind: "polyline", points: [gap.a, gap.b], role: landing(gap) !== null ? "insert" : "refused" };
|
|
2528
|
+
if (!scratch.open) return null;
|
|
2529
|
+
return resolver.overlays(scratch.route, scratch.fixed, scratch.current, scratch.deps.view, scratch.moving, scratch.ends.trait.waypoints);
|
|
2147
2530
|
},
|
|
2148
2531
|
onEnd(endCtx, reason) {
|
|
2149
|
-
|
|
2532
|
+
scratch.open = false;
|
|
2533
|
+
if (reason === "cancel") return;
|
|
2534
|
+
const target = resolver.target(scratch.fixed, { x: endCtx.world.x, y: endCtx.world.y }, scratch.deps.view);
|
|
2535
|
+
if (target === null || sameEnd(scratch.ends, scratch.moving, target)) return;
|
|
2536
|
+
commit({ edgeId: scratch.edgeId, end: scratch.moving, target }, { ...endCtx, deps: scratch.deps });
|
|
2150
2537
|
}
|
|
2151
2538
|
};
|
|
2152
2539
|
}
|
|
2153
|
-
}
|
|
2154
|
-
|
|
2155
|
-
}
|
|
2156
|
-
var ANYWHERE = bodyPredicate(() => true);
|
|
2157
|
-
function reorderBinding(actionId = REORDER_ACTION_ID, on = ANYWHERE) {
|
|
2158
|
-
return { spec: { kind: "drag", target: { kindOf: on } }, actionId, opts: { id: on === ANYWHERE ? `${actionId}.drag` : `${actionId}.bodyDrag` } };
|
|
2159
|
-
}
|
|
2160
|
-
function reorderContribution(opts) {
|
|
2161
|
-
const actionId = opts.id ?? REORDER_ACTION_ID;
|
|
2162
|
-
return {
|
|
2163
|
-
id: `weasel-diagram-reorder:${actionId}`,
|
|
2164
|
-
eligibility: { always: true },
|
|
2165
|
-
actions: [createReorderAction({ ...opts, id: actionId })],
|
|
2166
|
-
bindings: [reorderBinding(actionId, opts.on)]
|
|
2540
|
+
},
|
|
2541
|
+
enabled: () => true
|
|
2167
2542
|
};
|
|
2168
2543
|
}
|
|
2169
|
-
function
|
|
2170
|
-
|
|
2171
|
-
if (node === void 0) return null;
|
|
2172
|
-
const drawn = drawnBoundsScene(scene);
|
|
2173
|
-
const route = diagramEdgeOf(node) === null ? null : drawn.derivedPathOf?.(id) ?? null;
|
|
2174
|
-
const mid = route === null ? null : pointAlongPath(route, 0.5);
|
|
2175
|
-
if (mid != null) return { x: mid.point.x, y: mid.point.y, width: 0, height: 0 };
|
|
2176
|
-
return drawnAABBOf(drawn, id, effectivePose(scene, node), { includeStroke: false });
|
|
2544
|
+
function reconnectBinding(actionId = RECONNECT_ACTION_ID) {
|
|
2545
|
+
return { spec: { kind: "drag", target: `affordance:${EDGE_END_AFFORDANCE_KIND}` }, actionId, opts: { id: `${actionId}.drag` } };
|
|
2177
2546
|
}
|
|
2178
|
-
function
|
|
2547
|
+
function diagramEdgeEnds(opts) {
|
|
2548
|
+
const actionId = opts.id ?? RECONNECT_ACTION_ID;
|
|
2179
2549
|
return {
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
}
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
const at = placement === CENTER ? { x: anchor.x + (anchor.width - size.width) / 2, y: anchor.y + (anchor.height - size.height) / 2 } : clampRectWithin(placeRect({ anchor, overlay: size, boundary: view, placement, offset: overlay.offset ?? 0 }).rect, view);
|
|
2191
|
-
el.style.transform = `translate(${Math.round(at.x)}px, ${Math.round(at.y)}px)`;
|
|
2192
|
-
}
|
|
2193
|
-
function useOverlayPlacement(canvasRef, scene, overlays) {
|
|
2194
|
-
const elements = useRef(/* @__PURE__ */ new Map());
|
|
2195
|
-
const current = useLatest$1(overlays);
|
|
2196
|
-
const placeAll = useCallback(() => {
|
|
2197
|
-
const canvas = canvasRef.current;
|
|
2198
|
-
const surface = canvas?.element;
|
|
2199
|
-
if (canvas == null || surface == null) return;
|
|
2200
|
-
const view = canvas.getView();
|
|
2201
|
-
for (const overlay of current.current) {
|
|
2202
|
-
const el = elements.current.get(overlay.key ?? overlay.target);
|
|
2203
|
-
if (el === void 0) continue;
|
|
2204
|
-
const world = worldRectOf(scene, overlay.target);
|
|
2205
|
-
el.hidden = world === null;
|
|
2206
|
-
if (world === null) continue;
|
|
2207
|
-
const rect = screenRectOf(world, view);
|
|
2208
|
-
const anchor = { ...rect, x: rect.x + surface.offsetLeft, y: rect.y + surface.offsetTop };
|
|
2209
|
-
place(el, overlay, anchor, {
|
|
2210
|
-
x: surface.offsetLeft,
|
|
2211
|
-
y: surface.offsetTop,
|
|
2212
|
-
width: surface.offsetWidth,
|
|
2213
|
-
height: surface.offsetHeight
|
|
2214
|
-
});
|
|
2215
|
-
}
|
|
2216
|
-
}, [canvasRef, scene, current]);
|
|
2217
|
-
useLayoutEffect(() => {
|
|
2218
|
-
placeAll();
|
|
2219
|
-
const canvas = canvasRef.current;
|
|
2220
|
-
if (canvas == null) return;
|
|
2221
|
-
const stop = canvas.subscribeFrame(placeAll);
|
|
2222
|
-
const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(placeAll);
|
|
2223
|
-
for (const el of elements.current.values()) observer?.observe(el);
|
|
2224
|
-
return () => {
|
|
2225
|
-
stop();
|
|
2226
|
-
observer?.disconnect();
|
|
2227
|
-
};
|
|
2228
|
-
}, [canvasRef, placeAll, overlays]);
|
|
2229
|
-
const refs = useRef(/* @__PURE__ */ new Map());
|
|
2230
|
-
return useCallback((key) => {
|
|
2231
|
-
let ref = refs.current.get(key);
|
|
2232
|
-
if (ref === void 0) {
|
|
2233
|
-
ref = (el) => {
|
|
2234
|
-
if (el !== null) {
|
|
2235
|
-
elements.current.set(key, el);
|
|
2236
|
-
return;
|
|
2237
|
-
}
|
|
2238
|
-
elements.current.delete(key);
|
|
2239
|
-
refs.current.delete(key);
|
|
2240
|
-
};
|
|
2241
|
-
refs.current.set(key, ref);
|
|
2242
|
-
}
|
|
2243
|
-
return ref;
|
|
2244
|
-
}, []);
|
|
2245
|
-
}
|
|
2246
|
-
|
|
2247
|
-
// src/reorderModel.ts
|
|
2248
|
-
var depsOf = (node) => Array.isArray(node?.dependsOn) ? node.dependsOn : [];
|
|
2249
|
-
function holders(scene) {
|
|
2250
|
-
const out = /* @__PURE__ */ new Map();
|
|
2251
|
-
for (const node of scene.renderOrderNodes()) {
|
|
2252
|
-
if (diagramGroupOf(node) === null) continue;
|
|
2253
|
-
for (const dep of depsOf(node)) {
|
|
2254
|
-
const member = scene.get(dep);
|
|
2255
|
-
if (member !== void 0 && diagramLabelOf(member) === null) out.set(dep, node.id);
|
|
2550
|
+
// `opts.id` names the action; the layer keeps its own id.
|
|
2551
|
+
layer: edgeEndLayer(opts.participants, opts.selected, { ...opts, id: EDGE_END_LAYER_ID }),
|
|
2552
|
+
contribution: {
|
|
2553
|
+
id: "weasel-diagram-edge-ends",
|
|
2554
|
+
eligibility: { claimed: true },
|
|
2555
|
+
actions: [createReconnectAction({ ...opts, id: actionId }), grabPortAction(GRAB_EDGE_END_ACTION_ID)],
|
|
2556
|
+
bindings: [
|
|
2557
|
+
reconnectBinding(actionId),
|
|
2558
|
+
...grabPortBindings(GRAB_EDGE_END_ACTION_ID, EDGE_END_AFFORDANCE_KIND)
|
|
2559
|
+
]
|
|
2256
2560
|
}
|
|
2257
|
-
}
|
|
2258
|
-
return out;
|
|
2259
|
-
}
|
|
2260
|
-
function membersOf(scene, group) {
|
|
2261
|
-
return depsOf(scene.get(group)).filter((id) => {
|
|
2262
|
-
const node = scene.get(id);
|
|
2263
|
-
return node !== void 0 && diagramLabelOf(node) === null;
|
|
2264
|
-
});
|
|
2265
|
-
}
|
|
2266
|
-
function reorderTargetOf(scene, id, boxOf) {
|
|
2267
|
-
const held = holders(scene);
|
|
2268
|
-
for (let member = id, group = held.get(id); group !== void 0; member = group, group = held.get(group)) {
|
|
2269
|
-
const axis = diagramGroupOf(scene.get(group))?.reorder;
|
|
2270
|
-
if (axis === void 0) continue;
|
|
2271
|
-
const start = (m) => {
|
|
2272
|
-
const box = boxOf(m);
|
|
2273
|
-
return box === null ? 0 : axis === "x" ? box.x : box.y;
|
|
2274
|
-
};
|
|
2275
|
-
const members2 = membersOf(scene, group).sort((a, b) => start(a) - start(b));
|
|
2276
|
-
return { group, axis, member, members: members2 };
|
|
2277
|
-
}
|
|
2278
|
-
return null;
|
|
2279
|
-
}
|
|
2280
|
-
function carriedWith(scene, member) {
|
|
2281
|
-
const out = /* @__PURE__ */ new Set();
|
|
2282
|
-
const visit = (id) => {
|
|
2283
|
-
if (out.has(id)) return;
|
|
2284
|
-
out.add(id);
|
|
2285
|
-
const node = scene.get(id);
|
|
2286
|
-
if (node !== void 0 && diagramGroupOf(node) !== null) depsOf(node).forEach(visit);
|
|
2287
|
-
scene.childrenOf?.(id).forEach(visit);
|
|
2288
2561
|
};
|
|
2289
|
-
visit(member);
|
|
2290
|
-
for (const node of scene.renderOrderNodes()) {
|
|
2291
|
-
const on = depsOf(node)[0];
|
|
2292
|
-
if (on !== void 0 && out.has(on) && diagramAnchorOf(node) !== null) out.add(node.id);
|
|
2293
|
-
}
|
|
2294
|
-
return out;
|
|
2295
2562
|
}
|
|
2296
|
-
|
|
2297
|
-
// src/groupReorder.ts
|
|
2298
2563
|
var STEP_REORDER_ACTION_ID = "diagram.stepReorder";
|
|
2299
2564
|
var sceneOf = (deps) => deps?.scene;
|
|
2300
2565
|
function targetAt(deps, id) {
|
|
@@ -2336,17 +2601,21 @@ function createStepReorderAction(report) {
|
|
|
2336
2601
|
if (target === null || params?.axis !== target.axis) return;
|
|
2337
2602
|
const { members: members2, member } = target;
|
|
2338
2603
|
const at = members2.indexOf(member);
|
|
2339
|
-
const to = at
|
|
2340
|
-
if (to
|
|
2341
|
-
|
|
2342
|
-
order.splice(at, 1);
|
|
2343
|
-
order.splice(to, 0, member);
|
|
2344
|
-
report({ group: target.group, order });
|
|
2604
|
+
const to = nudgeGap([at], params.step === -1 ? -1 : 1, members2.length);
|
|
2605
|
+
if (to === null) return;
|
|
2606
|
+
report({ parent: target.parent, order: moveItems(members2, [at], to) });
|
|
2345
2607
|
}
|
|
2346
2608
|
},
|
|
2347
2609
|
enabled: (deps) => selectedTarget(deps) !== null ? true : ActionDisabledReason.NotApplicable
|
|
2348
2610
|
};
|
|
2349
2611
|
}
|
|
2612
|
+
function containerRegion(deps, member) {
|
|
2613
|
+
const scene = sceneOf(deps);
|
|
2614
|
+
const parent = targetAt(deps, member)?.parent;
|
|
2615
|
+
const node = parent === void 0 ? void 0 : scene?.get(parent);
|
|
2616
|
+
if (scene === void 0 || node === void 0 || diagramContainerOf(node)?.role !== "container") return null;
|
|
2617
|
+
return worldRectOf(scene, parent);
|
|
2618
|
+
}
|
|
2350
2619
|
var onBody2 = bodyPredicate((target) => target === "selected-body" || target === "unselected-body");
|
|
2351
2620
|
function groupReorderContribution(report) {
|
|
2352
2621
|
const drag = reorderContribution({
|
|
@@ -2354,9 +2623,10 @@ function groupReorderContribution(report) {
|
|
|
2354
2623
|
requires: ["nodeAtPoint"],
|
|
2355
2624
|
show: "slide",
|
|
2356
2625
|
on: onBody2,
|
|
2626
|
+
region: (deps, item) => containerRegion(deps, item.id),
|
|
2357
2627
|
commit: (move, ctx) => {
|
|
2358
|
-
const
|
|
2359
|
-
if (
|
|
2628
|
+
const parent = targetAt(ctx.deps, move.id)?.parent;
|
|
2629
|
+
if (parent !== void 0) report({ parent, order: move.order });
|
|
2360
2630
|
}
|
|
2361
2631
|
});
|
|
2362
2632
|
return { ...drag, id: "weasel-diagram-reorder", actions: [...drag.actions ?? [], createStepReorderAction(report)] };
|
|
@@ -2365,11 +2635,8 @@ function groupReorderContribution(report) {
|
|
|
2365
2635
|
// src/labels/specLabels.ts
|
|
2366
2636
|
var routeOf = edgeRouter();
|
|
2367
2637
|
function inputOf(specs) {
|
|
2368
|
-
const
|
|
2369
|
-
const dep = (
|
|
2370
|
-
const s = byId.get(id);
|
|
2371
|
-
return s === void 0 ? void 0 : { node: { ...s, parent: null }, pose: s.pose, path: null };
|
|
2372
|
-
};
|
|
2638
|
+
const world = specWorldPoses(specs);
|
|
2639
|
+
const dep = specDeps(specs, world);
|
|
2373
2640
|
const shapes = [];
|
|
2374
2641
|
const routes = [];
|
|
2375
2642
|
const labels = [];
|
|
@@ -2389,7 +2656,7 @@ function inputOf(specs) {
|
|
|
2389
2656
|
}
|
|
2390
2657
|
const node = { id, kind: spec.kind, data: spec.data };
|
|
2391
2658
|
if (spec.derivePose !== void 0 || diagramContainerOf(node) !== null || diagramGroupOf(node) !== null || diagramNodeOf(node) === null) continue;
|
|
2392
|
-
shapes.push({ id, box: spec.pose });
|
|
2659
|
+
shapes.push({ id, box: world.get(id) ?? spec.pose });
|
|
2393
2660
|
}
|
|
2394
2661
|
return { shapes, routes, labels };
|
|
2395
2662
|
}
|
|
@@ -2500,7 +2767,8 @@ var LAYOUTS = Object.freeze({
|
|
|
2500
2767
|
layered,
|
|
2501
2768
|
tree,
|
|
2502
2769
|
force,
|
|
2503
|
-
chain
|
|
2770
|
+
chain,
|
|
2771
|
+
radial
|
|
2504
2772
|
});
|
|
2505
2773
|
function applyLayout(scene, result, opts = {}) {
|
|
2506
2774
|
const poses = layoutPoses(scene, result, opts.geometry);
|
|
@@ -2518,39 +2786,52 @@ function applyLayout(scene, result, opts = {}) {
|
|
|
2518
2786
|
return moved;
|
|
2519
2787
|
}
|
|
2520
2788
|
function layoutPoses(scene, result, geometry = AUTO_POSE_DESCRIPTOR) {
|
|
2789
|
+
const sc = scene;
|
|
2790
|
+
const { compose, decompose } = sc.composition;
|
|
2791
|
+
const stored = scenePoseAdapter(sc);
|
|
2521
2792
|
const out = /* @__PURE__ */ new Map();
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2793
|
+
const decided = { ...stored, getPose: (id) => out.has(id) ? out.get(id) : stored.getPose(id) };
|
|
2794
|
+
const depth = (id) => {
|
|
2795
|
+
let d = 0;
|
|
2796
|
+
for (let at = scene.get(id)?.parent; at != null; at = scene.get(at)?.parent) d++;
|
|
2797
|
+
return d;
|
|
2798
|
+
};
|
|
2799
|
+
const ids = [...result.keys()].filter((id) => scene.get(id) !== void 0).sort((a, b) => depth(a) - depth(b));
|
|
2800
|
+
for (const id of ids) {
|
|
2801
|
+
const at = result.get(id);
|
|
2802
|
+
const world = composeWorldPose(stored, id, compose);
|
|
2803
|
+
const bounds = geometry.getBounds(world);
|
|
2526
2804
|
const dx = at.x - bounds.x;
|
|
2527
2805
|
const dy = at.y - bounds.y;
|
|
2528
2806
|
if (dx === 0 && dy === 0) continue;
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
out.set(descendant, translatePoseViaDescriptor(child.pose, dx, dy, geometry));
|
|
2534
|
-
}
|
|
2535
|
-
}
|
|
2536
|
-
return out;
|
|
2537
|
-
}
|
|
2538
|
-
function subtreeOf(scene, id) {
|
|
2539
|
-
const out = [];
|
|
2540
|
-
const stack = [...scene.childrenOf(id)];
|
|
2541
|
-
while (stack.length > 0) {
|
|
2542
|
-
const next = stack.pop();
|
|
2543
|
-
out.push(next);
|
|
2544
|
-
stack.push(...scene.childrenOf(next));
|
|
2807
|
+
const parent = scene.get(id).parent;
|
|
2808
|
+
out.set(id, rebaseLocalPose(decided, translatePoseViaDescriptor(world, dx, dy, geometry), parent, compose, decompose));
|
|
2809
|
+
const carried = carriedDescendants(sceneCarrySource(sc), id, (w) => translatePoseViaDescriptor(w, dx, dy, geometry));
|
|
2810
|
+
for (const [d, pose] of carried) out.set(d, pose);
|
|
2545
2811
|
}
|
|
2546
2812
|
return out;
|
|
2547
2813
|
}
|
|
2548
2814
|
var LAYOUT_ACTION_ID = "diagram.layout";
|
|
2549
|
-
function
|
|
2550
|
-
const
|
|
2551
|
-
const
|
|
2815
|
+
function layoutDiagram(scene, source, algorithm, opts = {}) {
|
|
2816
|
+
const fn = typeof algorithm === "function" ? algorithm : LAYOUTS[algorithm] ?? layered;
|
|
2817
|
+
const graph = buildGraph(source, {
|
|
2552
2818
|
...opts.read ? { read: opts.read } : {},
|
|
2553
2819
|
...opts.geometry ? { geometry: opts.geometry } : {}
|
|
2820
|
+
});
|
|
2821
|
+
const plan = planOf(fn, graph, opts.layout);
|
|
2822
|
+
return applyLayout(scene, plan.moves, {
|
|
2823
|
+
...opts.geometry ? { geometry: opts.geometry } : {},
|
|
2824
|
+
...opts.label ? { label: opts.label } : {},
|
|
2825
|
+
routes: routesOf(graph, plan)
|
|
2826
|
+
});
|
|
2827
|
+
}
|
|
2828
|
+
function createLayoutAction(opts) {
|
|
2829
|
+
const { read, geometry, layout, label } = opts;
|
|
2830
|
+
const options = {
|
|
2831
|
+
...read ? { read } : {},
|
|
2832
|
+
...geometry ? { geometry } : {},
|
|
2833
|
+
...layout ? { layout } : {},
|
|
2834
|
+
...label ? { label } : {}
|
|
2554
2835
|
};
|
|
2555
2836
|
return {
|
|
2556
2837
|
id: opts.id ?? LAYOUT_ACTION_ID,
|
|
@@ -2561,14 +2842,7 @@ function createLayoutAction(opts) {
|
|
|
2561
2842
|
timing: "immediate",
|
|
2562
2843
|
run: (deps) => {
|
|
2563
2844
|
const scene = deps["scene"];
|
|
2564
|
-
if (scene
|
|
2565
|
-
const graph = buildGraph(opts.source, build);
|
|
2566
|
-
const plan = planOf(algorithm, graph, opts.layout);
|
|
2567
|
-
applyLayout(scene, plan.moves, {
|
|
2568
|
-
...opts.geometry ? { geometry: opts.geometry } : {},
|
|
2569
|
-
...opts.label ? { label: opts.label } : {},
|
|
2570
|
-
routes: routesOf(graph, plan)
|
|
2571
|
-
});
|
|
2845
|
+
if (scene !== void 0) layoutDiagram(scene, opts.source, opts.algorithm ?? "layered", options);
|
|
2572
2846
|
}
|
|
2573
2847
|
},
|
|
2574
2848
|
enabled: () => true
|
|
@@ -2965,14 +3239,27 @@ var EASED_LAYOUTS = Object.freeze({
|
|
|
2965
3239
|
tree,
|
|
2966
3240
|
chain
|
|
2967
3241
|
});
|
|
3242
|
+
function bodyRules(trait, box) {
|
|
3243
|
+
const layout = trait.body;
|
|
3244
|
+
if (layout?.dividers === void 0 || trait.outline === void 0) return [];
|
|
3245
|
+
const rows = recordedRows(layout);
|
|
3246
|
+
if (rows === null) return [];
|
|
3247
|
+
const spec = { outline: trait.outline, rows, padding: layout.padding, gap: layout.gap, verticalAlign: layout.verticalAlign };
|
|
3248
|
+
return layoutBody(spec, box).filter((b) => b.row.kind === "divider").map((b) => {
|
|
3249
|
+
const y = b.y + b.height / 2;
|
|
3250
|
+
return polylineFromPoints([{ x: b.x, y }, { x: b.x + b.width, y }]);
|
|
3251
|
+
});
|
|
3252
|
+
}
|
|
3253
|
+
|
|
3254
|
+
// src/shape.ts
|
|
2968
3255
|
function diagramShape(opts = {}) {
|
|
2969
3256
|
const geometry = opts.geometry ?? RECT_POSE_DESCRIPTOR;
|
|
2970
|
-
const
|
|
3257
|
+
const outlineOf2 = (node) => diagramNodeOf(node, opts.read)?.outline ?? laneOutlineOf(node);
|
|
2971
3258
|
return {
|
|
2972
3259
|
id: opts.id ?? "diagram:outline",
|
|
2973
|
-
matches: (node) =>
|
|
3260
|
+
matches: (node) => outlineOf2(node) !== void 0,
|
|
2974
3261
|
paint: (node, pose, ctx) => {
|
|
2975
|
-
const outline =
|
|
3262
|
+
const outline = outlineOf2(node);
|
|
2976
3263
|
if (outline === void 0) return [];
|
|
2977
3264
|
const d = node.data;
|
|
2978
3265
|
const box = geometry.getBounds(pose);
|
|
@@ -2982,17 +3269,21 @@ function diagramShape(opts = {}) {
|
|
|
2982
3269
|
const stroke = declaredStroke && commandStroke(declaredStroke, box);
|
|
2983
3270
|
const header = headerBox(box, diagramContainerOf(node)?.header);
|
|
2984
3271
|
const divider = header === null || !stroke ? [] : [{ kind: "path", path: dividerPath(box, header), stroke }];
|
|
3272
|
+
const detailPath = stroke ? outlineDetail(outline, box) : null;
|
|
3273
|
+
const detail = stroke && detailPath ? [{ kind: "path", path: detailPath, stroke }] : [];
|
|
3274
|
+
const trait = diagramNodeOf(node, opts.read);
|
|
3275
|
+
const rules = stroke && trait !== null ? bodyRules(trait, box).map((path) => ({ kind: "path", path, stroke })) : [];
|
|
2985
3276
|
return [{
|
|
2986
3277
|
kind: "path",
|
|
2987
3278
|
path: outlinePath(outline, box),
|
|
2988
3279
|
...fill ? { fill } : {},
|
|
2989
3280
|
...stroke ? { stroke } : {}
|
|
2990
|
-
}, ...divider];
|
|
3281
|
+
}, ...detail, ...rules, ...divider];
|
|
2991
3282
|
},
|
|
2992
3283
|
// The drawn boundary, so picking and clipping follow the diamond rather
|
|
2993
3284
|
// than its bounding box.
|
|
2994
3285
|
silhouette: (node, pose) => {
|
|
2995
|
-
const outline =
|
|
3286
|
+
const outline = outlineOf2(node);
|
|
2996
3287
|
return outline === void 0 ? null : outlinePath(outline, geometry.getBounds(pose));
|
|
2997
3288
|
}
|
|
2998
3289
|
};
|
|
@@ -3007,17 +3298,24 @@ function registerDiagramShape(opts = {}) {
|
|
|
3007
3298
|
// src/bodyNode.ts
|
|
3008
3299
|
var isDiagramBody = (node) => diagramContainerOf(node) === null && diagramGroupOf(node) === null && dataKeyReader(node) !== null;
|
|
3009
3300
|
var isBodyOrRegion = (node) => diagramContainerOf(node) !== null || isDiagramBody(node);
|
|
3301
|
+
function agrees(selection, want) {
|
|
3302
|
+
if (typeof want === "string" || want === null) return (selection.at(-1) ?? null) === want;
|
|
3303
|
+
return selection.length === want.length && want.every((id) => selection.includes(id));
|
|
3304
|
+
}
|
|
3010
3305
|
function useMirroredSelection(selection, selected, onSelect) {
|
|
3011
3306
|
const want = selected ?? null;
|
|
3307
|
+
const key = typeof want === "string" || want === null ? want : `\0${want.join("\0")}`;
|
|
3012
3308
|
const pushed = useRef(void 0);
|
|
3013
3309
|
useEffect(() => {
|
|
3014
|
-
if (pushed.current ===
|
|
3015
|
-
pushed.current =
|
|
3016
|
-
if ((selection.get()
|
|
3017
|
-
|
|
3310
|
+
if (pushed.current === key) return;
|
|
3311
|
+
pushed.current = key;
|
|
3312
|
+
if (agrees(selection.get(), want)) return;
|
|
3313
|
+
selection.set(want === null ? [] : typeof want === "string" ? [want] : [...want]);
|
|
3314
|
+
}, [key]);
|
|
3018
3315
|
const onSelectRef = useLatest(onSelect);
|
|
3019
3316
|
return useCallback(() => {
|
|
3020
|
-
|
|
3317
|
+
const ids = [...selection.get()];
|
|
3318
|
+
onSelectRef.current?.(ids.at(-1) ?? null, ids);
|
|
3021
3319
|
}, [selection, onSelectRef]);
|
|
3022
3320
|
}
|
|
3023
3321
|
|
|
@@ -3035,10 +3333,11 @@ function laneList(scene, poolId) {
|
|
|
3035
3333
|
function readRegions(scene) {
|
|
3036
3334
|
const regions = /* @__PURE__ */ new Map();
|
|
3037
3335
|
const pools = /* @__PURE__ */ new Map();
|
|
3336
|
+
const at = worldWith(scene);
|
|
3038
3337
|
for (const n of scene.renderOrderNodes()) {
|
|
3039
3338
|
const trait = diagramContainerOf(n);
|
|
3040
3339
|
if (trait === null) continue;
|
|
3041
|
-
const { x, y, width, height } = n.
|
|
3340
|
+
const { x, y, width, height } = at(n.id);
|
|
3042
3341
|
regions.set(n.id, { box: { x, y, width, height }, parent: n.parent, fitted: trait.fitted === true });
|
|
3043
3342
|
if (trait.role === "pool") pools.set(n.id, laneList(scene, n.id));
|
|
3044
3343
|
}
|
|
@@ -3086,9 +3385,11 @@ var routeOf2 = (edge) => ({ waypoints: edge.waypoints ?? NONE, ports: portsOf2(e
|
|
|
3086
3385
|
function readScene(scene) {
|
|
3087
3386
|
const nodes = /* @__PURE__ */ new Map();
|
|
3088
3387
|
const routes = /* @__PURE__ */ new Map();
|
|
3388
|
+
const at = worldWith(scene);
|
|
3089
3389
|
for (const n of scene.renderOrderNodes()) {
|
|
3090
3390
|
if (isDiagramBody(n)) {
|
|
3091
|
-
|
|
3391
|
+
const { x, y } = at(n.id);
|
|
3392
|
+
nodes.set(n.id, { x, y, parent: n.parent });
|
|
3092
3393
|
continue;
|
|
3093
3394
|
}
|
|
3094
3395
|
if (diagramContainerOf(n) !== null) continue;
|
|
@@ -3101,18 +3402,19 @@ function diagramLayoutOf(specs) {
|
|
|
3101
3402
|
const moves = [];
|
|
3102
3403
|
const routes = [];
|
|
3103
3404
|
const containers = [];
|
|
3405
|
+
const world = worldPosesOf(specs, RIGID_POSE_COMPOSITION);
|
|
3104
3406
|
for (const s of specs) {
|
|
3105
3407
|
const id = s.id;
|
|
3106
3408
|
if (id === void 0) continue;
|
|
3107
3409
|
if (isBodyOrRegion(s)) {
|
|
3108
3410
|
const region = diagramContainerOf(s);
|
|
3109
3411
|
const pinned = isPinned({ id, kind: s.kind, data: s.data });
|
|
3412
|
+
const { x, y, width, height } = world.get(id);
|
|
3110
3413
|
if (region !== null) {
|
|
3111
3414
|
if (!pinned && region.role !== "lane" && region.fitted !== true) {
|
|
3112
|
-
const { x, y, width, height } = s.pose;
|
|
3113
3415
|
containers.push({ kind: "box", id, box: { x, y, width, height } });
|
|
3114
3416
|
}
|
|
3115
|
-
} else if (!pinned) moves.push({ id, x
|
|
3417
|
+
} else if (!pinned) moves.push({ id, x, y });
|
|
3116
3418
|
continue;
|
|
3117
3419
|
}
|
|
3118
3420
|
const edge = diagramEdgeOf(s);
|
|
@@ -3177,14 +3479,20 @@ function laneResizeReports(scene, resize) {
|
|
|
3177
3479
|
const lanes = laneList(scene, pool.id).map((l) => l.id === resize.laneId ? { ...l, size: resize.size } : l);
|
|
3178
3480
|
const moves = [];
|
|
3179
3481
|
const boxes = [];
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
const
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3482
|
+
const after = worldWith(scene, laneResizeFrame(scene, pool, index, resize.size));
|
|
3483
|
+
const before = worldWith(scene);
|
|
3484
|
+
for (const lane of pool.lanes.slice(index + 1)) {
|
|
3485
|
+
for (const id of descendantsOf(scene, lane.id)) {
|
|
3486
|
+
const node = scene.get(id);
|
|
3487
|
+
if (node === void 0) continue;
|
|
3488
|
+
const { x, y, width, height } = after(id);
|
|
3489
|
+
const was = before(id);
|
|
3490
|
+
if (x === was.x && y === was.y) continue;
|
|
3491
|
+
const region = diagramContainerOf(node);
|
|
3492
|
+
if (region !== null) {
|
|
3493
|
+
if (region.role === "container" && region.fitted !== true) boxes.push({ kind: "box", id, box: { x, y, width, height } });
|
|
3494
|
+
} else if (isDiagramBody(node)) moves.push({ id, x, y });
|
|
3495
|
+
}
|
|
3188
3496
|
}
|
|
3189
3497
|
return { lanes: { kind: "lanes", pool: pool.id, lanes }, moves, boxes };
|
|
3190
3498
|
}
|
|
@@ -3214,6 +3522,11 @@ function regionEditing(scene, selected, reports) {
|
|
|
3214
3522
|
};
|
|
3215
3523
|
return { layer: containers.layer, contributions: [containers.contribution, title] };
|
|
3216
3524
|
}
|
|
3525
|
+
function descendantsOf(scene, id) {
|
|
3526
|
+
const out = [];
|
|
3527
|
+
for (const c of scene.childrenOf(id)) out.push(c, ...descendantsOf(scene, c));
|
|
3528
|
+
return out;
|
|
3529
|
+
}
|
|
3217
3530
|
|
|
3218
3531
|
// src/diagramEditing.ts
|
|
3219
3532
|
var AMBIENT = [dragPanContribution()];
|
|
@@ -3366,8 +3679,14 @@ function usePagesView(pages) {
|
|
|
3366
3679
|
};
|
|
3367
3680
|
}, [placed, fill]);
|
|
3368
3681
|
}
|
|
3682
|
+
var DEFAULT_FIT_PADDING = 16;
|
|
3369
3683
|
function diagramBounds(specs, extra) {
|
|
3370
|
-
const
|
|
3684
|
+
const world = worldPosesOf(specs, RIGID_POSE_COMPOSITION);
|
|
3685
|
+
const boxes = specs.filter((s) => isBodyOrRegion(s) || diagramGroupOf(s) !== null).map((s) => world.get(s.id ?? "") ?? s.pose);
|
|
3686
|
+
for (const s of specs) {
|
|
3687
|
+
const edge = diagramEdgeOf(s);
|
|
3688
|
+
for (const p of [...edge?.waypoints ?? [], ...edge?.route ?? []]) boxes.push({ x: p.x, y: p.y, width: 0, height: 0 });
|
|
3689
|
+
}
|
|
3371
3690
|
if (extra !== void 0) boxes.push(extra);
|
|
3372
3691
|
if (boxes.length === 0) return null;
|
|
3373
3692
|
const x = Math.min(...boxes.map((b) => b.x));
|
|
@@ -3382,12 +3701,17 @@ function diagramBounds(specs, extra) {
|
|
|
3382
3701
|
function fitDiagram(specs, size, fit = {}, extra) {
|
|
3383
3702
|
const bounds = diagramBounds(specs, extra);
|
|
3384
3703
|
if (bounds === null) return void 0;
|
|
3385
|
-
|
|
3704
|
+
const view = fitViewToBounds(bounds, size, { x: 0, y: 0, scale: { x: 1, y: 1 } }, {
|
|
3386
3705
|
maxScale: fit.maxScale ?? 1,
|
|
3387
3706
|
...fit.minScale !== void 0 ? { minScale: fit.minScale } : {},
|
|
3388
3707
|
...fit.anchor !== void 0 ? { anchor: fit.anchor } : {},
|
|
3389
|
-
|
|
3708
|
+
padding: fit.padding ?? DEFAULT_FIT_PADDING
|
|
3390
3709
|
});
|
|
3710
|
+
return fit.bounds === void 0 ? view : clampView(view, fit.bounds, size);
|
|
3711
|
+
}
|
|
3712
|
+
function diagramViewBounds(specs, padding, extra) {
|
|
3713
|
+
const b = diagramBounds(specs, extra);
|
|
3714
|
+
return b === null ? void 0 : { x: b.x - padding, y: b.y - padding, width: b.width + 2 * padding, height: b.height + 2 * padding };
|
|
3391
3715
|
}
|
|
3392
3716
|
var IDENTITY = { x: 0, y: 0, scale: { x: 1, y: 1 } };
|
|
3393
3717
|
var sameView = (a, b) => a.x === b.x && a.y === b.y && a.scale.x === b.scale.x && a.scale.y === b.scale.y;
|
|
@@ -3401,14 +3725,14 @@ function useDiagramFit(args) {
|
|
|
3401
3725
|
const unsized = size.width === 0 || size.height === 0;
|
|
3402
3726
|
const defaultView = useMemo(
|
|
3403
3727
|
() => controlled || unsized ? void 0 : fitDiagram(specs, size, fit, extra),
|
|
3404
|
-
// A later `specs` is the
|
|
3728
|
+
// A later `specs` is the refit check's to answer.
|
|
3405
3729
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
3406
3730
|
[controlled, unsized, size.width, size.height, fit.minScale, fit.maxScale, fit.anchor, fit.padding, extra]
|
|
3407
3731
|
);
|
|
3408
3732
|
const [later, setLater] = useState(null);
|
|
3409
3733
|
const target = later !== null && later.over === defaultView ? later.view : defaultView;
|
|
3410
3734
|
const fitted = useRef(target);
|
|
3411
|
-
const opts = useLatest({ controlled, unsized, size, fit, extra, defaultView, current: args.current });
|
|
3735
|
+
const opts = useLatest({ controlled, unsized, size, fit, extra, defaultView, current: args.current, refit: args.refit ?? "overflow" });
|
|
3412
3736
|
useEffect(() => {
|
|
3413
3737
|
const canvas = canvasRef.current;
|
|
3414
3738
|
const was = fitted.current;
|
|
@@ -3423,10 +3747,11 @@ function useDiagramFit(args) {
|
|
|
3423
3747
|
first.current = false;
|
|
3424
3748
|
return;
|
|
3425
3749
|
}
|
|
3426
|
-
const { controlled: c, unsized: u, size: box, fit: f, extra: more, defaultView: over } = opts.current;
|
|
3750
|
+
const { controlled: c, unsized: u, size: box, fit: f, extra: more, defaultView: over, refit } = opts.current;
|
|
3427
3751
|
const canvas = canvasRef.current;
|
|
3428
3752
|
const bounds = diagramBounds(specs, more);
|
|
3429
|
-
if (c || u || canvas === null || bounds === null
|
|
3753
|
+
if (c || u || canvas === null || bounds === null) return;
|
|
3754
|
+
if (refit === "overflow" && inView(bounds, canvas.getView(), box)) return;
|
|
3430
3755
|
setLater({ view: fitDiagram(specs, box, f, more), over });
|
|
3431
3756
|
}, [specs, canvasRef, opts]);
|
|
3432
3757
|
return useMemo(() => {
|
|
@@ -3464,6 +3789,7 @@ function Inner({
|
|
|
3464
3789
|
hops,
|
|
3465
3790
|
width,
|
|
3466
3791
|
height,
|
|
3792
|
+
selectionMode = "single",
|
|
3467
3793
|
selected,
|
|
3468
3794
|
onSelect,
|
|
3469
3795
|
onHover,
|
|
@@ -3478,6 +3804,9 @@ function Inner({
|
|
|
3478
3804
|
onReorder,
|
|
3479
3805
|
onEditContainer,
|
|
3480
3806
|
connectBodies,
|
|
3807
|
+
canReparent,
|
|
3808
|
+
onDropItem,
|
|
3809
|
+
canDropItem,
|
|
3481
3810
|
pickEdges,
|
|
3482
3811
|
overlays = NO_OVERLAYS,
|
|
3483
3812
|
ambient: extraAmbient,
|
|
@@ -3490,6 +3819,8 @@ function Inner({
|
|
|
3490
3819
|
maxScale,
|
|
3491
3820
|
anchor,
|
|
3492
3821
|
fitPadding,
|
|
3822
|
+
bounded,
|
|
3823
|
+
refit,
|
|
3493
3824
|
background,
|
|
3494
3825
|
pages,
|
|
3495
3826
|
view,
|
|
@@ -3516,7 +3847,7 @@ function Inner({
|
|
|
3516
3847
|
initial: specs,
|
|
3517
3848
|
registry
|
|
3518
3849
|
});
|
|
3519
|
-
const selection = useSelection({ scene, mode: "single" });
|
|
3850
|
+
const selection = useSelection({ scene, mode: selectionMode === "multiple" ? "multi" : "single" });
|
|
3520
3851
|
const onClick = useMirroredSelection(selection, selected, onSelect);
|
|
3521
3852
|
useSceneSync(scene, specs, { onMove, onEditContainer });
|
|
3522
3853
|
const source = useMemo(() => sceneParticipants(scene), [scene]);
|
|
@@ -3527,14 +3858,19 @@ function Inner({
|
|
|
3527
3858
|
const { layers, box: pagesBox } = usePagesView(pages);
|
|
3528
3859
|
const canvasRef = useRef(null);
|
|
3529
3860
|
const current = useCallback(() => Array.from(source(), ({ node, pose }) => ({ ...node, pose })), [source]);
|
|
3861
|
+
const viewBounds = useMemo(
|
|
3862
|
+
() => bounded ? diagramViewBounds(specs, fitPadding ?? DEFAULT_FIT_PADDING, pagesBox) : void 0,
|
|
3863
|
+
[bounded, specs, fitPadding, pagesBox]
|
|
3864
|
+
);
|
|
3530
3865
|
const fitting = useDiagramFit({
|
|
3531
3866
|
canvasRef,
|
|
3532
3867
|
specs,
|
|
3533
3868
|
current,
|
|
3534
3869
|
controlled,
|
|
3535
3870
|
size: { width: width ?? measured?.width ?? 0, height: height ?? measured?.height ?? 0 },
|
|
3536
|
-
fit: { minScale, maxScale, anchor, padding: fitPadding },
|
|
3537
|
-
extra: pagesBox
|
|
3871
|
+
fit: { minScale, maxScale, anchor, padding: fitPadding, ...viewBounds ? { bounds: viewBounds } : {} },
|
|
3872
|
+
extra: pagesBox,
|
|
3873
|
+
...refit ? { refit } : {}
|
|
3538
3874
|
});
|
|
3539
3875
|
const { defaultView } = fitting;
|
|
3540
3876
|
const settled = useLatest((moved) => {
|
|
@@ -3573,6 +3909,8 @@ function Inner({
|
|
|
3573
3909
|
portOptions,
|
|
3574
3910
|
ambient: extraAmbient
|
|
3575
3911
|
});
|
|
3912
|
+
const itemDrop = useItemDrop(source, { onDropItem, canDropItem });
|
|
3913
|
+
const ambient = useMemo(() => itemDrop === null ? editing.ambient : [...editing.ambient, itemDrop], [editing.ambient, itemDrop]);
|
|
3576
3914
|
useEffect(() => {
|
|
3577
3915
|
const canvas = canvasRef.current;
|
|
3578
3916
|
if (canvas === null) return;
|
|
@@ -3596,7 +3934,7 @@ function Inner({
|
|
|
3596
3934
|
features,
|
|
3597
3935
|
...resizes ? { selectTool } : {},
|
|
3598
3936
|
enableKeybindings: enableKeybindings ?? edits,
|
|
3599
|
-
ambient
|
|
3937
|
+
ambient,
|
|
3600
3938
|
actions: editing.actions,
|
|
3601
3939
|
onClick,
|
|
3602
3940
|
...onHover ? { onHover } : {},
|
|
@@ -3606,9 +3944,10 @@ function Inner({
|
|
|
3606
3944
|
...defaultView ? { defaultView } : {},
|
|
3607
3945
|
...view ? { view } : {},
|
|
3608
3946
|
...onViewChange ? { onViewChange } : {},
|
|
3947
|
+
...viewBounds ? { viewBounds } : {},
|
|
3609
3948
|
...background ? { backgroundFill: background } : {},
|
|
3610
3949
|
...layers ? { layers } : {},
|
|
3611
|
-
children: /* @__PURE__ */ jsx(DropRules, { scene })
|
|
3950
|
+
children: /* @__PURE__ */ jsx(DropRules, { scene, canReparent })
|
|
3612
3951
|
}
|
|
3613
3952
|
),
|
|
3614
3953
|
overlays.map((o) => {
|
|
@@ -3618,8 +3957,13 @@ function Inner({
|
|
|
3618
3957
|
] });
|
|
3619
3958
|
}
|
|
3620
3959
|
var NO_OVERLAYS = [];
|
|
3621
|
-
function DropRules({ scene }) {
|
|
3622
|
-
const
|
|
3960
|
+
function DropRules({ scene, canReparent }) {
|
|
3961
|
+
const latest = useLatest(canReparent);
|
|
3962
|
+
const asks = canReparent !== void 0;
|
|
3963
|
+
const canDropInto = useMemo(
|
|
3964
|
+
() => diagramCanDropInto(scene, asks ? (ids, into) => latest.current?.(ids, into) ?? true : void 0),
|
|
3965
|
+
[scene, asks, latest]
|
|
3966
|
+
);
|
|
3623
3967
|
useDepSource("canDropInto", () => canDropInto);
|
|
3624
3968
|
return null;
|
|
3625
3969
|
}
|
|
@@ -3653,6 +3997,6 @@ function useDiagramApi(apiRef, canvasRef, scene, live, setRun, fitNow) {
|
|
|
3653
3997
|
}), [canvasRef, live, scene, setRun, fitNow]);
|
|
3654
3998
|
}
|
|
3655
3999
|
|
|
3656
|
-
export { ADD_LANE_ACTION_ID, ANYWHERE, CONNECT_ACTION_ID, CONTAINER_STENCILS, DEFAULT_EDGE_STROKE, DiagramView, EASED_LAYOUTS, EDGE_END_AFFORDANCE_KIND, EDGE_END_LAYER_ID, EDIT_CONTAINER_TITLE_ACTION_ID, GRAB_EDGE_END_ACTION_ID, GRAB_LABEL_ACTION_ID, GRAB_PORT_ACTION_ID, GRAB_WAYPOINT_ACTION_ID, LANE_AFFORDANCE_KIND, LANE_LAYER_ID, LANE_RESIZE_ACTION_ID, LAYOUTS, LAYOUT_ACTION_ID, MOVE_LABEL_ACTION_ID, PORT_AFFORDANCE_KIND, PORT_LAYER_ID, RECONNECT_ACTION_ID, RELEASE_MEMBERS_ACTION_ID, REMOVE_WAYPOINT_ACTION_ID, REORDER_ACTION_ID, RESET_LABEL_ACTION_ID, RESET_ROUTE_ACTION_ID, ROUTER_LABELS, ROUTE_EDIT_ACTION_ID, SELECT_LABEL_EDGE_ACTION_ID, SET_HOPS_ACTION_ID, SET_ROUTER_ACTION_IDS, STEP_REORDER_ACTION_ID, WAYPOINT_AFFORDANCE_KIND, WAYPOINT_HIT_PX, WAYPOINT_LAYER_ID, addLane, applyLayout, avoidSceneInput, bodyAt, commitEdgeToScene, commitLabelToScene, commitLaneResizeToScene, commitReconnectToScene, commitRouteToScene, commitRouterToScene, connectBinding, containerBuild, createAddLaneAction, createConnectAction, createDiagramContribution, createEdgeEndAffordance, createLabelDragAction, createLaneAffordance, createLaneResizeAction, createLayoutAction, createPortAffordance, createReconnectAction, createReleaseMembersAction, createRemoveWaypointAction, createReorderAction, createResetLabelAction, createResetRouteAction, createRouteEditAction, createSetHopsAction, createSetRouterAction, createSpecHops, createSpecLabelPlacer, createSpecRouter, createStepReorderAction, createWaypointAffordance, defaultCanConnect, diagramCanDropInto, diagramContainers, diagramDropParentAt, diagramEdgeEnds, diagramLabelDrag, diagramLayoutOf, diagramPorts, diagramResizable, diagramShape, diagramWaypoints, dragSegment, dragVertex, easedProducer, edgeEndLayer, edgeEndScratchOf, edgePolylines, editContainerTitleAction, editContainerTitleBinding, forceProducer, grabPortAction, grabPortBindings, groupReorderContribution, groupReorderSlots, headerIdOf, hopDataOps, inOrUnder, insertWaypoint, isBodyEnd, isBodyOrRegion, isDiagramBody, laneLayer, laneResizeFrame, laneScratchOf, lanesOf, layoutPoses, movedLabelData, pinSegment, poolAxisOf, poolsOf, portLayer, portScratchOf, reconnectBinding, regionHeaderAt, registerDiagramShape, releaseMembers, removeSegment, removeWaypoint, reorderBinding, reorderContribution, routeHandles, routeStateOf, routeWrites, routedData, routerActions, routesOf, sceneEdges, sceneParticipants, segmentAxis, sequenceSlots, themeWrites, unpinSegment, useLiveLayout, useMirroredSelection, waypointLayer, waypointScratchOf, withHopsData, withRouter };
|
|
3657
|
-
//# sourceMappingURL=chunk-
|
|
3658
|
-
//# sourceMappingURL=chunk-
|
|
4000
|
+
export { ADD_LANE_ACTION_ID, ANYWHERE, CONNECT_ACTION_ID, CONTAINERS_GROUP, CONTAINER_STENCILS, DEFAULT_EDGE_STROKE, DIAGRAM_ITEM_CARRY, DiagramView, EASED_LAYOUTS, EDGE_END_AFFORDANCE_KIND, EDGE_END_LAYER_ID, EDIT_CONTAINER_TITLE_ACTION_ID, GRAB_EDGE_END_ACTION_ID, GRAB_LABEL_ACTION_ID, GRAB_PORT_ACTION_ID, GRAB_WAYPOINT_ACTION_ID, ITEM_DROP_ACTION_ID, LANE_AFFORDANCE_KIND, LANE_LAYER_ID, LANE_RESIZE_ACTION_ID, LAYOUTS, LAYOUT_ACTION_ID, MOVE_LABEL_ACTION_ID, PORT_AFFORDANCE_KIND, PORT_LAYER_ID, RECONNECT_ACTION_ID, RELEASE_MEMBERS_ACTION_ID, REMOVE_WAYPOINT_ACTION_ID, REORDER_ACTION_ID, RESET_LABEL_ACTION_ID, RESET_ROUTE_ACTION_ID, ROUTER_LABELS, ROUTE_EDIT_ACTION_ID, SELECT_LABEL_EDGE_ACTION_ID, SET_HOPS_ACTION_ID, SET_ROUTER_ACTION_IDS, STEP_REORDER_ACTION_ID, WAYPOINT_AFFORDANCE_KIND, WAYPOINT_HIT_PX, WAYPOINT_LAYER_ID, addLane, applyLayout, avoidSceneInput, bodyAt, commitEdgeToScene, commitLabelToScene, commitLaneResizeToScene, commitReconnectToScene, commitRouteToScene, commitRouterToScene, connectBinding, containerBuild, createAddLaneAction, createConnectAction, createDiagramContribution, createEdgeEndAffordance, createItemDropAction, createLabelDragAction, createLaneAffordance, createLaneResizeAction, createLayoutAction, createPortAffordance, createReconnectAction, createReleaseMembersAction, createRemoveWaypointAction, createReorderAction, createResetLabelAction, createResetRouteAction, createRouteEditAction, createSetHopsAction, createSetRouterAction, createSpecHops, createSpecLabelPlacer, createSpecRouter, createStepReorderAction, createWaypointAffordance, defaultCanConnect, diagramCanDropInto, diagramContainers, diagramDropParentAt, diagramEdgeEnds, diagramLabelDrag, diagramLayoutOf, diagramPorts, diagramResizable, diagramShape, diagramWaypoints, dragItemProps, dragSegment, dragVertex, easedProducer, edgeEndLayer, edgeEndScratchOf, edgePolylines, editContainerTitleAction, editContainerTitleBinding, forceProducer, grabPortAction, grabPortBindings, groupReorderContribution, groupReorderSlots, headerIdOf, hopDataOps, inOrUnder, insertWaypoint, isBodyEnd, isBodyOrRegion, isDiagramBody, itemDropContribution, laneLayer, laneResizeFrame, laneScratchOf, lanesOf, layoutDiagram, layoutPoses, movedLabelData, pinSegment, poolAxisOf, poolsOf, portLayer, portScratchOf, reconnectBinding, regionHeaderAt, registerDiagramShape, releaseMembers, removeSegment, removeWaypoint, reorderBinding, reorderContribution, routeHandles, routeStateOf, routeWrites, routedData, routerActions, routesOf, sceneEdges, sceneParticipants, segmentAxis, sequenceSlots, startItemDrag, themeWrites, unpinSegment, useLiveLayout, useMirroredSelection, waypointLayer, waypointScratchOf, withHopsData, withRouter, worldWith };
|
|
4001
|
+
//# sourceMappingURL=chunk-62SMY45E.js.map
|
|
4002
|
+
//# sourceMappingURL=chunk-62SMY45E.js.map
|