@weasel-js/diagram 1.15.0 → 1.15.2
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 +114 -5
- package/dist/{chunk-GR2DK4S2.js → chunk-6YQSQXQT.js} +1485 -266
- package/dist/chunk-6YQSQXQT.js.map +1 -0
- package/dist/{chunk-FDCODMFW.js → chunk-7BV2MBED.js} +906 -606
- package/dist/chunk-7BV2MBED.js.map +1 -0
- package/dist/{chunk-IYUU5KUY.js → chunk-FQVZWS24.js} +11 -101
- package/dist/chunk-FQVZWS24.js.map +1 -0
- package/dist/{chunk-AXL4JQBM.js → chunk-LPTNAYER.js} +3 -3
- package/dist/{chunk-AXL4JQBM.js.map → chunk-LPTNAYER.js.map} +1 -1
- package/dist/{chunk-DE7UILJA.js → chunk-QGL6QJE4.js} +183 -23
- package/dist/chunk-QGL6QJE4.js.map +1 -0
- package/dist/{dataTypes-BIkhAOc4.d.ts → dataTypes-1ch6cMtq.d.ts} +12 -3
- package/dist/effect-audio.d.ts +2 -2
- package/dist/effect-audio.js +7 -11
- package/dist/effect-audio.js.map +1 -1
- package/dist/effect.d.ts +5 -5
- package/dist/effect.js +13 -20
- package/dist/effect.js.map +1 -1
- package/dist/icons.table.js +44 -0
- package/dist/icons.table.js.map +1 -1
- package/dist/icons.table.json +44 -0
- package/dist/index.d.ts +932 -37
- package/dist/index.js +707 -34
- package/dist/index.js.map +1 -1
- package/dist/{labelTrait-BBjRgxbz.d.ts → labelTrait-moIciyXr.d.ts} +4 -0
- package/dist/layout.d.ts +3 -3
- package/dist/{layout.entry-CbmGuqE-.d.ts → layout.entry-zstuljAl.d.ts} +2 -2
- package/dist/layout.js +2 -2
- package/dist/{stageTypes-UiO25hcn.d.ts → stageTypes-B7oF8Lww.d.ts} +1 -1
- package/dist/{types-B0n2CNTW.d.ts → types-D5ybp8Xh.d.ts} +32 -1
- package/package.json +12 -11
- package/dist/chunk-DE7UILJA.js.map +0 -1
- package/dist/chunk-FDCODMFW.js.map +0 -1
- package/dist/chunk-GR2DK4S2.js.map +0 -1
- package/dist/chunk-IYUU5KUY.js.map +0 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { createAvoidRouter, placeLabels, edgeHops,
|
|
2
|
-
import { edgeRouter, registerMarkShape, withDiagramRegistry, diagramLabelOf, editEdgeLabelBinding, editEdgeLabelAction, diagramGroupOf, diagramContainerOf, headerBox,
|
|
3
|
-
import { diagramEdgeOf, isPromise, diagramNodeOf, portsOf, portSitesOf, outlinePath, outlineDetail, samePoints, layoutWritesRoute, plannerPlan, planOf, layered, dataKeyReader, layoutBody, forceRelaxation, center,
|
|
4
|
-
import { bodyPredicate, dragPanContribution, WeaselProvider, useScene, useSelection, useLatest, SceneCanvas, useDepSource, RIGID_POSE_COMPOSITION, registerNodeShape, reconcileSpecs, pagesBounds, pagesLayer,
|
|
1
|
+
import { createAvoidRouter, placeLabels, edgeHops, DEFAULT_HOP_SIZE, HOPS_DOC_KEY, simplifyStops } from './chunk-FQVZWS24.js';
|
|
2
|
+
import { edgeRouter, registerMarkShape, withDiagramRegistry, diagramLabelOf, routesOf, layoutPoses, routeWrites, editEdgeLabelBinding, editEdgeLabelAction, diagramGroupOf, diagramContainerOf, headerBox, buildGraph, LAYOUTS, 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, diagramThemeFrom, themeFont, bodyBox, boxCenter, roleEdgeStyle, DEFAULT_FONT, withColors, roleStyle, GLYPH, themeLabelFont, firstLanes, DEFAULT_LANE_SIZE } from './chunk-QGL6QJE4.js';
|
|
3
|
+
import { diagramEdgeOf, isPromise, diagramNodeOf, portsOf, portSitesOf, outlinePath, outlineDetail, outlineSolid, samePoints, layoutWritesRoute, plannerPlan, planOf, layered, dataKeyReader, layoutBody, forceRelaxation, center, portOf, labelCenter, labelSpotAt, contentBox, boxForContent, grid, chain, tree, isPinned, layoutOwnsRoute } from './chunk-7BV2MBED.js';
|
|
4
|
+
import { bodyPredicate, dragPanContribution, WeaselProvider, useScene, useSelection, useLatest, SceneCanvas, useDepSource, viewRectToScreen, RIGID_POSE_COMPOSITION, registerNodeShape, reconcileSpecs, pagesBounds, pagesLayer, usePoseRun, drawnBoundsScene, pointAlongPath, scenePoseAdapter, drawnAABBOf, composeWorldPose, worldPosesOf, RECT_POSE_DESCRIPTOR, resolveFill, resolveStroke, commandFill, commandStroke, nodeGates, scenePoseFrame, fitViewToBounds, clampView, viewportWorldRect, AUTO_POSE_DESCRIPTOR, composeAffordanceLayer, createResizeAction, createDeleteAction, isTextData, effectivePose, polylineFromPoints, SNAP_ROLES, ActionDisabledReason, commitDeleteToScene, refusalOf, snapMods, documentDepOf, translatePoseViaDescriptor, definesFrame, paintsLayer, viewToTransform, ellipsePath, carriedDescendants, sceneCarrySource, derivedDepOf, localizeWorldPoses, PathBuilder, startCarry, createInsertOp, createSetDataOp, documentPose } from '@weasel-js/core';
|
|
5
5
|
import { createSettledCache } from '@weasel-js/geom/nd';
|
|
6
6
|
import { AUTO_POSE_DESCRIPTOR as AUTO_POSE_DESCRIPTOR$1, rotatePoint } from '@weasel-js/core/math';
|
|
7
|
-
import { clampRectWithin, placeRect, pathContainsPoint } from '@weasel-js/geom';
|
|
8
|
-
import { nudgeGap, moveItems, nearestGap, isNoopDrop } from '@weasel-js/
|
|
7
|
+
import { unionRects, clampRectWithin, placeRect, lerpPoint, rectContainsPoint, pathContainsPoint, rectContainsPointHalfOpen } from '@weasel-js/geom';
|
|
8
|
+
import { nudgeGap, moveItems, nearestGap, isNoopDrop } from '@weasel-js/ui-logic';
|
|
9
9
|
import { forwardRef, useMemo, useEffect, useState, useRef, useCallback, useImperativeHandle, useLayoutEffect } from 'react';
|
|
10
10
|
import { useLatest as useLatest$1, adoptCssOnce } from '@weasel-js/react';
|
|
11
11
|
import '@weasel-js/animator';
|
|
@@ -109,10 +109,10 @@ function createPortAffordance(participants, opts = {}) {
|
|
|
109
109
|
const built = index();
|
|
110
110
|
const rx = hitRadiusPx / Math.abs(view.scale.x);
|
|
111
111
|
const ry = hitRadiusPx / Math.abs(view.scale.y);
|
|
112
|
-
const
|
|
113
|
-
if (!built || !
|
|
112
|
+
const near = built && boxesMeeting(built.boxes, wx - rx, wy - ry, wx + rx, wy + ry);
|
|
113
|
+
if (!built || !near) return regions(state);
|
|
114
114
|
const out = [];
|
|
115
|
-
for (const i of
|
|
115
|
+
for (const i of near) {
|
|
116
116
|
const entry = built.at[i];
|
|
117
117
|
if (shown(entry.participant, state) && showsPort(entry.port)) out.push(regionAt(entry));
|
|
118
118
|
}
|
|
@@ -136,8 +136,8 @@ function bodyAt(participants, at, opts = {}) {
|
|
|
136
136
|
if (bounds.width <= 0 || bounds.height <= 0) continue;
|
|
137
137
|
const rotation = geometry.getRotation?.(participant.pose) ?? 0;
|
|
138
138
|
const local = rotation === 0 ? at : rotatePoint(at.x, at.y, bounds.x + bounds.width / 2, bounds.y + bounds.height / 2, -rotation);
|
|
139
|
-
const
|
|
140
|
-
if (
|
|
139
|
+
const inside = trait.outline === void 0 ? local.x >= bounds.x && local.x <= bounds.x + bounds.width && local.y >= bounds.y && local.y <= bounds.y + bounds.height : pathContainsPoint(outlinePath(trait.outline, bounds), local.x, local.y);
|
|
140
|
+
if (inside) hit = { end: { kind: "body", nodeId: node.id, point: { x: at.x, y: at.y } }, participant };
|
|
141
141
|
}
|
|
142
142
|
return hit;
|
|
143
143
|
}
|
|
@@ -352,8 +352,6 @@ function createConnectAction(opts) {
|
|
|
352
352
|
enabled: () => true
|
|
353
353
|
};
|
|
354
354
|
}
|
|
355
|
-
|
|
356
|
-
// src/containers/canDropInto.ts
|
|
357
355
|
function diagramCanDropInto(scene, canReparent) {
|
|
358
356
|
const nodeOf = (id) => scene.get(id);
|
|
359
357
|
const roleOf = (id) => {
|
|
@@ -391,8 +389,7 @@ function diagramDropParentAt(scene, point, role) {
|
|
|
391
389
|
const world = frame === null || compose === void 0 ? pose : compose(frame, pose);
|
|
392
390
|
const trait = diagramContainerOf(node);
|
|
393
391
|
const b = bodyBox(world, trait?.header);
|
|
394
|
-
|
|
395
|
-
if (!inside2) continue;
|
|
392
|
+
if (!rectContainsPointHalfOpen(b, point.x, point.y)) continue;
|
|
396
393
|
if (trait?.role === "container" || trait?.role === "lane") best = id;
|
|
397
394
|
walk(scene.childrenOf(id), world);
|
|
398
395
|
}
|
|
@@ -452,7 +449,6 @@ function slideOthers(scene, slots, carried) {
|
|
|
452
449
|
// src/reorder.ts
|
|
453
450
|
var REORDER_ACTION_ID = "diagram.reorder";
|
|
454
451
|
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
452
|
function sequenceSlots(axis, items) {
|
|
457
453
|
const [along2, across, size, depth] = axis === "x" ? ["x", "y", "width", "height"] : ["y", "x", "height", "width"];
|
|
458
454
|
const lo = (b) => b[along2];
|
|
@@ -474,7 +470,7 @@ function sequenceSlots(axis, items) {
|
|
|
474
470
|
return { axis, items, gaps };
|
|
475
471
|
}
|
|
476
472
|
function pickAt(slots, at) {
|
|
477
|
-
const from = slots?.items.findIndex((i) => i.grip !== null &&
|
|
473
|
+
const from = slots?.items.findIndex((i) => i.grip !== null && rectContainsPoint(i.grip ?? i.bounds, at.x, at.y)) ?? -1;
|
|
478
474
|
return from < 0 ? null : { from, item: slots.items[from] };
|
|
479
475
|
}
|
|
480
476
|
function gapAt(held, by) {
|
|
@@ -507,8 +503,8 @@ function createReorderAction(opts) {
|
|
|
507
503
|
if (slots === null || picked === null) return {};
|
|
508
504
|
const held = { slots, ...picked };
|
|
509
505
|
const { axis } = slots;
|
|
510
|
-
const
|
|
511
|
-
const leaves = (at) =>
|
|
506
|
+
const region2 = opts.region?.(ctx.deps, held.item) ?? null;
|
|
507
|
+
const leaves = (at) => region2 !== null && !rectContainsPoint(region2, at.x, at.y);
|
|
512
508
|
let by = ctx.world[axis] - start[axis];
|
|
513
509
|
let open = true;
|
|
514
510
|
const verdicts = /* @__PURE__ */ new Map();
|
|
@@ -597,14 +593,6 @@ function worldRectOf(scene, id) {
|
|
|
597
593
|
const live = { ...scenePoseAdapter(scene), getPose: (at) => effectivePose(scene, scene.get(at)) };
|
|
598
594
|
return drawnAABBOf(drawn, id, composeWorldPose(live, id, scene.composition.compose), { includeStroke: false });
|
|
599
595
|
}
|
|
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
596
|
var CENTER = "center";
|
|
609
597
|
function place(el, overlay, anchor, view) {
|
|
610
598
|
const size = { width: el.offsetWidth, height: el.offsetHeight };
|
|
@@ -626,7 +614,7 @@ function useOverlayPlacement(canvasRef, scene, overlays) {
|
|
|
626
614
|
const world = worldRectOf(scene, overlay.target);
|
|
627
615
|
el.hidden = world === null;
|
|
628
616
|
if (world === null) continue;
|
|
629
|
-
const rect =
|
|
617
|
+
const rect = viewRectToScreen(view, world);
|
|
630
618
|
const anchor = { ...rect, x: rect.x + surface.offsetLeft, y: rect.y + surface.offsetTop };
|
|
631
619
|
place(el, overlay, anchor, {
|
|
632
620
|
x: surface.offsetLeft,
|
|
@@ -920,6 +908,7 @@ function themeWrites(scene, theme, ids, from) {
|
|
|
920
908
|
const data = node.data;
|
|
921
909
|
if (data === null || typeof data !== "object") continue;
|
|
922
910
|
const trait = traitOf(data);
|
|
911
|
+
if (trait !== null && ("seq" in trait || "sequence" in trait)) continue;
|
|
923
912
|
if (trait !== null && "from" in trait && "to" in trait) {
|
|
924
913
|
if (ids !== void 0 && !ids.has(node.id)) continue;
|
|
925
914
|
const look2 = edgeLook(trait, theme);
|
|
@@ -980,7 +969,7 @@ function containerBuild(stencil, bounds, opts) {
|
|
|
980
969
|
const id = opts.newId();
|
|
981
970
|
const axis = stencil.axis ?? "y";
|
|
982
971
|
const at = placedBounds(stencil, bounds);
|
|
983
|
-
const title = stencil.role === "pool" ? "Pool" : stencil.label;
|
|
972
|
+
const title = stencil.title ?? (stencil.role === "pool" ? "Pool" : stencil.label);
|
|
984
973
|
const style = stencil.role === "pool" && opts.laneStyles ? opts.laneStyles.header : opts.style;
|
|
985
974
|
const specs = [
|
|
986
975
|
...regionSpecs(stencil.role, axis, at, title, {
|
|
@@ -989,7 +978,7 @@ function containerBuild(stencil, bounds, opts) {
|
|
|
989
978
|
parent: opts.parent ?? null,
|
|
990
979
|
...style ? { style } : {}
|
|
991
980
|
}),
|
|
992
|
-
...stencil.role === "pool" ? firstLanes(id, axis, at, opts) : []
|
|
981
|
+
...stencil.role === "pool" ? firstLanes(id, axis, at, opts, stencil.lanes) : []
|
|
993
982
|
];
|
|
994
983
|
const label = `Insert ${stencil.label}`;
|
|
995
984
|
const stored = localizeWorldPoses(specs, RIGID_POSE_COMPOSITION);
|
|
@@ -1442,19 +1431,19 @@ function createLabelDragAction(opts = {}) {
|
|
|
1442
1431
|
if (p === null) return {};
|
|
1443
1432
|
let spot = null;
|
|
1444
1433
|
let open = true;
|
|
1445
|
-
const
|
|
1434
|
+
const proposed2 = () => open ? spot : null;
|
|
1446
1435
|
return {
|
|
1447
1436
|
kind: "diagram-move-label",
|
|
1448
1437
|
onMove(moveCtx) {
|
|
1449
1438
|
spot = spotAt(p, moveCtx);
|
|
1450
1439
|
},
|
|
1451
|
-
previewIds: () =>
|
|
1440
|
+
previewIds: () => proposed2() === null ? null : [p.labelId],
|
|
1452
1441
|
previewPose: (id) => {
|
|
1453
|
-
const next =
|
|
1442
|
+
const next = proposed2();
|
|
1454
1443
|
return id === p.labelId && next !== null ? poseAt(p, next) : null;
|
|
1455
1444
|
},
|
|
1456
1445
|
previewData: (id) => {
|
|
1457
|
-
const next =
|
|
1446
|
+
const next = proposed2();
|
|
1458
1447
|
return id === p.labelId && next !== null ? movedLabelData(p.data, next) : null;
|
|
1459
1448
|
},
|
|
1460
1449
|
onEnd(endCtx, reason) {
|
|
@@ -1928,16 +1917,16 @@ function createRouteEditAction(opts) {
|
|
|
1928
1917
|
const session = beginSnap(ctx, p, snapPx);
|
|
1929
1918
|
let waypoints = null;
|
|
1930
1919
|
let open = true;
|
|
1931
|
-
const
|
|
1920
|
+
const proposed2 = () => open ? waypoints : null;
|
|
1932
1921
|
return {
|
|
1933
1922
|
kind: "diagram-edit-route",
|
|
1934
1923
|
onMove(moveCtx) {
|
|
1935
1924
|
waypoints = waypointsAt(p, moveOf2(p, session, moveCtx));
|
|
1936
1925
|
},
|
|
1937
|
-
previewIds: () =>
|
|
1938
|
-
previewPose: (id) => id === p.edgeId &&
|
|
1926
|
+
previewIds: () => proposed2() === null ? null : [p.edgeId],
|
|
1927
|
+
previewPose: (id) => id === p.edgeId && proposed2() !== null ? p.pose : null,
|
|
1939
1928
|
previewData: (id) => {
|
|
1940
|
-
const next =
|
|
1929
|
+
const next = proposed2();
|
|
1941
1930
|
return id === p.edgeId && next !== null ? routedData(p.data, p.trait, next) : null;
|
|
1942
1931
|
},
|
|
1943
1932
|
onEnd(endCtx, reason) {
|
|
@@ -2255,8 +2244,8 @@ function avoidSceneInput(scene, opts = {}, poseOf) {
|
|
|
2255
2244
|
const header = container === null ? null : headerBox(box, container.header);
|
|
2256
2245
|
if (header !== null) obstacles.push({ id: headerIdOf(node.id), kind: "shape", box: header });
|
|
2257
2246
|
if (container?.role === "lane") continue;
|
|
2258
|
-
const
|
|
2259
|
-
obstacles.push(
|
|
2247
|
+
const region2 = container !== null || diagramGroupOf(node) !== null;
|
|
2248
|
+
obstacles.push(region2 ? { id: node.id, kind: "region", box, members: members(sc, node, /* @__PURE__ */ new Set()) } : { id: node.id, kind: "shape", box });
|
|
2260
2249
|
}
|
|
2261
2250
|
for (const id of split) {
|
|
2262
2251
|
const node = labelNodes.get(id);
|
|
@@ -2304,10 +2293,10 @@ function obstaclesOf(specs, world) {
|
|
|
2304
2293
|
if (s.parent == null) continue;
|
|
2305
2294
|
children.set(s.parent, [...children.get(s.parent) ?? [], s.id]);
|
|
2306
2295
|
}
|
|
2307
|
-
const
|
|
2296
|
+
const inside = (id, into) => {
|
|
2308
2297
|
for (const child of children.get(id) ?? []) {
|
|
2309
2298
|
into.add(child);
|
|
2310
|
-
|
|
2299
|
+
inside(child, into);
|
|
2311
2300
|
}
|
|
2312
2301
|
return into;
|
|
2313
2302
|
};
|
|
@@ -2315,13 +2304,13 @@ function obstaclesOf(specs, world) {
|
|
|
2315
2304
|
for (const spec of specs) {
|
|
2316
2305
|
const node = { id: spec.id, kind: spec.kind, data: spec.data };
|
|
2317
2306
|
if (spec.derivePose !== void 0 || diagramEdgeOf(spec) !== null) continue;
|
|
2318
|
-
const
|
|
2319
|
-
if (diagramNodeOf(node) === null &&
|
|
2307
|
+
const region2 = diagramContainerOf(spec);
|
|
2308
|
+
if (diagramNodeOf(node) === null && region2?.role !== "lane") continue;
|
|
2320
2309
|
const box = world.get(node.id) ?? spec.pose;
|
|
2321
|
-
const header =
|
|
2310
|
+
const header = region2 === null ? null : headerBox(box, region2.header);
|
|
2322
2311
|
if (header !== null) out.push({ id: headerIdOf(node.id), kind: "shape", box: header });
|
|
2323
|
-
if (
|
|
2324
|
-
out.push(
|
|
2312
|
+
if (region2?.role === "lane") continue;
|
|
2313
|
+
out.push(region2 === null ? { id: node.id, kind: "shape", box } : { id: node.id, kind: "region", box, members: inside(node.id, /* @__PURE__ */ new Set()) });
|
|
2325
2314
|
}
|
|
2326
2315
|
return out;
|
|
2327
2316
|
}
|
|
@@ -2684,168 +2673,27 @@ function createSpecLabelPlacer(opts = {}) {
|
|
|
2684
2673
|
};
|
|
2685
2674
|
}
|
|
2686
2675
|
|
|
2676
|
+
// src/sequence/trait.ts
|
|
2677
|
+
var traitOf2 = (node) => {
|
|
2678
|
+
const data = node.data;
|
|
2679
|
+
if (data === null || typeof data !== "object") return null;
|
|
2680
|
+
const trait = data.diagram;
|
|
2681
|
+
return trait !== null && typeof trait === "object" ? trait : null;
|
|
2682
|
+
};
|
|
2683
|
+
function sequenceRootOf(node) {
|
|
2684
|
+
const trait = traitOf2(node);
|
|
2685
|
+
return trait !== null && "sequence" in trait && "geometry" in trait ? trait : null;
|
|
2686
|
+
}
|
|
2687
|
+
function sequencePartOf(node) {
|
|
2688
|
+
const seq = traitOf2(node)?.seq;
|
|
2689
|
+
return seq !== null && typeof seq === "object" && typeof seq.root === "string" ? seq : null;
|
|
2690
|
+
}
|
|
2691
|
+
|
|
2687
2692
|
// src/resizable.ts
|
|
2688
2693
|
function diagramResizable(scene) {
|
|
2689
2694
|
return (id) => {
|
|
2690
2695
|
const node = scene.get(id);
|
|
2691
|
-
return node === void 0 || diagramEdgeOf(node) === null && diagramLabelOf(node) === null && diagramGroupOf(node) === null && diagramContainerOf(node)?.role !== "lane";
|
|
2692
|
-
};
|
|
2693
|
-
}
|
|
2694
|
-
|
|
2695
|
-
// src/routeWrites.ts
|
|
2696
|
-
var ROUTE_TOLERANCE = 1e-3;
|
|
2697
|
-
function routesOf(graph, plan) {
|
|
2698
|
-
return {
|
|
2699
|
-
edges: new Set(graph.edges.map((e) => e.origin?.id ?? e.id)),
|
|
2700
|
-
waypoints: plan.waypoints ?? /* @__PURE__ */ new Map(),
|
|
2701
|
-
...plan.labels !== void 0 ? { labels: plan.labels } : {},
|
|
2702
|
-
...plan.ports !== void 0 ? { ports: plan.ports } : {}
|
|
2703
|
-
};
|
|
2704
|
-
}
|
|
2705
|
-
function routeWrites(scene, routes) {
|
|
2706
|
-
const out = /* @__PURE__ */ new Map();
|
|
2707
|
-
const owned = /* @__PURE__ */ new Set();
|
|
2708
|
-
for (const id of routes.edges) {
|
|
2709
|
-
const node = scene.get(id);
|
|
2710
|
-
const edge = node === void 0 ? null : diagramEdgeOf(node);
|
|
2711
|
-
if (node === void 0 || edge === null || scene.isLocked(id)) {
|
|
2712
|
-
if (edge !== null && layoutWritesRoute(edge)) owned.add(id);
|
|
2713
|
-
continue;
|
|
2714
|
-
}
|
|
2715
|
-
let next = edge;
|
|
2716
|
-
if (layoutWritesRoute(edge)) {
|
|
2717
|
-
owned.add(id);
|
|
2718
|
-
next = rerouted(edge, routes.waypoints.get(id));
|
|
2719
|
-
}
|
|
2720
|
-
const ports = routes.ports?.get(id);
|
|
2721
|
-
const from = renamed(edge.from, ports?.from);
|
|
2722
|
-
const to = renamed(edge.to, ports?.to);
|
|
2723
|
-
if (from !== edge.from || to !== edge.to) next = { ...next, from, to };
|
|
2724
|
-
if (next !== edge) out.set(id, withTrait(node.data, next));
|
|
2725
|
-
}
|
|
2726
|
-
const stationed = /* @__PURE__ */ new Set();
|
|
2727
|
-
for (const node of scene.renderOrderNodes()) {
|
|
2728
|
-
const label = diagramLabelOf(node);
|
|
2729
|
-
const deps = node.dependsOn;
|
|
2730
|
-
if (label === null || !Array.isArray(deps) || deps.length !== 1 || !owned.has(deps[0])) continue;
|
|
2731
|
-
if (!labelFollowsLayout(label.at) || stationed.has(deps[0])) continue;
|
|
2732
|
-
stationed.add(deps[0]);
|
|
2733
|
-
const near2 = routes.labels?.get(deps[0]);
|
|
2734
|
-
const at = near2 === void 0 ? "mid" : { near: near2 };
|
|
2735
|
-
if (sameAt(label.at, at) || scene.isLocked(node.id)) continue;
|
|
2736
|
-
out.set(node.id, withTrait(node.data, { ...label, at }, "label"));
|
|
2737
|
-
}
|
|
2738
|
-
return out;
|
|
2739
|
-
}
|
|
2740
|
-
function rerouted(edge, route) {
|
|
2741
|
-
if (route === void 0 || route.length === 0) {
|
|
2742
|
-
if ((edge.waypoints ?? []).length === 0 && edge.laidOut === void 0) return edge;
|
|
2743
|
-
const { waypoints: _w, laidOut: _l, ...rest } = edge;
|
|
2744
|
-
return rest;
|
|
2745
|
-
}
|
|
2746
|
-
if (edge.waypoints !== void 0 && near(edge.waypoints, route)) return edge;
|
|
2747
|
-
return { ...edge, waypoints: route, laidOut: route };
|
|
2748
|
-
}
|
|
2749
|
-
function renamed(end, port) {
|
|
2750
|
-
if (!layoutOwnsPort(end) || end.port === port && (port === void 0 || end.laidOut === port)) return end;
|
|
2751
|
-
const { port: _p, laidOut: _l, ...rest } = end;
|
|
2752
|
-
return port === void 0 ? rest : { ...rest, port, laidOut: port };
|
|
2753
|
-
}
|
|
2754
|
-
function near(a, b) {
|
|
2755
|
-
return a.length === b.length && a.every((p, i) => Math.abs(p.x - b[i].x) <= ROUTE_TOLERANCE && Math.abs(p.y - b[i].y) <= ROUTE_TOLERANCE);
|
|
2756
|
-
}
|
|
2757
|
-
function sameAt(a, b) {
|
|
2758
|
-
if (typeof a === "object" && typeof b === "object") return near([a.near], [b.near]);
|
|
2759
|
-
return a === b;
|
|
2760
|
-
}
|
|
2761
|
-
function withTrait(data, value, key) {
|
|
2762
|
-
const own = data;
|
|
2763
|
-
const diagram = key === void 0 ? value : { ...own.diagram, [key]: value };
|
|
2764
|
-
return { ...own, diagram };
|
|
2765
|
-
}
|
|
2766
|
-
var LAYOUTS = Object.freeze({
|
|
2767
|
-
layered,
|
|
2768
|
-
tree,
|
|
2769
|
-
force,
|
|
2770
|
-
chain,
|
|
2771
|
-
radial
|
|
2772
|
-
});
|
|
2773
|
-
function applyLayout(scene, result, opts = {}) {
|
|
2774
|
-
const poses = layoutPoses(scene, result, opts.geometry);
|
|
2775
|
-
const data = opts.routes === void 0 ? /* @__PURE__ */ new Map() : routeWrites(scene, opts.routes);
|
|
2776
|
-
if (poses.size === 0 && data.size === 0) return 0;
|
|
2777
|
-
const open = [...poses].filter(([id]) => !scene.isLocked(id));
|
|
2778
|
-
if (open.length === 0 && data.size === 0) return 0;
|
|
2779
|
-
scene.batch(opts.label ?? "Layout", () => {
|
|
2780
|
-
for (const [id, pose] of open) scene.setPose(id, pose);
|
|
2781
|
-
for (const [id, d] of data) scene.update(id, { data: d });
|
|
2782
|
-
});
|
|
2783
|
-
const wrote = new Set(open.map(([id]) => id));
|
|
2784
|
-
let moved = 0;
|
|
2785
|
-
for (const id of result.keys()) if (wrote.has(id)) moved++;
|
|
2786
|
-
return moved;
|
|
2787
|
-
}
|
|
2788
|
-
function layoutPoses(scene, result, geometry = AUTO_POSE_DESCRIPTOR) {
|
|
2789
|
-
const sc = scene;
|
|
2790
|
-
const { compose, decompose } = sc.composition;
|
|
2791
|
-
const stored = scenePoseAdapter(sc);
|
|
2792
|
-
const out = /* @__PURE__ */ new Map();
|
|
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);
|
|
2804
|
-
const dx = at.x - bounds.x;
|
|
2805
|
-
const dy = at.y - bounds.y;
|
|
2806
|
-
if (dx === 0 && dy === 0) continue;
|
|
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);
|
|
2811
|
-
}
|
|
2812
|
-
return out;
|
|
2813
|
-
}
|
|
2814
|
-
var LAYOUT_ACTION_ID = "diagram.layout";
|
|
2815
|
-
function layoutDiagram(scene, source, algorithm, opts = {}) {
|
|
2816
|
-
const fn = typeof algorithm === "function" ? algorithm : LAYOUTS[algorithm] ?? layered;
|
|
2817
|
-
const graph = buildGraph(source, {
|
|
2818
|
-
...opts.read ? { read: opts.read } : {},
|
|
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 } : {}
|
|
2835
|
-
};
|
|
2836
|
-
return {
|
|
2837
|
-
id: opts.id ?? LAYOUT_ACTION_ID,
|
|
2838
|
-
label: opts.label ?? "Lay out diagram",
|
|
2839
|
-
group: opts.group ?? "diagram",
|
|
2840
|
-
requires: ["scene"],
|
|
2841
|
-
invoker: {
|
|
2842
|
-
timing: "immediate",
|
|
2843
|
-
run: (deps) => {
|
|
2844
|
-
const scene = deps["scene"];
|
|
2845
|
-
if (scene !== void 0) layoutDiagram(scene, opts.source, opts.algorithm ?? "layered", options);
|
|
2846
|
-
}
|
|
2847
|
-
},
|
|
2848
|
-
enabled: () => true
|
|
2696
|
+
return node === void 0 || diagramEdgeOf(node) === null && diagramLabelOf(node) === null && diagramGroupOf(node) === null && diagramContainerOf(node)?.role !== "lane" && sequencePartOf(node) === null;
|
|
2849
2697
|
};
|
|
2850
2698
|
}
|
|
2851
2699
|
var EASE_FRAMES = 24;
|
|
@@ -2880,14 +2728,14 @@ function forceProducer(graph, opts = {}) {
|
|
|
2880
2728
|
}
|
|
2881
2729
|
relaxation.sim.alphaTarget(pinned.size > 0 ? dragAlpha : 0);
|
|
2882
2730
|
relaxation.sim.tick();
|
|
2883
|
-
let
|
|
2731
|
+
let moved2 = 0;
|
|
2884
2732
|
relaxation.bodies.forEach((body, i) => {
|
|
2885
2733
|
const at = was[i];
|
|
2886
|
-
|
|
2734
|
+
moved2 = Math.max(moved2, Math.hypot(body.x - at.x, body.y - at.y));
|
|
2887
2735
|
at.x = body.x;
|
|
2888
2736
|
at.y = body.y;
|
|
2889
2737
|
});
|
|
2890
|
-
still = pinned.size === 0 &&
|
|
2738
|
+
still = pinned.size === 0 && moved2 < restDistance ? still + 1 : 0;
|
|
2891
2739
|
const done = still >= restTicks || relaxation.sim.isSettled();
|
|
2892
2740
|
return { result: relaxation.placed(), done };
|
|
2893
2741
|
};
|
|
@@ -2956,19 +2804,18 @@ function usePendingPlan() {
|
|
|
2956
2804
|
useEffect(() => () => pending.drop(), [pending]);
|
|
2957
2805
|
return pending;
|
|
2958
2806
|
}
|
|
2959
|
-
var lerp = (a, b, u) => ({ x: a.x + (b.x - a.x) * u, y: a.y + (b.y - a.y) * u });
|
|
2960
2807
|
function along(points, s) {
|
|
2961
2808
|
if (points.length === 1) return points[0];
|
|
2962
2809
|
const lengths = points.slice(1).map((p, i) => Math.hypot(p.x - points[i].x, p.y - points[i].y));
|
|
2963
2810
|
const total = lengths.reduce((a, b) => a + b, 0);
|
|
2964
2811
|
let left = s * total;
|
|
2965
2812
|
for (const [i, len] of lengths.entries()) {
|
|
2966
|
-
if (left <= len || i === lengths.length - 1) return
|
|
2813
|
+
if (left <= len || i === lengths.length - 1) return lerpPoint(points[i], points[i + 1], len === 0 ? 0 : Math.min(left / len, 1));
|
|
2967
2814
|
left -= len;
|
|
2968
2815
|
}
|
|
2969
2816
|
return points[points.length - 1];
|
|
2970
2817
|
}
|
|
2971
|
-
var straightRun = (count, start, end) => Array.from({ length: count }, (_, k) =>
|
|
2818
|
+
var straightRun = (count, start, end) => Array.from({ length: count }, (_, k) => lerpPoint(start, end, (k + 1) / (count + 1)));
|
|
2972
2819
|
function respread(points, count, start, end) {
|
|
2973
2820
|
if (points.length === count) return [...points];
|
|
2974
2821
|
if (points.length === 0) return straightRun(count, start, end);
|
|
@@ -3029,7 +2876,7 @@ function easedPaths(scene, easings, u, poseOf, geometry = AUTO_POSE_DESCRIPTOR)
|
|
|
3029
2876
|
const [a, b] = deps;
|
|
3030
2877
|
target = u >= 1 || a === void 0 || b === void 0 ? [] : straightRun(from.length, centerOf(a), centerOf(b));
|
|
3031
2878
|
}
|
|
3032
|
-
const waypoints = target.length === 0 && u >= 1 ? [] : from.map((p, k) =>
|
|
2879
|
+
const waypoints = target.length === 0 && u >= 1 ? [] : from.map((p, k) => lerpPoint(p, target[k], u));
|
|
3033
2880
|
const data = node.data;
|
|
3034
2881
|
const diagram = { ...data.diagram, ...ends, waypoints };
|
|
3035
2882
|
const path = node.derivePath({ ...node, data: { ...data, diagram } }, deps);
|
|
@@ -3143,7 +2990,7 @@ function useLiveLayout(opts) {
|
|
|
3143
2990
|
const o = optsRef.current;
|
|
3144
2991
|
producerFrame.current = 0;
|
|
3145
2992
|
held.current = false;
|
|
3146
|
-
routes.current = routesOf(graph, plan ?? { });
|
|
2993
|
+
routes.current = routesOf(graph, plan ?? { moves: /* @__PURE__ */ new Map() });
|
|
3147
2994
|
eased.set(routeEasings(o.scene, graph, routes.current));
|
|
3148
2995
|
producer.current = producerFor(graph, plan, o);
|
|
3149
2996
|
}, [optsRef, eased]);
|
|
@@ -3168,9 +3015,9 @@ function useLiveLayout(opts) {
|
|
|
3168
3015
|
scene: opts.scene,
|
|
3169
3016
|
step,
|
|
3170
3017
|
// Eased routes go once the routes are in the document, so no frame shows the old ones.
|
|
3171
|
-
onCommit: (
|
|
3018
|
+
onCommit: (moved2) => {
|
|
3172
3019
|
eased.drop();
|
|
3173
|
-
optsRef.current.onSettle?.(
|
|
3020
|
+
optsRef.current.onSettle?.(moved2);
|
|
3174
3021
|
},
|
|
3175
3022
|
// Routes match where the layout was going, so only a run that got there
|
|
3176
3023
|
// with nothing held along the way writes them.
|
|
@@ -3237,7 +3084,8 @@ function useLiveLayout(opts) {
|
|
|
3237
3084
|
var EASED_LAYOUTS = Object.freeze({
|
|
3238
3085
|
layered,
|
|
3239
3086
|
tree,
|
|
3240
|
-
chain
|
|
3087
|
+
chain,
|
|
3088
|
+
grid
|
|
3241
3089
|
});
|
|
3242
3090
|
function bodyRules(trait, box) {
|
|
3243
3091
|
const layout = trait.body;
|
|
@@ -3254,7 +3102,7 @@ function bodyRules(trait, box) {
|
|
|
3254
3102
|
// src/shape.ts
|
|
3255
3103
|
function diagramShape(opts = {}) {
|
|
3256
3104
|
const geometry = opts.geometry ?? RECT_POSE_DESCRIPTOR;
|
|
3257
|
-
const outlineOf2 = (node) => diagramNodeOf(node, opts.read)?.outline ?? laneOutlineOf(node);
|
|
3105
|
+
const outlineOf2 = (node) => diagramNodeOf(node, opts.read)?.outline ?? laneOutlineOf(node) ?? sequenceOutlineOf(node);
|
|
3258
3106
|
return {
|
|
3259
3107
|
id: opts.id ?? "diagram:outline",
|
|
3260
3108
|
matches: (node) => outlineOf2(node) !== void 0,
|
|
@@ -3271,6 +3119,9 @@ function diagramShape(opts = {}) {
|
|
|
3271
3119
|
const divider = header === null || !stroke ? [] : [{ kind: "path", path: dividerPath(box, header), stroke }];
|
|
3272
3120
|
const detailPath = stroke ? outlineDetail(outline, box) : null;
|
|
3273
3121
|
const detail = stroke && detailPath ? [{ kind: "path", path: detailPath, stroke }] : [];
|
|
3122
|
+
const solidPath = declaredStroke ? outlineSolid(outline, box) : null;
|
|
3123
|
+
const solidFill = declaredStroke && solidPath ? commandFill({ paint: declaredStroke.paint }, box) : null;
|
|
3124
|
+
const solid = solidPath && solidFill ? [{ kind: "path", path: solidPath, fill: solidFill }] : [];
|
|
3274
3125
|
const trait = diagramNodeOf(node, opts.read);
|
|
3275
3126
|
const rules = stroke && trait !== null ? bodyRules(trait, box).map((path) => ({ kind: "path", path, stroke })) : [];
|
|
3276
3127
|
return [{
|
|
@@ -3278,7 +3129,7 @@ function diagramShape(opts = {}) {
|
|
|
3278
3129
|
path: outlinePath(outline, box),
|
|
3279
3130
|
...fill ? { fill } : {},
|
|
3280
3131
|
...stroke ? { stroke } : {}
|
|
3281
|
-
}, ...detail, ...rules, ...divider];
|
|
3132
|
+
}, ...solid, ...detail, ...rules, ...divider];
|
|
3282
3133
|
},
|
|
3283
3134
|
// The drawn boundary, so picking and clipping follow the diamond rather
|
|
3284
3135
|
// than its bounding box.
|
|
@@ -3288,6 +3139,10 @@ function diagramShape(opts = {}) {
|
|
|
3288
3139
|
}
|
|
3289
3140
|
};
|
|
3290
3141
|
}
|
|
3142
|
+
function sequenceOutlineOf(node) {
|
|
3143
|
+
if (sequencePartOf(node) === null) return void 0;
|
|
3144
|
+
return node.data.diagram?.outline;
|
|
3145
|
+
}
|
|
3291
3146
|
function dividerPath(box, header) {
|
|
3292
3147
|
return header.width < box.width ? polylineFromPoints([{ x: header.x + header.width, y: box.y }, { x: header.x + header.width, y: box.y + box.height }]) : polylineFromPoints([{ x: box.x, y: header.y + header.height }, { x: box.x + box.width, y: header.y + header.height }]);
|
|
3293
3148
|
}
|
|
@@ -3298,6 +3153,1338 @@ function registerDiagramShape(opts = {}) {
|
|
|
3298
3153
|
// src/bodyNode.ts
|
|
3299
3154
|
var isDiagramBody = (node) => diagramContainerOf(node) === null && diagramGroupOf(node) === null && dataKeyReader(node) !== null;
|
|
3300
3155
|
var isBodyOrRegion = (node) => diagramContainerOf(node) !== null || isDiagramBody(node);
|
|
3156
|
+
|
|
3157
|
+
// src/sequence/edit.ts
|
|
3158
|
+
var sameList = (a, b) => a === b || a !== null && b !== null && a.fragment === b.fragment && a.section === b.section;
|
|
3159
|
+
var hasId = (s) => "id" in s;
|
|
3160
|
+
function stepListOf(steps, parent) {
|
|
3161
|
+
if (parent === null) return steps;
|
|
3162
|
+
const frag = findStep(steps, parent.fragment);
|
|
3163
|
+
return frag?.kind === "fragment" ? frag.sections[parent.section]?.steps ?? null : null;
|
|
3164
|
+
}
|
|
3165
|
+
function findStep(steps, id) {
|
|
3166
|
+
for (const s of steps) {
|
|
3167
|
+
if (hasId(s) && s.id === id) return s;
|
|
3168
|
+
if (s.kind === "fragment") {
|
|
3169
|
+
for (const section of s.sections) {
|
|
3170
|
+
const found = findStep(section.steps, id);
|
|
3171
|
+
if (found !== null) return found;
|
|
3172
|
+
}
|
|
3173
|
+
}
|
|
3174
|
+
}
|
|
3175
|
+
return null;
|
|
3176
|
+
}
|
|
3177
|
+
function placeOf(steps, id, parent = null) {
|
|
3178
|
+
for (const [index, s] of steps.entries()) {
|
|
3179
|
+
if (hasId(s) && s.id === id) return { parent, index };
|
|
3180
|
+
if (s.kind === "fragment") {
|
|
3181
|
+
for (const [k, section] of s.sections.entries()) {
|
|
3182
|
+
const found = placeOf(section.steps, id, { fragment: s.id, section: k });
|
|
3183
|
+
if (found !== null) return found;
|
|
3184
|
+
}
|
|
3185
|
+
}
|
|
3186
|
+
}
|
|
3187
|
+
return null;
|
|
3188
|
+
}
|
|
3189
|
+
function mapSteps(steps, fn) {
|
|
3190
|
+
let changed = false;
|
|
3191
|
+
const out = steps.map((s) => {
|
|
3192
|
+
let next = fn(s);
|
|
3193
|
+
if (next.kind === "fragment") {
|
|
3194
|
+
const frag = next;
|
|
3195
|
+
const sections = frag.sections.map((section) => {
|
|
3196
|
+
const inner = mapSteps(section.steps, fn);
|
|
3197
|
+
return inner === section.steps ? section : { ...section, steps: inner };
|
|
3198
|
+
});
|
|
3199
|
+
if (sections.some((sec, k) => sec !== frag.sections[k])) next = { ...frag, sections };
|
|
3200
|
+
}
|
|
3201
|
+
if (next !== s) changed = true;
|
|
3202
|
+
return next;
|
|
3203
|
+
});
|
|
3204
|
+
return changed ? out : steps;
|
|
3205
|
+
}
|
|
3206
|
+
function pruneSteps(steps, fn) {
|
|
3207
|
+
let changed = false;
|
|
3208
|
+
const out = [];
|
|
3209
|
+
for (const s of steps) {
|
|
3210
|
+
let step = s;
|
|
3211
|
+
if (s.kind === "fragment") {
|
|
3212
|
+
const sections = s.sections.map((sec) => {
|
|
3213
|
+
const inner = pruneSteps(sec.steps, fn);
|
|
3214
|
+
return inner === sec.steps ? sec : { ...sec, steps: inner };
|
|
3215
|
+
});
|
|
3216
|
+
if (sections.some((sec, k) => sec !== s.sections[k])) step = { ...s, sections };
|
|
3217
|
+
}
|
|
3218
|
+
const next = fn(step);
|
|
3219
|
+
if (next !== s) changed = true;
|
|
3220
|
+
if (next !== null) out.push(next);
|
|
3221
|
+
}
|
|
3222
|
+
return changed ? out : steps;
|
|
3223
|
+
}
|
|
3224
|
+
function withList(steps, parent, fn) {
|
|
3225
|
+
if (parent === null) return fn(steps);
|
|
3226
|
+
return mapSteps(steps, (s) => {
|
|
3227
|
+
if (s.kind !== "fragment" || s.id !== parent.fragment) return s;
|
|
3228
|
+
const sections = s.sections.map((sec, k) => k === parent.section ? { ...sec, steps: fn(sec.steps) } : sec);
|
|
3229
|
+
return { ...s, sections };
|
|
3230
|
+
});
|
|
3231
|
+
}
|
|
3232
|
+
var contains = (step, id) => step.kind === "fragment" && step.sections.some((sec) => sec.steps.some((s) => hasId(s) && s.id === id || contains(s, id)));
|
|
3233
|
+
function moveStep(data, id, to) {
|
|
3234
|
+
const from = placeOf(data.steps, id);
|
|
3235
|
+
const step = findStep(data.steps, id);
|
|
3236
|
+
if (from === null || step === null) return data;
|
|
3237
|
+
if (to.parent !== null && (to.parent.fragment === id || contains(step, to.parent.fragment))) return data;
|
|
3238
|
+
if (stepListOf(data.steps, to.parent) === null) return data;
|
|
3239
|
+
const within = sameList(from.parent, to.parent);
|
|
3240
|
+
if (within && (to.index === from.index || to.index === from.index + 1)) return data;
|
|
3241
|
+
const removed = withList(data.steps, from.parent, (list) => list.filter((_, i) => i !== from.index));
|
|
3242
|
+
const index = within && to.index > from.index ? to.index - 1 : to.index;
|
|
3243
|
+
const steps = withList(removed, to.parent, (list) => [...list.slice(0, index), step, ...list.slice(index)]);
|
|
3244
|
+
return { ...data, steps };
|
|
3245
|
+
}
|
|
3246
|
+
function moveParticipant(data, id, index) {
|
|
3247
|
+
const from = data.participants.findIndex((p2) => p2.id === id);
|
|
3248
|
+
if (from < 0 || index === from || index === from + 1) return data;
|
|
3249
|
+
const p = data.participants[from];
|
|
3250
|
+
const rest = data.participants.filter((_, i) => i !== from);
|
|
3251
|
+
const at = index > from ? index - 1 : index;
|
|
3252
|
+
return { ...data, participants: [...rest.slice(0, at), p, ...rest.slice(at)] };
|
|
3253
|
+
}
|
|
3254
|
+
function reconnectMessage(data, id, end, participant) {
|
|
3255
|
+
const step = findStep(data.steps, id);
|
|
3256
|
+
if (step?.kind !== "message" || step[end] === participant || !data.participants.some((p) => p.id === participant)) return data;
|
|
3257
|
+
return { ...data, steps: mapSteps(data.steps, (s) => s === step ? { ...step, [end]: participant } : s) };
|
|
3258
|
+
}
|
|
3259
|
+
function setSequenceText(data, target, text) {
|
|
3260
|
+
if ("participant" in target) {
|
|
3261
|
+
const at = data.participants.findIndex((p2) => p2.id === target.participant);
|
|
3262
|
+
const p = data.participants[at];
|
|
3263
|
+
if (p === void 0 || (p.label ?? p.id) === text) return data;
|
|
3264
|
+
const { label: _label, ...rest } = p;
|
|
3265
|
+
const next = text === "" || text === p.id ? rest : { ...rest, label: text };
|
|
3266
|
+
return { ...data, participants: data.participants.map((q, i) => i === at ? next : q) };
|
|
3267
|
+
}
|
|
3268
|
+
if ("box" in target) {
|
|
3269
|
+
const boxes = data.boxes?.map((b) => {
|
|
3270
|
+
if (b.id !== target.box || (b.label ?? "") === text) return b;
|
|
3271
|
+
const { label: _label, ...rest } = b;
|
|
3272
|
+
return text === "" ? rest : { ...rest, label: text };
|
|
3273
|
+
});
|
|
3274
|
+
return boxes === void 0 || boxes.every((b, i) => b === data.boxes[i]) ? data : { ...data, boxes };
|
|
3275
|
+
}
|
|
3276
|
+
if ("fragment" in target) {
|
|
3277
|
+
const steps2 = mapSteps(data.steps, (s) => {
|
|
3278
|
+
if (s.kind !== "fragment" || s.id !== target.fragment) return s;
|
|
3279
|
+
const section = s.sections[target.section];
|
|
3280
|
+
if (section === void 0 || (section.label ?? "") === text) return s;
|
|
3281
|
+
const { label: _label, ...rest } = section;
|
|
3282
|
+
const next = text === "" ? rest : { ...rest, label: text };
|
|
3283
|
+
return { ...s, sections: s.sections.map((sec, k) => k === target.section ? next : sec) };
|
|
3284
|
+
});
|
|
3285
|
+
return steps2 === data.steps ? data : { ...data, steps: steps2 };
|
|
3286
|
+
}
|
|
3287
|
+
const steps = mapSteps(data.steps, (s) => {
|
|
3288
|
+
if (s.kind === "note" && s.id === target.step && s.text !== text) return { ...s, text };
|
|
3289
|
+
if (s.kind !== "message" || s.id !== target.step || (s.text ?? "") === text) return s;
|
|
3290
|
+
const { text: _text, ...rest } = s;
|
|
3291
|
+
return text === "" ? rest : { ...rest, text };
|
|
3292
|
+
});
|
|
3293
|
+
return steps === data.steps ? data : { ...data, steps };
|
|
3294
|
+
}
|
|
3295
|
+
function sequenceTextOf(data, target) {
|
|
3296
|
+
if ("participant" in target) {
|
|
3297
|
+
const p = data.participants.find((q) => q.id === target.participant);
|
|
3298
|
+
return p === void 0 ? null : p.label ?? p.id;
|
|
3299
|
+
}
|
|
3300
|
+
if ("box" in target) {
|
|
3301
|
+
const b = data.boxes?.find((q) => q.id === target.box);
|
|
3302
|
+
return b === void 0 ? null : b.label ?? "";
|
|
3303
|
+
}
|
|
3304
|
+
if ("fragment" in target) {
|
|
3305
|
+
const f = findStep(data.steps, target.fragment);
|
|
3306
|
+
const section = f?.kind === "fragment" ? f.sections[target.section] : void 0;
|
|
3307
|
+
return section === void 0 ? null : section.label ?? "";
|
|
3308
|
+
}
|
|
3309
|
+
const s = findStep(data.steps, target.step);
|
|
3310
|
+
return s?.kind === "message" ? s.text ?? "" : s?.kind === "note" ? s.text : null;
|
|
3311
|
+
}
|
|
3312
|
+
|
|
3313
|
+
// src/sequence/metrics.ts
|
|
3314
|
+
var SEQ = {
|
|
3315
|
+
/** Around the whole diagram. */
|
|
3316
|
+
margin: 12,
|
|
3317
|
+
/** Between a head's outline and its text. */
|
|
3318
|
+
headPad: 10,
|
|
3319
|
+
headMinWidth: 80,
|
|
3320
|
+
headMinHeight: 36,
|
|
3321
|
+
/** The least room between two heads. */
|
|
3322
|
+
columnGap: 32,
|
|
3323
|
+
/** Either side of a message's text along its line. */
|
|
3324
|
+
textPad: 10,
|
|
3325
|
+
/** Above every row. */
|
|
3326
|
+
rowGap: 14,
|
|
3327
|
+
/** Between a message's text and its line. */
|
|
3328
|
+
textGap: 3,
|
|
3329
|
+
/** A self-message's loop: how far it reaches right, and how tall it is. */
|
|
3330
|
+
loopWidth: 32,
|
|
3331
|
+
loopHeight: 20,
|
|
3332
|
+
/** An activation bar's width, and how far each nested one steps right. */
|
|
3333
|
+
barWidth: 10,
|
|
3334
|
+
barStep: 5,
|
|
3335
|
+
notePad: 6,
|
|
3336
|
+
noteMinWidth: 60,
|
|
3337
|
+
/** Between a lifeline and a note beside it. */
|
|
3338
|
+
noteGap: 10,
|
|
3339
|
+
/** How far a note over two lifelines reaches past them. */
|
|
3340
|
+
noteOverhang: 16,
|
|
3341
|
+
/** Between a fragment's box and the lifelines it holds, and between nested boxes. */
|
|
3342
|
+
fragmentPad: 14,
|
|
3343
|
+
fragmentNest: 8,
|
|
3344
|
+
/** Around a fragment's type label in its tab. */
|
|
3345
|
+
tabPad: 6,
|
|
3346
|
+
/** Between a box's border and the heads it holds. */
|
|
3347
|
+
boxPad: 10,
|
|
3348
|
+
/** Below the last row. */
|
|
3349
|
+
footer: 16,
|
|
3350
|
+
/** The X at a destroyed lifeline's end. */
|
|
3351
|
+
cross: 12
|
|
3352
|
+
};
|
|
3353
|
+
function seqMeasure(measure, font = DEFAULT_FONT) {
|
|
3354
|
+
const line = measure ?? ((text, s) => {
|
|
3355
|
+
const size = s.fontSize ?? font.fontSize;
|
|
3356
|
+
return { width: text.length * GLYPH * size, height: Math.ceil(1.4 * size) };
|
|
3357
|
+
});
|
|
3358
|
+
return {
|
|
3359
|
+
font,
|
|
3360
|
+
text: (text, style = {}) => {
|
|
3361
|
+
const s = { ...style, fontFamily: style.fontFamily ?? font.fontFamily, fontSize: style.fontSize ?? font.fontSize };
|
|
3362
|
+
const sizes = text.split("\n").map((l) => line(l, s));
|
|
3363
|
+
return { width: Math.max(0, ...sizes.map((z) => z.width)), height: sizes.reduce((h, z) => h + z.height, 0) };
|
|
3364
|
+
}
|
|
3365
|
+
};
|
|
3366
|
+
}
|
|
3367
|
+
function headSize(p, m) {
|
|
3368
|
+
const t = m.text(p.label ?? p.id);
|
|
3369
|
+
const outline = p.outline ?? "rect";
|
|
3370
|
+
const box = boxForContent(outline, { width: t.width + SEQ.headPad * 2, height: t.height + SEQ.headPad * 2 });
|
|
3371
|
+
return { width: Math.max(SEQ.headMinWidth, Math.ceil(box.width)), height: Math.max(SEQ.headMinHeight, Math.ceil(box.height)) };
|
|
3372
|
+
}
|
|
3373
|
+
|
|
3374
|
+
// src/sequence/columns.ts
|
|
3375
|
+
function* flatSteps(steps) {
|
|
3376
|
+
for (const s of steps) {
|
|
3377
|
+
yield s;
|
|
3378
|
+
if (s.kind === "fragment") for (const section of s.sections) yield* flatSteps(section.steps);
|
|
3379
|
+
}
|
|
3380
|
+
}
|
|
3381
|
+
function noteNeeds(over, side, width, half) {
|
|
3382
|
+
const [a, b] = [Math.min(...over), Math.max(...over)];
|
|
3383
|
+
const room = width + SEQ.noteGap * 2;
|
|
3384
|
+
if (side === "right") return [{ a, b: a + 1, room: room + half(a + 1) }];
|
|
3385
|
+
if (side === "left") return [{ a: a - 1, b: a, room: room + half(a - 1) }];
|
|
3386
|
+
if (a === b) return [{ a: a - 1, b: a, room: width / 2 + half(a - 1) + SEQ.noteGap }, { a, b: a + 1, room: width / 2 + half(a + 1) + SEQ.noteGap }];
|
|
3387
|
+
return [{ a, b, room: width - SEQ.noteOverhang * 2 }];
|
|
3388
|
+
}
|
|
3389
|
+
function needsOf(data, m, index, half) {
|
|
3390
|
+
const out = [];
|
|
3391
|
+
const numbered = data.autonumber !== void 0;
|
|
3392
|
+
for (const s of flatSteps(data.steps)) {
|
|
3393
|
+
if (s.kind === "message") {
|
|
3394
|
+
const a = index.get(s.from);
|
|
3395
|
+
const b = index.get(s.to);
|
|
3396
|
+
if (a === void 0 || b === void 0) continue;
|
|
3397
|
+
const text = (s.text ?? "") === "" ? 0 : m.text(s.text).width;
|
|
3398
|
+
const room = text + SEQ.textPad * 2 + SEQ.barWidth * 2 + (numbered ? SEQ.textPad * 2 : 0);
|
|
3399
|
+
if (a === b) out.push({ a, b: a + 1, room: SEQ.loopWidth + room + half(a + 1) });
|
|
3400
|
+
else out.push({ a: Math.min(a, b), b: Math.max(a, b), room });
|
|
3401
|
+
} else if (s.kind === "note") {
|
|
3402
|
+
const over = s.over.map((id) => index.get(id)).filter((i) => i !== void 0);
|
|
3403
|
+
if (over.length > 0) out.push(...noteNeeds(over, s.side, Math.max(SEQ.noteMinWidth, m.text(s.text).width + SEQ.notePad * 2), half));
|
|
3404
|
+
}
|
|
3405
|
+
}
|
|
3406
|
+
return out;
|
|
3407
|
+
}
|
|
3408
|
+
function columnsOf(data, m, left) {
|
|
3409
|
+
const ps = data.participants;
|
|
3410
|
+
const heads = ps.map((p) => headSize(p, m));
|
|
3411
|
+
const index = new Map(ps.map((p, i) => [p.id, i]));
|
|
3412
|
+
const boxOf = /* @__PURE__ */ new Map();
|
|
3413
|
+
for (const b of data.boxes ?? []) for (const id of b.members) if (!boxOf.has(id)) boxOf.set(id, b.id);
|
|
3414
|
+
const half = (i) => (heads[i]?.width ?? 0) / 2;
|
|
3415
|
+
const boxPad = (i) => i >= 0 && i < ps.length && boxOf.has(ps[i].id) ? SEQ.boxPad : 0;
|
|
3416
|
+
const gaps = Array.from({ length: ps.length + 1 }, (_, i) => {
|
|
3417
|
+
if (i === 0) return half(0) + boxPad(0);
|
|
3418
|
+
if (i === ps.length) return half(i - 1) + boxPad(i - 1);
|
|
3419
|
+
const split = boxOf.get(ps[i - 1].id) !== boxOf.get(ps[i].id);
|
|
3420
|
+
return half(i - 1) + half(i) + SEQ.columnGap + (split ? boxPad(i - 1) + boxPad(i) : 0);
|
|
3421
|
+
});
|
|
3422
|
+
const needs = needsOf(data, m, index, half).sort((p, q) => p.b - p.a - (q.b - q.a));
|
|
3423
|
+
for (const { a, b, room } of needs) {
|
|
3424
|
+
const lo = Math.max(a, -1) + 1;
|
|
3425
|
+
const hi = Math.min(b, ps.length) + 1;
|
|
3426
|
+
let have = 0;
|
|
3427
|
+
for (let i = lo; i < hi; i++) have += gaps[i];
|
|
3428
|
+
const lack = room - have;
|
|
3429
|
+
if (lack > 0) for (let i = lo; i < hi; i++) gaps[i] += lack / (hi - lo);
|
|
3430
|
+
}
|
|
3431
|
+
const x = [];
|
|
3432
|
+
let at = left;
|
|
3433
|
+
for (let i = 0; i < ps.length; i++) {
|
|
3434
|
+
at += gaps[i];
|
|
3435
|
+
x.push(at);
|
|
3436
|
+
}
|
|
3437
|
+
return { x, heads, right: at + gaps[ps.length] };
|
|
3438
|
+
}
|
|
3439
|
+
|
|
3440
|
+
// src/sequence/rows.ts
|
|
3441
|
+
var spanOf = (rects) => {
|
|
3442
|
+
const x0 = Math.min(...rects.map((r) => r.x));
|
|
3443
|
+
return { x0, x1: Math.max(...rects.map((r) => r.x + r.width)) };
|
|
3444
|
+
};
|
|
3445
|
+
var RowWalk = class {
|
|
3446
|
+
constructor(data, m, cols, top) {
|
|
3447
|
+
this.data = data;
|
|
3448
|
+
this.m = m;
|
|
3449
|
+
this.cols = cols;
|
|
3450
|
+
this.index = new Map(data.participants.map((p, i) => [p.id, i]));
|
|
3451
|
+
this.created = new Set(flatMarks(data.steps, "create"));
|
|
3452
|
+
this.number = data.autonumber;
|
|
3453
|
+
const shown = data.participants.filter((p) => !this.created.has(p.id));
|
|
3454
|
+
const tall = Math.max(SEQ.headMinHeight, ...shown.map((p) => cols.heads[this.index.get(p.id)].height));
|
|
3455
|
+
for (const p of shown) this.placeHead(p.id, top + tall - cols.heads[this.index.get(p.id)].height, false);
|
|
3456
|
+
for (const l of this.lifeline.values()) l.y0 = top + tall;
|
|
3457
|
+
this.y = top + tall;
|
|
3458
|
+
}
|
|
3459
|
+
data;
|
|
3460
|
+
m;
|
|
3461
|
+
cols;
|
|
3462
|
+
out = { heads: [], lifelines: [], messages: [], activations: [], notes: [], fragments: [], slots: [], bottom: 0 };
|
|
3463
|
+
index;
|
|
3464
|
+
created;
|
|
3465
|
+
lifeline = /* @__PURE__ */ new Map();
|
|
3466
|
+
bars = /* @__PURE__ */ new Map();
|
|
3467
|
+
/** Lifelines a `destroy` ends at the next message to them, each with where it ends if none comes. */
|
|
3468
|
+
destroying = /* @__PURE__ */ new Map();
|
|
3469
|
+
frames = [];
|
|
3470
|
+
last = null;
|
|
3471
|
+
number;
|
|
3472
|
+
y;
|
|
3473
|
+
cx(id) {
|
|
3474
|
+
return this.cols.x[this.index.get(id)];
|
|
3475
|
+
}
|
|
3476
|
+
headRect(id) {
|
|
3477
|
+
const size = this.cols.heads[this.index.get(id)];
|
|
3478
|
+
return { x: this.cx(id) - size.width / 2, y: 0, width: size.width, height: 0 };
|
|
3479
|
+
}
|
|
3480
|
+
placeHead(id, y, created) {
|
|
3481
|
+
const size = this.cols.heads[this.index.get(id)];
|
|
3482
|
+
this.out.heads.push({ id, box: { x: this.cx(id) - size.width / 2, y, width: size.width, height: size.height }, created });
|
|
3483
|
+
const line = { id, x: this.cx(id), y0: y + size.height, y1: null };
|
|
3484
|
+
this.lifeline.set(id, line);
|
|
3485
|
+
this.out.lifelines.push(line);
|
|
3486
|
+
return y + size.height;
|
|
3487
|
+
}
|
|
3488
|
+
/** Into every open fragment's reach. */
|
|
3489
|
+
touch(rect) {
|
|
3490
|
+
for (const f of this.frames) f.push(rect);
|
|
3491
|
+
}
|
|
3492
|
+
/** Where a line meets `id`'s lifeline from `side`: the edge of its innermost open activation. */
|
|
3493
|
+
endX(id, side) {
|
|
3494
|
+
const depth = this.bars.get(id)?.length ?? 0;
|
|
3495
|
+
if (depth === 0) return this.cx(id);
|
|
3496
|
+
return this.cx(id) + side * (SEQ.barWidth / 2) + (depth - 1) * SEQ.barStep;
|
|
3497
|
+
}
|
|
3498
|
+
open(id, y, opener) {
|
|
3499
|
+
const stack = this.bars.get(id) ?? [];
|
|
3500
|
+
stack.push({ y, opener });
|
|
3501
|
+
this.bars.set(id, stack);
|
|
3502
|
+
}
|
|
3503
|
+
close(id, y, closer) {
|
|
3504
|
+
const stack = this.bars.get(id);
|
|
3505
|
+
const bar = stack?.pop();
|
|
3506
|
+
if (bar === void 0) return;
|
|
3507
|
+
const start = bar.y;
|
|
3508
|
+
const depth = stack.length;
|
|
3509
|
+
const x = this.cx(id) - SEQ.barWidth / 2 + depth * SEQ.barStep;
|
|
3510
|
+
const box = { x, y: start, width: SEQ.barWidth, height: Math.max(y, start + 8) - start };
|
|
3511
|
+
this.out.activations.push({ participant: id, depth, box, opener: bar.opener, ...closer ? { closer } : {} });
|
|
3512
|
+
}
|
|
3513
|
+
steps(list, parent) {
|
|
3514
|
+
list.forEach((step, index) => {
|
|
3515
|
+
this.out.slots.push({ y: this.y, place: { parent, index } });
|
|
3516
|
+
if (step.kind === "message") this.message(step);
|
|
3517
|
+
else if (step.kind === "note") this.note(step);
|
|
3518
|
+
else if (step.kind === "fragment") this.fragment(step);
|
|
3519
|
+
else this.mark(step, { parent, index });
|
|
3520
|
+
});
|
|
3521
|
+
this.out.slots.push({ y: this.y, place: { parent, index: list.length } });
|
|
3522
|
+
}
|
|
3523
|
+
message(s) {
|
|
3524
|
+
if (!this.index.has(s.from) || !this.index.has(s.to)) return;
|
|
3525
|
+
this.y += SEQ.rowGap;
|
|
3526
|
+
const text = (s.text ?? "") === "" ? null : this.m.text(s.text);
|
|
3527
|
+
const lineY = this.y + Math.max(text === null ? 0 : text.height + SEQ.textGap, 6);
|
|
3528
|
+
if (s.activate !== void 0) this.open(s[s.activate], lineY, { message: s.id, flag: "activate" });
|
|
3529
|
+
const self = s.from === s.to;
|
|
3530
|
+
const dir = self ? 1 : Math.sign(this.cx(s.to) - this.cx(s.from));
|
|
3531
|
+
const x1 = this.endX(s.from, dir);
|
|
3532
|
+
const x2 = self ? x1 : this.endX(s.to, -dir);
|
|
3533
|
+
const place2 = { id: s.id, y: lineY, x1, x2 };
|
|
3534
|
+
if (self) place2.loopY = lineY + SEQ.loopHeight;
|
|
3535
|
+
if (text !== null) {
|
|
3536
|
+
const x = self ? x1 + SEQ.textGap + 2 : (x1 + x2) / 2 - text.width / 2;
|
|
3537
|
+
place2.text = { x, y: lineY - SEQ.textGap - text.height, ...text };
|
|
3538
|
+
this.touch(place2.text);
|
|
3539
|
+
}
|
|
3540
|
+
if (this.number !== void 0) {
|
|
3541
|
+
const n = this.m.text(String(this.number), { fontSize: Math.max(8, this.m.font.fontSize - 2) });
|
|
3542
|
+
const d = Math.max(16, n.width + 6);
|
|
3543
|
+
place2.number = { n: this.number++, box: { x: x1 - d / 2, y: lineY - d / 2, width: d, height: d } };
|
|
3544
|
+
}
|
|
3545
|
+
this.out.messages.push(place2);
|
|
3546
|
+
this.touch(this.headRect(s.from));
|
|
3547
|
+
this.touch(this.headRect(s.to));
|
|
3548
|
+
if (self) this.touch({ x: x1, y: lineY, width: SEQ.loopWidth, height: 0 });
|
|
3549
|
+
if (s.deactivate !== void 0) this.close(s[s.deactivate], self ? place2.loopY : lineY, { message: s.id, flag: "deactivate" });
|
|
3550
|
+
for (const id of /* @__PURE__ */ new Set([s.from, s.to])) {
|
|
3551
|
+
if (!this.destroying.delete(id)) continue;
|
|
3552
|
+
const line = this.lifeline.get(id);
|
|
3553
|
+
if (line !== void 0) line.y1 = self ? place2.loopY : lineY;
|
|
3554
|
+
}
|
|
3555
|
+
this.y = place2.loopY ?? lineY;
|
|
3556
|
+
this.last = { y: this.y, ends: [s.from, s.to] };
|
|
3557
|
+
}
|
|
3558
|
+
note(s) {
|
|
3559
|
+
const over = s.over.filter((id) => this.index.has(id));
|
|
3560
|
+
if (over.length === 0) return;
|
|
3561
|
+
this.y += SEQ.rowGap;
|
|
3562
|
+
const t = this.m.text(s.text);
|
|
3563
|
+
const width = Math.max(SEQ.noteMinWidth, t.width + SEQ.notePad * 2);
|
|
3564
|
+
const height = t.height + SEQ.notePad * 2;
|
|
3565
|
+
const xs = over.map((id) => this.cx(id));
|
|
3566
|
+
const [lo, hi] = [Math.min(...xs), Math.max(...xs)];
|
|
3567
|
+
const x = s.side === "right" ? lo + SEQ.barWidth / 2 + SEQ.noteGap : s.side === "left" ? lo - SEQ.barWidth / 2 - SEQ.noteGap - width : lo === hi ? lo - width / 2 : Math.min(lo - SEQ.noteOverhang, (lo + hi) / 2 - width / 2);
|
|
3568
|
+
const box = { x, y: this.y, width: s.side === void 0 && lo !== hi ? Math.max(width, hi - lo + SEQ.noteOverhang * 2) : width, height };
|
|
3569
|
+
this.out.notes.push({ id: s.id, box });
|
|
3570
|
+
this.touch(box);
|
|
3571
|
+
for (const id of over) this.touch(this.headRect(id));
|
|
3572
|
+
this.y += height;
|
|
3573
|
+
this.last = null;
|
|
3574
|
+
}
|
|
3575
|
+
mark(s, at) {
|
|
3576
|
+
if (!this.index.has(s.participant)) return;
|
|
3577
|
+
this.touch(this.headRect(s.participant));
|
|
3578
|
+
const y = this.last?.ends.includes(s.participant) ? this.last.y : this.y + SEQ.rowGap / 2;
|
|
3579
|
+
if (s.kind === "activate") this.open(s.participant, y, { place: at });
|
|
3580
|
+
else if (s.kind === "deactivate") this.close(s.participant, y, { place: at });
|
|
3581
|
+
else if (s.kind === "destroy") {
|
|
3582
|
+
this.destroying.set(s.participant, this.last?.ends.includes(s.participant) ? this.last.y : null);
|
|
3583
|
+
const line = this.lifeline.get(s.participant);
|
|
3584
|
+
if (line !== void 0) line.destroyedBy = at;
|
|
3585
|
+
} else if (this.out.heads.every((h) => h.id !== s.participant)) {
|
|
3586
|
+
this.y = this.placeHead(s.participant, this.y + SEQ.rowGap, true);
|
|
3587
|
+
this.last = null;
|
|
3588
|
+
}
|
|
3589
|
+
}
|
|
3590
|
+
fragment(s) {
|
|
3591
|
+
this.y += SEQ.rowGap;
|
|
3592
|
+
const top = this.y;
|
|
3593
|
+
const tabText = s.type === "region" ? null : this.m.text(s.type, { bold: true });
|
|
3594
|
+
const tab = tabText === null ? null : { width: tabText.width + SEQ.tabPad * 2, height: tabText.height + SEQ.tabPad };
|
|
3595
|
+
const labels = s.sections.map((sec) => (sec.label ?? "") === "" ? null : this.m.text(sec.label));
|
|
3596
|
+
this.y += Math.max(tab?.height ?? 0, (labels[0]?.height ?? 0) + 4, 8);
|
|
3597
|
+
const frame = [];
|
|
3598
|
+
this.frames.push(frame);
|
|
3599
|
+
const sections = [];
|
|
3600
|
+
s.sections.forEach((sec, k) => {
|
|
3601
|
+
if (k > 0) {
|
|
3602
|
+
this.y += SEQ.rowGap / 2;
|
|
3603
|
+
sections.push({ y: this.y, height: 0 });
|
|
3604
|
+
this.y += (labels[k]?.height ?? 0) + 4;
|
|
3605
|
+
} else sections.push({ y: top, height: 0 });
|
|
3606
|
+
this.steps(sec.steps, { fragment: s.id, section: k });
|
|
3607
|
+
this.last = null;
|
|
3608
|
+
});
|
|
3609
|
+
this.frames.pop();
|
|
3610
|
+
this.y += SEQ.rowGap / 2;
|
|
3611
|
+
const reach = frame.length > 0 ? spanOf(frame) : { x0: this.cols.x[0] ?? 0, x1: this.cols.x.at(-1) ?? 0 };
|
|
3612
|
+
const minWidth = (tab?.width ?? 0) + (labels[0]?.width ?? 0) + SEQ.tabPad * 4;
|
|
3613
|
+
const x0 = reach.x0 - SEQ.fragmentPad / 2;
|
|
3614
|
+
const width = Math.max(reach.x1 - reach.x0 + SEQ.fragmentPad, minWidth);
|
|
3615
|
+
const box = { x: x0, y: top, width, height: this.y - top };
|
|
3616
|
+
const place2 = {
|
|
3617
|
+
id: s.id,
|
|
3618
|
+
type: s.type,
|
|
3619
|
+
box,
|
|
3620
|
+
sections: sections.map((sec, k) => {
|
|
3621
|
+
const l = labels[k];
|
|
3622
|
+
if (l === null || l === void 0) return { y: sec.y };
|
|
3623
|
+
const label = k === 0 ? { x: x0 + (tab?.width ?? 0) + SEQ.tabPad * 2, y: top + 2, ...l } : { x: x0 + width / 2 - l.width / 2, y: sec.y + 2, ...l };
|
|
3624
|
+
return { y: sec.y, label };
|
|
3625
|
+
})
|
|
3626
|
+
};
|
|
3627
|
+
if (tab !== null) place2.tab = { x: x0, y: top, ...tab };
|
|
3628
|
+
this.out.fragments.push(place2);
|
|
3629
|
+
this.touch({ x: box.x - SEQ.fragmentNest, y: top, width: box.width + SEQ.fragmentNest * 2, height: 0 });
|
|
3630
|
+
}
|
|
3631
|
+
};
|
|
3632
|
+
function flatMarks(steps, kind) {
|
|
3633
|
+
const out = [];
|
|
3634
|
+
for (const s of steps) {
|
|
3635
|
+
if (s.kind === kind) out.push(s.participant);
|
|
3636
|
+
if (s.kind === "fragment") for (const sec of s.sections) out.push(...flatMarks(sec.steps, kind));
|
|
3637
|
+
}
|
|
3638
|
+
return out;
|
|
3639
|
+
}
|
|
3640
|
+
function walkRows(data, m, cols, top) {
|
|
3641
|
+
const walk = new RowWalk(data, m, cols, top);
|
|
3642
|
+
walk.steps(data.steps, null);
|
|
3643
|
+
const bottom = walk.y + SEQ.rowGap;
|
|
3644
|
+
for (const [id, fallback] of walk.destroying) {
|
|
3645
|
+
const line = walk.lifeline.get(id);
|
|
3646
|
+
if (line !== void 0) line.y1 = fallback ?? bottom;
|
|
3647
|
+
}
|
|
3648
|
+
for (const [id, stack] of walk.bars) while (stack.length > 0) walk.close(id, bottom + SEQ.footer);
|
|
3649
|
+
walk.out.bottom = bottom;
|
|
3650
|
+
return walk.out;
|
|
3651
|
+
}
|
|
3652
|
+
|
|
3653
|
+
// src/sequence/layout.ts
|
|
3654
|
+
function sequenceLayout(data, opts = {}) {
|
|
3655
|
+
const m = seqMeasure(opts.measure, opts.font);
|
|
3656
|
+
const boxLabelHeight = Math.max(0, ...(data.boxes ?? []).map((b) => (b.label ?? "") === "" ? 0 : m.text(b.label).height + 4));
|
|
3657
|
+
const cols = columnsOf(data, m, SEQ.margin);
|
|
3658
|
+
const top = SEQ.margin + (data.boxes?.length ? SEQ.boxPad : 0) + boxLabelHeight;
|
|
3659
|
+
const rows = walkRows(data, m, cols, top);
|
|
3660
|
+
const columns = data.participants.map((p, i) => ({ id: p.id, x: cols.x[i] }));
|
|
3661
|
+
const foot = rows.bottom + SEQ.footer;
|
|
3662
|
+
const lifelines = rows.lifelines.map((l) => ({ ...l, y1: l.y1 ?? foot, destroyed: l.y1 !== null }));
|
|
3663
|
+
const boxes = (data.boxes ?? []).flatMap((b) => {
|
|
3664
|
+
const heads = rows.heads.filter((h) => b.members.includes(h.id)).map((h) => h.box);
|
|
3665
|
+
const around = unionRects(heads);
|
|
3666
|
+
if (around === null) return [];
|
|
3667
|
+
const label = (b.label ?? "") === "" ? null : m.text(b.label);
|
|
3668
|
+
const x = around.x - SEQ.boxPad;
|
|
3669
|
+
const width = around.width + SEQ.boxPad * 2;
|
|
3670
|
+
const y = SEQ.margin;
|
|
3671
|
+
const box = { x, y, width, height: foot + SEQ.boxPad - y };
|
|
3672
|
+
return [{ id: b.id, box, ...label ? { label: { x: x + (width - label.width) / 2, y: y + 2, ...label } } : {} }];
|
|
3673
|
+
});
|
|
3674
|
+
const all = unionRects([
|
|
3675
|
+
...rows.heads.map((h) => h.box),
|
|
3676
|
+
...rows.notes.map((n) => n.box),
|
|
3677
|
+
...rows.fragments.map((f) => f.box),
|
|
3678
|
+
...boxes.map((b) => b.box),
|
|
3679
|
+
...rows.messages.flatMap((msg) => msg.text ? [msg.text] : []),
|
|
3680
|
+
{ x: 0, y: 0, width: cols.right, height: foot }
|
|
3681
|
+
]);
|
|
3682
|
+
return {
|
|
3683
|
+
width: Math.ceil(all.x + all.width + SEQ.margin),
|
|
3684
|
+
height: Math.ceil(Math.max(foot, all.y + all.height) + SEQ.margin),
|
|
3685
|
+
columns,
|
|
3686
|
+
heads: rows.heads,
|
|
3687
|
+
lifelines,
|
|
3688
|
+
messages: rows.messages,
|
|
3689
|
+
activations: rows.activations,
|
|
3690
|
+
notes: rows.notes,
|
|
3691
|
+
fragments: rows.fragments,
|
|
3692
|
+
boxes,
|
|
3693
|
+
slots: rows.slots
|
|
3694
|
+
};
|
|
3695
|
+
}
|
|
3696
|
+
var SEQUENCE_ROLES = { note: "warning", number: "accent" };
|
|
3697
|
+
var DASH = [6, 4];
|
|
3698
|
+
var LIFELINE_DASH = [4, 4];
|
|
3699
|
+
var MARKERS = { arrow: "arrow", open: "arrow-open", cross: null, none: null };
|
|
3700
|
+
var PartSpecs = class {
|
|
3701
|
+
constructor(root, at, font) {
|
|
3702
|
+
this.root = root;
|
|
3703
|
+
this.at = at;
|
|
3704
|
+
this.font = font;
|
|
3705
|
+
}
|
|
3706
|
+
root;
|
|
3707
|
+
at;
|
|
3708
|
+
font;
|
|
3709
|
+
specs = [];
|
|
3710
|
+
place(r) {
|
|
3711
|
+
return { x: this.at.x + r.x, y: this.at.y + r.y, width: r.width, height: r.height };
|
|
3712
|
+
}
|
|
3713
|
+
push(id, seq, r, data) {
|
|
3714
|
+
this.specs.push({
|
|
3715
|
+
id: `${this.root}/${id}`,
|
|
3716
|
+
kind: "mark",
|
|
3717
|
+
layer: "main",
|
|
3718
|
+
parent: this.root,
|
|
3719
|
+
pickable: false,
|
|
3720
|
+
pose: this.place(r),
|
|
3721
|
+
data: { ...data, diagram: { ...data.diagram, seq: { root: this.root, ...seq } } }
|
|
3722
|
+
});
|
|
3723
|
+
}
|
|
3724
|
+
body(id, part, of, r, outline, style, dash) {
|
|
3725
|
+
const lines = dash ?? style.dash;
|
|
3726
|
+
this.push(id, { part, ...of !== void 0 ? { id: of } : {} }, r, {
|
|
3727
|
+
diagram: { outline },
|
|
3728
|
+
fill: { paint: { color: style.fill } },
|
|
3729
|
+
stroke: { paint: { color: style.stroke }, width: style.strokeWidth ?? 1.5, ...lines !== void 0 && lines.length > 0 ? { dash: lines } : {} }
|
|
3730
|
+
});
|
|
3731
|
+
}
|
|
3732
|
+
line(id, part, of, r, d, stroke, fill) {
|
|
3733
|
+
this.push(id, { part, ...of !== void 0 ? { id: of } : {} }, r, {
|
|
3734
|
+
diagram: { mark: { d } },
|
|
3735
|
+
stroke,
|
|
3736
|
+
...fill !== void 0 ? { fill: { paint: { color: fill } } } : {}
|
|
3737
|
+
});
|
|
3738
|
+
}
|
|
3739
|
+
/** The marks the part just pushed draws, onto its trait. */
|
|
3740
|
+
marks(refs) {
|
|
3741
|
+
const seq = this.specs.at(-1).data.diagram.seq;
|
|
3742
|
+
seq.marks = refs;
|
|
3743
|
+
}
|
|
3744
|
+
text(id, r, text, color, target, face = {}, centered = true) {
|
|
3745
|
+
const box = { ...r, x: r.x - 1, width: r.width + 2 };
|
|
3746
|
+
this.push(id, { part: "text", ...target ? { target } : {} }, box, {
|
|
3747
|
+
text,
|
|
3748
|
+
style: { ...this.font, ...centered ? { align: "center" } : {}, ...face },
|
|
3749
|
+
verticalAlign: "center",
|
|
3750
|
+
fill: { paint: { color } }
|
|
3751
|
+
});
|
|
3752
|
+
}
|
|
3753
|
+
};
|
|
3754
|
+
function participantSpecs(out, data, layout, theme) {
|
|
3755
|
+
for (const l of layout.lifelines) {
|
|
3756
|
+
const p = data.participants.find((q) => q.id === l.id);
|
|
3757
|
+
const style = withColors(roleStyle(theme, p.style), p.colors);
|
|
3758
|
+
out.line(
|
|
3759
|
+
`life:${l.id}`,
|
|
3760
|
+
"lifeline",
|
|
3761
|
+
l.id,
|
|
3762
|
+
{ x: l.x - 1, y: l.y0, width: 2, height: Math.max(l.y1 - l.y0, 0) },
|
|
3763
|
+
"M0.5 0V1",
|
|
3764
|
+
{ paint: { color: style.stroke }, width: 1, dash: LIFELINE_DASH }
|
|
3765
|
+
);
|
|
3766
|
+
if (l.destroyed) {
|
|
3767
|
+
const c = SEQ.cross;
|
|
3768
|
+
out.line(
|
|
3769
|
+
`destroy:${l.id}`,
|
|
3770
|
+
"destroy",
|
|
3771
|
+
l.id,
|
|
3772
|
+
{ x: l.x - c / 2, y: l.y1 - c / 2, width: c, height: c },
|
|
3773
|
+
"M0 0L1 1M1 0L0 1",
|
|
3774
|
+
{ paint: { color: style.stroke }, width: 2 }
|
|
3775
|
+
);
|
|
3776
|
+
if (l.destroyedBy) out.marks([{ place: l.destroyedBy }]);
|
|
3777
|
+
}
|
|
3778
|
+
}
|
|
3779
|
+
for (const [i, a] of layout.activations.entries()) {
|
|
3780
|
+
const p = data.participants.find((q) => q.id === a.participant);
|
|
3781
|
+
out.body(`act:${i}`, "activation", a.participant, a.box, "rect", withColors(roleStyle(theme, p.style), p.colors));
|
|
3782
|
+
out.marks(a.closer ? [a.opener, a.closer] : [a.opener]);
|
|
3783
|
+
}
|
|
3784
|
+
}
|
|
3785
|
+
function headSpecs(out, data, layout, theme) {
|
|
3786
|
+
for (const h of layout.heads) {
|
|
3787
|
+
const p = data.participants.find((q) => q.id === h.id);
|
|
3788
|
+
const style = withColors(roleStyle(theme, p.style), p.colors);
|
|
3789
|
+
const outline = p.outline ?? "rect";
|
|
3790
|
+
out.body(`head:${p.id}`, "head", p.id, h.box, outline, style);
|
|
3791
|
+
out.text(`head:${p.id}/text`, contentBox(outline, h.box), p.label ?? p.id, style.text, { participant: p.id });
|
|
3792
|
+
}
|
|
3793
|
+
}
|
|
3794
|
+
function messageSpecs(out, steps, layout, theme) {
|
|
3795
|
+
const byId = new Map(layout.messages.map((m) => [m.id, m]));
|
|
3796
|
+
const edge = roleEdgeStyle(theme);
|
|
3797
|
+
const width = edge.width ?? 1.5;
|
|
3798
|
+
for (const msg of flatSteps(steps)) {
|
|
3799
|
+
const at = msg.kind === "message" ? byId.get(msg.id) : void 0;
|
|
3800
|
+
if (at === void 0 || msg.kind !== "message") continue;
|
|
3801
|
+
const s = msg;
|
|
3802
|
+
const marker = MARKERS[msg.head];
|
|
3803
|
+
const stroke = {
|
|
3804
|
+
paint: { color: edge.stroke },
|
|
3805
|
+
width,
|
|
3806
|
+
...msg.line === "dashed" ? { dash: DASH } : {},
|
|
3807
|
+
...marker !== null ? { markerEnd: marker } : {},
|
|
3808
|
+
...marker !== null && msg.both ? { markerStart: marker } : {}
|
|
3809
|
+
};
|
|
3810
|
+
if (at.loopY !== void 0) {
|
|
3811
|
+
out.line(`msg:${s.id}`, "message", s.id, { x: at.x1, y: at.y, width: SEQ.loopWidth, height: at.loopY - at.y }, "M0 0H1V1H0", stroke);
|
|
3812
|
+
} else {
|
|
3813
|
+
const d = at.x2 >= at.x1 ? "M0 0H1" : "M1 0H0";
|
|
3814
|
+
out.line(`msg:${s.id}`, "message", s.id, { x: Math.min(at.x1, at.x2), y: at.y, width: Math.abs(at.x2 - at.x1), height: 0 }, d, stroke);
|
|
3815
|
+
}
|
|
3816
|
+
if (msg.head === "cross") {
|
|
3817
|
+
const c = SEQ.cross * 0.8;
|
|
3818
|
+
const end = { x: at.x2, y: at.loopY ?? at.y };
|
|
3819
|
+
out.line(
|
|
3820
|
+
`msg:${s.id}/cross`,
|
|
3821
|
+
"message",
|
|
3822
|
+
s.id,
|
|
3823
|
+
{ x: end.x - c / 2, y: end.y - c / 2, width: c, height: c },
|
|
3824
|
+
"M0 0L1 1M1 0L0 1",
|
|
3825
|
+
{ paint: { color: edge.stroke }, width }
|
|
3826
|
+
);
|
|
3827
|
+
}
|
|
3828
|
+
if (at.text) out.text(`msg:${s.id}/text`, at.text, msg.text ?? "", edge.label ?? edge.stroke, { step: s.id }, {}, msg.from !== msg.to);
|
|
3829
|
+
if (at.number) {
|
|
3830
|
+
const style = roleStyle(theme, SEQUENCE_ROLES.number);
|
|
3831
|
+
out.body(`msg:${s.id}/number`, "number", s.id, at.number.box, "ellipse", style);
|
|
3832
|
+
out.text(`msg:${s.id}/number/text`, at.number.box, String(at.number.n), style.text, void 0, { fontSize: Math.max(8, out.font.fontSize - 2) });
|
|
3833
|
+
}
|
|
3834
|
+
}
|
|
3835
|
+
}
|
|
3836
|
+
function noteSpecs(out, steps, layout, theme) {
|
|
3837
|
+
const byId = new Map(layout.notes.map((n) => [n.id, n]));
|
|
3838
|
+
const style = roleStyle(theme, SEQUENCE_ROLES.note);
|
|
3839
|
+
for (const s of flatSteps(steps)) {
|
|
3840
|
+
const at = s.kind === "note" ? byId.get(s.id) : void 0;
|
|
3841
|
+
if (at === void 0 || s.kind !== "note") continue;
|
|
3842
|
+
out.body(`note:${s.id}`, "note", s.id, at.box, "note", style);
|
|
3843
|
+
out.text(`note:${s.id}/text`, contentBox("note", at.box), s.text, style.text, { step: s.id });
|
|
3844
|
+
}
|
|
3845
|
+
}
|
|
3846
|
+
function fragmentSpecs(out, steps, layout, theme, boxesOnly) {
|
|
3847
|
+
const byId = new Map(layout.fragments.map((f) => [f.id, f]));
|
|
3848
|
+
for (const s of flatSteps(steps)) {
|
|
3849
|
+
const at = s.kind === "fragment" ? byId.get(s.id) : void 0;
|
|
3850
|
+
if (at === void 0 || s.kind !== "fragment") continue;
|
|
3851
|
+
const style = withColors({ ...theme.container, ...s.type === "region" ? {} : { fill: "transparent" } }, s.colors);
|
|
3852
|
+
if (boxesOnly) {
|
|
3853
|
+
out.body(`frag:${s.id}`, "fragment", s.id, at.box, "rect", { ...style, strokeWidth: 1 }, []);
|
|
3854
|
+
continue;
|
|
3855
|
+
}
|
|
3856
|
+
at.sections.forEach((sec, k) => {
|
|
3857
|
+
if (k > 0) {
|
|
3858
|
+
out.line(
|
|
3859
|
+
`frag:${s.id}/divider:${k}`,
|
|
3860
|
+
"divider",
|
|
3861
|
+
s.id,
|
|
3862
|
+
{ x: at.box.x, y: sec.y, width: at.box.width, height: 0 },
|
|
3863
|
+
"M0 0H1",
|
|
3864
|
+
{ paint: { color: style.stroke }, width: 1, dash: DASH }
|
|
3865
|
+
);
|
|
3866
|
+
}
|
|
3867
|
+
if (sec.label) out.text(`frag:${s.id}/label:${k}`, sec.label, s.sections[k].label ?? "", style.text, { fragment: s.id, section: k });
|
|
3868
|
+
});
|
|
3869
|
+
if (at.tab) {
|
|
3870
|
+
out.line(`frag:${s.id}/tab`, "fragment", s.id, at.tab, "M0 0H1V0.6L0.85 1H0Z", { paint: { color: style.stroke }, width: 1 }, theme.container.fill);
|
|
3871
|
+
out.text(`frag:${s.id}/type`, at.tab, s.type, style.text, void 0, { fontWeight: 700 });
|
|
3872
|
+
}
|
|
3873
|
+
}
|
|
3874
|
+
}
|
|
3875
|
+
function boxSpecs(out, data, layout, theme) {
|
|
3876
|
+
for (const b of layout.boxes) {
|
|
3877
|
+
const own = data.boxes?.find((q) => q.id === b.id);
|
|
3878
|
+
const style = withColors(theme.container, own?.colors);
|
|
3879
|
+
out.body(`box:${b.id}`, "box", b.id, b.box, "rect", { ...style, strokeWidth: 1 }, []);
|
|
3880
|
+
if (b.label) out.text(`box:${b.id}/text`, b.label, own?.label ?? "", style.text, { box: b.id });
|
|
3881
|
+
}
|
|
3882
|
+
}
|
|
3883
|
+
var geometryOf = (layout) => ({
|
|
3884
|
+
columns: layout.columns,
|
|
3885
|
+
slots: layout.slots,
|
|
3886
|
+
messages: layout.messages,
|
|
3887
|
+
notes: layout.notes,
|
|
3888
|
+
heads: layout.heads.map(({ id, box }) => ({ id, box })),
|
|
3889
|
+
fragments: layout.fragments.map(({ id, box, tab }) => ({ id, grip: tab ?? { ...box, height: Math.min(box.height, SEQ.rowGap) } })),
|
|
3890
|
+
boxes: layout.boxes.map(({ id, box, label }) => ({ id, grip: { ...box, height: (label?.height ?? 0) + SEQ.boxPad } })),
|
|
3891
|
+
marks: [
|
|
3892
|
+
...layout.activations.map((a, i) => ({ id: `act:${i}`, grip: a.box })),
|
|
3893
|
+
...layout.lifelines.flatMap((l) => l.destroyedBy === void 0 ? [] : [{ id: `destroy:${l.id}`, grip: crossGrip(l.x, l.y1) }])
|
|
3894
|
+
]
|
|
3895
|
+
});
|
|
3896
|
+
var crossGrip = (x, y) => {
|
|
3897
|
+
const c = Math.max(SEQ.cross, 12);
|
|
3898
|
+
return { x: x - c / 2, y: y - c / 2, width: c, height: c };
|
|
3899
|
+
};
|
|
3900
|
+
function sequenceSpecs(data, opts) {
|
|
3901
|
+
const theme = opts.theme ?? diagramThemeFrom(void 0);
|
|
3902
|
+
const font = opts.font ?? themeFont(theme);
|
|
3903
|
+
const layout = sequenceLayout(data, { font, ...opts.measure ? { measure: opts.measure } : {} });
|
|
3904
|
+
const at = opts.at ?? { x: 0, y: 0 };
|
|
3905
|
+
const out = new PartSpecs(opts.id, at, font);
|
|
3906
|
+
out.specs.push({
|
|
3907
|
+
id: opts.id,
|
|
3908
|
+
kind: "mark",
|
|
3909
|
+
layer: "main",
|
|
3910
|
+
pose: out.place({ x: 0, y: 0, width: layout.width, height: layout.height }),
|
|
3911
|
+
data: { diagram: { sequence: data, geometry: geometryOf(layout) } }
|
|
3912
|
+
});
|
|
3913
|
+
boxSpecs(out, data, layout, theme);
|
|
3914
|
+
fragmentSpecs(out, data.steps, layout, theme, true);
|
|
3915
|
+
participantSpecs(out, data, layout, theme);
|
|
3916
|
+
messageSpecs(out, data.steps, layout, theme);
|
|
3917
|
+
noteSpecs(out, data.steps, layout, theme);
|
|
3918
|
+
headSpecs(out, data, layout, theme);
|
|
3919
|
+
fragmentSpecs(out, data.steps, layout, theme, false);
|
|
3920
|
+
const layer = opts.layer ?? "main";
|
|
3921
|
+
return localizeWorldPoses(out.specs, opts.composition ?? RIGID_POSE_COMPOSITION).map((s) => ({ ...s, layer }));
|
|
3922
|
+
}
|
|
3923
|
+
var worldPose = (scene, id) => composeWorldPose(scenePoseAdapter(scene), id, scene.composition.compose);
|
|
3924
|
+
function rebuild(scene, id, data, opts) {
|
|
3925
|
+
const root = scene.get(id);
|
|
3926
|
+
const at = worldPose(scene, id);
|
|
3927
|
+
const [own, ...parts] = sequenceSpecs(data, {
|
|
3928
|
+
id,
|
|
3929
|
+
at,
|
|
3930
|
+
theme: opts.theme ?? diagramThemeOf(scene),
|
|
3931
|
+
layer: root.layer,
|
|
3932
|
+
composition: scene.composition,
|
|
3933
|
+
...opts.measure ? { measure: opts.measure } : {}
|
|
3934
|
+
});
|
|
3935
|
+
const selected = scene.getSelection();
|
|
3936
|
+
scene.removeMany([...scene.childrenOf(id)]);
|
|
3937
|
+
const pose = root.pose;
|
|
3938
|
+
scene.update(id, { data: own.data });
|
|
3939
|
+
scene.setPose(id, { ...pose, width: own.pose.width, height: own.pose.height });
|
|
3940
|
+
for (const part of parts) scene.add(part);
|
|
3941
|
+
const kept = selected.filter((s) => scene.get(s) !== void 0);
|
|
3942
|
+
if (kept.length !== scene.getSelection().length) scene.setSelection(kept);
|
|
3943
|
+
}
|
|
3944
|
+
function commitSequence(scene, id, data, label, opts = {}) {
|
|
3945
|
+
const node = scene.get(id);
|
|
3946
|
+
if (node === void 0 || sequenceRootOf(node) === null) return;
|
|
3947
|
+
scene.batch(label, () => rebuild(scene, id, data, opts));
|
|
3948
|
+
}
|
|
3949
|
+
function startSequence(scene, at, data, label, opts = {}) {
|
|
3950
|
+
const id = scene.mintId();
|
|
3951
|
+
const layer = opts.layer ?? scene.layers[0]?.id ?? "main";
|
|
3952
|
+
const specs = sequenceSpecs(data, {
|
|
3953
|
+
id,
|
|
3954
|
+
at,
|
|
3955
|
+
theme: opts.theme ?? diagramThemeOf(scene),
|
|
3956
|
+
layer,
|
|
3957
|
+
composition: scene.composition,
|
|
3958
|
+
...opts.measure ? { measure: opts.measure } : {}
|
|
3959
|
+
});
|
|
3960
|
+
scene.batch(label, () => {
|
|
3961
|
+
for (const spec of specs) scene.add(spec);
|
|
3962
|
+
});
|
|
3963
|
+
return id;
|
|
3964
|
+
}
|
|
3965
|
+
function redrawSequences(scene, opts = {}) {
|
|
3966
|
+
for (const node of scene.renderOrderNodes()) {
|
|
3967
|
+
const root = sequenceRootOf(node);
|
|
3968
|
+
if (root !== null) rebuild(scene, node.id, root.sequence, opts);
|
|
3969
|
+
}
|
|
3970
|
+
}
|
|
3971
|
+
function foldSequenceText(scene, textId, commit) {
|
|
3972
|
+
const node = scene.get(textId);
|
|
3973
|
+
const part = node === void 0 ? null : sequencePartOf(node);
|
|
3974
|
+
const root = part === null ? void 0 : scene.get(part.root);
|
|
3975
|
+
const held = root === void 0 ? null : sequenceRootOf(root);
|
|
3976
|
+
if (part === null || held === null || part.target === void 0) return;
|
|
3977
|
+
const text = node.data.text;
|
|
3978
|
+
if (typeof text !== "string") return;
|
|
3979
|
+
const next = setSequenceText(held.sequence, part.target, text);
|
|
3980
|
+
if (next === held.sequence) return;
|
|
3981
|
+
scene.undo();
|
|
3982
|
+
commit({ id: part.root, data: next, label: "Edit text" });
|
|
3983
|
+
}
|
|
3984
|
+
function sequenceCommitOf(opts) {
|
|
3985
|
+
return opts.commit ?? ((edit, ctx) => {
|
|
3986
|
+
const scene = ctx.deps.scene;
|
|
3987
|
+
if (scene !== void 0) commitSequence(scene, edit.id, edit.data, edit.label, opts);
|
|
3988
|
+
});
|
|
3989
|
+
}
|
|
3990
|
+
|
|
3991
|
+
// src/sequence/author.ts
|
|
3992
|
+
function idsOf(data) {
|
|
3993
|
+
const ids = new Set(data.participants.map((p) => p.id));
|
|
3994
|
+
for (const s of flatSteps(data.steps)) if (hasId(s)) ids.add(s.id);
|
|
3995
|
+
for (const b of data.boxes ?? []) ids.add(b.id);
|
|
3996
|
+
return ids;
|
|
3997
|
+
}
|
|
3998
|
+
var seqIdTaken = (data, id) => idsOf(data).has(id);
|
|
3999
|
+
function freshSeqId(data, prefix) {
|
|
4000
|
+
const ids = idsOf(data);
|
|
4001
|
+
let n = 1;
|
|
4002
|
+
while (ids.has(`${prefix}${n}`)) n++;
|
|
4003
|
+
return `${prefix}${n}`;
|
|
4004
|
+
}
|
|
4005
|
+
function newParticipant(data, kind = "participant") {
|
|
4006
|
+
const id = freshSeqId(data, kind === "actor" ? "A" : "P");
|
|
4007
|
+
const label = `${kind === "actor" ? "Actor" : "Participant"} ${id.slice(1)}`;
|
|
4008
|
+
return { id, label, ...kind === "actor" ? { outline: "actor" } : {} };
|
|
4009
|
+
}
|
|
4010
|
+
function addParticipant(data, p, index = data.participants.length) {
|
|
4011
|
+
if (seqIdTaken(data, p.id)) return data;
|
|
4012
|
+
const at = Math.max(0, Math.min(index, data.participants.length));
|
|
4013
|
+
return { ...data, participants: [...data.participants.slice(0, at), p, ...data.participants.slice(at)] };
|
|
4014
|
+
}
|
|
4015
|
+
function removeParticipant(data, id) {
|
|
4016
|
+
if (!data.participants.some((p) => p.id === id)) return data;
|
|
4017
|
+
const steps = pruneSteps(data.steps, (s) => {
|
|
4018
|
+
if (s.kind === "message") return s.from === id || s.to === id ? null : s;
|
|
4019
|
+
if (s.kind === "note") {
|
|
4020
|
+
if (!s.over.includes(id)) return s;
|
|
4021
|
+
const over = s.over.filter((q) => q !== id);
|
|
4022
|
+
return over.length === 0 ? null : { ...s, over };
|
|
4023
|
+
}
|
|
4024
|
+
if (s.kind === "fragment") return s;
|
|
4025
|
+
return s.participant === id ? null : s;
|
|
4026
|
+
});
|
|
4027
|
+
const boxes = data.boxes?.flatMap((b) => {
|
|
4028
|
+
if (!b.members.includes(id)) return [b];
|
|
4029
|
+
const members2 = b.members.filter((m) => m !== id);
|
|
4030
|
+
return members2.length === 0 ? [] : [{ ...b, members: members2 }];
|
|
4031
|
+
});
|
|
4032
|
+
const { boxes: _boxes, ...rest } = data;
|
|
4033
|
+
return {
|
|
4034
|
+
...rest,
|
|
4035
|
+
participants: data.participants.filter((p) => p.id !== id),
|
|
4036
|
+
steps,
|
|
4037
|
+
...boxes !== void 0 && boxes.length > 0 ? { boxes } : {}
|
|
4038
|
+
};
|
|
4039
|
+
}
|
|
4040
|
+
function setParticipantOutline(data, id, outline) {
|
|
4041
|
+
const at = data.participants.findIndex((p2) => p2.id === id);
|
|
4042
|
+
const p = data.participants[at];
|
|
4043
|
+
const next = outline === "rect" ? void 0 : outline;
|
|
4044
|
+
if (p === void 0 || p.outline === next) return data;
|
|
4045
|
+
const { outline: _outline, ...rest } = p;
|
|
4046
|
+
const q = next === void 0 ? rest : { ...rest, outline: next };
|
|
4047
|
+
return { ...data, participants: data.participants.map((r, i) => i === at ? q : r) };
|
|
4048
|
+
}
|
|
4049
|
+
function addBox(data, box) {
|
|
4050
|
+
const members2 = data.participants.filter((p) => box.members.includes(p.id));
|
|
4051
|
+
if (members2.length === 0 || seqIdTaken(data, box.id)) return data;
|
|
4052
|
+
const taken = new Set(members2.map((p) => p.id));
|
|
4053
|
+
const first = data.participants.findIndex((p) => taken.has(p.id));
|
|
4054
|
+
const before = data.participants.slice(0, first).filter((p) => !taken.has(p.id));
|
|
4055
|
+
const after = data.participants.slice(first).filter((p) => !taken.has(p.id));
|
|
4056
|
+
const others = (data.boxes ?? []).flatMap((b) => {
|
|
4057
|
+
const left = b.members.filter((m) => !taken.has(m));
|
|
4058
|
+
return left.length === 0 ? [] : [left.length === b.members.length ? b : { ...b, members: left }];
|
|
4059
|
+
});
|
|
4060
|
+
return {
|
|
4061
|
+
...data,
|
|
4062
|
+
participants: [...before, ...members2, ...after],
|
|
4063
|
+
boxes: [...others, { ...box, members: members2.map((p) => p.id) }]
|
|
4064
|
+
};
|
|
4065
|
+
}
|
|
4066
|
+
function removeBox(data, id) {
|
|
4067
|
+
if (!data.boxes?.some((b) => b.id === id)) return data;
|
|
4068
|
+
const boxes = data.boxes.filter((b) => b.id !== id);
|
|
4069
|
+
const { boxes: _boxes, ...rest } = data;
|
|
4070
|
+
return boxes.length === 0 ? rest : { ...rest, boxes };
|
|
4071
|
+
}
|
|
4072
|
+
function setAutonumber(data, from) {
|
|
4073
|
+
if (data.autonumber === from) return data;
|
|
4074
|
+
const { autonumber: _n, ...rest } = data;
|
|
4075
|
+
return from === void 0 ? rest : { ...rest, autonumber: from };
|
|
4076
|
+
}
|
|
4077
|
+
|
|
4078
|
+
// src/sequence/authorSteps.ts
|
|
4079
|
+
var SEQ_ARROWS = {
|
|
4080
|
+
/** A call: a solid line, a filled head. */
|
|
4081
|
+
sync: { line: "solid", head: "arrow" },
|
|
4082
|
+
/** An answer: a dashed line, a filled head. */
|
|
4083
|
+
reply: { line: "dashed", head: "arrow" },
|
|
4084
|
+
/** Sent without waiting: a solid line, an open head. */
|
|
4085
|
+
async: { line: "solid", head: "open", async: true },
|
|
4086
|
+
/** Lost or refused: a solid line ending in a cross. */
|
|
4087
|
+
lost: { line: "solid", head: "cross" },
|
|
4088
|
+
/** A plain solid line. */
|
|
4089
|
+
line: { line: "solid", head: "none" },
|
|
4090
|
+
/** A plain dashed line. */
|
|
4091
|
+
dashed: { line: "dashed", head: "none" },
|
|
4092
|
+
/** Both ways: a solid line, filled heads at both ends. */
|
|
4093
|
+
both: { line: "solid", head: "arrow", both: true }
|
|
4094
|
+
};
|
|
4095
|
+
var SEQ_MULTI_SECTION = /* @__PURE__ */ new Set(["alt", "par", "critical"]);
|
|
4096
|
+
function insertStep(data, step, at) {
|
|
4097
|
+
const list = stepListOf(data.steps, at.parent);
|
|
4098
|
+
if (list === null || hasId(step) && seqIdTaken(data, step.id)) return data;
|
|
4099
|
+
const index = Math.max(0, Math.min(at.index, list.length));
|
|
4100
|
+
return { ...data, steps: withList(data.steps, at.parent, (l) => [...l.slice(0, index), step, ...l.slice(index)]) };
|
|
4101
|
+
}
|
|
4102
|
+
var known = (data, ...ids) => ids.every((id) => data.participants.some((p) => p.id === id));
|
|
4103
|
+
function insertMessage(data, m) {
|
|
4104
|
+
if (!known(data, m.from, m.to)) return data;
|
|
4105
|
+
const arrow = typeof m.arrow === "object" ? m.arrow : SEQ_ARROWS[m.arrow ?? "sync"];
|
|
4106
|
+
const step = {
|
|
4107
|
+
kind: "message",
|
|
4108
|
+
id: m.id ?? freshSeqId(data, "m"),
|
|
4109
|
+
from: m.from,
|
|
4110
|
+
to: m.to,
|
|
4111
|
+
...arrow,
|
|
4112
|
+
...m.text ? { text: m.text } : {}
|
|
4113
|
+
};
|
|
4114
|
+
return insertStep(data, step, m.at);
|
|
4115
|
+
}
|
|
4116
|
+
function insertNote(data, n) {
|
|
4117
|
+
if (n.over.length === 0 || !known(data, ...n.over)) return data;
|
|
4118
|
+
const step = {
|
|
4119
|
+
kind: "note",
|
|
4120
|
+
id: n.id ?? freshSeqId(data, "n"),
|
|
4121
|
+
over: n.side === void 0 ? [...n.over] : [n.over[0]],
|
|
4122
|
+
...n.side ? { side: n.side } : {},
|
|
4123
|
+
text: n.text ?? "Note"
|
|
4124
|
+
};
|
|
4125
|
+
return insertStep(data, step, n.at);
|
|
4126
|
+
}
|
|
4127
|
+
function insertMark(data, mark, at) {
|
|
4128
|
+
if (!known(data, mark.participant)) return data;
|
|
4129
|
+
if (mark.kind === "create" && hasMark(data.steps, "create", mark.participant)) return data;
|
|
4130
|
+
return insertStep(data, { kind: mark.kind, participant: mark.participant }, at);
|
|
4131
|
+
}
|
|
4132
|
+
function hasMark(steps, kind, participant) {
|
|
4133
|
+
return steps.some((s) => s.kind === kind && s.participant === participant || s.kind === "fragment" && s.sections.some((sec) => hasMark(sec.steps, kind, participant)));
|
|
4134
|
+
}
|
|
4135
|
+
function removeStep(data, id) {
|
|
4136
|
+
if (findStep(data.steps, id) === null) return data;
|
|
4137
|
+
return { ...data, steps: pruneSteps(data.steps, (s) => hasId(s) && s.id === id ? null : s) };
|
|
4138
|
+
}
|
|
4139
|
+
function removeStepAt(data, at) {
|
|
4140
|
+
const list = stepListOf(data.steps, at.parent);
|
|
4141
|
+
if (list === null || at.index < 0 || at.index >= list.length) return data;
|
|
4142
|
+
return { ...data, steps: withList(data.steps, at.parent, (l) => l.filter((_, i) => i !== at.index)) };
|
|
4143
|
+
}
|
|
4144
|
+
function removeMarks(data, refs) {
|
|
4145
|
+
const key = (p) => p === null ? "" : `${p.fragment}/${p.section}`;
|
|
4146
|
+
const places = refs.flatMap((r) => "place" in r ? [r.place] : []).filter((p) => {
|
|
4147
|
+
const s = stepListOf(data.steps, p.parent)?.[p.index];
|
|
4148
|
+
return s !== void 0 && !hasId(s);
|
|
4149
|
+
}).sort((a, b) => key(a.parent) === key(b.parent) ? b.index - a.index : key(a.parent) < key(b.parent) ? -1 : 1);
|
|
4150
|
+
let next = places.reduce((d, p) => removeStepAt(d, p), data);
|
|
4151
|
+
for (const r of refs) if ("message" in r) next = setMessageActivation(next, r.message, { [r.flag]: void 0 });
|
|
4152
|
+
return next;
|
|
4153
|
+
}
|
|
4154
|
+
function removeMarksOf(data, kind, participant) {
|
|
4155
|
+
const steps = pruneSteps(data.steps, (s) => s.kind === kind && s.participant === participant ? null : s);
|
|
4156
|
+
return steps === data.steps ? data : { ...data, steps };
|
|
4157
|
+
}
|
|
4158
|
+
function wrapSteps(data, parent, start, end, f) {
|
|
4159
|
+
const list = stepListOf(data.steps, parent);
|
|
4160
|
+
const id = f.id ?? freshSeqId(data, "f");
|
|
4161
|
+
if (list === null || seqIdTaken(data, id)) return data;
|
|
4162
|
+
const lo = Math.max(0, Math.min(start, end, list.length));
|
|
4163
|
+
const hi = Math.max(lo, Math.min(Math.max(start, end), list.length));
|
|
4164
|
+
const frag = {
|
|
4165
|
+
kind: "fragment",
|
|
4166
|
+
id,
|
|
4167
|
+
type: f.type,
|
|
4168
|
+
sections: [{ ...f.label ? { label: f.label } : {}, steps: list.slice(lo, hi) }]
|
|
4169
|
+
};
|
|
4170
|
+
return { ...data, steps: withList(data.steps, parent, (l) => [...l.slice(0, lo), frag, ...l.slice(hi)]) };
|
|
4171
|
+
}
|
|
4172
|
+
function unwrapFragment(data, id) {
|
|
4173
|
+
const place2 = placeOf(data.steps, id);
|
|
4174
|
+
const frag = findStep(data.steps, id);
|
|
4175
|
+
if (place2 === null || frag?.kind !== "fragment") return data;
|
|
4176
|
+
const inner = frag.sections.flatMap((sec) => sec.steps);
|
|
4177
|
+
return { ...data, steps: withList(data.steps, place2.parent, (l) => [...l.slice(0, place2.index), ...inner, ...l.slice(place2.index + 1)]) };
|
|
4178
|
+
}
|
|
4179
|
+
function withFragment(data, id, fn) {
|
|
4180
|
+
const steps = mapSteps(data.steps, (s) => s.kind === "fragment" && s.id === id ? fn(s) : s);
|
|
4181
|
+
return steps === data.steps ? data : { ...data, steps };
|
|
4182
|
+
}
|
|
4183
|
+
function addSection(data, id, label, index) {
|
|
4184
|
+
return withFragment(data, id, (f) => {
|
|
4185
|
+
if (!SEQ_MULTI_SECTION.has(f.type)) return f;
|
|
4186
|
+
const at = Math.max(1, Math.min(index ?? f.sections.length, f.sections.length));
|
|
4187
|
+
const section = { ...label ? { label } : {}, steps: [] };
|
|
4188
|
+
return { ...f, sections: [...f.sections.slice(0, at), section, ...f.sections.slice(at)] };
|
|
4189
|
+
});
|
|
4190
|
+
}
|
|
4191
|
+
function removeSection(data, id, index) {
|
|
4192
|
+
return withFragment(data, id, (f) => f.sections.length < 2 || f.sections[index] === void 0 ? f : { ...f, sections: f.sections.filter((_, k) => k !== index) });
|
|
4193
|
+
}
|
|
4194
|
+
function setFragmentType(data, id, type) {
|
|
4195
|
+
return withFragment(data, id, (f) => f.type === type || f.sections.length > 1 && !SEQ_MULTI_SECTION.has(type) ? f : { ...f, type });
|
|
4196
|
+
}
|
|
4197
|
+
function withMessage(data, id, fn) {
|
|
4198
|
+
const steps = mapSteps(data.steps, (s) => s.kind === "message" && s.id === id ? fn(s) : s);
|
|
4199
|
+
return steps === data.steps ? data : { ...data, steps };
|
|
4200
|
+
}
|
|
4201
|
+
function setMessageArrow(data, id, arrow) {
|
|
4202
|
+
const next = typeof arrow === "object" ? arrow : SEQ_ARROWS[arrow];
|
|
4203
|
+
return withMessage(data, id, (m) => {
|
|
4204
|
+
if (m.line === next.line && m.head === next.head && m.both === next.both && m.async === next.async) return m;
|
|
4205
|
+
const { both: _both, async: _async, ...rest } = m;
|
|
4206
|
+
return { ...rest, line: next.line, head: next.head, ...next.both ? { both: true } : {}, ...next.async ? { async: true } : {} };
|
|
4207
|
+
});
|
|
4208
|
+
}
|
|
4209
|
+
function setMessageActivation(data, id, flags) {
|
|
4210
|
+
return withMessage(data, id, (m) => {
|
|
4211
|
+
const activate = "activate" in flags ? flags.activate : m.activate;
|
|
4212
|
+
const deactivate = "deactivate" in flags ? flags.deactivate : m.deactivate;
|
|
4213
|
+
if (activate === m.activate && deactivate === m.deactivate) return m;
|
|
4214
|
+
const { activate: _a, deactivate: _d, ...rest } = m;
|
|
4215
|
+
return { ...rest, ...activate ? { activate } : {}, ...deactivate ? { deactivate } : {} };
|
|
4216
|
+
});
|
|
4217
|
+
}
|
|
4218
|
+
var SEQUENCE_LAYER_ID = "diagram-sequence";
|
|
4219
|
+
var SEQUENCE_AFFORDANCE_KIND = `layer:${SEQUENCE_LAYER_ID}`;
|
|
4220
|
+
function sequenceScratchOf(hit) {
|
|
4221
|
+
const p = hit?.payload;
|
|
4222
|
+
return p !== void 0 && typeof p.root === "string" && typeof p.id === "string" && typeof p.part === "string" ? p : null;
|
|
4223
|
+
}
|
|
4224
|
+
function placedSequences(scene) {
|
|
4225
|
+
const out = [];
|
|
4226
|
+
const poses = scenePoseAdapter(scene);
|
|
4227
|
+
for (const node of scene.renderOrderNodes()) {
|
|
4228
|
+
const root = sequenceRootOf(node);
|
|
4229
|
+
if (root === null || scene.isLocked(node.id) || scene.isHidden(node.id)) continue;
|
|
4230
|
+
const at = composeWorldPose(poses, node.id, scene.composition.compose);
|
|
4231
|
+
out.push({ id: node.id, origin: { x: at.x, y: at.y }, width: at.width, height: at.height, geometry: root.geometry });
|
|
4232
|
+
}
|
|
4233
|
+
return out;
|
|
4234
|
+
}
|
|
4235
|
+
var REACH = 6;
|
|
4236
|
+
var END = 7;
|
|
4237
|
+
var moved = (r, o) => ({ x: r.x + o.x, y: r.y + o.y, width: r.width, height: r.height });
|
|
4238
|
+
function messageReach(m) {
|
|
4239
|
+
const x0 = Math.min(m.x1, m.x2);
|
|
4240
|
+
const x1 = m.loopY !== void 0 ? m.x1 + 32 : Math.max(m.x1, m.x2);
|
|
4241
|
+
const y1 = (m.loopY ?? m.y) + REACH;
|
|
4242
|
+
const top = Math.min(m.y - REACH, m.text?.y ?? Infinity);
|
|
4243
|
+
const left = Math.min(x0, m.text?.x ?? Infinity);
|
|
4244
|
+
const right = Math.max(x1, m.text ? m.text.x + m.text.width : -Infinity);
|
|
4245
|
+
return { x: left, y: top, width: right - left, height: y1 - top };
|
|
4246
|
+
}
|
|
4247
|
+
function region(id, r, scratch, cursor) {
|
|
4248
|
+
return {
|
|
4249
|
+
id,
|
|
4250
|
+
targetId: null,
|
|
4251
|
+
shape: { kind: "rect", x: r.x, y: r.y, width: r.width, height: r.height },
|
|
4252
|
+
cursor,
|
|
4253
|
+
strength: "exclusive",
|
|
4254
|
+
claimedKinds: ["pointer"],
|
|
4255
|
+
bind: () => ({ initialScratch: scratch })
|
|
4256
|
+
};
|
|
4257
|
+
}
|
|
4258
|
+
function regionsOf(all) {
|
|
4259
|
+
const parts = [];
|
|
4260
|
+
const ends = [];
|
|
4261
|
+
for (const { id: root, origin, geometry } of all) {
|
|
4262
|
+
const base = { targetId: root, root };
|
|
4263
|
+
for (const h of geometry.heads) parts.push(region(`${root}/head:${h.id}`, moved(h.box, origin), { ...base, part: "head", id: h.id }, "ew-resize"));
|
|
4264
|
+
for (const m of geometry.marks ?? []) parts.push(region(`${root}/${m.id}`, moved(m.grip, origin), { ...base, part: "mark", id: m.id }, "default"));
|
|
4265
|
+
for (const b of geometry.boxes ?? []) parts.push(region(`${root}/box:${b.id}`, moved(b.grip, origin), { ...base, part: "box", id: b.id }, "default"));
|
|
4266
|
+
for (const f of geometry.fragments ?? []) parts.push(region(`${root}/frag:${f.id}`, moved(f.grip, origin), { ...base, part: "step", id: f.id }, "ns-resize"));
|
|
4267
|
+
for (const n of geometry.notes) parts.push(region(`${root}/note:${n.id}`, moved(n.box, origin), { ...base, part: "step", id: n.id }, "ns-resize"));
|
|
4268
|
+
for (const m of geometry.messages) {
|
|
4269
|
+
parts.push(region(`${root}/msg:${m.id}`, moved(messageReach(m), origin), { ...base, part: "step", id: m.id }, "ns-resize"));
|
|
4270
|
+
const at = [["from", m.x1, m.y], ["to", m.x2, m.loopY ?? m.y]];
|
|
4271
|
+
for (const [end, x, y] of at) {
|
|
4272
|
+
const r = { x: x - END + origin.x, y: y - END + origin.y, width: END * 2, height: END * 2 };
|
|
4273
|
+
ends.push(region(`${root}/msg:${m.id}/${end}`, r, { ...base, part: "end", id: m.id, end }, "crosshair"));
|
|
4274
|
+
}
|
|
4275
|
+
}
|
|
4276
|
+
}
|
|
4277
|
+
return { parts, ends };
|
|
4278
|
+
}
|
|
4279
|
+
var INDEXES = /* @__PURE__ */ new WeakMap();
|
|
4280
|
+
function regionsIn(scene) {
|
|
4281
|
+
let index = INDEXES.get(scene);
|
|
4282
|
+
if (index === void 0) {
|
|
4283
|
+
index = keyedIndex(
|
|
4284
|
+
() => typeof scene.getVersion === "function" ? [scene.getVersion()] : void 0,
|
|
4285
|
+
() => regionsOf(placedSequences(scene))
|
|
4286
|
+
);
|
|
4287
|
+
INDEXES.set(scene, index);
|
|
4288
|
+
}
|
|
4289
|
+
return index() ?? regionsOf(placedSequences(scene));
|
|
4290
|
+
}
|
|
4291
|
+
function sequenceLayer(scene) {
|
|
4292
|
+
const parts = { id: SEQUENCE_LAYER_ID, regions: () => regionsIn(scene).parts };
|
|
4293
|
+
const ends = { id: `${SEQUENCE_LAYER_ID}-ends`, regions: () => regionsIn(scene).ends };
|
|
4294
|
+
return composeAffordanceLayer(SEQUENCE_LAYER_ID, "Sequence diagram parts", [parts, ends]);
|
|
4295
|
+
}
|
|
4296
|
+
var SELECT_SEQUENCE_PART_ACTION_ID = "diagram.sequence.select";
|
|
4297
|
+
function sequencePartNodeOf(scene, s) {
|
|
4298
|
+
if (s.part === "mark") return scene.get(`${s.root}/${s.id}`) === void 0 ? null : `${s.root}/${s.id}`;
|
|
4299
|
+
const names = s.part === "head" ? [`head:${s.id}`] : s.part === "box" ? [`box:${s.id}`] : s.part === "end" ? [`msg:${s.id}`] : [`msg:${s.id}`, `note:${s.id}`, `frag:${s.id}`];
|
|
4300
|
+
return names.map((n) => `${s.root}/${n}`).find((id) => scene.get(id) !== void 0) ?? null;
|
|
4301
|
+
}
|
|
4302
|
+
var selectSequencePartAction = {
|
|
4303
|
+
id: SELECT_SEQUENCE_PART_ACTION_ID,
|
|
4304
|
+
label: "Select sequence part",
|
|
4305
|
+
group: "diagram",
|
|
4306
|
+
requires: ["scene", "selection"],
|
|
4307
|
+
invoker: {
|
|
4308
|
+
timing: "immediate",
|
|
4309
|
+
run(deps, params) {
|
|
4310
|
+
const scene = deps.scene;
|
|
4311
|
+
const selection = deps.selection;
|
|
4312
|
+
const scratch = sequenceScratchOf(params?.affordance);
|
|
4313
|
+
const id = scene === void 0 || scratch === null ? null : sequencePartNodeOf(scene, scratch);
|
|
4314
|
+
if (id === null || selection === void 0) return;
|
|
4315
|
+
const held = selection.get();
|
|
4316
|
+
const shift = params?.mods?.shift === true;
|
|
4317
|
+
selection.set(!shift ? [id] : held.includes(id) ? held.filter((h) => h !== id) : [...held, id]);
|
|
4318
|
+
}
|
|
4319
|
+
},
|
|
4320
|
+
enabled: () => true
|
|
4321
|
+
};
|
|
4322
|
+
function removing(data, part) {
|
|
4323
|
+
if (part.part === "head" || part.part === "lifeline") return removeParticipant(data, part.id);
|
|
4324
|
+
if (part.part === "box") return removeBox(data, part.id);
|
|
4325
|
+
if (part.part === "message" || part.part === "note" || part.part === "fragment" || part.part === "divider") return removeStep(data, part.id);
|
|
4326
|
+
return data;
|
|
4327
|
+
}
|
|
4328
|
+
function sequenceDeletes(scene, ids) {
|
|
4329
|
+
const byRoot = /* @__PURE__ */ new Map();
|
|
4330
|
+
const rest = [];
|
|
4331
|
+
for (const id of ids) {
|
|
4332
|
+
const node = scene.get(id);
|
|
4333
|
+
const part = node === void 0 ? null : sequencePartOf(node);
|
|
4334
|
+
if (part === null) {
|
|
4335
|
+
rest.push(id);
|
|
4336
|
+
continue;
|
|
4337
|
+
}
|
|
4338
|
+
const root = scene.get(part.root);
|
|
4339
|
+
const held = root === void 0 ? null : sequenceRootOf(root);
|
|
4340
|
+
if (held === null) continue;
|
|
4341
|
+
const at = byRoot.get(part.root) ?? { was: held.sequence, marks: [], parts: [] };
|
|
4342
|
+
byRoot.set(part.root, at);
|
|
4343
|
+
if (part.marks !== void 0) at.marks.push(...part.marks);
|
|
4344
|
+
else if (part.id !== void 0) at.parts.push({ part: part.part, id: part.id });
|
|
4345
|
+
}
|
|
4346
|
+
const edits = [...byRoot].flatMap(([id, { was, marks, parts }]) => {
|
|
4347
|
+
const data = parts.reduce(removing, removeMarks(was, marks));
|
|
4348
|
+
return data === was ? [] : [{ id, data }];
|
|
4349
|
+
});
|
|
4350
|
+
return { edits, rest };
|
|
4351
|
+
}
|
|
4352
|
+
function createSequenceDeleteAction(opts = {}) {
|
|
4353
|
+
const commit = sequenceCommitOf(opts);
|
|
4354
|
+
const others = opts.others ?? commitDeleteToScene;
|
|
4355
|
+
return createDeleteAction({
|
|
4356
|
+
commit: (ids, deps) => {
|
|
4357
|
+
const scene = deps.scene;
|
|
4358
|
+
if (scene === void 0) return;
|
|
4359
|
+
const { edits, rest } = sequenceDeletes(scene, ids);
|
|
4360
|
+
for (const edit of edits) commit({ ...edit, label: "Delete" }, { deps });
|
|
4361
|
+
if (rest.length > 0) others(rest, deps);
|
|
4362
|
+
else deps.selection?.set([]);
|
|
4363
|
+
}
|
|
4364
|
+
});
|
|
4365
|
+
}
|
|
4366
|
+
|
|
4367
|
+
// src/sequence/actions.ts
|
|
4368
|
+
var SEQUENCE_DRAG_ACTION_ID = "diagram.sequence.drag";
|
|
4369
|
+
var GRAB_SEQUENCE_ACTION_ID = "diagram.sequence.grab";
|
|
4370
|
+
function carries(s, part) {
|
|
4371
|
+
if (s.part === "head") {
|
|
4372
|
+
if (part.target !== void 0) return "participant" in part.target && part.target.participant === s.id;
|
|
4373
|
+
return part.id === s.id && ["head", "lifeline", "destroy", "activation"].includes(part.part);
|
|
4374
|
+
}
|
|
4375
|
+
if (s.part === "step") {
|
|
4376
|
+
return part.id === s.id || part.target !== void 0 && ("step" in part.target && part.target.step === s.id || "fragment" in part.target && part.target.fragment === s.id);
|
|
4377
|
+
}
|
|
4378
|
+
return s.part === "end" && part.part === "message" && part.id === s.id;
|
|
4379
|
+
}
|
|
4380
|
+
function pressOf2(ctx) {
|
|
4381
|
+
const scratch = sequenceScratchOf(ctx.drag?.affordance);
|
|
4382
|
+
const scene = ctx.deps.scene;
|
|
4383
|
+
const node = scratch === null ? void 0 : scene?.get(scratch.root);
|
|
4384
|
+
const held = node === void 0 ? null : sequenceRootOf(node);
|
|
4385
|
+
const root = scene === void 0 || held === null ? void 0 : placedSequences(scene).find((p) => p.id === scratch.root);
|
|
4386
|
+
if (scratch === null || scene === void 0 || held === null || root === void 0) return null;
|
|
4387
|
+
const carried = /* @__PURE__ */ new Map();
|
|
4388
|
+
for (const id of scene.childrenOf(scratch.root)) {
|
|
4389
|
+
const child = scene.get(id);
|
|
4390
|
+
const part = sequencePartOf(child);
|
|
4391
|
+
if (part !== null && carries(scratch, part)) carried.set(id, child.pose);
|
|
4392
|
+
}
|
|
4393
|
+
return { scratch, scene, root, data: held.sequence, start: ctx.drag?.start ?? { x: ctx.world.x, y: ctx.world.y }, carried };
|
|
4394
|
+
}
|
|
4395
|
+
var nearest = (xs, x) => xs.reduce((best, v, i) => Math.abs(v - x) < Math.abs(xs[best] - x) ? i : best, 0);
|
|
4396
|
+
function proposed(p, at) {
|
|
4397
|
+
const { geometry, origin } = p.root;
|
|
4398
|
+
const local = { x: at.x - origin.x, y: at.y - origin.y };
|
|
4399
|
+
const { scratch } = p;
|
|
4400
|
+
if (scratch.part === "box" || scratch.part === "mark") return { data: p.data, label: "" };
|
|
4401
|
+
if (scratch.part === "head") {
|
|
4402
|
+
const index = geometry.columns.filter((c) => c.x < local.x).length;
|
|
4403
|
+
return { data: moveParticipant(p.data, scratch.id, index), label: "Move participant" };
|
|
4404
|
+
}
|
|
4405
|
+
if (scratch.part === "end") {
|
|
4406
|
+
const column = geometry.columns[nearest(geometry.columns.map((c) => c.x), local.x)];
|
|
4407
|
+
if (column === void 0) return { data: p.data, label: "" };
|
|
4408
|
+
return { data: reconnectMessage(p.data, scratch.id, scratch.end, column.id), label: "Reconnect message" };
|
|
4409
|
+
}
|
|
4410
|
+
const slot = geometry.slots[nearest(geometry.slots.map((s) => s.y), local.y)];
|
|
4411
|
+
if (slot === void 0) return { data: p.data, label: "" };
|
|
4412
|
+
return { data: moveStep(p.data, scratch.id, slot.place), label: "Move step" };
|
|
4413
|
+
}
|
|
4414
|
+
function previewOf(p, id, pose, at) {
|
|
4415
|
+
const dx = at.x - p.start.x;
|
|
4416
|
+
const dy = at.y - p.start.y;
|
|
4417
|
+
if (p.scratch.part === "head") return { ...pose, x: pose.x + dx };
|
|
4418
|
+
if (p.scratch.part === "step") return { ...pose, y: pose.y + dy };
|
|
4419
|
+
const m = p.root.geometry.messages.find((q) => q.id === p.scratch.id);
|
|
4420
|
+
if (m === void 0 || id !== `${p.scratch.root}/msg:${p.scratch.id}` || m.loopY !== void 0) return pose;
|
|
4421
|
+
const shift = pose.x - Math.min(m.x1, m.x2);
|
|
4422
|
+
const still = (p.scratch.end === "from" ? m.x2 : m.x1) + shift;
|
|
4423
|
+
const moving = (p.scratch.end === "from" ? m.x1 : m.x2) + shift + dx;
|
|
4424
|
+
return { ...pose, x: Math.min(still, moving), width: Math.abs(moving - still) };
|
|
4425
|
+
}
|
|
4426
|
+
function createSequenceDragAction(opts = {}) {
|
|
4427
|
+
const commit = sequenceCommitOf(opts);
|
|
4428
|
+
return {
|
|
4429
|
+
id: opts.id ?? SEQUENCE_DRAG_ACTION_ID,
|
|
4430
|
+
label: "Move sequence part",
|
|
4431
|
+
group: "diagram",
|
|
4432
|
+
requires: ["scene"],
|
|
4433
|
+
eligible: { mode: "normal" },
|
|
4434
|
+
invoker: {
|
|
4435
|
+
timing: "ongoing",
|
|
4436
|
+
start(ctx) {
|
|
4437
|
+
const p = pressOf2(ctx);
|
|
4438
|
+
if (p === null) return {};
|
|
4439
|
+
let at = null;
|
|
4440
|
+
let open = true;
|
|
4441
|
+
return {
|
|
4442
|
+
kind: "diagram-sequence-drag",
|
|
4443
|
+
onMove(move) {
|
|
4444
|
+
at = { x: move.world.x, y: move.world.y };
|
|
4445
|
+
},
|
|
4446
|
+
previewIds: () => open && at !== null ? [...p.carried.keys()] : null,
|
|
4447
|
+
previewPose: (id) => {
|
|
4448
|
+
const pose = p.carried.get(id);
|
|
4449
|
+
return open && at !== null && pose !== void 0 ? previewOf(p, id, pose, at) : null;
|
|
4450
|
+
},
|
|
4451
|
+
onEnd(end, reason) {
|
|
4452
|
+
open = false;
|
|
4453
|
+
if (reason !== "commit" || at === null) return;
|
|
4454
|
+
const next = proposed(p, { x: end.world.x, y: end.world.y });
|
|
4455
|
+
if (next.data !== p.data) commit({ id: p.scratch.root, data: next.data, label: next.label }, { ...end, deps: { ...end.deps, scene: p.scene } });
|
|
4456
|
+
}
|
|
4457
|
+
};
|
|
4458
|
+
}
|
|
4459
|
+
},
|
|
4460
|
+
enabled: () => true
|
|
4461
|
+
};
|
|
4462
|
+
}
|
|
4463
|
+
var grab = {
|
|
4464
|
+
id: GRAB_SEQUENCE_ACTION_ID,
|
|
4465
|
+
label: "Grab sequence part",
|
|
4466
|
+
group: "diagram",
|
|
4467
|
+
invoker: { timing: "immediate", run: () => {
|
|
4468
|
+
} },
|
|
4469
|
+
enabled: () => true
|
|
4470
|
+
};
|
|
4471
|
+
function diagramSequence(opts) {
|
|
4472
|
+
const actionId = opts.id ?? SEQUENCE_DRAG_ACTION_ID;
|
|
4473
|
+
const target = `affordance:${SEQUENCE_AFFORDANCE_KIND}`;
|
|
4474
|
+
return {
|
|
4475
|
+
layer: sequenceLayer(opts.scene),
|
|
4476
|
+
contribution: {
|
|
4477
|
+
id: "weasel-diagram-sequence",
|
|
4478
|
+
eligibility: { claimed: true },
|
|
4479
|
+
actions: [createSequenceDragAction({ ...opts, id: actionId }), grab, selectSequencePartAction],
|
|
4480
|
+
bindings: [
|
|
4481
|
+
{ spec: { kind: "drag", target }, actionId, opts: { id: "weasel-diagram-sequence.drag" } },
|
|
4482
|
+
{ spec: { kind: "pointerDown", target }, actionId: GRAB_SEQUENCE_ACTION_ID, opts: { id: "weasel-diagram-sequence.pointerDown" } },
|
|
4483
|
+
{ spec: { kind: "click", target, mods: { shift: "optional" } }, actionId: SELECT_SEQUENCE_PART_ACTION_ID, opts: { id: "weasel-diagram-sequence.click" } }
|
|
4484
|
+
]
|
|
4485
|
+
}
|
|
4486
|
+
};
|
|
4487
|
+
}
|
|
3301
4488
|
function agrees(selection, want) {
|
|
3302
4489
|
if (typeof want === "string" || want === null) return (selection.at(-1) ?? null) === want;
|
|
3303
4490
|
return selection.length === want.length && want.every((id) => selection.includes(id));
|
|
@@ -3407,11 +4594,11 @@ function diagramLayoutOf(specs) {
|
|
|
3407
4594
|
const id = s.id;
|
|
3408
4595
|
if (id === void 0) continue;
|
|
3409
4596
|
if (isBodyOrRegion(s)) {
|
|
3410
|
-
const
|
|
4597
|
+
const region2 = diagramContainerOf(s);
|
|
3411
4598
|
const pinned = isPinned({ id, kind: s.kind, data: s.data });
|
|
3412
4599
|
const { x, y, width, height } = world.get(id);
|
|
3413
|
-
if (
|
|
3414
|
-
if (!pinned &&
|
|
4600
|
+
if (region2 !== null) {
|
|
4601
|
+
if (!pinned && region2.role !== "lane" && region2.fitted !== true) {
|
|
3415
4602
|
containers.push({ kind: "box", id, box: { x, y, width, height } });
|
|
3416
4603
|
}
|
|
3417
4604
|
} else if (!pinned) moves.push({ id, x, y });
|
|
@@ -3439,8 +4626,8 @@ function changes(was, now) {
|
|
|
3439
4626
|
for (const [id, after] of now.routes) {
|
|
3440
4627
|
const before = was.routes.get(id);
|
|
3441
4628
|
if (before === void 0) continue;
|
|
3442
|
-
const
|
|
3443
|
-
if (
|
|
4629
|
+
const moved2 = before.waypoints !== after.waypoints && !samePoints(before.waypoints, after.waypoints);
|
|
4630
|
+
if (moved2 || !samePorts(before.ports, after.ports)) routes.push({ id, ...after });
|
|
3444
4631
|
}
|
|
3445
4632
|
return { moves, routes, containers: regionChanges(was.regions, now.regions) };
|
|
3446
4633
|
}
|
|
@@ -3449,11 +4636,11 @@ function useSceneSync(scene, specs, reports) {
|
|
|
3449
4636
|
const syncing = useRef(false);
|
|
3450
4637
|
const applied = useRef(specs);
|
|
3451
4638
|
useEffect(() => {
|
|
3452
|
-
let
|
|
4639
|
+
let known2 = readScene(scene);
|
|
3453
4640
|
return scene.subscribe(() => {
|
|
3454
4641
|
const now = readScene(scene);
|
|
3455
|
-
const found = syncing.current ? null : changes(
|
|
3456
|
-
|
|
4642
|
+
const found = syncing.current ? null : changes(known2, now);
|
|
4643
|
+
known2 = now;
|
|
3457
4644
|
if (found === null) return;
|
|
3458
4645
|
if (found.moves.length > 0 || found.routes.length > 0) reportsRef.current.onMove?.(found.moves, found.routes);
|
|
3459
4646
|
for (const edit of found.containers) reportsRef.current.onEditContainer?.(edit);
|
|
@@ -3470,6 +4657,15 @@ function useSceneSync(scene, specs, reports) {
|
|
|
3470
4657
|
applied.current = specs;
|
|
3471
4658
|
}, [scene, specs]);
|
|
3472
4659
|
}
|
|
4660
|
+
var isSequenceText = (data) => isTextData(data) && sequencePartOf({ data })?.target !== void 0;
|
|
4661
|
+
function useSequenceTextEdit(scene, report) {
|
|
4662
|
+
const latest = useLatest(report);
|
|
4663
|
+
const on = report !== void 0;
|
|
4664
|
+
return useMemo(() => on ? {
|
|
4665
|
+
isText: isSequenceText,
|
|
4666
|
+
onEnd: (id) => foldSequenceText(scene, id, (edit) => latest.current?.(edit))
|
|
4667
|
+
} : void 0, [on, scene, latest]);
|
|
4668
|
+
}
|
|
3473
4669
|
|
|
3474
4670
|
// src/regionEditing.ts
|
|
3475
4671
|
function laneResizeReports(scene, resize) {
|
|
@@ -3488,9 +4684,9 @@ function laneResizeReports(scene, resize) {
|
|
|
3488
4684
|
const { x, y, width, height } = after(id);
|
|
3489
4685
|
const was = before(id);
|
|
3490
4686
|
if (x === was.x && y === was.y) continue;
|
|
3491
|
-
const
|
|
3492
|
-
if (
|
|
3493
|
-
if (
|
|
4687
|
+
const region2 = diagramContainerOf(node);
|
|
4688
|
+
if (region2 !== null) {
|
|
4689
|
+
if (region2.role === "container" && region2.fitted !== true) boxes.push({ kind: "box", id, box: { x, y, width, height } });
|
|
3494
4690
|
} else if (isDiagramBody(node)) moves.push({ id, x, y });
|
|
3495
4691
|
}
|
|
3496
4692
|
}
|
|
@@ -3565,7 +4761,16 @@ function reportDeleteAction(report) {
|
|
|
3565
4761
|
}
|
|
3566
4762
|
});
|
|
3567
4763
|
}
|
|
3568
|
-
function
|
|
4764
|
+
function sequenceDeleteAction(scene, edit, report) {
|
|
4765
|
+
return createSequenceDeleteAction({
|
|
4766
|
+
commit: edit,
|
|
4767
|
+
others: (ids) => {
|
|
4768
|
+
const removed = removedData(scene, ids);
|
|
4769
|
+
if (report !== null && (removed.nodes.length > 0 || removed.edges.length > 0)) report(removed);
|
|
4770
|
+
}
|
|
4771
|
+
});
|
|
4772
|
+
}
|
|
4773
|
+
function reportResizeAction(report, region2) {
|
|
3569
4774
|
return createResizeAction({
|
|
3570
4775
|
commit: (poses, deps) => {
|
|
3571
4776
|
const picked = new Set(deps.selection?.get() ?? []);
|
|
@@ -3574,7 +4779,7 @@ function reportResizeAction(report, region) {
|
|
|
3574
4779
|
if (!picked.has(id)) continue;
|
|
3575
4780
|
const { x, y, width, height } = pose;
|
|
3576
4781
|
const node = deps.scene?.get(id);
|
|
3577
|
-
if (node !== void 0 && diagramContainerOf(node) !== null)
|
|
4782
|
+
if (node !== void 0 && diagramContainerOf(node) !== null) region2?.({ kind: "box", id, box: { x, y, width, height } });
|
|
3578
4783
|
else resizes.push({ id, x, y, width, height });
|
|
3579
4784
|
}
|
|
3580
4785
|
if (resizes.length > 0) report?.(resizes);
|
|
@@ -3601,6 +4806,7 @@ function useDiagramEditing(props) {
|
|
|
3601
4806
|
const reorders = props.onReorder !== void 0;
|
|
3602
4807
|
const resizes = props.onResize !== void 0;
|
|
3603
4808
|
const regionEdits = props.onEditContainer !== void 0;
|
|
4809
|
+
const sequences = props.onEditSequence !== void 0;
|
|
3604
4810
|
const ports = useMemo(() => mode === "connect" ? diagramPorts({
|
|
3605
4811
|
...portOptions,
|
|
3606
4812
|
participants: source,
|
|
@@ -3648,24 +4854,28 @@ function useDiagramEditing(props) {
|
|
|
3648
4854
|
[reorders, report]
|
|
3649
4855
|
);
|
|
3650
4856
|
const regions = useMemo(() => regionEdits ? regionEditing(scene, selected, report) : null, [regionEdits, scene, selected, report]);
|
|
4857
|
+
const sequence = useMemo(
|
|
4858
|
+
() => sequences ? diagramSequence({ scene, commit: (edit) => report.current.onEditSequence?.(edit) }) : null,
|
|
4859
|
+
[sequences, scene, report]
|
|
4860
|
+
);
|
|
3651
4861
|
const actions = useMemo(() => ({
|
|
3652
4862
|
...NO_MARQUEE,
|
|
3653
|
-
...deletes ? { delete: reportDeleteAction((r) => report.current.onDelete?.(r)) } : {},
|
|
4863
|
+
...sequences ? { delete: sequenceDeleteAction(scene, (edit) => report.current.onEditSequence?.(edit), deletes ? (r) => report.current.onDelete?.(r) : null) } : deletes ? { delete: reportDeleteAction((r) => report.current.onDelete?.(r)) } : {},
|
|
3654
4864
|
...resizes || regionEdits ? { resize: reportResizeAction((r) => report.current.onResize?.(r), (e) => report.current.onEditContainer?.(e)) } : {}
|
|
3655
|
-
}), [deletes, resizes, regionEdits, report]);
|
|
4865
|
+
}), [deletes, sequences, scene, resizes, regionEdits, report]);
|
|
3656
4866
|
const portsLayer = ports?.layer ?? shown;
|
|
3657
4867
|
const layers = useMemo(
|
|
3658
|
-
// Edge ends
|
|
3659
|
-
() => [regions?.layer, labelDrag?.layer, portsLayer, route?.layer, ends?.layer].filter((l) => l != null),
|
|
3660
|
-
[regions, labelDrag, portsLayer, route, ends]
|
|
4868
|
+
// Edge ends after ports and routes: an edge's end sits on a port and beside a route handle, and the last layer is hit first.
|
|
4869
|
+
() => [regions?.layer, labelDrag?.layer, portsLayer, route?.layer, ends?.layer, sequence?.layer].filter((l) => l != null),
|
|
4870
|
+
[regions, labelDrag, portsLayer, route, ends, sequence]
|
|
3661
4871
|
);
|
|
3662
4872
|
const extra = props.ambient;
|
|
3663
4873
|
const ambient = useMemo(() => [
|
|
3664
4874
|
...AMBIENT,
|
|
3665
|
-
...[ports?.contribution, route?.contribution, ends?.contribution, labelDrag?.contribution, label, reorder].filter((c) => c != null),
|
|
4875
|
+
...[ports?.contribution, route?.contribution, ends?.contribution, labelDrag?.contribution, label, reorder, sequence?.contribution].filter((c) => c != null),
|
|
3666
4876
|
...regions?.contributions ?? [],
|
|
3667
4877
|
...extra ?? []
|
|
3668
|
-
], [ports, route, ends, labelDrag, label, reorder, regions, extra]);
|
|
4878
|
+
], [ports, route, ends, labelDrag, label, reorder, sequence, regions, extra]);
|
|
3669
4879
|
return { layers, ambient, actions };
|
|
3670
4880
|
}
|
|
3671
4881
|
function usePagesView(pages) {
|
|
@@ -3688,15 +4898,7 @@ function diagramBounds(specs, extra) {
|
|
|
3688
4898
|
for (const p of [...edge?.waypoints ?? [], ...edge?.route ?? []]) boxes.push({ x: p.x, y: p.y, width: 0, height: 0 });
|
|
3689
4899
|
}
|
|
3690
4900
|
if (extra !== void 0) boxes.push(extra);
|
|
3691
|
-
|
|
3692
|
-
const x = Math.min(...boxes.map((b) => b.x));
|
|
3693
|
-
const y = Math.min(...boxes.map((b) => b.y));
|
|
3694
|
-
return {
|
|
3695
|
-
x,
|
|
3696
|
-
y,
|
|
3697
|
-
width: Math.max(...boxes.map((b) => b.x + b.width)) - x,
|
|
3698
|
-
height: Math.max(...boxes.map((b) => b.y + b.height)) - y
|
|
3699
|
-
};
|
|
4901
|
+
return unionRects(boxes);
|
|
3700
4902
|
}
|
|
3701
4903
|
function fitDiagram(specs, size, fit = {}, extra) {
|
|
3702
4904
|
const bounds = diagramBounds(specs, extra);
|
|
@@ -3716,9 +4918,8 @@ function diagramViewBounds(specs, padding, extra) {
|
|
|
3716
4918
|
var IDENTITY = { x: 0, y: 0, scale: { x: 1, y: 1 } };
|
|
3717
4919
|
var sameView = (a, b) => a.x === b.x && a.y === b.y && a.scale.x === b.scale.x && a.scale.y === b.scale.y;
|
|
3718
4920
|
function inView(inner, view, size) {
|
|
3719
|
-
const
|
|
3720
|
-
|
|
3721
|
-
return inner.x >= view.x && inner.y >= view.y && inner.x + inner.width <= x1 && inner.y + inner.height <= y1;
|
|
4921
|
+
const shown = viewportWorldRect(view, size);
|
|
4922
|
+
return inner.x >= shown.x && inner.y >= shown.y && inner.x + inner.width <= shown.x + shown.width && inner.y + inner.height <= shown.y + shown.height;
|
|
3722
4923
|
}
|
|
3723
4924
|
function useDiagramFit(args) {
|
|
3724
4925
|
const { canvasRef, specs, controlled, size, fit, extra } = args;
|
|
@@ -3801,6 +5002,7 @@ function Inner({
|
|
|
3801
5002
|
onDelete,
|
|
3802
5003
|
onEditLabel,
|
|
3803
5004
|
onMoveLabel,
|
|
5005
|
+
onEditSequence,
|
|
3804
5006
|
onReorder,
|
|
3805
5007
|
onEditContainer,
|
|
3806
5008
|
connectBodies,
|
|
@@ -3810,6 +5012,8 @@ function Inner({
|
|
|
3810
5012
|
pickEdges,
|
|
3811
5013
|
overlays = NO_OVERLAYS,
|
|
3812
5014
|
ambient: extraAmbient,
|
|
5015
|
+
tools,
|
|
5016
|
+
onToolsCreated,
|
|
3813
5017
|
enableKeybindings,
|
|
3814
5018
|
canConnect,
|
|
3815
5019
|
portOptions,
|
|
@@ -3873,16 +5077,16 @@ function Inner({
|
|
|
3873
5077
|
...refit ? { refit } : {}
|
|
3874
5078
|
});
|
|
3875
5079
|
const { defaultView } = fitting;
|
|
3876
|
-
const settled = useLatest((
|
|
3877
|
-
liveOpts?.onSettle?.(
|
|
3878
|
-
if (
|
|
5080
|
+
const settled = useLatest((moved2) => {
|
|
5081
|
+
liveOpts?.onSettle?.(moved2);
|
|
5082
|
+
if (moved2 !== 0) fitting.settle();
|
|
3879
5083
|
});
|
|
3880
5084
|
const live = useLiveLayout({
|
|
3881
5085
|
...liveOpts,
|
|
3882
5086
|
scene,
|
|
3883
5087
|
source,
|
|
3884
5088
|
algorithm: run?.algorithm ?? "force",
|
|
3885
|
-
onSettle: (
|
|
5089
|
+
onSettle: (moved2) => settled.current(moved2)
|
|
3886
5090
|
});
|
|
3887
5091
|
useEffect(() => {
|
|
3888
5092
|
if (run !== null) live.restart();
|
|
@@ -3904,6 +5108,7 @@ function Inner({
|
|
|
3904
5108
|
onResize,
|
|
3905
5109
|
onEditContainer,
|
|
3906
5110
|
onMove,
|
|
5111
|
+
onEditSequence,
|
|
3907
5112
|
connectBodies,
|
|
3908
5113
|
canConnect,
|
|
3909
5114
|
portOptions,
|
|
@@ -3911,16 +5116,19 @@ function Inner({
|
|
|
3911
5116
|
});
|
|
3912
5117
|
const itemDrop = useItemDrop(source, { onDropItem, canDropItem });
|
|
3913
5118
|
const ambient = useMemo(() => itemDrop === null ? editing.ambient : [...editing.ambient, itemDrop], [editing.ambient, itemDrop]);
|
|
3914
|
-
|
|
3915
|
-
const canvas = canvasRef.current;
|
|
3916
|
-
if (canvas === null) return;
|
|
3917
|
-
const disposers = editing.layers.map((layer) => canvas.registerLayer(layer));
|
|
3918
|
-
return () => disposers.forEach((dispose) => dispose());
|
|
3919
|
-
}, [editing.layers]);
|
|
5119
|
+
useRegisteredLayers(canvasRef, editing.layers);
|
|
3920
5120
|
const moves = onMove !== void 0 || onEditContainer !== void 0;
|
|
3921
5121
|
const resizes = onResize !== void 0 || onEditContainer !== void 0;
|
|
3922
|
-
const
|
|
3923
|
-
const
|
|
5122
|
+
const sequences = onEditSequence !== void 0;
|
|
5123
|
+
const edits = moves || onConnect !== void 0 || onReorder !== void 0 || resizes || edgesPick || sequences;
|
|
5124
|
+
const features = useMemo(() => [
|
|
5125
|
+
"view",
|
|
5126
|
+
"pick",
|
|
5127
|
+
...moves ? ["move"] : [],
|
|
5128
|
+
...resizes ? ["resize"] : [],
|
|
5129
|
+
...sequences ? ["edit"] : []
|
|
5130
|
+
], [moves, resizes, sequences]);
|
|
5131
|
+
const textEditor = useSequenceTextEdit(scene, onEditSequence);
|
|
3924
5132
|
const selectTool = useMemo(() => ({ resize: { resizable: diagramResizable(scene) } }), [scene]);
|
|
3925
5133
|
const overlayRef = useOverlayPlacement(canvasRef, scene, overlays);
|
|
3926
5134
|
const fillsOn = [width === void 0 ? "fill-x" : "", height === void 0 ? "fill-y" : ""].join(" ").trim();
|
|
@@ -3933,9 +5141,12 @@ function Inner({
|
|
|
3933
5141
|
selection,
|
|
3934
5142
|
features,
|
|
3935
5143
|
...resizes ? { selectTool } : {},
|
|
5144
|
+
...textEditor ? { textEditor } : {},
|
|
3936
5145
|
enableKeybindings: enableKeybindings ?? edits,
|
|
3937
5146
|
ambient,
|
|
3938
5147
|
actions: editing.actions,
|
|
5148
|
+
...tools ? { tools } : {},
|
|
5149
|
+
...onToolsCreated ? { onToolsCreated } : {},
|
|
3939
5150
|
onClick,
|
|
3940
5151
|
...onHover ? { onHover } : {},
|
|
3941
5152
|
width,
|
|
@@ -3957,6 +5168,14 @@ function Inner({
|
|
|
3957
5168
|
] });
|
|
3958
5169
|
}
|
|
3959
5170
|
var NO_OVERLAYS = [];
|
|
5171
|
+
function useRegisteredLayers(canvasRef, layers) {
|
|
5172
|
+
useEffect(() => {
|
|
5173
|
+
const canvas = canvasRef.current;
|
|
5174
|
+
if (canvas === null) return;
|
|
5175
|
+
const disposers = layers.map((layer) => canvas.registerLayer(layer));
|
|
5176
|
+
return () => disposers.forEach((dispose) => dispose());
|
|
5177
|
+
}, [canvasRef, layers]);
|
|
5178
|
+
}
|
|
3960
5179
|
function DropRules({ scene, canReparent }) {
|
|
3961
5180
|
const latest = useLatest(canReparent);
|
|
3962
5181
|
const asks = canReparent !== void 0;
|
|
@@ -3989,7 +5208,7 @@ function useDiagramApi(apiRef, canvasRef, scene, live, setRun, fitNow) {
|
|
|
3989
5208
|
const world = worldRectOf(scene, id);
|
|
3990
5209
|
const r = canvas?.element?.getBoundingClientRect();
|
|
3991
5210
|
if (canvas == null || world === null || r === void 0) return null;
|
|
3992
|
-
const rect =
|
|
5211
|
+
const rect = viewRectToScreen(canvas.getView(), world);
|
|
3993
5212
|
return { ...rect, x: rect.x + r.left, y: rect.y + r.top };
|
|
3994
5213
|
},
|
|
3995
5214
|
subscribeFrame: (fn) => canvasRef.current?.subscribeFrame(() => fn()) ?? (() => {
|
|
@@ -3997,6 +5216,6 @@ function useDiagramApi(apiRef, canvasRef, scene, live, setRun, fitNow) {
|
|
|
3997
5216
|
}), [canvasRef, live, scene, setRun, fitNow]);
|
|
3998
5217
|
}
|
|
3999
5218
|
|
|
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,
|
|
4001
|
-
//# sourceMappingURL=chunk-
|
|
4002
|
-
//# sourceMappingURL=chunk-
|
|
5219
|
+
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_SEQUENCE_ACTION_ID, GRAB_WAYPOINT_ACTION_ID, ITEM_DROP_ACTION_ID, LANE_AFFORDANCE_KIND, LANE_LAYER_ID, LANE_RESIZE_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, SELECT_SEQUENCE_PART_ACTION_ID, SEQ, SEQUENCE_AFFORDANCE_KIND, SEQUENCE_DRAG_ACTION_ID, SEQUENCE_LAYER_ID, SEQUENCE_ROLES, SEQ_ARROWS, SEQ_MULTI_SECTION, SET_HOPS_ACTION_ID, SET_ROUTER_ACTION_IDS, STEP_REORDER_ACTION_ID, WAYPOINT_AFFORDANCE_KIND, WAYPOINT_HIT_PX, WAYPOINT_LAYER_ID, addBox, addLane, addParticipant, addSection, avoidSceneInput, bodyAt, commitEdgeToScene, commitLabelToScene, commitLaneResizeToScene, commitReconnectToScene, commitRouteToScene, commitRouterToScene, commitSequence, connectBinding, containerBuild, createAddLaneAction, createConnectAction, createDiagramContribution, createEdgeEndAffordance, createItemDropAction, createLabelDragAction, createLaneAffordance, createLaneResizeAction, createPortAffordance, createReconnectAction, createReleaseMembersAction, createRemoveWaypointAction, createReorderAction, createResetLabelAction, createResetRouteAction, createRouteEditAction, createSequenceDeleteAction, createSequenceDragAction, createSetHopsAction, createSetRouterAction, createSpecHops, createSpecLabelPlacer, createSpecRouter, createStepReorderAction, createWaypointAffordance, defaultCanConnect, diagramCanDropInto, diagramContainers, diagramDropParentAt, diagramEdgeEnds, diagramLabelDrag, diagramLayoutOf, diagramPorts, diagramResizable, diagramSequence, diagramShape, diagramWaypoints, dragItemProps, dragSegment, dragVertex, easedProducer, edgeEndLayer, edgeEndScratchOf, edgePolylines, editContainerTitleAction, editContainerTitleBinding, findStep, foldSequenceText, forceProducer, freshSeqId, grabPortAction, grabPortBindings, groupReorderContribution, groupReorderSlots, headerIdOf, hopDataOps, inOrUnder, insertMark, insertMessage, insertNote, insertStep, insertWaypoint, isBodyEnd, isBodyOrRegion, isDiagramBody, isSequenceText, itemDropContribution, laneLayer, laneResizeFrame, laneScratchOf, lanesOf, moveParticipant, moveStep, movedLabelData, newParticipant, pinSegment, placeOf, placedSequences, poolAxisOf, poolsOf, portLayer, portScratchOf, reconnectBinding, reconnectMessage, redrawSequences, regionHeaderAt, registerDiagramShape, releaseMembers, removeBox, removeMarks, removeMarksOf, removeParticipant, removeSection, removeSegment, removeStep, removeStepAt, removeWaypoint, reorderBinding, reorderContribution, routeHandles, routeStateOf, routedData, routerActions, sameList, sceneEdges, sceneParticipants, segmentAxis, selectSequencePartAction, seqIdTaken, sequenceCommitOf, sequenceDeletes, sequenceLayout, sequencePartNodeOf, sequencePartOf, sequenceRootOf, sequenceScratchOf, sequenceSlots, sequenceSpecs, sequenceTextOf, setAutonumber, setFragmentType, setMessageActivation, setMessageArrow, setParticipantOutline, setSequenceText, startItemDrag, startSequence, stepListOf, themeWrites, unpinSegment, unwrapFragment, useLiveLayout, useMirroredSelection, useSequenceTextEdit, waypointLayer, waypointScratchOf, withHopsData, withRouter, worldWith, wrapSteps };
|
|
5220
|
+
//# sourceMappingURL=chunk-6YQSQXQT.js.map
|
|
5221
|
+
//# sourceMappingURL=chunk-6YQSQXQT.js.map
|