@weasel-js/diagram 1.9.2 → 1.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/dist/{chunk-UBM2J3HJ.js → chunk-ERT3XWBS.js} +189 -20
- package/dist/chunk-ERT3XWBS.js.map +1 -0
- package/dist/index.d.ts +94 -6
- package/dist/index.js +185 -9
- package/dist/index.js.map +1 -1
- package/dist/layout.d.ts +1 -1
- package/dist/{layout.entry-W-8PYfGM.d.ts → layout.entry-KHbx5uGC.d.ts} +331 -273
- package/dist/layout.js +1 -1
- package/package.json +3 -3
- package/dist/chunk-UBM2J3HJ.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as Port, a as PortsOptions, D as DiagramNodeLike, b as DiagramNodeReader, R as Row, O as Outline,
|
|
2
|
-
export {
|
|
1
|
+
import { P as Port, a as PortsOptions, D as DiagramNodeLike, b as DiagramNodeReader, c as PortAnchor, d as DiagramGroup, R as Row, O as Outline, e as PortSpec, B as BodySpec, f as DiagramNode, L as LayoutFn, g as LayoutOptions, M as MeasureRowText, G as GraphSource, F as ForceOptions, h as LayoutResult, i as Graph } from './layout.entry-KHbx5uGC.js';
|
|
2
|
+
export { j as BodyFloor, k as BodyNodeSpec, l as Bounds, m as BuildBodyOptions, n as BuildGraphOptions, C as COMPASS, o as DEFAULT_GROUP_INSET, p as DEFAULT_NODE_GAP, q as DEFAULT_PORTS, r as DEFAULT_RANK_GAP, s as DIAGRAM_TRAIT_KEY, t as DiagramNodeEntry, u as ForceBody, v as ForceRelaxation, w as GraphEdge, x as GraphGroup, y as GraphNode, z as GraphNodeLike, A as GroupInset, E as LayoutAxes, H as LayoutDirection, I as RowBox, J as RowNodeData, K as RowPort, N as RowPortBox, Q as RowTextStyle, S as Slot, T as axesFor, U as backEdges, V as bodyOutline, W as bodyTrait, X as boxForContent, Y as buildBody, Z as buildGraph, _ as canvasMeasure, $ as contentBox, a0 as createDiagramNodes, a1 as dataKeyReader, a2 as diagramNodeOf, a3 as expandGroupEdges, a4 as force, a5 as forceRelaxation, a6 as forestOf, a7 as graphOrder, a8 as insetOf, a9 as isPinned, aa as layered, ab as layoutBody, ac as layoutRowPorts, ad as measureBody, ae as outlinePath, af as outlinePolyline, ag as packAcross, ah as packClusters, ai as pinnedSet, aj as portOf, ak as portsOf, al as portsOnOutline, am as rankGaps, an as ranksOf, ao as rayHit, ap as seededOrder, aq as settle, ar as sizeToBody, as as translated, at as tree } from './layout.entry-KHbx5uGC.js';
|
|
3
3
|
import { Vec2, DerivedDep, Path, SceneRegistry, PoseDescriptor, ChromeState, FillStyle, Stroke, CursorSpec, Affordance, InvocationCtx, GestureBinding, Action, Contribution, RenderLayer, MarkerRef, AddNodeSpec, RectPose, Scene, EasingFn, NodeId, NodeShapeEntry, View, SelectionApi } from '@weasel-js/core';
|
|
4
4
|
import { Vec2 as Vec2$1 } from '@weasel-js/core/math';
|
|
5
5
|
import * as react from 'react';
|
|
@@ -417,6 +417,64 @@ declare const LABEL_DERIVE_POSE: (node: {
|
|
|
417
417
|
data: unknown;
|
|
418
418
|
}, deps: readonly (DerivedDep<unknown, unknown, string> | undefined)[]) => unknown;
|
|
419
419
|
|
|
420
|
+
/**
|
|
421
|
+
* A group's box, and anything pinned to a point on another node.
|
|
422
|
+
*
|
|
423
|
+
* The box is an ordinary node that depends on its members and derives its pose
|
|
424
|
+
* from theirs: their union, grown by the inset. Drag a member and the box
|
|
425
|
+
* follows; lay the diagram out and the box lands around wherever the members
|
|
426
|
+
* went. Nothing has to keep it in sync, because nothing stores it.
|
|
427
|
+
*
|
|
428
|
+
* An anchored node — a group's title is the first — derives its pose from one
|
|
429
|
+
* dependency's bounds the same way, so it travels with the box it labels.
|
|
430
|
+
*/
|
|
431
|
+
|
|
432
|
+
/** Where an anchored node sits on its dependency. */
|
|
433
|
+
interface DiagramAnchor {
|
|
434
|
+
/** The point on the dependency's bounds, normalized as a port's is. */
|
|
435
|
+
at: PortAnchor;
|
|
436
|
+
/** The point on the node's own bounds that lands there. Default `at`, so
|
|
437
|
+
* `{ u: 0, v: 0 }` puts the node's top-left on the dependency's. */
|
|
438
|
+
self?: PortAnchor;
|
|
439
|
+
/** Then moved by this much, in world units. */
|
|
440
|
+
offset?: Vec2$1;
|
|
441
|
+
}
|
|
442
|
+
/** The group trait on a node's data, or `null`. */
|
|
443
|
+
declare function diagramGroupOf(node: {
|
|
444
|
+
data: unknown;
|
|
445
|
+
}): DiagramGroup | null;
|
|
446
|
+
/** The anchor trait on a node's data, or `null`. */
|
|
447
|
+
declare function diagramAnchorOf(node: {
|
|
448
|
+
data: unknown;
|
|
449
|
+
}): DiagramAnchor | null;
|
|
450
|
+
/** Options for {@link groupDerivePose} and {@link anchorDerivePose}.
|
|
451
|
+
* `geometry` defaults to the kit's `AUTO_POSE_DESCRIPTOR`. */
|
|
452
|
+
interface GroupPoseOptions<TPose> {
|
|
453
|
+
geometry?: PoseDescriptor<TPose>;
|
|
454
|
+
}
|
|
455
|
+
type DerivePose<TPose> = (node: {
|
|
456
|
+
pose: TPose;
|
|
457
|
+
data: unknown;
|
|
458
|
+
}, deps: readonly (DerivedDep<TPose> | undefined)[]) => TPose | null;
|
|
459
|
+
/**
|
|
460
|
+
* A `derivePose` that draws a group's box around its members: their union,
|
|
461
|
+
* grown by the trait's inset. `null` — the authored pose stands — when the node
|
|
462
|
+
* carries no group trait or every member is gone.
|
|
463
|
+
*/
|
|
464
|
+
declare function groupDerivePose<TPose>(opts?: GroupPoseOptions<TPose>): DerivePose<TPose>;
|
|
465
|
+
/**
|
|
466
|
+
* A `derivePose` that keeps a node at a point on its dependency's bounds.
|
|
467
|
+
* `null` when the node carries no anchor trait or the dependency is gone.
|
|
468
|
+
*/
|
|
469
|
+
declare function anchorDerivePose<TPose>(opts?: GroupPoseOptions<TPose>): DerivePose<TPose>;
|
|
470
|
+
/** Registry key for {@link GROUP_DERIVE_POSE}. */
|
|
471
|
+
declare const DIAGRAM_GROUP = "diagram:group";
|
|
472
|
+
/** Registry key for {@link ANCHOR_DERIVE_POSE}. */
|
|
473
|
+
declare const DIAGRAM_ANCHOR = "diagram:anchor";
|
|
474
|
+
/** The default derivations, as stable references so `toJSON` can find their keys. */
|
|
475
|
+
declare const GROUP_DERIVE_POSE: DerivePose<unknown>;
|
|
476
|
+
declare const ANCHOR_DERIVE_POSE: DerivePose<unknown>;
|
|
477
|
+
|
|
420
478
|
/**
|
|
421
479
|
* `diagramScene` — a laid-out diagram from plain `{ nodes, edges }`.
|
|
422
480
|
*
|
|
@@ -452,6 +510,7 @@ interface DataNode {
|
|
|
452
510
|
verticalAlign?: BodySpec['verticalAlign'];
|
|
453
511
|
}
|
|
454
512
|
interface DataEdge {
|
|
513
|
+
/** A node's id, or a group's: an edge naming a group meets its box. */
|
|
455
514
|
from: string;
|
|
456
515
|
to: string;
|
|
457
516
|
/** The port to leave from or arrive at. Unset, the end facing the other
|
|
@@ -466,9 +525,26 @@ interface DataEdge {
|
|
|
466
525
|
/** World points the route passes through. Layout does not move them. */
|
|
467
526
|
waypoints?: readonly Vec2$1[];
|
|
468
527
|
}
|
|
528
|
+
/** A box drawn around some nodes, which layout keeps together. */
|
|
529
|
+
interface DataGroup {
|
|
530
|
+
/** Shares one namespace with node ids. */
|
|
531
|
+
id: string;
|
|
532
|
+
/** Node ids. A node a later group also names stays in this one, and an id
|
|
533
|
+
* that is not a node is ignored. */
|
|
534
|
+
members: readonly string[];
|
|
535
|
+
/** Drawn inside the box, above its members. */
|
|
536
|
+
label?: string;
|
|
537
|
+
/** Between the box and its members, and around the label. Default
|
|
538
|
+
* `DiagramSceneOptions.groupPadding`. */
|
|
539
|
+
padding?: number;
|
|
540
|
+
}
|
|
469
541
|
interface DiagramData {
|
|
470
542
|
nodes: readonly DataNode[];
|
|
471
543
|
edges: readonly DataEdge[];
|
|
544
|
+
/** Flat: a group is never a member of another. `layered` and `tree` keep
|
|
545
|
+
* each group's members together; `force` and `'none'` ignore groups but
|
|
546
|
+
* still draw them. */
|
|
547
|
+
groups?: readonly DataGroup[];
|
|
472
548
|
}
|
|
473
549
|
interface NodeStyle {
|
|
474
550
|
fill: string;
|
|
@@ -497,6 +573,8 @@ type TextStyle = {
|
|
|
497
573
|
interface DiagramSceneData {
|
|
498
574
|
diagram?: DiagramNode | DiagramEdge | {
|
|
499
575
|
label: DiagramLabel;
|
|
576
|
+
} | {
|
|
577
|
+
anchor: DiagramAnchor;
|
|
500
578
|
};
|
|
501
579
|
fill?: {
|
|
502
580
|
color: string;
|
|
@@ -535,12 +613,17 @@ interface DiagramSceneOptions {
|
|
|
535
613
|
/** Where edge labels sit. Default `{ at: 'mid', offset: 9 }`. */
|
|
536
614
|
labelPlacement?: DiagramLabel;
|
|
537
615
|
nodeStyle?: (node: DataNode) => NodeStyle;
|
|
616
|
+
/** Default 12. */
|
|
617
|
+
groupPadding?: number;
|
|
618
|
+
/** A group's box and label. `text` colors the label. */
|
|
619
|
+
groupStyle?: (group: DataGroup) => NodeStyle;
|
|
538
620
|
edgeStyle?: (edge: DataEdge) => EdgeStyle;
|
|
539
621
|
}
|
|
540
622
|
declare const edgeIdOf: (e: DataEdge) => string;
|
|
541
|
-
/** Every spec carries an id — a node's own, `<id>/<k>` for its text,
|
|
542
|
-
* `<edge id>/label` — so a later call's
|
|
543
|
-
* one's. Throws on a repeated
|
|
623
|
+
/** Every spec carries an id — a node's own, `<id>/<k>` for its text,
|
|
624
|
+
* `<edge id>/label`, a group's own and `<group id>/label` — so a later call's
|
|
625
|
+
* specs can be reconciled against this one's. Throws on a repeated id among
|
|
626
|
+
* nodes and groups, which names two things as one. An edge whose
|
|
544
627
|
* end is not a node is dropped, and so is a repeat of an earlier edge with
|
|
545
628
|
* the same ends, ports and label: it would draw exactly on top of the first. */
|
|
546
629
|
declare function diagramScene(data: DiagramData, opts?: DiagramSceneOptions): DiagramSpec[];
|
|
@@ -845,6 +928,11 @@ interface DiagramViewProps {
|
|
|
845
928
|
/** Set to make ports grabbable. Dragging one port onto another reports the
|
|
846
929
|
* edge it describes and draws nothing; add it to the data to keep it. */
|
|
847
930
|
onConnect?: (edge: DataEdge) => void;
|
|
931
|
+
/** Whether the canvas's keyboard shortcuts — held Space to pan among them —
|
|
932
|
+
* run. They listen page-wide, so a view-only diagram beside other UI would
|
|
933
|
+
* claim keys meant for the page. Default: on when `onMove` or `onConnect`
|
|
934
|
+
* is set. */
|
|
935
|
+
enableKeybindings?: boolean;
|
|
848
936
|
/** Which ports may be joined. Default: a port may not join itself, and two
|
|
849
937
|
* typed ports must share a type. */
|
|
850
938
|
canConnect?: CanConnect;
|
|
@@ -896,4 +984,4 @@ declare const DiagramView: react.ForwardRefExoticComponent<DiagramViewProps & re
|
|
|
896
984
|
* `<SceneCanvas onClick>`. */
|
|
897
985
|
declare function useMirroredSelection(selection: Pick<SelectionApi, 'get' | 'set'>, selected: string | null | undefined, onSelect: ((id: string | null) => void) | undefined): () => void;
|
|
898
986
|
|
|
899
|
-
export { type ApplyLayoutOptions, BodySpec, CONNECT_ACTION_ID, type CanConnect, type ConnectActionOptions, DEFAULT_EDGE_STROKE, DIAGRAM_EDGE, DIAGRAM_LABEL, type DataEdge, type DataNode, type DiagramContributionOptions, type DiagramData, type DiagramEdge, type DiagramLabel, DiagramNode, DiagramNodeLike, DiagramNodeReader, type DiagramSceneData, type DiagramSceneOptions, type DiagramShapeOptions, type DiagramSpec, DiagramView, type DiagramViewApi, type DiagramViewProps, EASED_LAYOUTS, EDGE_DERIVE_PATH, type EasedProducerOptions, type EdgeEnd, type EdgeRouteOptions, type EdgeStyle, ForceOptions, type ForceProducerOptions, GRAB_PORT_ACTION_ID, Graph, GraphSource, LABEL_DERIVE_POSE, LAYOUTS, LAYOUT_ACTION_ID, type LabelPoseOptions, type LayoutActionOptions, LayoutFn, LayoutOptions, LayoutResult, type LiveLayout, type LiveLayoutCtx, type LiveLayoutFrame, type LiveLayoutProducer, MeasureRowText, type NodeMove, type NodeStyle, Outline, PORT_AFFORDANCE_KIND, PORT_LAYER_ID, type ParticipantPose, type ParticipantScene, type ParticipantSource, type PendingEdge, Port, type PortAffordanceOptions, type PortScratch, PortSpec, PortsOptions, ROUTERS, type RouteRequest, type Router$1 as Router, Row, type UseLiveLayoutOptions, applyLayout, bezier, commitEdgeToScene, connectBinding, createConnectAction, createDiagramContribution, createLayoutAction, createPortAffordance, defaultCanConnect, diagramEdgeOf, diagramLabelOf, diagramPorts, diagramScene, diagramShape, easedProducer, edgeDerivePath, edgeIdOf, forceProducer, grabPortAction, grabPortBindings, labelDerivePose, layoutPoses, orthogonal, portLayer, portScratchOf, registerDiagramShape, resolveEnd, sceneParticipants, straight, useLiveLayout, useMirroredSelection, withDiagramRegistry };
|
|
987
|
+
export { ANCHOR_DERIVE_POSE, type ApplyLayoutOptions, BodySpec, CONNECT_ACTION_ID, type CanConnect, type ConnectActionOptions, DEFAULT_EDGE_STROKE, DIAGRAM_ANCHOR, DIAGRAM_EDGE, DIAGRAM_GROUP, DIAGRAM_LABEL, type DataEdge, type DataGroup, type DataNode, type DiagramAnchor, type DiagramContributionOptions, type DiagramData, type DiagramEdge, DiagramGroup, type DiagramLabel, DiagramNode, DiagramNodeLike, DiagramNodeReader, type DiagramSceneData, type DiagramSceneOptions, type DiagramShapeOptions, type DiagramSpec, DiagramView, type DiagramViewApi, type DiagramViewProps, EASED_LAYOUTS, EDGE_DERIVE_PATH, type EasedProducerOptions, type EdgeEnd, type EdgeRouteOptions, type EdgeStyle, ForceOptions, type ForceProducerOptions, GRAB_PORT_ACTION_ID, GROUP_DERIVE_POSE, Graph, GraphSource, type GroupPoseOptions, LABEL_DERIVE_POSE, LAYOUTS, LAYOUT_ACTION_ID, type LabelPoseOptions, type LayoutActionOptions, LayoutFn, LayoutOptions, LayoutResult, type LiveLayout, type LiveLayoutCtx, type LiveLayoutFrame, type LiveLayoutProducer, MeasureRowText, type NodeMove, type NodeStyle, Outline, PORT_AFFORDANCE_KIND, PORT_LAYER_ID, type ParticipantPose, type ParticipantScene, type ParticipantSource, type PendingEdge, Port, type PortAffordanceOptions, PortAnchor, type PortScratch, PortSpec, PortsOptions, ROUTERS, type RouteRequest, type Router$1 as Router, Row, type UseLiveLayoutOptions, anchorDerivePose, applyLayout, bezier, commitEdgeToScene, connectBinding, createConnectAction, createDiagramContribution, createLayoutAction, createPortAffordance, defaultCanConnect, diagramAnchorOf, diagramEdgeOf, diagramGroupOf, diagramLabelOf, diagramPorts, diagramScene, diagramShape, easedProducer, edgeDerivePath, edgeIdOf, forceProducer, grabPortAction, grabPortBindings, groupDerivePose, labelDerivePose, layoutPoses, orthogonal, portLayer, portScratchOf, registerDiagramShape, resolveEnd, sceneParticipants, straight, useLiveLayout, useMirroredSelection, withDiagramRegistry };
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,73 @@
|
|
|
1
|
-
import { layered, portOf, outlinePath, diagramNodeOf, forceRelaxation, center, force, tree, portsOf, sizeToBody, measureBody, buildBody } from './chunk-
|
|
2
|
-
export { COMPASS, DEFAULT_NODE_GAP, DEFAULT_PORTS, DEFAULT_RANK_GAP, DIAGRAM_TRAIT_KEY, axesFor, backEdges, bodyOutline, bodyTrait, boxForContent, buildBody, canvasMeasure, contentBox, createDiagramNodes, dataKeyReader, diagramNodeOf, force, forceRelaxation, forestOf, graphOrder, isPinned, layered, layoutBody, layoutRowPorts, measureBody, outlinePath, outlinePolyline, packAcross, pinnedSet, portOf, portsOf, portsOnOutline, ranksOf, rayHit, seededOrder, settle, sizeToBody, translated, tree } from './chunk-
|
|
3
|
-
import { dragPanContribution, WeaselProvider, useScene, useSelection, useLatest, reconcileSpecs, SceneCanvas, fitViewToBounds, AUTO_POSE_DESCRIPTOR,
|
|
1
|
+
import { insetOf, layered, portOf, outlinePath, diagramNodeOf, expandGroupEdges, forceRelaxation, center, force, tree, DIAGRAM_TRAIT_KEY, portsOf, DEFAULT_GROUP_INSET, sizeToBody, measureBody, buildBody } from './chunk-ERT3XWBS.js';
|
|
2
|
+
export { COMPASS, DEFAULT_GROUP_INSET, DEFAULT_NODE_GAP, DEFAULT_PORTS, DEFAULT_RANK_GAP, DIAGRAM_TRAIT_KEY, axesFor, backEdges, bodyOutline, bodyTrait, boxForContent, buildBody, canvasMeasure, contentBox, createDiagramNodes, dataKeyReader, diagramNodeOf, expandGroupEdges, force, forceRelaxation, forestOf, graphOrder, insetOf, isPinned, layered, layoutBody, layoutRowPorts, measureBody, outlinePath, outlinePolyline, packAcross, packClusters, pinnedSet, portOf, portsOf, portsOnOutline, rankGaps, ranksOf, rayHit, seededOrder, settle, sizeToBody, translated, tree } from './chunk-ERT3XWBS.js';
|
|
3
|
+
import { dragPanContribution, WeaselProvider, useScene, useSelection, useLatest, reconcileSpecs, SceneCanvas, fitViewToBounds, AUTO_POSE_DESCRIPTOR, translatePoseViaDescriptor, pointAlongPath, polylineFromPoints, registerNodeShape, effectivePose, usePoseRun, RECT_POSE_DESCRIPTOR, composeAffordanceLayer } from '@weasel-js/core';
|
|
4
4
|
import { portCurvePoints } from '@weasel-js/geom';
|
|
5
5
|
import { AUTO_POSE_DESCRIPTOR as AUTO_POSE_DESCRIPTOR$1 } from '@weasel-js/core/math';
|
|
6
6
|
import { forwardRef, useEffect, useMemo, useRef, useState, useImperativeHandle, useCallback } from 'react';
|
|
7
7
|
import { jsx } from 'react/jsx-runtime';
|
|
8
8
|
|
|
9
|
+
function traitOf(node) {
|
|
10
|
+
const data = node.data;
|
|
11
|
+
if (data === null || typeof data !== "object") return null;
|
|
12
|
+
const trait = data[DIAGRAM_TRAIT_KEY];
|
|
13
|
+
return trait !== null && typeof trait === "object" ? trait : null;
|
|
14
|
+
}
|
|
15
|
+
function diagramGroupOf(node) {
|
|
16
|
+
const group = traitOf(node)?.group;
|
|
17
|
+
return group !== null && typeof group === "object" ? group : null;
|
|
18
|
+
}
|
|
19
|
+
function diagramAnchorOf(node) {
|
|
20
|
+
const anchor = traitOf(node)?.anchor;
|
|
21
|
+
return anchor !== null && typeof anchor === "object" && "at" in anchor ? anchor : null;
|
|
22
|
+
}
|
|
23
|
+
function groupDerivePose(opts = {}) {
|
|
24
|
+
const geometry = opts.geometry ?? AUTO_POSE_DESCRIPTOR;
|
|
25
|
+
return (node, deps) => {
|
|
26
|
+
const group = diagramGroupOf(node);
|
|
27
|
+
if (group === null) return null;
|
|
28
|
+
let x0 = Infinity;
|
|
29
|
+
let y0 = Infinity;
|
|
30
|
+
let x1 = -Infinity;
|
|
31
|
+
let y1 = -Infinity;
|
|
32
|
+
for (const dep of deps) {
|
|
33
|
+
if (dep === void 0) continue;
|
|
34
|
+
const b = geometry.getBounds(dep.pose);
|
|
35
|
+
x0 = Math.min(x0, b.x);
|
|
36
|
+
y0 = Math.min(y0, b.y);
|
|
37
|
+
x1 = Math.max(x1, b.x + b.width);
|
|
38
|
+
y1 = Math.max(y1, b.y + b.height);
|
|
39
|
+
}
|
|
40
|
+
if (!Number.isFinite(x0)) return null;
|
|
41
|
+
const inset = insetOf(group.inset);
|
|
42
|
+
return geometry.fromBounds(
|
|
43
|
+
{
|
|
44
|
+
x: x0 - inset.left,
|
|
45
|
+
y: y0 - inset.top,
|
|
46
|
+
width: x1 - x0 + inset.left + inset.right,
|
|
47
|
+
height: y1 - y0 + inset.top + inset.bottom
|
|
48
|
+
},
|
|
49
|
+
node.pose
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function anchorDerivePose(opts = {}) {
|
|
54
|
+
const geometry = opts.geometry ?? AUTO_POSE_DESCRIPTOR;
|
|
55
|
+
return (node, deps) => {
|
|
56
|
+
const anchor = diagramAnchorOf(node);
|
|
57
|
+
const dep = deps[0];
|
|
58
|
+
if (anchor === null || dep === void 0) return null;
|
|
59
|
+
const target = geometry.getBounds(dep.pose);
|
|
60
|
+
const own = geometry.getBounds(node.pose);
|
|
61
|
+
const self = anchor.self ?? anchor.at;
|
|
62
|
+
const x = target.x + anchor.at.u * target.width + (anchor.offset?.x ?? 0) - self.u * own.width;
|
|
63
|
+
const y = target.y + anchor.at.v * target.height + (anchor.offset?.y ?? 0) - self.v * own.height;
|
|
64
|
+
return translatePoseViaDescriptor(node.pose, x - own.x, y - own.y, geometry);
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
var DIAGRAM_GROUP = "diagram:group";
|
|
68
|
+
var DIAGRAM_ANCHOR = "diagram:anchor";
|
|
69
|
+
var GROUP_DERIVE_POSE = groupDerivePose();
|
|
70
|
+
var ANCHOR_DERIVE_POSE = anchorDerivePose();
|
|
9
71
|
var NAMED = { start: 0, mid: 0.5, end: 1 };
|
|
10
72
|
function diagramLabelOf(node) {
|
|
11
73
|
const data = node.data;
|
|
@@ -161,6 +223,8 @@ function withDiagramRegistry(registry = {}) {
|
|
|
161
223
|
},
|
|
162
224
|
derivePose: {
|
|
163
225
|
[DIAGRAM_LABEL]: LABEL_DERIVE_POSE,
|
|
226
|
+
[DIAGRAM_GROUP]: GROUP_DERIVE_POSE,
|
|
227
|
+
[DIAGRAM_ANCHOR]: ANCHOR_DERIVE_POSE,
|
|
164
228
|
...registry.derivePose
|
|
165
229
|
}
|
|
166
230
|
};
|
|
@@ -403,6 +467,7 @@ function buildGraph(source, opts = {}) {
|
|
|
403
467
|
const nodes = [];
|
|
404
468
|
const byId = /* @__PURE__ */ new Map();
|
|
405
469
|
const candidates = [];
|
|
470
|
+
const groupNodes = [];
|
|
406
471
|
for (const { node, pose } of source()) {
|
|
407
472
|
if (diagramEdgeOf(node) !== null) {
|
|
408
473
|
const deps = node.dependsOn;
|
|
@@ -413,6 +478,15 @@ function buildGraph(source, opts = {}) {
|
|
|
413
478
|
}
|
|
414
479
|
const trait = diagramNodeOf(node, opts.read);
|
|
415
480
|
if (trait === null) continue;
|
|
481
|
+
if (trait.group !== void 0) {
|
|
482
|
+
const deps = node.dependsOn;
|
|
483
|
+
groupNodes.push({
|
|
484
|
+
id: node.id,
|
|
485
|
+
members: Array.isArray(deps) ? deps : [],
|
|
486
|
+
inset: insetOf(trait.group.inset)
|
|
487
|
+
});
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
416
490
|
const entry = {
|
|
417
491
|
id: node.id,
|
|
418
492
|
bounds: geometry.getBounds(pose),
|
|
@@ -421,7 +495,19 @@ function buildGraph(source, opts = {}) {
|
|
|
421
495
|
nodes.push(entry);
|
|
422
496
|
byId.set(entry.id, entry);
|
|
423
497
|
}
|
|
424
|
-
const
|
|
498
|
+
const groups = [];
|
|
499
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
500
|
+
for (const g of groupNodes) {
|
|
501
|
+
const members = g.members.filter((id) => byId.has(id) && !claimed.has(id));
|
|
502
|
+
for (const id of members) {
|
|
503
|
+
claimed.add(id);
|
|
504
|
+
byId.get(id).group = g.id;
|
|
505
|
+
}
|
|
506
|
+
groups.push({ ...g, members });
|
|
507
|
+
}
|
|
508
|
+
const live = groups.filter((g) => g.members.length > 0);
|
|
509
|
+
const known = (id) => byId.has(id) || live.some((g) => g.id === id);
|
|
510
|
+
const edges = expandGroupEdges(candidates.filter((e) => known(e.from) && known(e.to)), live);
|
|
425
511
|
const out = /* @__PURE__ */ new Map();
|
|
426
512
|
const into = /* @__PURE__ */ new Map();
|
|
427
513
|
for (const edge of edges) {
|
|
@@ -432,6 +518,7 @@ function buildGraph(source, opts = {}) {
|
|
|
432
518
|
return {
|
|
433
519
|
nodes,
|
|
434
520
|
edges,
|
|
521
|
+
groups: live,
|
|
435
522
|
node: (id) => byId.get(id),
|
|
436
523
|
outgoing: (id) => out.get(id) ?? NONE,
|
|
437
524
|
incoming: (id) => into.get(id) ?? NONE
|
|
@@ -443,17 +530,81 @@ function push(index, key, edge) {
|
|
|
443
530
|
else list.push(edge);
|
|
444
531
|
}
|
|
445
532
|
|
|
533
|
+
// src/dataGroups.ts
|
|
534
|
+
function resolveGroups(groups, ids, nodeIds, opts) {
|
|
535
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
536
|
+
const out = [];
|
|
537
|
+
for (const group of groups) {
|
|
538
|
+
if (ids.has(group.id)) throw new Error(`diagramScene: group id "${group.id}" is already a node or group id`);
|
|
539
|
+
ids.add(group.id);
|
|
540
|
+
const members = group.members.filter((m) => nodeIds.has(m) && !claimed.has(m));
|
|
541
|
+
for (const m of members) claimed.add(m);
|
|
542
|
+
if (members.length === 0) continue;
|
|
543
|
+
const pad = group.padding ?? opts.padding;
|
|
544
|
+
const title = group.label ? opts.title(group.label) : null;
|
|
545
|
+
const inset = { top: pad + (title ? title.height + pad / 2 : 0), right: pad, bottom: pad, left: pad };
|
|
546
|
+
out.push({ group, members, inset, pad, title });
|
|
547
|
+
}
|
|
548
|
+
return out;
|
|
549
|
+
}
|
|
550
|
+
function groupSpecs(groups, placedAt, styleOf) {
|
|
551
|
+
const specs = [];
|
|
552
|
+
for (const g of groups) {
|
|
553
|
+
const { style, text } = styleOf(g.group);
|
|
554
|
+
const rs = g.members.map((m) => placedAt.get(m));
|
|
555
|
+
const x = Math.min(...rs.map((r) => r.x)) - g.inset.left;
|
|
556
|
+
const y = Math.min(...rs.map((r) => r.y)) - g.inset.top;
|
|
557
|
+
specs.push({
|
|
558
|
+
id: g.group.id,
|
|
559
|
+
kind: "leaf",
|
|
560
|
+
layer: "main",
|
|
561
|
+
pickable: false,
|
|
562
|
+
pose: {
|
|
563
|
+
x,
|
|
564
|
+
y,
|
|
565
|
+
width: Math.max(...rs.map((r) => r.x + r.width)) + g.inset.right - x,
|
|
566
|
+
height: Math.max(...rs.map((r) => r.y + r.height)) + g.inset.bottom - y
|
|
567
|
+
},
|
|
568
|
+
data: {
|
|
569
|
+
diagram: { outline: "rect", group: { inset: g.inset } },
|
|
570
|
+
fill: { color: style.fill },
|
|
571
|
+
stroke: {
|
|
572
|
+
paint: { color: style.stroke },
|
|
573
|
+
width: style.strokeWidth ?? 1,
|
|
574
|
+
...style.dash ? { dash: style.dash } : {}
|
|
575
|
+
}
|
|
576
|
+
},
|
|
577
|
+
dependsOn: g.members,
|
|
578
|
+
derivePose: GROUP_DERIVE_POSE
|
|
579
|
+
});
|
|
580
|
+
if (g.title === null || !g.group.label) continue;
|
|
581
|
+
const anchor = { at: { u: 0, v: 0 }, offset: { x: g.pad, y: g.pad } };
|
|
582
|
+
specs.push({
|
|
583
|
+
id: `${g.group.id}/label`,
|
|
584
|
+
kind: "leaf",
|
|
585
|
+
layer: "main",
|
|
586
|
+
pickable: false,
|
|
587
|
+
pose: { x: x + g.pad, y: y + g.pad, width: g.title.width, height: g.title.height },
|
|
588
|
+
data: { diagram: { anchor }, text: g.group.label, style: text, fill: { color: style.text } },
|
|
589
|
+
dependsOn: [g.group.id],
|
|
590
|
+
derivePose: ANCHOR_DERIVE_POSE
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
return specs;
|
|
594
|
+
}
|
|
595
|
+
|
|
446
596
|
// src/fromData.ts
|
|
447
597
|
var FONT = { fontFamily: "sans-serif", fontSize: 12 };
|
|
448
598
|
var NODE = { fill: "#16222c", stroke: "#7ba7c7", text: "#dbe7f2" };
|
|
449
599
|
var EDGE = { stroke: "#7ba7c7" };
|
|
600
|
+
var GROUP = { fill: "rgba(123, 167, 199, 0.08)", stroke: "#7ba7c7", text: "#9fbdd6", strokeWidth: 1, dash: [4, 3] };
|
|
450
601
|
var LABEL_PLACEMENT = { at: "mid", offset: 9 };
|
|
451
602
|
var LINE = 1.4;
|
|
452
603
|
var GLYPH = 0.6;
|
|
453
604
|
var MIN_WIDTH = 64;
|
|
454
605
|
var LAYOUTS = { layered, tree, force };
|
|
455
606
|
var edgeIdOf = (e) => `edge:${e.from}${e.fromPort ? `.${e.fromPort}` : ""}->${e.to}${e.toPort ? `.${e.toPort}` : ""}${e.label ? `:${e.label}` : ""}`;
|
|
456
|
-
function graphOf(nodes, edges) {
|
|
607
|
+
function graphOf(nodes, edges, groups = []) {
|
|
457
608
|
const byId = new Map(nodes.map((n) => [n.id, n]));
|
|
458
609
|
const out = /* @__PURE__ */ new Map();
|
|
459
610
|
const inc = /* @__PURE__ */ new Map();
|
|
@@ -464,6 +615,7 @@ function graphOf(nodes, edges) {
|
|
|
464
615
|
return {
|
|
465
616
|
nodes,
|
|
466
617
|
edges,
|
|
618
|
+
groups,
|
|
467
619
|
node: (id) => byId.get(id),
|
|
468
620
|
outgoing: (id) => out.get(id) ?? [],
|
|
469
621
|
incoming: (id) => inc.get(id) ?? []
|
|
@@ -485,6 +637,12 @@ function diagramScene(data, opts = {}) {
|
|
|
485
637
|
if (ids.has(n.id)) throw new Error(`diagramScene: node id "${n.id}" appears more than once`);
|
|
486
638
|
ids.add(n.id);
|
|
487
639
|
}
|
|
640
|
+
const nodeIds = new Set(ids);
|
|
641
|
+
const titleStyle = { ...font, fontSize: font.fontSize - 1 };
|
|
642
|
+
const groups = resolveGroups(data.groups ?? [], ids, nodeIds, {
|
|
643
|
+
padding: opts.groupPadding ?? DEFAULT_GROUP_INSET,
|
|
644
|
+
title: (text) => measure(text, titleStyle)
|
|
645
|
+
});
|
|
488
646
|
const seen = /* @__PURE__ */ new Set();
|
|
489
647
|
const edges = data.edges.filter((e) => {
|
|
490
648
|
const id = edgeIdOf(e);
|
|
@@ -507,13 +665,29 @@ function diagramScene(data, opts = {}) {
|
|
|
507
665
|
);
|
|
508
666
|
return { id: n.id, bounds: pose, pinned: n.pinned === true };
|
|
509
667
|
});
|
|
510
|
-
const
|
|
668
|
+
const graphGroups = groups.map((g) => ({ id: g.group.id, members: g.members, inset: g.inset }));
|
|
669
|
+
const seedOf = new Map(seeds.map((n) => [n.id, n]));
|
|
670
|
+
for (const g of groups) for (const m of g.members) seedOf.get(m).group = g.group.id;
|
|
671
|
+
const graph = graphOf(
|
|
672
|
+
seeds,
|
|
673
|
+
expandGroupEdges(edges.map((e) => ({ id: edgeIdOf(e), from: e.from, to: e.to })), graphGroups),
|
|
674
|
+
graphGroups
|
|
675
|
+
);
|
|
511
676
|
const layout = typeof opts.layout === "function" ? opts.layout : opts.layout === "none" ? null : LAYOUTS[opts.layout ?? "layered"];
|
|
512
677
|
const moved = layout === null ? /* @__PURE__ */ new Map() : layout(graph, { order: "barycenter", ...opts.layoutOptions });
|
|
513
678
|
const specs = [];
|
|
514
|
-
|
|
679
|
+
const placedAt = new Map(data.nodes.map((n, i) => {
|
|
515
680
|
const seed = seeds[i].bounds;
|
|
516
681
|
const at = moved.get(n.id) ?? seed;
|
|
682
|
+
return [n.id, { x: at.x, y: at.y, width: seed.width, height: seed.height }];
|
|
683
|
+
}));
|
|
684
|
+
specs.push(...groupSpecs(groups, placedAt, (g) => ({
|
|
685
|
+
style: opts.groupStyle?.(g) ?? GROUP,
|
|
686
|
+
text: styleOf(titleStyle)
|
|
687
|
+
})));
|
|
688
|
+
for (const [i, n] of data.nodes.entries()) {
|
|
689
|
+
const seed = seeds[i].bounds;
|
|
690
|
+
const at = placedAt.get(n.id);
|
|
517
691
|
const style = opts.nodeStyle?.(n) ?? NODE;
|
|
518
692
|
const textData = (text, s) => ({ text, style: styleOf(s), fill: { color: style.text } });
|
|
519
693
|
const built = buildBody(
|
|
@@ -874,6 +1048,7 @@ function Inner({
|
|
|
874
1048
|
onSelect,
|
|
875
1049
|
onMove,
|
|
876
1050
|
onConnect,
|
|
1051
|
+
enableKeybindings,
|
|
877
1052
|
canConnect,
|
|
878
1053
|
portOptions,
|
|
879
1054
|
live: liveOpts,
|
|
@@ -972,6 +1147,7 @@ function Inner({
|
|
|
972
1147
|
scene,
|
|
973
1148
|
selection,
|
|
974
1149
|
features: onMove ? ["view", "pick", "move"] : ["view", "pick"],
|
|
1150
|
+
enableKeybindings: enableKeybindings ?? (onMove !== void 0 || onConnect !== void 0),
|
|
975
1151
|
ambient,
|
|
976
1152
|
onClick,
|
|
977
1153
|
width,
|
|
@@ -985,7 +1161,7 @@ function Inner({
|
|
|
985
1161
|
);
|
|
986
1162
|
}
|
|
987
1163
|
function fitDiagram(specs, size, fit = {}) {
|
|
988
|
-
const boxes = specs.filter((s) => s.kind === "container").map((s) => s.pose);
|
|
1164
|
+
const boxes = specs.filter((s) => s.kind === "container" || diagramGroupOf(s) !== null).map((s) => s.pose);
|
|
989
1165
|
if (boxes.length === 0) return void 0;
|
|
990
1166
|
const x = Math.min(...boxes.map((b) => b.x));
|
|
991
1167
|
const y = Math.min(...boxes.map((b) => b.y));
|
|
@@ -1003,6 +1179,6 @@ function fitDiagram(specs, size, fit = {}) {
|
|
|
1003
1179
|
});
|
|
1004
1180
|
}
|
|
1005
1181
|
|
|
1006
|
-
export { CONNECT_ACTION_ID, DEFAULT_EDGE_STROKE, DIAGRAM_EDGE, DIAGRAM_LABEL, DiagramView, EASED_LAYOUTS, EDGE_DERIVE_PATH, GRAB_PORT_ACTION_ID, LABEL_DERIVE_POSE, LAYOUTS2 as LAYOUTS, LAYOUT_ACTION_ID, PORT_AFFORDANCE_KIND, PORT_LAYER_ID, ROUTERS, applyLayout, bezier, buildGraph, commitEdgeToScene, connectBinding, createConnectAction, createDiagramContribution, createLayoutAction, createPortAffordance, defaultCanConnect, diagramEdgeOf, diagramLabelOf, diagramPorts, diagramScene, diagramShape, easedProducer, edgeDerivePath, edgeIdOf, forceProducer, grabPortAction, grabPortBindings, labelDerivePose, layoutPoses, orthogonal, portLayer, portScratchOf, registerDiagramShape, resolveEnd, sceneParticipants, straight, useLiveLayout, useMirroredSelection, withDiagramRegistry };
|
|
1182
|
+
export { ANCHOR_DERIVE_POSE, CONNECT_ACTION_ID, DEFAULT_EDGE_STROKE, DIAGRAM_ANCHOR, DIAGRAM_EDGE, DIAGRAM_GROUP, DIAGRAM_LABEL, DiagramView, EASED_LAYOUTS, EDGE_DERIVE_PATH, GRAB_PORT_ACTION_ID, GROUP_DERIVE_POSE, LABEL_DERIVE_POSE, LAYOUTS2 as LAYOUTS, LAYOUT_ACTION_ID, PORT_AFFORDANCE_KIND, PORT_LAYER_ID, ROUTERS, anchorDerivePose, applyLayout, bezier, buildGraph, commitEdgeToScene, connectBinding, createConnectAction, createDiagramContribution, createLayoutAction, createPortAffordance, defaultCanConnect, diagramAnchorOf, diagramEdgeOf, diagramGroupOf, diagramLabelOf, diagramPorts, diagramScene, diagramShape, easedProducer, edgeDerivePath, edgeIdOf, forceProducer, grabPortAction, grabPortBindings, groupDerivePose, labelDerivePose, layoutPoses, orthogonal, portLayer, portScratchOf, registerDiagramShape, resolveEnd, sceneParticipants, straight, useLiveLayout, useMirroredSelection, withDiagramRegistry };
|
|
1007
1183
|
//# sourceMappingURL=index.js.map
|
|
1008
1184
|
//# sourceMappingURL=index.js.map
|