@weasel-js/diagram 1.8.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
- import { P as Port, a as PortsOptions, D as DiagramNodeLike, b as DiagramNodeReader, L as LayoutFn, c as LayoutOptions, F as ForceOptions, d as LayoutResult, G as GraphSource, e as Graph } from './layout.entry-BvR06k9j.js';
2
- export { B as BodyFloor, f as BodyNodeSpec, g as BodySpec, h as Bounds, i as BuildBodyOptions, j as BuildGraphOptions, C as COMPASS, k as DEFAULT_NODE_GAP, l as DEFAULT_PORTS, m as DEFAULT_RANK_GAP, n as DIAGRAM_TRAIT_KEY, o as DiagramNode, p as DiagramNodeEntry, q as ForceBody, r as ForceRelaxation, s as GraphEdge, t as GraphNode, u as GraphNodeLike, v as LayoutAxes, w as LayoutDirection, M as MeasureRowText, O as Outline, x as PortAnchor, y as PortSpec, R as Row, z as RowBox, A as RowNodeData, E as RowPort, H as RowPortBox, I as RowTextStyle, S as Slot, J as axesFor, K as backEdges, N as bodyOutline, Q as bodyTrait, T as boxForContent, U as buildBody, V as buildGraph, W as canvasMeasure, X as contentBox, Y as createDiagramNodes, Z as dataKeyReader, _ as diagramNodeOf, $ as force, a0 as forceRelaxation, a1 as forestOf, a2 as graphOrder, a3 as isPinned, a4 as layered, a5 as layoutBody, a6 as layoutRowPorts, a7 as measureBody, a8 as outlinePath, a9 as outlinePolyline, aa as packAcross, ab as pinnedSet, ac as portOf, ad as portsOf, ae as portsOnOutline, af as ranksOf, ag as rayHit, ah as seededOrder, ai as settle, aj as sizeToBody, ak as translated, al as tree } from './layout.entry-BvR06k9j.js';
3
- import { Vec2, DerivedDep, Path, SceneRegistry, PoseDescriptor, ChromeState, FillStyle, Stroke, CursorSpec, Affordance, InvocationCtx, GestureBinding, Action, Contribution, RenderLayer, EasingFn, NodeId, Scene, NodeShapeEntry } from '@weasel-js/core';
1
+ import { P as Port, a as PortsOptions, D as DiagramNodeLike, b as DiagramNodeReader, c as DiagramNode, L as LayoutFn, d as LayoutOptions, F as ForceOptions, e as LayoutResult, G as GraphSource, f as Graph } from './layout.entry-ClWUwH16.js';
2
+ export { B as BodyFloor, g as BodyNodeSpec, h as BodySpec, i as Bounds, j as BuildBodyOptions, k as BuildGraphOptions, C as COMPASS, l as DEFAULT_NODE_GAP, m as DEFAULT_PORTS, n as DEFAULT_RANK_GAP, o as DIAGRAM_TRAIT_KEY, p as DiagramNodeEntry, q as ForceBody, r as ForceRelaxation, s as GraphEdge, t as GraphNode, u as GraphNodeLike, v as LayoutAxes, w as LayoutDirection, M as MeasureRowText, O as Outline, x as PortAnchor, y as PortSpec, R as Row, z as RowBox, A as RowNodeData, E as RowPort, H as RowPortBox, I as RowTextStyle, S as Slot, J as axesFor, K as backEdges, N as bodyOutline, Q as bodyTrait, T as boxForContent, U as buildBody, V as buildGraph, W as canvasMeasure, X as contentBox, Y as createDiagramNodes, Z as dataKeyReader, _ as diagramNodeOf, $ as force, a0 as forceRelaxation, a1 as forestOf, a2 as graphOrder, a3 as isPinned, a4 as layered, a5 as layoutBody, a6 as layoutRowPorts, a7 as measureBody, a8 as outlinePath, a9 as outlinePolyline, aa as packAcross, ab as pinnedSet, ac as portOf, ad as portsOf, ae as portsOnOutline, af as ranksOf, ag as rayHit, ah as seededOrder, ai as settle, aj as sizeToBody, ak as translated, al as tree } from './layout.entry-ClWUwH16.js';
3
+ import { Vec2, DerivedDep, Path, SceneRegistry, PoseDescriptor, ChromeState, FillStyle, Stroke, CursorSpec, Affordance, InvocationCtx, GestureBinding, Action, Contribution, RenderLayer, AddNodeSpec, RectPose, EasingFn, NodeId, Scene, NodeShapeEntry, SelectionApi } from '@weasel-js/core';
4
+ import * as react from 'react';
4
5
  import '@weasel-js/geom';
5
6
  import '@weasel-js/core/math';
6
7
 
@@ -67,8 +68,6 @@ declare const straight: Router;
67
68
  * across the corner, which reads as aimed at nothing.
68
69
  */
69
70
  declare const orthogonal: Router;
70
- /** How far a bezier reaches along each end's normal, as a fraction of the
71
- * straight-line distance between the two points it joins. */
72
71
  /**
73
72
  * A smooth route, sampled into points.
74
73
  *
@@ -119,10 +118,10 @@ declare const DIAGRAM_EDGE = "diagram:edge";
119
118
  * `edgeDerivePath(...)` under a key of their choosing. */
120
119
  declare const EDGE_DERIVE_PATH: (node: {
121
120
  data: unknown;
122
- }, deps: readonly (DerivedDep<unknown> | undefined)[]) => Path | null;
121
+ }, deps: readonly (DerivedDep<unknown, unknown, string> | undefined)[]) => Path | null;
123
122
  /** Merge this package's registry entries under a consumer's, so an edge
124
123
  * round-trips through `toJSON` without the consumer wiring the key. */
125
- declare function withDiagramRegistry<TPose>(registry?: SceneRegistry<TPose>): SceneRegistry<TPose>;
124
+ declare function withDiagramRegistry<TPose, TData = unknown, TLayer extends string = string>(registry?: SceneRegistry<TPose, TData, TLayer>): SceneRegistry<TPose, TData, TLayer>;
126
125
 
127
126
  /**
128
127
  * Ports as affordances — what makes a painted port a grabbable one.
@@ -416,7 +415,69 @@ declare const DIAGRAM_LABEL = "diagram:label";
416
415
  declare const LABEL_DERIVE_POSE: (node: {
417
416
  pose: unknown;
418
417
  data: unknown;
419
- }, deps: readonly (DerivedDep<unknown> | undefined)[]) => unknown;
418
+ }, deps: readonly (DerivedDep<unknown, unknown, string> | undefined)[]) => unknown;
419
+
420
+ /**
421
+ * `diagramScene` — a laid-out diagram from plain `{ nodes, edges }`.
422
+ *
423
+ * Sizes are estimated from character counts, not measured, so layout runs
424
+ * without a canvas and gives the same answer in node and in a browser.
425
+ */
426
+
427
+ interface DataNode {
428
+ id: string;
429
+ lines: readonly string[];
430
+ }
431
+ interface DataEdge {
432
+ from: string;
433
+ to: string;
434
+ label?: string;
435
+ }
436
+ interface DiagramData {
437
+ nodes: readonly DataNode[];
438
+ edges: readonly DataEdge[];
439
+ }
440
+ interface NodeStyle {
441
+ fill: string;
442
+ stroke: string;
443
+ text: string;
444
+ strokeWidth?: number;
445
+ }
446
+ interface EdgeStyle {
447
+ stroke: string;
448
+ width?: number;
449
+ }
450
+ interface DiagramSceneData {
451
+ diagram?: DiagramNode | DiagramEdge | {
452
+ label: DiagramLabel;
453
+ };
454
+ fill?: {
455
+ color: string;
456
+ };
457
+ stroke?: Stroke;
458
+ text?: string;
459
+ style?: {
460
+ fontFamily: string;
461
+ fontSize: number;
462
+ };
463
+ }
464
+ type DiagramSpec = AddNodeSpec<DiagramSceneData, 'main', RectPose>;
465
+ interface DiagramSceneOptions {
466
+ layout?: LayoutFn;
467
+ layoutOptions?: LayoutOptions;
468
+ router?: 'straight' | 'bezier' | 'orthogonal';
469
+ font?: {
470
+ fontFamily: string;
471
+ fontSize: number;
472
+ };
473
+ nodeStyle?: (node: DataNode) => NodeStyle;
474
+ edgeStyle?: (edge: DataEdge) => EdgeStyle;
475
+ }
476
+ declare const edgeIdOf: (e: DataEdge) => string;
477
+ /** Throws on a repeated node id, which names two nodes as one. An edge whose
478
+ * end is not a node is dropped, and so is a repeat of an earlier edge with
479
+ * the same ends and label: it would draw exactly on top of the first. */
480
+ declare function diagramScene(data: DiagramData, opts?: DiagramSceneOptions): DiagramSpec[];
420
481
 
421
482
  /** What a live producer is told, and what it answers with. */
422
483
  interface LiveLayoutCtx<TPose> {
@@ -691,4 +752,35 @@ declare function diagramShape<TPose>(opts?: DiagramShapeOptions<TPose>): NodeSha
691
752
  */
692
753
  declare function registerDiagramShape<TPose>(opts?: DiagramShapeOptions<TPose>): () => void;
693
754
 
694
- export { type ApplyLayoutOptions, CONNECT_ACTION_ID, type CanConnect, type ConnectActionOptions, DEFAULT_EDGE_STROKE, DIAGRAM_EDGE, DIAGRAM_LABEL, type DiagramContributionOptions, type DiagramEdge, type DiagramLabel, DiagramNodeLike, DiagramNodeReader, type DiagramShapeOptions, EASED_LAYOUTS, EDGE_DERIVE_PATH, type EasedProducerOptions, type EdgeEnd, type EdgeRouteOptions, 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, PORT_AFFORDANCE_KIND, PORT_LAYER_ID, type ParticipantPose, type ParticipantScene, type ParticipantSource, type PendingEdge, Port, type PortAffordanceOptions, type PortScratch, PortsOptions, ROUTERS, type RouteRequest, type Router, type UseLiveLayoutOptions, applyLayout, bezier, commitEdgeToScene, connectBinding, createConnectAction, createDiagramContribution, createLayoutAction, createPortAffordance, defaultCanConnect, diagramEdgeOf, diagramLabelOf, diagramPorts, diagramShape, easedProducer, edgeDerivePath, forceProducer, grabPortAction, grabPortBindings, labelDerivePose, layoutPoses, orthogonal, portLayer, portScratchOf, registerDiagramShape, resolveEnd, sceneParticipants, straight, useLiveLayout, withDiagramRegistry };
755
+ interface DiagramViewProps {
756
+ specs: readonly DiagramSpec[];
757
+ width: number;
758
+ height: number;
759
+ /** A participant id, or null for none. */
760
+ selected?: string | null;
761
+ /** Reports what each click leaves picked, `null` for none — a re-click on the
762
+ * picked node reports it again. The canvas keeps the pick even if
763
+ * `selected` does not follow it; only a change to `selected` is pushed in. */
764
+ onSelect?: (id: string | null) => void;
765
+ /** The smallest scale the initial fit may use. Unset, the whole diagram fits
766
+ * however small that makes it; set, it stops here and the rest is a pan away. */
767
+ minScale?: number;
768
+ /** Which part of a diagram too big for the box it opens on: its middle
769
+ * (default) or its top-left, where a layered flow begins. */
770
+ anchor?: 'center' | 'start';
771
+ className?: string;
772
+ }
773
+ /** A read-only diagram: drag or wheel to pan, zoom, and pick a node, nothing else. A new `specs`
774
+ * array is a new scene, fitted to the box, so memoize it: one built inline
775
+ * remounts the scene on every render. Labels paint only in a registered font
776
+ * family; `diagramScene` defaults to sans-serif, so call
777
+ * `registerCanvasFont('sans-serif')` or text renders blank. */
778
+ declare function DiagramView(props: DiagramViewProps): react.JSX.Element;
779
+
780
+ /** Keeps a canvas selection and a controlled `selected` prop in step: a prop
781
+ * change is pushed in, and every click reports what the selection holds after
782
+ * it — a re-click on the picked node included. Wire the returned handler to
783
+ * `<SceneCanvas onClick>`. */
784
+ declare function useMirroredSelection(selection: Pick<SelectionApi, 'get' | 'set'>, selected: string | null | undefined, onSelect: ((id: string | null) => void) | undefined): () => void;
785
+
786
+ export { type ApplyLayoutOptions, 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 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, type NodeStyle, PORT_AFFORDANCE_KIND, PORT_LAYER_ID, type ParticipantPose, type ParticipantScene, type ParticipantSource, type PendingEdge, Port, type PortAffordanceOptions, type PortScratch, PortsOptions, ROUTERS, type RouteRequest, type Router, 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 };
package/dist/index.js CHANGED
@@ -1,9 +1,10 @@
1
- import { portOf, portsOf, diagramNodeOf, force, tree, layered, forceRelaxation, center, outlinePath } from './chunk-4KXQABRM.js';
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-4KXQABRM.js';
3
- import { AUTO_POSE_DESCRIPTOR, pointAlongPath, translatePoseViaDescriptor, polylineFromPoints, effectivePose, composeAffordanceLayer, useLatest, usePoseRun, RECT_POSE_DESCRIPTOR, registerNodeShape } from '@weasel-js/core';
1
+ import { portOf, portsOf, diagramNodeOf, layered, force, tree, forceRelaxation, center, outlinePath } from './chunk-6VYT4WCM.js';
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-6VYT4WCM.js';
3
+ import { dragPanContribution, AUTO_POSE_DESCRIPTOR, pointAlongPath, translatePoseViaDescriptor, polylineFromPoints, effectivePose, composeAffordanceLayer, useLatest, usePoseRun, RECT_POSE_DESCRIPTOR, registerNodeShape, WeaselProvider, useScene, useSelection, SceneCanvas, fitViewToBounds } 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 { useRef, useCallback, useEffect, useMemo } from 'react';
7
+ import { jsx } from 'react/jsx-runtime';
7
8
 
8
9
  var NAMED = { start: 0, mid: 0.5, end: 1 };
9
10
  function diagramLabelOf(node) {
@@ -260,7 +261,7 @@ function grabPortBindings(actionId = GRAB_PORT_ACTION_ID) {
260
261
  ];
261
262
  }
262
263
  function commitEdgeToScene(edge, ctx) {
263
- const scene = ctx.deps["scene"];
264
+ const scene = ctx.deps.scene;
264
265
  if (scene === void 0) return;
265
266
  const trait = {
266
267
  from: { port: edge.from.id },
@@ -441,6 +442,128 @@ function push(index, key, edge) {
441
442
  if (list === void 0) index.set(key, [edge]);
442
443
  else list.push(edge);
443
444
  }
445
+
446
+ // src/fromData.ts
447
+ var FONT = { fontFamily: "sans-serif", fontSize: 12 };
448
+ var NODE = { fill: "#16222c", stroke: "#7ba7c7", text: "#dbe7f2" };
449
+ var EDGE = { stroke: "#7ba7c7" };
450
+ var PAD = { x: 12, y: 8 };
451
+ var LINE = 1.4;
452
+ var GLYPH = 0.6;
453
+ var MIN_WIDTH = 64;
454
+ var edgeIdOf = (e) => `edge:${e.from}->${e.to}${e.label ? `:${e.label}` : ""}`;
455
+ function sizeOf(node, fontSize) {
456
+ const longest = Math.max(0, ...node.lines.map((l) => l.length));
457
+ return {
458
+ width: Math.max(MIN_WIDTH, Math.ceil(longest * GLYPH * fontSize + 2 * PAD.x)),
459
+ height: Math.ceil(Math.max(1, node.lines.length) * LINE * fontSize + 2 * PAD.y)
460
+ };
461
+ }
462
+ function graphOf(nodes, edges) {
463
+ const byId = new Map(nodes.map((n) => [n.id, n]));
464
+ const out = /* @__PURE__ */ new Map();
465
+ const inc = /* @__PURE__ */ new Map();
466
+ for (const e of edges) {
467
+ out.set(e.from, [...out.get(e.from) ?? [], e]);
468
+ inc.set(e.to, [...inc.get(e.to) ?? [], e]);
469
+ }
470
+ return {
471
+ nodes,
472
+ edges,
473
+ node: (id) => byId.get(id),
474
+ outgoing: (id) => out.get(id) ?? [],
475
+ incoming: (id) => inc.get(id) ?? []
476
+ };
477
+ }
478
+ function diagramScene(data, opts = {}) {
479
+ const font = opts.font ?? FONT;
480
+ const ids = /* @__PURE__ */ new Set();
481
+ for (const n of data.nodes) {
482
+ if (ids.has(n.id)) throw new Error(`diagramScene: node id "${n.id}" appears more than once`);
483
+ ids.add(n.id);
484
+ }
485
+ const seen = /* @__PURE__ */ new Set();
486
+ const edges = data.edges.filter((e) => {
487
+ const id = edgeIdOf(e);
488
+ if (seen.has(id) || !ids.has(e.from) || !ids.has(e.to)) return false;
489
+ seen.add(id);
490
+ return true;
491
+ });
492
+ const seeds = data.nodes.map((n, i) => ({
493
+ id: n.id,
494
+ bounds: { x: i, y: i, ...sizeOf(n, font.fontSize) },
495
+ pinned: false
496
+ }));
497
+ const graph = graphOf(seeds, edges.map((e) => ({ id: edgeIdOf(e), from: e.from, to: e.to })));
498
+ const moved = (opts.layout ?? layered)(graph, opts.layoutOptions ?? { order: "barycenter" });
499
+ const specs = [];
500
+ for (const [i, n] of data.nodes.entries()) {
501
+ const seed = seeds[i].bounds;
502
+ const at = moved.get(n.id) ?? seed;
503
+ const pose = { x: at.x, y: at.y, width: seed.width, height: seed.height };
504
+ const style = opts.nodeStyle?.(n) ?? NODE;
505
+ specs.push({
506
+ id: n.id,
507
+ kind: "container",
508
+ layer: "main",
509
+ pose,
510
+ data: {
511
+ diagram: { outline: "rect" },
512
+ fill: { color: style.fill },
513
+ stroke: { paint: { color: style.stroke }, width: style.strokeWidth ?? 2 }
514
+ }
515
+ });
516
+ for (const [k, text] of n.lines.entries()) {
517
+ specs.push({
518
+ kind: "leaf",
519
+ layer: "main",
520
+ parent: n.id,
521
+ pickable: false,
522
+ pose: {
523
+ x: pose.x + PAD.x,
524
+ y: pose.y + PAD.y + k * LINE * font.fontSize,
525
+ width: pose.width - 2 * PAD.x,
526
+ height: LINE * font.fontSize
527
+ },
528
+ data: { text, style: font, fill: { color: style.text } }
529
+ });
530
+ }
531
+ }
532
+ for (const e of edges) {
533
+ const id = edgeIdOf(e);
534
+ const style = opts.edgeStyle?.(e) ?? EDGE;
535
+ specs.push({
536
+ id,
537
+ kind: "leaf",
538
+ layer: "main",
539
+ pickable: false,
540
+ pose: { x: 0, y: 0, width: 0, height: 0 },
541
+ data: {
542
+ diagram: { from: {}, to: {}, router: opts.router ?? "bezier" },
543
+ stroke: { paint: { color: style.stroke }, width: style.width ?? 1.5, markerEnd: "arrow" }
544
+ },
545
+ dependsOn: [e.from, e.to],
546
+ derivePath: EDGE_DERIVE_PATH
547
+ });
548
+ if (e.label) {
549
+ specs.push({
550
+ kind: "leaf",
551
+ layer: "main",
552
+ pickable: false,
553
+ pose: { x: 0, y: 0, width: e.label.length * GLYPH * (font.fontSize - 2), height: font.fontSize },
554
+ data: {
555
+ diagram: { label: { at: "mid", offset: 9 } },
556
+ text: e.label,
557
+ style: { ...font, fontSize: font.fontSize - 2 },
558
+ fill: { color: style.stroke }
559
+ },
560
+ dependsOn: [id],
561
+ derivePose: LABEL_DERIVE_POSE
562
+ });
563
+ }
564
+ }
565
+ return specs;
566
+ }
444
567
  var LAYOUTS = Object.freeze({
445
568
  layered,
446
569
  tree,
@@ -700,7 +823,77 @@ function diagramShape(opts = {}) {
700
823
  function registerDiagramShape(opts = {}) {
701
824
  return registerNodeShape(diagramShape(opts), { priority: "high" });
702
825
  }
826
+ function useMirroredSelection(selection, selected, onSelect) {
827
+ const want = selected ?? null;
828
+ const pushed = useRef(void 0);
829
+ useEffect(() => {
830
+ if (pushed.current === want) return;
831
+ pushed.current = want;
832
+ if ((selection.get()[0] ?? null) !== want) selection.set(want === null ? [] : [want]);
833
+ }, [want]);
834
+ const onSelectRef = useLatest(onSelect);
835
+ return useCallback(() => {
836
+ onSelectRef.current?.(selection.get()[0] ?? null);
837
+ }, [selection, onSelectRef]);
838
+ }
839
+ var made = 0;
840
+ var keys = /* @__PURE__ */ new WeakMap();
841
+ var keyOf = (specs) => {
842
+ let k = keys.get(specs);
843
+ if (k === void 0) keys.set(specs, k = ++made);
844
+ return k;
845
+ };
846
+ var AMBIENT = [dragPanContribution()];
847
+ function DiagramView(props) {
848
+ return /* @__PURE__ */ jsx(WeaselProvider, { children: /* @__PURE__ */ jsx(Inner, { ...props }, keyOf(props.specs)) });
849
+ }
850
+ function Inner({ specs, width, height, selected, onSelect, minScale, anchor, className }) {
851
+ useEffect(() => registerDiagramShape(), []);
852
+ const registry = useMemo(() => withDiagramRegistry(), []);
853
+ const scene = useScene({
854
+ systemLayers: [{ id: "main" }],
855
+ initial: specs,
856
+ registry
857
+ });
858
+ const selection = useSelection({ scene, mode: "single" });
859
+ const onClick = useMirroredSelection(selection, selected, onSelect);
860
+ const defaultView = useMemo(
861
+ () => fitDiagram(specs, { width, height }, minScale, anchor),
862
+ [specs, width, height, minScale, anchor]
863
+ );
864
+ return /* @__PURE__ */ jsx(
865
+ SceneCanvas,
866
+ {
867
+ scene,
868
+ selection,
869
+ features: ["view", "pick"],
870
+ ambient: AMBIENT,
871
+ onClick,
872
+ width,
873
+ height,
874
+ className,
875
+ ...defaultView ? { defaultView } : {}
876
+ }
877
+ );
878
+ }
879
+ function fitDiagram(specs, size, minScale, anchor) {
880
+ const boxes = specs.filter((s) => s.kind === "container").map((s) => s.pose);
881
+ if (boxes.length === 0) return void 0;
882
+ const x = Math.min(...boxes.map((b) => b.x));
883
+ const y = Math.min(...boxes.map((b) => b.y));
884
+ const bounds = {
885
+ x,
886
+ y,
887
+ width: Math.max(...boxes.map((b) => b.x + b.width)) - x,
888
+ height: Math.max(...boxes.map((b) => b.y + b.height)) - y
889
+ };
890
+ return fitViewToBounds(bounds, size, { x: 0, y: 0, scale: { x: 1, y: 1 } }, {
891
+ maxScale: 1,
892
+ ...minScale !== void 0 ? { minScale } : {},
893
+ ...anchor !== void 0 ? { anchor } : {}
894
+ });
895
+ }
703
896
 
704
- export { CONNECT_ACTION_ID, DEFAULT_EDGE_STROKE, DIAGRAM_EDGE, DIAGRAM_LABEL, EASED_LAYOUTS, EDGE_DERIVE_PATH, GRAB_PORT_ACTION_ID, LABEL_DERIVE_POSE, LAYOUTS, LAYOUT_ACTION_ID, PORT_AFFORDANCE_KIND, PORT_LAYER_ID, ROUTERS, applyLayout, bezier, buildGraph, commitEdgeToScene, connectBinding, createConnectAction, createDiagramContribution, createLayoutAction, createPortAffordance, defaultCanConnect, diagramEdgeOf, diagramLabelOf, diagramPorts, diagramShape, easedProducer, edgeDerivePath, forceProducer, grabPortAction, grabPortBindings, labelDerivePose, layoutPoses, orthogonal, portLayer, portScratchOf, registerDiagramShape, resolveEnd, sceneParticipants, straight, useLiveLayout, withDiagramRegistry };
897
+ export { CONNECT_ACTION_ID, DEFAULT_EDGE_STROKE, DIAGRAM_EDGE, DIAGRAM_LABEL, DiagramView, EASED_LAYOUTS, EDGE_DERIVE_PATH, GRAB_PORT_ACTION_ID, LABEL_DERIVE_POSE, LAYOUTS, LAYOUT_ACTION_ID, PORT_AFFORDANCE_KIND, PORT_LAYER_ID, ROUTERS, applyLayout, bezier, buildGraph, commitEdgeToScene, connectBinding, createConnectAction, createDiagramContribution, createLayoutAction, createPortAffordance, defaultCanConnect, diagramEdgeOf, diagramLabelOf, diagramPorts, diagramScene, diagramShape, easedProducer, edgeDerivePath, edgeIdOf, forceProducer, grabPortAction, grabPortBindings, labelDerivePose, layoutPoses, orthogonal, portLayer, portScratchOf, registerDiagramShape, resolveEnd, sceneParticipants, straight, useLiveLayout, useMirroredSelection, withDiagramRegistry };
705
898
  //# sourceMappingURL=index.js.map
706
899
  //# sourceMappingURL=index.js.map