@likec4/diagram 1.20.1 → 1.20.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.
Files changed (134) hide show
  1. package/dist/LikeC4Diagram.css.d.ts +1 -0
  2. package/dist/LikeC4Diagram.css.js +3 -1
  3. package/dist/base/BaseXYFlow.js +15 -14
  4. package/dist/base/Link.css.d.ts +3 -0
  5. package/dist/base/Link.css.js +9 -0
  6. package/dist/base/Link.d.ts +5 -0
  7. package/dist/base/Link.js +58 -0
  8. package/dist/base/const.d.ts +1 -0
  9. package/dist/base/const.js +2 -1
  10. package/dist/base/example/custom.d.ts +1 -1
  11. package/dist/base/index.d.ts +1 -0
  12. package/dist/base/primitives/compound/CompoundActionButton.js +2 -1
  13. package/dist/base/primitives/compound/CompoundDetailsButton.js +2 -1
  14. package/dist/base/primitives/compound/CompoundNodeContainer.css.d.ts +0 -1
  15. package/dist/base/primitives/compound/CompoundNodeContainer.css.js +4 -6
  16. package/dist/base/primitives/compound/CompoundNodeContainer.js +2 -2
  17. package/dist/base/primitives/customEdge.d.ts +1 -1
  18. package/dist/base/primitives/customEdge.js +1 -1
  19. package/dist/base/primitives/customNode.js +1 -1
  20. package/dist/base/primitives/edge/EdgeLabel.js +3 -1
  21. package/dist/base/primitives/edge/EdgePath.d.ts +2 -1
  22. package/dist/base/primitives/edge/EdgePath.js +2 -0
  23. package/dist/base/primitives/element/ElementActionButtons.js +2 -1
  24. package/dist/base/primitives/element/ElementDetailsButton.js +2 -1
  25. package/dist/base/primitives/element/ElementNodeContainer.css.d.ts +0 -1
  26. package/dist/base/primitives/element/ElementNodeContainer.css.js +1 -3
  27. package/dist/base/primitives/element/ElementNodeContainer.js +2 -3
  28. package/dist/base/primitives/element/ElementShape.js +4 -2
  29. package/dist/base/primitives/element/ElementTitle.js +2 -1
  30. package/dist/base/updateEdges.js +3 -3
  31. package/dist/base/updateNodes.js +2 -2
  32. package/dist/context/FramerMotionConfig.js +1 -1
  33. package/dist/hooks/useDiagram.d.ts +313 -90
  34. package/dist/hooks/useDiagramActor.d.ts +2 -2
  35. package/dist/hooks/useDiagramContext.d.ts +1 -1
  36. package/dist/hooks/useXYFlow.d.ts +2 -1
  37. package/dist/hooks/useXYFlow.js +5 -0
  38. package/dist/likec4diagram/XYFlow.js +12 -10
  39. package/dist/likec4diagram/custom/edges/RelationshipEdge.d.ts +1 -1
  40. package/dist/likec4diagram/custom/edges/RelationshipsDropdownMenu.js +2 -2
  41. package/dist/likec4diagram/custom/index.d.ts +1 -1
  42. package/dist/likec4diagram/custom/nodes/nodeTypes.js +2 -2
  43. package/dist/likec4diagram/state/actorContext.d.ts +1438 -364
  44. package/dist/likec4diagram/state/assign.js +2 -2
  45. package/dist/likec4diagram/state/machine.d.ts +321 -93
  46. package/dist/likec4diagram/state/syncManualLayoutActor.d.ts +1 -1
  47. package/dist/likec4diagram/types.d.ts +1 -0
  48. package/dist/likec4diagram/ui/diagram-title/DiagramTitlePanel.js +4 -3
  49. package/dist/likec4diagram/useLayoutConstraints.d.ts +2 -1
  50. package/dist/likec4diagram/useLayoutConstraints.js +14 -15
  51. package/dist/likec4diagram/useViewToNodesEdges.js +3 -1
  52. package/dist/overlays/element-details/ElementDetailsCard.css.d.ts +0 -1
  53. package/dist/overlays/element-details/ElementDetailsCard.css.js +1 -3
  54. package/dist/overlays/element-details/ElementDetailsCard.js +5 -30
  55. package/dist/overlays/element-details/TabPanelRelationships.js +2 -2
  56. package/dist/overlays/relationship-details/RelationshipDetails.js +4 -4
  57. package/dist/overlays/relationship-details/custom/ElementActions.js +1 -3
  58. package/dist/overlays/relationship-details/custom/edgeTypes.d.ts +1 -1
  59. package/dist/overlays/relationship-details/hooks.d.ts +2 -2
  60. package/dist/overlays/relationship-details/hooks.js +7 -7
  61. package/dist/overlays/relationship-details/useLayoutedDetails.js +7 -10
  62. package/dist/overlays/relationships-browser/-useRelationshipsView.d.ts +4 -1
  63. package/dist/overlays/relationships-browser/-useRelationshipsView.js +12 -11
  64. package/dist/overlays/relationships-browser/RelationshipsBrowser.js +81 -66
  65. package/dist/overlays/relationships-browser/_types.d.ts +2 -1
  66. package/dist/overlays/relationships-browser/actor.d.ts +15 -2
  67. package/dist/overlays/relationships-browser/actor.js +262 -65
  68. package/dist/overlays/relationships-browser/const.d.ts +1 -0
  69. package/dist/overlays/relationships-browser/const.js +4 -0
  70. package/dist/overlays/relationships-browser/custom/ElementActions.js +13 -3
  71. package/dist/overlays/relationships-browser/custom/edgeTypes.d.ts +3 -2
  72. package/dist/overlays/relationships-browser/custom/edgeTypes.js +24 -6
  73. package/dist/overlays/relationships-browser/custom/nodeTypes.js +3 -2
  74. package/dist/overlays/relationships-browser/hooks.d.ts +6 -3
  75. package/dist/overlays/relationships-browser/hooks.js +4 -2
  76. package/dist/overlays/relationships-browser/useViewToNodesEdges.js +7 -2
  77. package/dist/style.css +1 -1
  78. package/dist/utils/xyflow.js +9 -1
  79. package/package.json +10 -10
  80. package/src/LikeC4Diagram.css.ts +8 -0
  81. package/src/base/BaseXYFlow.tsx +23 -14
  82. package/src/base/Link.css.ts +49 -0
  83. package/src/base/Link.tsx +66 -0
  84. package/src/base/const.ts +1 -0
  85. package/src/base/index.ts +2 -0
  86. package/src/base/primitives/compound/CompoundActionButton.tsx +2 -1
  87. package/src/base/primitives/compound/CompoundDetailsButton.tsx +2 -1
  88. package/src/base/primitives/compound/CompoundNodeContainer.css.ts +16 -10
  89. package/src/base/primitives/compound/CompoundNodeContainer.tsx +1 -1
  90. package/src/base/primitives/customEdge.ts +2 -0
  91. package/src/base/primitives/customNode.ts +3 -0
  92. package/src/base/primitives/edge/EdgeLabel.tsx +3 -1
  93. package/src/base/primitives/edge/EdgePath.tsx +3 -0
  94. package/src/base/primitives/element/ElementActionButtons.tsx +2 -1
  95. package/src/base/primitives/element/ElementDetailsButton.tsx +2 -1
  96. package/src/base/primitives/element/ElementNodeContainer.css.ts +9 -4
  97. package/src/base/primitives/element/ElementNodeContainer.tsx +1 -2
  98. package/src/base/primitives/element/ElementShape.tsx +4 -3
  99. package/src/base/primitives/element/ElementTitle.tsx +2 -1
  100. package/src/base/updateEdges.ts +3 -2
  101. package/src/base/updateNodes.ts +3 -2
  102. package/src/context/DiagramFeatures.tsx +1 -0
  103. package/src/context/FramerMotionConfig.tsx +1 -1
  104. package/src/hooks/useDiagramActor.ts +7 -5
  105. package/src/hooks/useDiagramContext.ts +4 -1
  106. package/src/hooks/useXYFlow.ts +6 -1
  107. package/src/likec4diagram/XYFlow.tsx +20 -9
  108. package/src/likec4diagram/custom/edges/RelationshipsDropdownMenu.tsx +2 -2
  109. package/src/likec4diagram/custom/nodes/nodeTypes.tsx +2 -2
  110. package/src/likec4diagram/types.ts +1 -0
  111. package/src/likec4diagram/ui/diagram-title/DiagramTitlePanel.tsx +4 -3
  112. package/src/likec4diagram/useLayoutConstraints.ts +23 -19
  113. package/src/likec4diagram/useViewToNodesEdges.ts +1 -0
  114. package/src/overlays/element-details/ElementDetailsCard.css.ts +0 -28
  115. package/src/overlays/element-details/ElementDetailsCard.tsx +2 -50
  116. package/src/overlays/element-details/TabPanelRelationships.tsx +2 -2
  117. package/src/overlays/relationship-details/RelationshipDetails.tsx +4 -4
  118. package/src/overlays/relationship-details/custom/ElementActions.tsx +1 -2
  119. package/src/overlays/relationship-details/custom/edgeTypes.tsx +0 -5
  120. package/src/overlays/relationship-details/hooks.ts +6 -6
  121. package/src/overlays/relationship-details/useLayoutedDetails.ts +7 -10
  122. package/src/overlays/relationships-browser/-useRelationshipsView.ts +16 -12
  123. package/src/overlays/relationships-browser/RelationshipsBrowser.tsx +105 -68
  124. package/src/overlays/relationships-browser/_types.ts +2 -1
  125. package/src/overlays/relationships-browser/actor.ts +321 -62
  126. package/src/overlays/relationships-browser/const.ts +1 -0
  127. package/src/overlays/relationships-browser/custom/ElementActions.tsx +13 -3
  128. package/src/overlays/relationships-browser/custom/edgeTypes.tsx +25 -6
  129. package/src/overlays/relationships-browser/custom/nodeTypes.tsx +3 -2
  130. package/src/overlays/relationships-browser/hooks.ts +3 -1
  131. package/src/overlays/relationships-browser/useViewToNodesEdges.ts +8 -2
  132. package/dist/likec4diagram/ui/diagram-title/Link.js +0 -36
  133. package/dist/ui/Link.d.ts +0 -4
  134. package/src/ui/Link.tsx +0 -40
@@ -23,7 +23,7 @@ export declare const syncManualLayoutActor: StateMachine<Readonly<Input>, Events
23
23
  }, {
24
24
  type: "same view";
25
25
  params: unknown;
26
- }, "timeout", "stopped" | "paused" | "synced" | "pending" | "idle", "pending" | "ready", Input, NonReducibleUnknown, EventObject, MetaObject, {
26
+ }, "timeout", "stopped" | "synced" | "pending" | "idle" | "paused", "pending" | "ready", Input, NonReducibleUnknown, EventObject, MetaObject, {
27
27
  readonly initial: "idle";
28
28
  readonly context: ({ input }: {
29
29
  spawn: {
@@ -10,6 +10,7 @@ type NonOptional<T extends object> = Simplify<{
10
10
  }>;
11
11
  export declare namespace Types {
12
12
  type LeafNodeData = Base.NodeData<NonOptional<Pick<DiagramNode, 'id' | 'title' | 'technology' | 'description' | 'color' | 'shape' | 'width' | 'level' | 'height' | 'style' | 'position'>> & {
13
+ isMultiple?: boolean | undefined;
13
14
  icon: string | null;
14
15
  }>;
15
16
  /**
@@ -6,7 +6,7 @@ import clsx from "clsx";
6
6
  import { AnimatePresence, m } from "framer-motion";
7
7
  import { useDiagramContext } from "../../../hooks/useDiagramContext.js";
8
8
  import { container, card, title, description } from "./DiagramTitlePanel.css.js";
9
- import { Link } from "./Link.js";
9
+ import { Link } from "../../../base/Link.js";
10
10
  const selector = (s) => ({
11
11
  id: s.view.id,
12
12
  title: s.view.title ?? "untitled",
@@ -140,10 +140,11 @@ function DiagramTitlePanel() {
140
140
  links && /* @__PURE__ */ jsx(
141
141
  Stack,
142
142
  {
143
- gap: 3,
143
+ mt: "xs",
144
+ gap: 4,
144
145
  justify: "stretch",
145
146
  align: "stretch",
146
- children: links.map((link) => /* @__PURE__ */ jsx(Link, { link }, link.url))
147
+ children: links.map((link) => /* @__PURE__ */ jsx(Link, { value: link }, link.url))
147
148
  }
148
149
  )
149
150
  ] })
@@ -21,9 +21,10 @@ declare abstract class Rect {
21
21
  };
22
22
  get position(): XYPosition;
23
23
  protected abstract parent: Compound | null;
24
+ constructor(xynode: InternalNode, parent?: Compound | null);
24
25
  }
25
26
  declare class Compound extends Rect {
26
- protected readonly parent: Compound | null;
27
+ readonly parent: Compound | null;
27
28
  readonly children: Rect[];
28
29
  constructor(xynode: InternalNode, parent?: Compound | null);
29
30
  }
@@ -10,12 +10,23 @@ import { useSyncedRef } from "@react-hookz/web";
10
10
  import { useDiagram } from "../hooks/useDiagram.js";
11
11
  import { useXYStoreApi } from "../hooks/useXYFlow.js";
12
12
  class Rect {
13
- constructor() {
13
+ constructor(xynode, parent = null) {
14
14
  __publicField(this, "id");
15
15
  __publicField(this, "minX", Infinity);
16
16
  __publicField(this, "minY", Infinity);
17
17
  __publicField(this, "maxX", -Infinity);
18
18
  __publicField(this, "maxY", -Infinity);
19
+ this.id = xynode.id;
20
+ this.positionAbsolute = !parent ? xynode.position : {
21
+ x: xynode.position.x + parent.minX,
22
+ y: xynode.position.y + parent.minY
23
+ };
24
+ const { width, height } = getNodeDimensions(xynode);
25
+ this.maxX = this.minX + Math.ceil(width);
26
+ this.maxY = this.minY + Math.ceil(height);
27
+ if (parent) {
28
+ parent.children.push(this);
29
+ }
19
30
  }
20
31
  get positionAbsolute() {
21
32
  return {
@@ -56,27 +67,15 @@ __publicField(Rect, "TopPadding", 55);
56
67
  __publicField(Rect, "BottomPadding", 40);
57
68
  class Compound extends Rect {
58
69
  constructor(xynode, parent = null) {
59
- super();
70
+ super(xynode, parent);
60
71
  __publicField(this, "children", []);
61
72
  this.parent = parent;
62
- this.id = xynode.id;
63
- if (parent) {
64
- parent.children.push(this);
65
- }
66
73
  }
67
74
  }
68
75
  class Leaf extends Rect {
69
76
  constructor(xynode, parent = null) {
70
- super();
77
+ super(xynode, parent);
71
78
  this.parent = parent;
72
- this.id = xynode.id;
73
- this.positionAbsolute = xynode.internals.positionAbsolute;
74
- const { width, height } = getNodeDimensions(xynode);
75
- this.maxX = this.minX + Math.ceil(width);
76
- this.maxY = this.minY + Math.ceil(height);
77
- if (parent) {
78
- parent.children.push(this);
79
- }
80
79
  }
81
80
  }
82
81
  function createLayoutConstraints(xyflowApi, editingNodeIds) {
@@ -3,6 +3,7 @@ import { useDeepCompareMemo } from "@react-hookz/web";
3
3
  import { pick, hasAtLeast } from "remeda";
4
4
  import { ZIndexes } from "../base/const.js";
5
5
  function viewToNodesEdge(opts) {
6
+ var _a;
6
7
  const {
7
8
  view,
8
9
  nodesDraggable: draggable,
@@ -94,7 +95,8 @@ function viewToNodesEdge(opts) {
94
95
  shape: node.shape,
95
96
  style: node.style,
96
97
  icon: node.icon ?? null,
97
- position: node.position
98
+ position: node.position,
99
+ isMultiple: ((_a = node.style) == null ? void 0 : _a.multiple) ?? false
98
100
  };
99
101
  if (node.kind === ElementKind.Group) {
100
102
  xynodes.push({
@@ -12,5 +12,4 @@ export declare const tabsTab: string;
12
12
  export declare const tabsPanel: string;
13
13
  export declare const propertiesGrid: string;
14
14
  export declare const propertyLabel: string;
15
- export declare const elementLink: string;
16
15
  export declare const resizeHandle: string;
@@ -12,14 +12,12 @@ var tabsTab = "paikoyd";
12
12
  var tabsPanel = "paikoye";
13
13
  var propertiesGrid = "paikoyf";
14
14
  var propertyLabel = "paikoyg";
15
- var elementLink = "paikoyh";
16
- var resizeHandle = "paikoyi";
15
+ var resizeHandle = "paikoyh";
17
16
  export {
18
17
  card,
19
18
  cardHeader,
20
19
  dialog,
21
20
  elementIcon,
22
- elementLink,
23
21
  propertiesGrid,
24
22
  propertyLabel,
25
23
  resizeHandle,
@@ -1,15 +1,15 @@
1
1
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
2
  import { isScopedElementView, isDeploymentView } from "@likec4/core";
3
- import { Divider as Divider$1, Tooltip as Tooltip$1, Text, RemoveScroll, Card, FocusTrap, FocusTrapInitialFocus, Box, Group, CloseButton, Badge, Flex, ActionIconGroup, ActionIcon, Tabs, TabsList, TabsTab, TabsPanel, ScrollArea, Stack, UnstyledButton, ThemeIcon, CopyButton, Anchor, Code } from "@mantine/core";
3
+ import { Divider as Divider$1, Tooltip as Tooltip$1, Text, RemoveScroll, Card, FocusTrap, FocusTrapInitialFocus, Box, Group, CloseButton, Badge, Flex, ActionIconGroup, ActionIcon, Tabs, TabsList, TabsTab, TabsPanel, ScrollArea, Stack, UnstyledButton, ThemeIcon, Code } from "@mantine/core";
4
4
  import { useViewportSize, useSessionStorage, useDebouncedCallback } from "@mantine/hooks";
5
- import { IconExternalLink, IconFileSymlink, IconZoomScan, IconStack2, IconCheck, IconCopy } from "@tabler/icons-react";
5
+ import { IconExternalLink, IconFileSymlink, IconZoomScan, IconStack2 } from "@tabler/icons-react";
6
6
  import "clsx";
7
7
  import { useDragControls, useMotionValue, m } from "framer-motion";
8
8
  import { memo, useCallback, useRef, useLayoutEffect } from "react";
9
9
  import { pipe, map, partition, only, clamp, isNullish } from "remeda";
10
10
  import { useDiagram } from "../../hooks/useDiagram.js";
11
11
  import { useDiagramContext } from "../../hooks/useDiagramContext.js";
12
- import { propertyLabel, elementIcon, dialog, card, cardHeader, title, tabsRoot, tabsList, tabsTab, tabsPanel, propertiesGrid, resizeHandle, viewButton, viewButtonTitle, elementLink } from "./ElementDetailsCard.css.js";
12
+ import { propertyLabel, elementIcon, dialog, card, cardHeader, title, tabsRoot, tabsList, tabsTab, tabsPanel, propertiesGrid, resizeHandle, viewButton, viewButtonTitle } from "./ElementDetailsCard.css.js";
13
13
  import { TabPanelDeployments } from "./TabPanelDeployments.js";
14
14
  import { TabPanelRelationships } from "./TabPanelRelationships.js";
15
15
  import { TabPanelStructure } from "./TabPanelStructure.js";
@@ -17,6 +17,7 @@ import { useLikeC4Model } from "../../likec4model/useLikeC4Model.js";
17
17
  import { useUpdateEffect } from "../../hooks/useUpdateEffect.js";
18
18
  import { IconRenderer } from "../../context/IconRenderer.js";
19
19
  import { IfEnabled } from "../../context/DiagramFeatures.js";
20
+ import { Link } from "../../base/Link.js";
20
21
  const Divider = Divider$1.withProps({
21
22
  mb: 8,
22
23
  labelPosition: "left",
@@ -344,7 +345,7 @@ const ElementDetailsCard = memo(({ fqn }) => {
344
345
  elementModel.technology && /* @__PURE__ */ jsx(ElementProperty, { title: "technology", children: elementModel.technology }),
345
346
  elementModel.links.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
346
347
  /* @__PURE__ */ jsx(PropertyLabel, { children: "links" }),
347
- /* @__PURE__ */ jsx(Stack, { gap: "xs", align: "flex-start", children: elementModel.links.map((link, i) => /* @__PURE__ */ jsx(ElementLink, { value: link }, i)) })
348
+ /* @__PURE__ */ jsx(Stack, { gap: "xs", align: "flex-start", children: elementModel.links.map((link, i) => /* @__PURE__ */ jsx(Link, { value: link }, i)) })
348
349
  ] }),
349
350
  elementModel.$element.metadata && /* @__PURE__ */ jsx(ElementMetata, { value: elementModel.$element.metadata })
350
351
  ] }) }) }),
@@ -440,32 +441,6 @@ function ElementProperty({
440
441
  )
441
442
  ] });
442
443
  }
443
- function ElementLink({
444
- value
445
- }) {
446
- const url = new URL(value.url, window.location.href).toString();
447
- return /* @__PURE__ */ jsx(CopyButton, { value: url, children: ({ copied, copy }) => /* @__PURE__ */ jsx(Anchor, { href: url, target: "_blank", underline: "never", className: elementLink, children: /* @__PURE__ */ jsxs(Group, { gap: 4, align: "center", wrap: "nowrap", children: [
448
- /* @__PURE__ */ jsx(
449
- ActionIcon,
450
- {
451
- tabIndex: -1,
452
- size: 24,
453
- variant: copied ? "light" : "subtle",
454
- color: copied ? "teal" : "gray",
455
- onClick: (e) => {
456
- e.stopPropagation();
457
- e.preventDefault();
458
- copy();
459
- },
460
- children: copied ? /* @__PURE__ */ jsx(IconCheck, {}) : /* @__PURE__ */ jsx(IconCopy, { style: { width: "65%", opacity: 0.8 } })
461
- }
462
- ),
463
- /* @__PURE__ */ jsxs(Box, { flex: 1, children: [
464
- /* @__PURE__ */ jsx(Text, { fz: "sm", truncate: true, lh: 1.3, fw: value.title ? 500 : 400, children: value.title || url }),
465
- value.title && /* @__PURE__ */ jsx(Text, { component: "div", fz: 10, c: "dimmed", lh: 1.2, truncate: true, children: url })
466
- ] })
467
- ] }) }) });
468
- }
469
444
  function ElementMetata({
470
445
  value
471
446
  }) {
@@ -20,8 +20,8 @@ function TabPanelRelationships({
20
20
  useState("view");
21
21
  const incoming = [...element.incoming()].map((r) => r.id);
22
22
  const outgoing = [...element.outgoing()].map((r) => r.id);
23
- const incomingInView = unique(node.incoming().flatMap((e) => e.$edge.relations).toArray());
24
- const outgoingInView = unique(node.outgoing().flatMap((e) => e.$edge.relations).toArray());
23
+ const incomingInView = unique([...node.incoming()].flatMap((e) => e.$edge.relations));
24
+ const outgoingInView = unique([...node.outgoing()].flatMap((e) => e.$edge.relations));
25
25
  const notIncludedRelations = [
26
26
  ...incoming,
27
27
  ...outgoing
@@ -11,7 +11,7 @@ import { updateEdges } from "../../base/updateEdges.js";
11
11
  import { updateNodes } from "../../base/updateNodes.js";
12
12
  import { edgeTypes } from "./custom/edgeTypes.js";
13
13
  import { nodeTypes } from "./custom/nodeTypes.js";
14
- import { RelationshipDetailsActorContext, useRelationshipDetails, useRelationshipsBrowserState } from "./hooks.js";
14
+ import { RelationshipDetailsActorContext, useRelationshipDetails, useRelationshipDetailsState } from "./hooks.js";
15
15
  import { SelectEdge } from "./SelectEdge.js";
16
16
  import { useLayoutedDetails } from "./useLayoutedDetails.js";
17
17
  import { useUpdateEffect } from "../../hooks/useUpdateEffect.js";
@@ -36,7 +36,7 @@ const RelationshipDetailsInner = memo(() => {
36
36
  edgeId,
37
37
  view,
38
38
  initialized
39
- } = useRelationshipsBrowserState(selector);
39
+ } = useRelationshipDetailsState(selector);
40
40
  const {
41
41
  edge,
42
42
  xynodes,
@@ -86,9 +86,9 @@ const RelationshipDetailsInner = memo(() => {
86
86
  e.stopPropagation();
87
87
  browser.send({ type: "xyflow.paneClick" });
88
88
  }),
89
- onViewportResize: () => {
89
+ onViewportResize: useCallbackRef(() => {
90
90
  browser.send({ type: "xyflow.resized" });
91
- },
91
+ }),
92
92
  nodesDraggable: false,
93
93
  fitView: false,
94
94
  pannable: true,
@@ -2,14 +2,12 @@ import { jsx } from "react/jsx-runtime";
2
2
  import { IconZoomScan } from "@tabler/icons-react";
3
3
  import { useEnabledFeature } from "../../../context/DiagramFeatures.js";
4
4
  import { useDiagram } from "../../../hooks/useDiagram.js";
5
- import { useRelationshipDetails } from "../hooks.js";
6
5
  import { ElementActionButtons } from "../../../base/primitives/element/ElementActionButtons.js";
7
6
  const ElementActions = (props) => {
8
7
  const { enableNavigateTo } = useEnabledFeature("NavigateTo");
9
8
  const diagram = useDiagram();
10
- useRelationshipDetails();
11
9
  const buttons = [];
12
- const { navigateTo, fqn } = props.data;
10
+ const { navigateTo } = props.data;
13
11
  if (navigateTo && enableNavigateTo) {
14
12
  buttons.push({
15
13
  key: "navigate",
@@ -7,9 +7,9 @@ export declare const edgeTypes: {
7
7
  target: string;
8
8
  style?: CSSProperties;
9
9
  id: string;
10
- selected?: boolean;
11
10
  selectable?: boolean;
12
11
  deletable?: boolean;
12
+ selected?: boolean;
13
13
  animated?: boolean;
14
14
  sourceX: number;
15
15
  sourceY: number;
@@ -4,8 +4,8 @@ import { Context, ReactNode, CSSProperties } from 'react';
4
4
  import { GeneralHelpers, ViewportHelperFunctions } from '@xyflow/react';
5
5
  import { EdgeMarkerType, HandleType } from '@xyflow/system';
6
6
  export declare const RelationshipDetailsActorContext: Context<RelationshipDetailsActorRef | null>;
7
- export declare function useRelationshipsBrowserActor(): RelationshipDetailsActorRef;
8
- export declare function useRelationshipsBrowserState<T>(selector: (state: RelationshipDetailsSnapshot) => T, compare?: (a: T, b: T) => boolean): T;
7
+ export declare function useRelationshipDetailsActor(): RelationshipDetailsActorRef;
8
+ export declare function useRelationshipDetailsState<T = unknown>(selector: (state: RelationshipDetailsSnapshot) => T, compare?: (a: NoInfer<T>, b: NoInfer<T>) => boolean): T;
9
9
  export declare function useRelationshipDetails(): {
10
10
  getState: () => Readonly< Input & {
11
11
  xyflow: ( GeneralHelpers<import("./_types").RelationshipDetailsTypes.Node, {
@@ -4,16 +4,16 @@ import { useSelector } from "@xstate/react";
4
4
  import { shallowEqual } from "fast-equals";
5
5
  import { createContext, useTransition, useMemo, useContext } from "react";
6
6
  const RelationshipDetailsActorContext = createContext(null);
7
- function useRelationshipsBrowserActor() {
8
- return nonNullable(useContext(RelationshipDetailsActorContext), "No RelationshipsBrowserActorContext");
7
+ function useRelationshipDetailsActor() {
8
+ return nonNullable(useContext(RelationshipDetailsActorContext), "No RelationshipDetailsActorContext");
9
9
  }
10
- function useRelationshipsBrowserState(selector, compare = shallowEqual) {
10
+ function useRelationshipDetailsState(selector, compare = shallowEqual) {
11
11
  const select = useCallbackRef(selector);
12
- const actor = useRelationshipsBrowserActor();
12
+ const actor = useRelationshipDetailsActor();
13
13
  return useSelector(actor, select, compare);
14
14
  }
15
15
  function useRelationshipDetails() {
16
- const actor = useRelationshipsBrowserActor();
16
+ const actor = useRelationshipDetailsActor();
17
17
  const [, startTransition] = useTransition();
18
18
  return useMemo(() => ({
19
19
  getState: () => actor.getSnapshot().context,
@@ -41,6 +41,6 @@ function useRelationshipDetails() {
41
41
  export {
42
42
  RelationshipDetailsActorContext,
43
43
  useRelationshipDetails,
44
- useRelationshipsBrowserActor,
45
- useRelationshipsBrowserState
44
+ useRelationshipDetailsActor,
45
+ useRelationshipDetailsState
46
46
  };
@@ -193,8 +193,8 @@ function layout(edge, view, likec4model) {
193
193
  type: "relationship",
194
194
  source: source.id,
195
195
  target: target.id,
196
- sourceHandle: target.id,
197
- targetHandle: source.id,
196
+ sourceHandle: source.id + "_out" + (source.data.ports.out.length - 1),
197
+ targetHandle: target.id + "_in" + (target.data.ports.in.length - 1),
198
198
  data: {
199
199
  relationId: relation.id,
200
200
  color: relation.color,
@@ -209,15 +209,12 @@ function layout(edge, view, likec4model) {
209
209
  ctx.xyedges.push(edge2);
210
210
  }
211
211
  const nodebounds = applyDagreLayout(ctx.g);
212
- const sortedPorts = (ports) => {
213
- if (ports.length < 2) {
214
- return ports;
215
- }
212
+ const sortedPorts = (nodeId, type, ports) => {
216
213
  return pipe(
217
214
  ports,
218
- map((port) => {
215
+ map((port, index) => {
219
216
  return {
220
- port,
217
+ port: nodeId + "_" + type + index,
221
218
  topY: nodebounds(port).position.y
222
219
  };
223
220
  }),
@@ -227,8 +224,8 @@ function layout(edge, view, likec4model) {
227
224
  };
228
225
  const xynodes = [...ctx.xynodes.values()].map((node) => {
229
226
  const { position, width, height } = nodebounds(node.id, node.parentId);
230
- node.data.ports.in = sortedPorts(node.data.ports.in);
231
- node.data.ports.out = sortedPorts(node.data.ports.out);
227
+ node.data.ports.in = sortedPorts(node.id, "in", node.data.ports.in);
228
+ node.data.ports.out = sortedPorts(node.id, "out", node.data.ports.out);
232
229
  if (node.type === "element") {
233
230
  node.data.width = width;
234
231
  node.data.height = height;
@@ -19,7 +19,10 @@ export declare namespace LayoutRelationshipsViewResult {
19
19
  column: RelationshipsBrowserTypes.Column;
20
20
  ports: RelationshipsBrowserTypes.Ports;
21
21
  };
22
- type Edge = DiagramEdge & {};
22
+ type Edge = DiagramEdge & {
23
+ sourceHandle: string;
24
+ targetHandle: string;
25
+ };
23
26
  }
24
27
  export declare function layoutRelationshipsView(data: RelationshipsViewData): LayoutRelationshipsViewResult;
25
28
  export declare function useRelationshipsView(fqn: Fqn): {
@@ -195,7 +195,9 @@ function layoutRelationshipsView(data) {
195
195
  edges.push({
196
196
  name,
197
197
  source: source.id,
198
+ sourceHandle: source.id + "_out" + (g.node(source.id).outPorts.length - 1),
198
199
  target: target.id,
200
+ targetHandle: target.id + "_in" + (g.node(target.id).inPorts.length - 1),
199
201
  relations: map(grouped, prop("relation"))
200
202
  });
201
203
  })
@@ -309,15 +311,12 @@ function layoutRelationshipsView(data) {
309
311
  }
310
312
  return 1 + Math.max(...children.map(nodeDepth));
311
313
  }
312
- const sortedPorts = (ports) => {
313
- if (ports.length < 2) {
314
- return ports;
315
- }
314
+ const sortedPorts = (nodeId, type, ports) => {
316
315
  return pipe(
317
316
  ports,
318
- map((port) => {
317
+ map((port, index) => {
319
318
  return {
320
- port,
319
+ port: nodeId + "_" + type + index,
321
320
  topY: nodeBounds(port).position.y
322
321
  };
323
322
  }),
@@ -417,15 +416,15 @@ function layoutRelationshipsView(data) {
417
416
  height,
418
417
  column,
419
418
  ports: {
420
- in: sortedPorts(inPorts),
421
- out: sortedPorts(outPorts)
419
+ in: sortedPorts(id, "in", inPorts),
420
+ out: sortedPorts(id, "out", outPorts)
422
421
  }
423
422
  };
424
423
  });
425
424
  return {
426
425
  bounds: {
427
- x: minX,
428
- y: minY,
426
+ x: Math.min(minX, 0),
427
+ y: Math.min(minY, 0),
429
428
  width: g.graph().width ?? 100,
430
429
  height: g.graph().height ?? 100
431
430
  },
@@ -437,7 +436,7 @@ function layoutRelationshipsView(data) {
437
436
  if (!ename) {
438
437
  return acc;
439
438
  }
440
- const { name, source, target, relations } = find(edges, (e2) => e2.name === ename);
439
+ const { name, source, target, relations, sourceHandle, targetHandle } = find(edges, (e2) => e2.name === ename);
441
440
  const onlyRelation = only(relations);
442
441
  const label = (onlyRelation == null ? void 0 : onlyRelation.title) ?? "untitled";
443
442
  const isMultiple = relations.length > 1;
@@ -449,7 +448,9 @@ function layoutRelationshipsView(data) {
449
448
  acc.push({
450
449
  id: name,
451
450
  source,
451
+ sourceHandle,
452
452
  target,
453
+ targetHandle,
453
454
  label: isMultiple ? `${relations.length} relationships` : label,
454
455
  relations: relations.map((r) => r.id),
455
456
  parent: null,
@@ -1,13 +1,15 @@
1
- import { jsx, jsxs } from "react/jsx-runtime";
1
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
2
  import { ActionIcon, Group, Box } from "@mantine/core";
3
- import { useStateHistory, useCallbackRef } from "@mantine/hooks";
3
+ import { useCallbackRef, useStateHistory } from "@mantine/hooks";
4
4
  import { IconX, IconChevronLeft, IconChevronRight } from "@tabler/icons-react";
5
- import { ReactFlowProvider, Panel } from "@xyflow/react";
5
+ import { ReactFlowProvider, useStoreApi, Panel } from "@xyflow/react";
6
6
  import clsx from "clsx";
7
+ import { shallowEqual } from "fast-equals";
7
8
  import { LayoutGroup, AnimatePresence, m } from "framer-motion";
8
9
  import { useRef, memo, useEffect } from "react";
9
10
  import { BaseXYFlow } from "../../base/BaseXYFlow.js";
10
11
  import { useRelationshipsView } from "./-useRelationshipsView.js";
12
+ import { ViewPadding } from "./const.js";
11
13
  import { edgeTypes } from "./custom/edgeTypes.js";
12
14
  import { nodeTypes } from "./custom/nodeTypes.js";
13
15
  import { RelationshipsBrowserActorContext, useRelationshipsBrowser, useRelationshipsBrowserState } from "./hooks.js";
@@ -16,61 +18,43 @@ function RelationshipsBrowser({ actorRef }) {
16
18
  const initialRef = useRef(null);
17
19
  if (initialRef.current == null) {
18
20
  initialRef.current = {
19
- defaultNodes: [],
20
- defaultEdges: []
21
+ initialNodes: [],
22
+ initialEdges: []
21
23
  };
22
24
  }
23
- return /* @__PURE__ */ jsx(RelationshipsBrowserActorContext.Provider, { value: actorRef, children: /* @__PURE__ */ jsx(ReactFlowProvider, { ...initialRef.current, children: /* @__PURE__ */ jsx(RelationshipsBrowserInner, {}) }) });
25
+ return /* @__PURE__ */ jsx(RelationshipsBrowserActorContext.Provider, { value: actorRef, children: /* @__PURE__ */ jsx(ReactFlowProvider, { ...initialRef.current, children: /* @__PURE__ */ jsx(RelationshipsBrowserXYFlow, {}) }) });
24
26
  }
25
27
  const selector = (state) => ({
26
- subjectId: state.context.subject,
27
28
  initialized: state.context.initialized,
28
- scope: state.context.scope,
29
+ isActive: state.hasTag("active"),
29
30
  nodes: state.context.xynodes,
30
- edges: state.context.xyedges,
31
- closeable: state.context.closeable,
32
- enableNavigationMenu: state.context.enableNavigationMenu
31
+ edges: state.context.xyedges
33
32
  });
34
- const RelationshipsBrowserInner = memo(() => {
33
+ const selectorEq = (a, b) => a.initialized === b.initialized && a.isActive === b.isActive && shallowEqual(a.nodes, b.nodes) && shallowEqual(a.edges, b.edges);
34
+ const RelationshipsBrowserXYFlow = memo(() => {
35
+ const xystore = useStoreApi();
35
36
  const browser = useRelationshipsBrowser();
36
37
  const {
37
- subjectId,
38
38
  initialized,
39
- scope,
39
+ isActive,
40
40
  nodes,
41
- edges,
42
- closeable,
43
- enableNavigationMenu
44
- } = useRelationshipsBrowserState(selector);
45
- const layouted = useRelationshipsView(subjectId);
46
- const [historySubjectId, historyOps, { history, current }] = useStateHistory(subjectId);
47
- useEffect(() => {
48
- if (historySubjectId !== subjectId) {
49
- historyOps.set(subjectId);
50
- }
51
- }, [subjectId]);
52
- useEffect(() => {
53
- if (historySubjectId !== subjectId) {
54
- browser.navigateTo(historySubjectId);
55
- }
56
- }, [historySubjectId]);
57
- useEffect(() => {
58
- browser.send({ type: "update.view", layouted });
59
- }, [layouted]);
60
- const hasStepBack = current > 0;
61
- const hasStepForward = current + 1 < history.length;
62
- return /* @__PURE__ */ jsx(LayoutGroup, { children: /* @__PURE__ */ jsxs(
41
+ edges
42
+ } = useRelationshipsBrowserState(
43
+ selector,
44
+ selectorEq
45
+ );
46
+ return /* @__PURE__ */ jsx(LayoutGroup, { children: /* @__PURE__ */ jsx(
63
47
  BaseXYFlow,
64
48
  {
65
49
  id: "relationships-browser",
66
50
  nodes,
67
51
  edges,
68
- className: clsx(initialized ? "initialized" : "not-initialized"),
52
+ className: clsx(initialized && isActive ? "initialized" : "not-initialized"),
69
53
  nodeTypes,
70
54
  edgeTypes,
71
- fitViewPadding: 0.05,
55
+ fitViewPadding: ViewPadding,
72
56
  onInit: useCallbackRef((instance) => {
73
- browser.send({ type: "xyflow.init", instance });
57
+ browser.send({ type: "xyflow.init", instance, store: xystore });
74
58
  }),
75
59
  onNodeClick: useCallbackRef((e, node) => {
76
60
  e.stopPropagation();
@@ -88,9 +72,9 @@ const RelationshipsBrowserInner = memo(() => {
88
72
  e.stopPropagation();
89
73
  browser.send({ type: "xyflow.paneDblClick" });
90
74
  }),
91
- onViewportResize: () => {
75
+ onViewportResize: useCallbackRef(() => {
92
76
  browser.send({ type: "xyflow.resized" });
93
- },
77
+ }),
94
78
  onNodesChange: useCallbackRef((changes) => {
95
79
  browser.send({ type: "xyflow.applyNodeChages", changes });
96
80
  }),
@@ -101,34 +85,65 @@ const RelationshipsBrowserInner = memo(() => {
101
85
  fitView: false,
102
86
  pannable: true,
103
87
  zoomable: true,
104
- children: [
105
- /* @__PURE__ */ jsx(
106
- TopLeftPanel,
107
- {
108
- enableNavigationMenu,
109
- subjectId,
110
- hasStepBack,
111
- hasStepForward,
112
- onStepBack: () => historyOps.back(),
113
- onStepForward: () => historyOps.forward()
114
- }
115
- ),
116
- closeable && /* @__PURE__ */ jsx(Panel, { position: "top-right", children: /* @__PURE__ */ jsx(
117
- ActionIcon,
118
- {
119
- variant: "default",
120
- color: "gray",
121
- onClick: (e) => {
122
- e.stopPropagation();
123
- browser.close();
124
- },
125
- children: /* @__PURE__ */ jsx(IconX, {})
126
- }
127
- ) })
128
- ]
88
+ children: initialized && /* @__PURE__ */ jsx(RelationshipsBrowserInner, {})
129
89
  }
130
90
  ) });
131
91
  });
92
+ const selector2 = (state) => ({
93
+ subjectId: state.context.subject,
94
+ closeable: state.context.closeable,
95
+ enableNavigationMenu: state.context.enableNavigationMenu
96
+ });
97
+ const RelationshipsBrowserInner = memo(() => {
98
+ const browser = useRelationshipsBrowser();
99
+ const {
100
+ subjectId,
101
+ closeable,
102
+ enableNavigationMenu
103
+ } = useRelationshipsBrowserState(selector2);
104
+ const layouted = useRelationshipsView(subjectId);
105
+ const [historySubjectId, historyOps, { history, current }] = useStateHistory(subjectId);
106
+ useEffect(() => {
107
+ if (historySubjectId !== subjectId) {
108
+ historyOps.set(subjectId);
109
+ }
110
+ }, [subjectId]);
111
+ useEffect(() => {
112
+ if (historySubjectId !== subjectId) {
113
+ browser.navigateTo(historySubjectId);
114
+ }
115
+ }, [historySubjectId]);
116
+ useEffect(() => {
117
+ browser.send({ type: "update.view", layouted });
118
+ }, [layouted]);
119
+ const hasStepBack = current > 0;
120
+ const hasStepForward = current + 1 < history.length;
121
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
122
+ /* @__PURE__ */ jsx(
123
+ TopLeftPanel,
124
+ {
125
+ enableNavigationMenu,
126
+ subjectId,
127
+ hasStepBack,
128
+ hasStepForward,
129
+ onStepBack: () => historyOps.back(),
130
+ onStepForward: () => historyOps.forward()
131
+ }
132
+ ),
133
+ closeable && /* @__PURE__ */ jsx(Panel, { position: "top-right", children: /* @__PURE__ */ jsx(
134
+ ActionIcon,
135
+ {
136
+ variant: "default",
137
+ color: "gray",
138
+ onClick: (e) => {
139
+ e.stopPropagation();
140
+ browser.close();
141
+ },
142
+ children: /* @__PURE__ */ jsx(IconX, {})
143
+ }
144
+ ) })
145
+ ] });
146
+ });
132
147
  const TopLeftPanel = ({
133
148
  enableNavigationMenu,
134
149
  subjectId,