@weasel-js/diagram 1.8.1 → 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.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