@likec4/diagram 1.23.1 → 1.24.1

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 (118) hide show
  1. package/dist/LikeC4Diagram.css.d.ts +5 -0
  2. package/dist/LikeC4Diagram.css.js +1 -0
  3. package/dist/LikeC4Diagram.d.ts +1 -1
  4. package/dist/LikeC4Diagram.js +6 -2
  5. package/dist/LikeC4Diagram.props.d.ts +6 -0
  6. package/dist/StaticLikeC4Diagram.js +2 -1
  7. package/dist/base/BaseXYFlow.d.ts +1 -1
  8. package/dist/base/BaseXYFlow.js +12 -3
  9. package/dist/base/const.d.ts +1 -1
  10. package/dist/base/const.js +1 -1
  11. package/dist/base/primitives/compound/CompoundActionButton.css.js +2 -0
  12. package/dist/base/primitives/compound/CompoundDetailsButton.css.js +2 -0
  13. package/dist/base/primitives/compound/CompoundNodeContainer.css.d.ts +0 -1
  14. package/dist/base/primitives/compound/CompoundNodeContainer.css.js +4 -4
  15. package/dist/base/primitives/compound/CompoundNodeContainer.js +35 -22
  16. package/dist/base/primitives/compound/CompoundTitle.css.js +3 -1
  17. package/dist/base/primitives/customNode.js +1 -1
  18. package/dist/base/primitives/edge/EdgeContainer.js +5 -4
  19. package/dist/base/primitives/edge/EdgeLabel.css.d.ts +0 -1
  20. package/dist/base/primitives/edge/EdgeLabel.css.js +2 -5
  21. package/dist/base/primitives/edge/EdgeLabel.js +5 -5
  22. package/dist/base/primitives/edge/edge.css.d.ts +1 -1
  23. package/dist/base/primitives/edge/edge.css.js +4 -5
  24. package/dist/base/primitives/element/ElementActionButtons.css.js +2 -0
  25. package/dist/base/primitives/element/ElementActionButtons.js +4 -3
  26. package/dist/base/primitives/element/ElementDetailsButton.css.js +2 -0
  27. package/dist/base/primitives/element/ElementNodeContainer.css.js +2 -0
  28. package/dist/base/primitives/element/ElementNodeContainer.js +3 -1
  29. package/dist/base/primitives/element/ElementShape.css.js +4 -2
  30. package/dist/base/primitives/element/ElementShape.js +2 -3
  31. package/dist/base/primitives/element/ElementTitle.css.js +2 -0
  32. package/dist/base/primitives/element/styles.css.js +2 -0
  33. package/dist/components/SearchControl.css.js +2 -0
  34. package/dist/context/FramerMotionConfig.d.ts +9 -1
  35. package/dist/context/FramerMotionConfig.js +5 -2
  36. package/dist/context/RootContainer.js +13 -1
  37. package/dist/diagram.css +1 -1
  38. package/dist/globals.css.d.ts +2 -0
  39. package/dist/hooks/safeContext.js +1 -0
  40. package/dist/hooks/useDiagram.d.ts +415 -693
  41. package/dist/hooks/useDiagram.js +1 -0
  42. package/dist/hooks/useIsReducedGraphics.d.ts +4 -0
  43. package/dist/hooks/useIsReducedGraphics.js +8 -0
  44. package/dist/hooks/useXYFlow.js +1 -1
  45. package/dist/likec4diagram/DiagramUI.d.ts +2 -1
  46. package/dist/likec4diagram/DiagramUI.js +5 -3
  47. package/dist/likec4diagram/custom/nodes/nodeTypes.js +2 -2
  48. package/dist/likec4diagram/ui/controls/styles.css.js +2 -0
  49. package/dist/likec4diagram/ui/diagram-title/DiagramTitlePanel.css.js +2 -0
  50. package/dist/likec4diagram/ui/diagram-title/DiagramTitlePanel.js +3 -4
  51. package/dist/likec4diagram/ui/search/LikeC4Search.css.js +2 -0
  52. package/dist/overlays/element-details/ElementDetailsCard.js +12 -8
  53. package/dist/overlays/element-details/actor.d.ts +9084 -2569
  54. package/dist/overlays/overlay/Overlay.js +6 -3
  55. package/dist/overlays/relationship-details/RelationshipDetails.js +31 -17
  56. package/dist/overlays/relationship-details/actor.d.ts +228 -3
  57. package/dist/overlays/relationship-details/actor.js +86 -14
  58. package/dist/overlays/relationship-details/hooks.d.ts +3 -0
  59. package/dist/overlays/relationship-details/hooks.js +6 -2
  60. package/dist/overlays/relationships-browser/-useRelationshipsView.js +12 -3
  61. package/dist/overlays/relationships-browser/RelationshipsBrowser.js +12 -3
  62. package/dist/overlays/relationships-browser/actor.d.ts +271 -54
  63. package/dist/overlays/relationships-browser/actor.js +86 -23
  64. package/dist/state/DiagramActorProvider.js +12 -2
  65. package/dist/state/diagram-machine.d.ts +426 -694
  66. package/dist/state/diagram-machine.js +89 -68
  67. package/dist/state/persistence.d.ts +5 -0
  68. package/dist/state/persistence.js +23 -0
  69. package/package.json +9 -9
  70. package/src/LikeC4Diagram.css.ts +13 -5
  71. package/src/LikeC4Diagram.props.ts +7 -0
  72. package/src/LikeC4Diagram.tsx +33 -24
  73. package/src/StaticLikeC4Diagram.tsx +2 -1
  74. package/src/base/BaseXYFlow.tsx +14 -9
  75. package/src/base/const.ts +1 -1
  76. package/src/base/primitives/compound/CompoundActionButton.css.ts +6 -0
  77. package/src/base/primitives/compound/CompoundDetailsButton.css.ts +4 -0
  78. package/src/base/primitives/compound/CompoundNodeContainer.css.ts +41 -59
  79. package/src/base/primitives/compound/CompoundNodeContainer.tsx +42 -22
  80. package/src/base/primitives/compound/CompoundTitle.css.ts +14 -13
  81. package/src/base/primitives/customNode.ts +3 -2
  82. package/src/base/primitives/edge/EdgeContainer.tsx +4 -3
  83. package/src/base/primitives/edge/EdgeLabel.css.ts +17 -11
  84. package/src/base/primitives/edge/EdgeLabel.tsx +4 -4
  85. package/src/base/primitives/edge/edge.css.ts +47 -27
  86. package/src/base/primitives/element/ElementActionButtons.css.ts +4 -0
  87. package/src/base/primitives/element/ElementActionButtons.tsx +4 -2
  88. package/src/base/primitives/element/ElementDetailsButton.css.ts +6 -8
  89. package/src/base/primitives/element/ElementNodeContainer.css.ts +18 -21
  90. package/src/base/primitives/element/ElementNodeContainer.tsx +3 -1
  91. package/src/base/primitives/element/ElementShape.css.ts +10 -6
  92. package/src/base/primitives/element/ElementShape.tsx +2 -2
  93. package/src/components/SearchControl.css.ts +11 -4
  94. package/src/context/FramerMotionConfig.tsx +13 -2
  95. package/src/context/RootContainer.tsx +9 -2
  96. package/src/globals.css.ts +3 -0
  97. package/src/hooks/safeContext.tsx +2 -1
  98. package/src/hooks/useDiagram.ts +1 -0
  99. package/src/hooks/useIsReducedGraphics.ts +5 -0
  100. package/src/hooks/useXYFlow.ts +1 -1
  101. package/src/likec4diagram/DiagramUI.tsx +5 -3
  102. package/src/likec4diagram/custom/nodes/nodeTypes.tsx +2 -2
  103. package/src/likec4diagram/ui/controls/styles.css.ts +11 -1
  104. package/src/likec4diagram/ui/diagram-title/DiagramTitlePanel.css.ts +10 -3
  105. package/src/likec4diagram/ui/diagram-title/DiagramTitlePanel.tsx +6 -3
  106. package/src/likec4diagram/ui/search/LikeC4Search.css.ts +1 -0
  107. package/src/overlays/element-details/ElementDetailsCard.tsx +238 -231
  108. package/src/overlays/overlay/Overlay.css.ts +1 -9
  109. package/src/overlays/overlay/Overlay.tsx +6 -3
  110. package/src/overlays/relationship-details/RelationshipDetails.tsx +83 -66
  111. package/src/overlays/relationship-details/actor.ts +106 -13
  112. package/src/overlays/relationship-details/hooks.ts +6 -2
  113. package/src/overlays/relationships-browser/-useRelationshipsView.ts +10 -0
  114. package/src/overlays/relationships-browser/RelationshipsBrowser.tsx +16 -8
  115. package/src/overlays/relationships-browser/actor.ts +92 -22
  116. package/src/state/DiagramActorProvider.tsx +13 -2
  117. package/src/state/diagram-machine.ts +88 -65
  118. package/src/state/persistence.ts +22 -0
@@ -5,6 +5,7 @@ import clsx from "clsx";
5
5
  import { m } from "framer-motion";
6
6
  import { useState, useRef, useLayoutEffect } from "react";
7
7
  import { body, dialog } from "./Overlay.css.js";
8
+ import { stopPropagation } from "../../utils/xyflow.js";
8
9
  function Overlay({ children, onClose, className, classes, ...rest }) {
9
10
  const [opened, setOpened] = useState(false);
10
11
  const dialogRef = useRef(null);
@@ -42,7 +43,7 @@ function Overlay({ children, onClose, className, classes, ...rest }) {
42
43
  m.dialog,
43
44
  {
44
45
  ref: dialogRef,
45
- className: clsx(dialog, classes == null ? void 0 : classes.dialog, className),
46
+ className: clsx(dialog, classes == null ? void 0 : classes.dialog, className, RemoveScroll.classNames.fullWidth),
46
47
  initial: {
47
48
  "--backdrop-blur": "0px",
48
49
  "--backdrop-opacity": "5%",
@@ -70,18 +71,20 @@ function Overlay({ children, onClose, className, classes, ...rest }) {
70
71
  },
71
72
  onClick: (e) => {
72
73
  var _a, _b, _c;
74
+ e.stopPropagation();
73
75
  if (((_b = (_a = e.target) == null ? void 0 : _a.nodeName) == null ? void 0 : _b.toUpperCase()) === "DIALOG") {
74
- e.stopPropagation();
75
76
  (_c = dialogRef.current) == null ? void 0 : _c.close();
76
77
  return;
77
78
  }
78
79
  },
80
+ onDoubleClick: stopPropagation,
81
+ onPointerDown: stopPropagation,
79
82
  onClose: (e) => {
80
83
  e.stopPropagation();
81
84
  close();
82
85
  },
83
86
  ...rest,
84
- children: /* @__PURE__ */ jsx(RemoveScroll, { forwardProps: true, children: /* @__PURE__ */ jsx(Box, { className: clsx(body, classes == null ? void 0 : classes.body), children: opened && /* @__PURE__ */ jsx(Fragment, { children }) }) })
87
+ children: /* @__PURE__ */ jsx(RemoveScroll, { forwardProps: true, removeScrollBar: false, children: /* @__PURE__ */ jsx(Box, { className: clsx(body, classes == null ? void 0 : classes.body), children: opened && /* @__PURE__ */ jsx(Fragment, { children }) }) })
85
88
  }
86
89
  );
87
90
  }
@@ -2,19 +2,16 @@ import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { ActionIcon, Group } from "@mantine/core";
3
3
  import { useStateHistory, useCallbackRef } from "@mantine/hooks";
4
4
  import { IconX, IconChevronLeft, IconChevronRight } from "@tabler/icons-react";
5
- import { ReactFlowProvider, useNodesState, useEdgesState, Panel } from "@xyflow/react";
5
+ import { ReactFlowProvider, Panel } from "@xyflow/react";
6
6
  import clsx from "clsx";
7
7
  import { LayoutGroup, AnimatePresence, m } from "framer-motion";
8
8
  import { useRef, memo, useEffect } from "react";
9
9
  import { BaseXYFlow } from "../../base/BaseXYFlow.js";
10
- import { updateEdges } from "../../base/updateEdges.js";
11
- import { updateNodes } from "../../base/updateNodes.js";
12
10
  import { edgeTypes } from "./custom/edgeTypes.js";
13
11
  import { nodeTypes } from "./custom/nodeTypes.js";
14
12
  import { RelationshipDetailsActorContext, useRelationshipDetails, useRelationshipDetailsState } from "./hooks.js";
15
13
  import { SelectEdge } from "./SelectEdge.js";
16
14
  import { useLayoutedDetails } from "./useLayoutedDetails.js";
17
- import { useUpdateEffect } from "../../hooks/useUpdateEffect.js";
18
15
  function RelationshipDetails({ actorRef }) {
19
16
  const initialRef = useRef(null);
20
17
  if (initialRef.current == null) {
@@ -23,19 +20,23 @@ function RelationshipDetails({ actorRef }) {
23
20
  defaultEdges: []
24
21
  };
25
22
  }
26
- return /* @__PURE__ */ jsx(RelationshipDetailsActorContext.Provider, { value: actorRef, children: /* @__PURE__ */ jsx(ReactFlowProvider, { ...initialRef.current, children: /* @__PURE__ */ jsx(RelationshipDetailsInner, {}) }) });
23
+ return /* @__PURE__ */ jsx(RelationshipDetailsActorContext.Provider, { value: actorRef, children: /* @__PURE__ */ jsx(ReactFlowProvider, { ...initialRef.current, children: /* @__PURE__ */ jsx(LayoutGroup, { id: actorRef.sessionId, inherit: false, children: /* @__PURE__ */ jsx(AnimatePresence, { children: /* @__PURE__ */ jsx(RelationshipDetailsInner, {}) }) }) }) });
27
24
  }
28
25
  const selector = (state) => ({
29
26
  edgeId: state.context.edgeId,
30
27
  view: state.context.view,
31
- initialized: state.context.initialized
28
+ initialized: state.context.initialized,
29
+ nodes: state.context.xynodes,
30
+ edges: state.context.xyedges
32
31
  });
33
32
  const RelationshipDetailsInner = memo(() => {
34
33
  const browser = useRelationshipDetails();
35
34
  const {
36
35
  edgeId,
37
36
  view,
38
- initialized
37
+ initialized,
38
+ nodes,
39
+ edges
39
40
  } = useRelationshipDetailsState(selector);
40
41
  const {
41
42
  edge,
@@ -43,11 +44,8 @@ const RelationshipDetailsInner = memo(() => {
43
44
  xyedges,
44
45
  bounds
45
46
  } = useLayoutedDetails(edgeId, view);
46
- const [nodes, setNodes, onNodesChange] = useNodesState(xynodes);
47
- const [edges, setEdges, onEdgesChange] = useEdgesState(xyedges);
48
- useUpdateEffect(() => {
49
- setNodes(updateNodes(xynodes));
50
- setEdges(updateEdges(xyedges));
47
+ useEffect(() => {
48
+ browser.send({ type: "update.xydata", xynodes, xyedges });
51
49
  }, [xynodes, xyedges]);
52
50
  const [historyEdgeId, historyOps, { history, current }] = useStateHistory(edgeId);
53
51
  useEffect(() => {
@@ -56,20 +54,27 @@ const RelationshipDetailsInner = memo(() => {
56
54
  }
57
55
  }, [edgeId]);
58
56
  useEffect(() => {
57
+ if (historyEdgeId !== edgeId) {
58
+ browser.navigateTo(historyEdgeId);
59
+ }
59
60
  }, [historyEdgeId]);
60
61
  const hasStepBack = current > 0;
61
62
  const hasStepForward = current + 1 < history.length;
62
- return /* @__PURE__ */ jsx(LayoutGroup, { children: /* @__PURE__ */ jsxs(
63
+ return /* @__PURE__ */ jsxs(
63
64
  BaseXYFlow,
64
65
  {
65
- id: "relationship-details",
66
+ id: `relationships-details-${browser.actor.sessionId}`,
66
67
  nodes,
67
68
  edges,
68
69
  className: clsx(initialized ? "initialized" : "not-initialized"),
69
70
  nodeTypes,
70
71
  edgeTypes,
71
- onEdgesChange,
72
- onNodesChange,
72
+ onNodesChange: useCallbackRef((changes) => {
73
+ browser.send({ type: "xyflow.applyNodeChanges", changes });
74
+ }),
75
+ onEdgesChange: useCallbackRef((changes) => {
76
+ browser.send({ type: "xyflow.applyEdgeChanges", changes });
77
+ }),
73
78
  fitViewPadding: 0.05,
74
79
  onInit: useCallbackRef((instance) => {
75
80
  browser.send({ type: "xyflow.init", instance });
@@ -89,6 +94,15 @@ const RelationshipDetailsInner = memo(() => {
89
94
  onViewportResize: useCallbackRef(() => {
90
95
  browser.send({ type: "xyflow.resized" });
91
96
  }),
97
+ onEdgeMouseEnter: useCallbackRef((_event, edge2) => {
98
+ browser.send({ type: "xyflow.edgeMouseEnter", edge: edge2 });
99
+ }),
100
+ onEdgeMouseLeave: useCallbackRef((_event, edge2) => {
101
+ browser.send({ type: "xyflow.edgeMouseLeave", edge: edge2 });
102
+ }),
103
+ onSelectionChange: useCallbackRef((params) => {
104
+ browser.send({ type: "xyflow.selectionChange", ...params });
105
+ }),
92
106
  nodesDraggable: false,
93
107
  fitView: false,
94
108
  pannable: true,
@@ -119,7 +133,7 @@ const RelationshipDetailsInner = memo(() => {
119
133
  ) })
120
134
  ]
121
135
  }
122
- ) });
136
+ );
123
137
  });
124
138
  const TopLeftPanel = ({
125
139
  edge,
@@ -1,7 +1,10 @@
1
- import { BBox, DiagramView, EdgeId } from '@likec4/core';
2
- import { ReactFlowInstance } from '@xyflow/react';
3
- import { ActorLogicFrom, ActorRefFromLogic, MachineSnapshot, SnapshotFrom, StateMachine, NonReducibleUnknown, EventObject, MetaObject, AnyActorLogic, ConditionalRequired, InputFrom, RequiredLogicInput, IsNotNever, ActorRef, AnyActorRef, StateValue, AnyEventObject, ActionFunction, CancelAction } from 'xstate';
1
+ import { BBox, DiagramView, EdgeId, RelationId, Color, ViewId, RelationshipLineType } from '@likec4/core';
2
+ import { EdgeChange, NodeChange, ReactFlowInstance } from '@xyflow/react';
3
+ import { ActorLogicFrom, ActorRefFromLogic, SnapshotFrom, StateMachine, NonReducibleUnknown, EventObject, MetaObject, AnyActorLogic, ConditionalRequired, InputFrom, RequiredLogicInput, IsNotNever, ActorRef, MachineSnapshot, AnyActorRef, StateValue, AnyEventObject, ActionFunction, CancelAction } from 'xstate';
4
+ import { Base } from '../../base';
4
5
  import { RelationshipDetailsTypes } from './_types';
6
+ import { EdgeMarkerType, HandleType } from '@xyflow/system';
7
+ import { ReactNode, CSSProperties } from 'react';
5
8
  type XYFLowInstance = ReactFlowInstance<RelationshipDetailsTypes.Node, RelationshipDetailsTypes.Edge>;
6
9
  export type Input = {
7
10
  edgeId: EdgeId;
@@ -10,6 +13,8 @@ export type Input = {
10
13
  export type Context = Readonly<Input & {
11
14
  xyflow: XYFLowInstance | null;
12
15
  initialized: boolean;
16
+ xynodes: RelationshipDetailsTypes.Node[];
17
+ xyedges: RelationshipDetailsTypes.Edge[];
13
18
  }>;
14
19
  export type Events = {
15
20
  type: 'xyflow.init';
@@ -20,10 +25,34 @@ export type Events = {
20
25
  } | {
21
26
  type: 'xyflow.edgeClick';
22
27
  edge: RelationshipDetailsTypes.Edge;
28
+ } | {
29
+ type: 'xyflow.edgeMouseEnter';
30
+ edge: RelationshipDetailsTypes.Edge;
31
+ } | {
32
+ type: 'xyflow.edgeMouseLeave';
33
+ edge: RelationshipDetailsTypes.Edge;
34
+ } | {
35
+ type: 'dim.nonhovered.edges';
36
+ } | {
37
+ type: 'undim.edges';
38
+ } | {
39
+ type: 'xyflow.selectionChange';
40
+ nodes: RelationshipDetailsTypes.Node[];
41
+ edges: RelationshipDetailsTypes.Edge[];
42
+ } | {
43
+ type: 'xyflow.applyNodeChanges';
44
+ changes: NodeChange<RelationshipDetailsTypes.Node>[];
45
+ } | {
46
+ type: 'xyflow.applyEdgeChanges';
47
+ changes: EdgeChange<RelationshipDetailsTypes.Edge>[];
23
48
  } | {
24
49
  type: 'xyflow.paneClick';
25
50
  } | {
26
51
  type: 'xyflow.resized';
52
+ } | {
53
+ type: 'update.xydata';
54
+ xynodes: RelationshipDetailsTypes.Node[];
55
+ xyedges: RelationshipDetailsTypes.Edge[];
27
56
  } | {
28
57
  type: 'fitDiagram';
29
58
  duration?: number;
@@ -37,6 +66,8 @@ export type Events = {
37
66
  export declare const relationshipDetailsLogic: StateMachine<Readonly<Input & {
38
67
  xyflow: XYFLowInstance | null;
39
68
  initialized: boolean;
69
+ xynodes: RelationshipDetailsTypes.Node[];
70
+ xyedges: RelationshipDetailsTypes.Edge[];
40
71
  }>, Events, {}, never, {
41
72
  type: "xyflow:fitDiagram";
42
73
  params: {
@@ -62,10 +93,14 @@ export declare const relationshipDetailsLogic: StateMachine<Readonly<Input & {
62
93
  self: ActorRef<MachineSnapshot<Readonly<Input & {
63
94
  xyflow: XYFLowInstance | null;
64
95
  initialized: boolean;
96
+ xynodes: RelationshipDetailsTypes.Node[];
97
+ xyedges: RelationshipDetailsTypes.Edge[];
65
98
  }>, Events, Record<string, AnyActorRef | undefined>, StateValue, string, unknown, any, any>, Events, AnyEventObject>;
66
99
  }) => {
67
100
  initialized: false;
68
101
  xyflow: null;
102
+ xynodes: never[];
103
+ xyedges: never[];
69
104
  edgeId: EdgeId;
70
105
  view: DiagramView;
71
106
  };
@@ -76,6 +111,8 @@ export declare const relationshipDetailsLogic: StateMachine<Readonly<Input & {
76
111
  readonly actions: ActionFunction<Readonly<Input & {
77
112
  xyflow: XYFLowInstance | null;
78
113
  initialized: boolean;
114
+ xynodes: RelationshipDetailsTypes.Node[];
115
+ xyedges: RelationshipDetailsTypes.Edge[];
79
116
  }>, {
80
117
  type: "xyflow.init";
81
118
  instance: XYFLowInstance;
@@ -95,16 +132,123 @@ export declare const relationshipDetailsLogic: StateMachine<Readonly<Input & {
95
132
  };
96
133
  };
97
134
  readonly on: {
135
+ readonly 'xyflow.edgeMouseEnter': {
136
+ readonly actions: readonly [ ActionFunction<Readonly<Input & {
137
+ xyflow: XYFLowInstance | null;
138
+ initialized: boolean;
139
+ xynodes: RelationshipDetailsTypes.Node[];
140
+ xyedges: RelationshipDetailsTypes.Edge[];
141
+ }>, {
142
+ type: "xyflow.edgeMouseEnter";
143
+ edge: RelationshipDetailsTypes.Edge;
144
+ }, Events, undefined, never, never, never, never, never>, CancelAction<Readonly<Input & {
145
+ xyflow: XYFLowInstance | null;
146
+ initialized: boolean;
147
+ xynodes: RelationshipDetailsTypes.Node[];
148
+ xyedges: RelationshipDetailsTypes.Edge[];
149
+ }>, {
150
+ type: "xyflow.edgeMouseEnter";
151
+ edge: RelationshipDetailsTypes.Edge;
152
+ }, undefined, Events>, CancelAction<Readonly<Input & {
153
+ xyflow: XYFLowInstance | null;
154
+ initialized: boolean;
155
+ xynodes: RelationshipDetailsTypes.Node[];
156
+ xyedges: RelationshipDetailsTypes.Edge[];
157
+ }>, {
158
+ type: "xyflow.edgeMouseEnter";
159
+ edge: RelationshipDetailsTypes.Edge;
160
+ }, undefined, Events>, ActionFunction<Readonly<Input & {
161
+ xyflow: XYFLowInstance | null;
162
+ initialized: boolean;
163
+ xynodes: RelationshipDetailsTypes.Node[];
164
+ xyedges: RelationshipDetailsTypes.Edge[];
165
+ }>, {
166
+ type: "xyflow.edgeMouseEnter";
167
+ edge: RelationshipDetailsTypes.Edge;
168
+ }, Events, undefined, never, never, never, never, never>];
169
+ };
170
+ readonly 'xyflow.edgeMouseLeave': {
171
+ readonly actions: readonly [ ActionFunction<Readonly<Input & {
172
+ xyflow: XYFLowInstance | null;
173
+ initialized: boolean;
174
+ xynodes: RelationshipDetailsTypes.Node[];
175
+ xyedges: RelationshipDetailsTypes.Edge[];
176
+ }>, {
177
+ type: "xyflow.edgeMouseLeave";
178
+ edge: RelationshipDetailsTypes.Edge;
179
+ }, Events, undefined, never, never, never, never, never>, CancelAction<Readonly<Input & {
180
+ xyflow: XYFLowInstance | null;
181
+ initialized: boolean;
182
+ xynodes: RelationshipDetailsTypes.Node[];
183
+ xyedges: RelationshipDetailsTypes.Edge[];
184
+ }>, {
185
+ type: "xyflow.edgeMouseLeave";
186
+ edge: RelationshipDetailsTypes.Edge;
187
+ }, undefined, Events>, ActionFunction<Readonly<Input & {
188
+ xyflow: XYFLowInstance | null;
189
+ initialized: boolean;
190
+ xynodes: RelationshipDetailsTypes.Node[];
191
+ xyedges: RelationshipDetailsTypes.Edge[];
192
+ }>, {
193
+ type: "xyflow.edgeMouseLeave";
194
+ edge: RelationshipDetailsTypes.Edge;
195
+ }, Events, undefined, never, never, never, never, never>];
196
+ };
197
+ readonly 'dim.nonhovered.edges': {
198
+ readonly actions: ActionFunction<Readonly<Input & {
199
+ xyflow: XYFLowInstance | null;
200
+ initialized: boolean;
201
+ xynodes: RelationshipDetailsTypes.Node[];
202
+ xyedges: RelationshipDetailsTypes.Edge[];
203
+ }>, {
204
+ type: "dim.nonhovered.edges";
205
+ }, Events, undefined, never, never, never, never, never>;
206
+ };
207
+ readonly 'undim.edges': {
208
+ readonly actions: ActionFunction<Readonly<Input & {
209
+ xyflow: XYFLowInstance | null;
210
+ initialized: boolean;
211
+ xynodes: RelationshipDetailsTypes.Node[];
212
+ xyedges: RelationshipDetailsTypes.Edge[];
213
+ }>, {
214
+ type: "undim.edges";
215
+ }, Events, undefined, never, never, never, never, never>;
216
+ };
217
+ readonly 'xyflow.selectionChange': {
218
+ readonly actions: ActionFunction<Readonly<Input & {
219
+ xyflow: XYFLowInstance | null;
220
+ initialized: boolean;
221
+ xynodes: RelationshipDetailsTypes.Node[];
222
+ xyedges: RelationshipDetailsTypes.Edge[];
223
+ }>, {
224
+ type: "xyflow.selectionChange";
225
+ nodes: RelationshipDetailsTypes.Node[];
226
+ edges: RelationshipDetailsTypes.Edge[];
227
+ }, Events, undefined, never, {
228
+ type: "xyflow:fitDiagram";
229
+ params: {
230
+ duration?: number;
231
+ bounds?: BBox;
232
+ } | undefined;
233
+ }, {
234
+ type: "enable: navigate.to";
235
+ params: unknown;
236
+ }, never, EventObject>;
237
+ };
98
238
  readonly 'navigate.to': {
99
239
  readonly actions: readonly [ ActionFunction<Readonly<Input & {
100
240
  xyflow: XYFLowInstance | null;
101
241
  initialized: boolean;
242
+ xynodes: RelationshipDetailsTypes.Node[];
243
+ xyedges: RelationshipDetailsTypes.Edge[];
102
244
  }>, {
103
245
  type: "navigate.to";
104
246
  edgeId: EdgeId;
105
247
  }, Events, undefined, never, never, never, never, never>, ActionFunction<Readonly<Input & {
106
248
  xyflow: XYFLowInstance | null;
107
249
  initialized: boolean;
250
+ xynodes: RelationshipDetailsTypes.Node[];
251
+ xyedges: RelationshipDetailsTypes.Edge[];
108
252
  }>, {
109
253
  type: "navigate.to";
110
254
  edgeId: EdgeId;
@@ -117,6 +261,8 @@ export declare const relationshipDetailsLogic: StateMachine<Readonly<Input & {
117
261
  readonly exit: ActionFunction<Readonly<Input & {
118
262
  xyflow: XYFLowInstance | null;
119
263
  initialized: boolean;
264
+ xynodes: RelationshipDetailsTypes.Node[];
265
+ xyedges: RelationshipDetailsTypes.Edge[];
120
266
  }>, Events, Events, undefined, never, never, never, never, never>;
121
267
  };
122
268
  readonly closed: {
@@ -131,6 +277,47 @@ export declare const relationshipDetailsLogic: StateMachine<Readonly<Input & {
131
277
  context: Readonly<Input & {
132
278
  xyflow: XYFLowInstance | null;
133
279
  initialized: boolean;
280
+ xynodes: RelationshipDetailsTypes.Node[];
281
+ xyedges: {
282
+ id: string;
283
+ source: string;
284
+ target: string;
285
+ sourceHandle?: string | null;
286
+ targetHandle?: string | null;
287
+ animated?: boolean;
288
+ hidden?: boolean;
289
+ deletable?: boolean;
290
+ selectable?: boolean;
291
+ selected?: boolean;
292
+ markerStart?: EdgeMarkerType;
293
+ markerEnd?: EdgeMarkerType;
294
+ zIndex?: number;
295
+ ariaLabel?: string;
296
+ interactionWidth?: number;
297
+ label?: ReactNode;
298
+ labelStyle?: CSSProperties;
299
+ labelShowBg?: boolean;
300
+ labelBgStyle?: CSSProperties;
301
+ labelBgPadding?: [number, number];
302
+ labelBgBorderRadius?: number;
303
+ style?: CSSProperties;
304
+ className?: string;
305
+ reconnectable?: boolean | HandleType;
306
+ focusable?: boolean;
307
+ type: "relationship";
308
+ data: {
309
+ relationId: RelationId;
310
+ color: Color | undefined;
311
+ label: string | null;
312
+ technology?: string | undefined | undefined;
313
+ description?: string | undefined | undefined;
314
+ navigateTo: ViewId | null;
315
+ line: RelationshipLineType;
316
+ hovered?: boolean;
317
+ active?: boolean;
318
+ dimmed?: Base.Dimmed;
319
+ };
320
+ }[];
134
321
  }>;
135
322
  event: {
136
323
  type: "fitDiagram";
@@ -148,15 +335,53 @@ export declare const relationshipDetailsLogic: StateMachine<Readonly<Input & {
148
335
  readonly actions: readonly [ CancelAction<Readonly<Input & {
149
336
  xyflow: XYFLowInstance | null;
150
337
  initialized: boolean;
338
+ xynodes: RelationshipDetailsTypes.Node[];
339
+ xyedges: RelationshipDetailsTypes.Edge[];
151
340
  }>, {
152
341
  type: "xyflow.resized";
153
342
  }, undefined, Events>, ActionFunction<Readonly<Input & {
154
343
  xyflow: XYFLowInstance | null;
155
344
  initialized: boolean;
345
+ xynodes: RelationshipDetailsTypes.Node[];
346
+ xyedges: RelationshipDetailsTypes.Edge[];
156
347
  }>, {
157
348
  type: "xyflow.resized";
158
349
  }, Events, undefined, never, never, never, never, never>];
159
350
  };
351
+ readonly 'update.xydata': {
352
+ readonly actions: ActionFunction<Readonly<Input & {
353
+ xyflow: XYFLowInstance | null;
354
+ initialized: boolean;
355
+ xynodes: RelationshipDetailsTypes.Node[];
356
+ xyedges: RelationshipDetailsTypes.Edge[];
357
+ }>, {
358
+ type: "update.xydata";
359
+ xynodes: RelationshipDetailsTypes.Node[];
360
+ xyedges: RelationshipDetailsTypes.Edge[];
361
+ }, Events, undefined, never, never, never, never, never>;
362
+ };
363
+ readonly 'xyflow.applyNodeChanges': {
364
+ readonly actions: ActionFunction<Readonly<Input & {
365
+ xyflow: XYFLowInstance | null;
366
+ initialized: boolean;
367
+ xynodes: RelationshipDetailsTypes.Node[];
368
+ xyedges: RelationshipDetailsTypes.Edge[];
369
+ }>, {
370
+ type: "xyflow.applyNodeChanges";
371
+ changes: NodeChange<RelationshipDetailsTypes.Node>[];
372
+ }, Events, undefined, never, never, never, never, never>;
373
+ };
374
+ readonly 'xyflow.applyEdgeChanges': {
375
+ readonly actions: ActionFunction<Readonly<Input & {
376
+ xyflow: XYFLowInstance | null;
377
+ initialized: boolean;
378
+ xynodes: RelationshipDetailsTypes.Node[];
379
+ xyedges: RelationshipDetailsTypes.Edge[];
380
+ }>, {
381
+ type: "xyflow.applyEdgeChanges";
382
+ changes: EdgeChange<RelationshipDetailsTypes.Edge>[];
383
+ }, Events, undefined, never, never, never, never, never>;
384
+ };
160
385
  };
161
386
  }>;
162
387
  export interface RelationshipDetailsLogic extends ActorLogicFrom<typeof relationshipDetailsLogic> {
@@ -1,8 +1,9 @@
1
1
  import { invariant } from "@likec4/core";
2
- import "@xyflow/react";
2
+ import { applyEdgeChanges, applyNodeChanges } from "@xyflow/react";
3
3
  import { prop } from "remeda";
4
- import { setup, cancel, raise, assign } from "xstate";
4
+ import { setup, assign, cancel, raise, enqueueActions } from "xstate";
5
5
  import { MinZoom } from "../../base/const.js";
6
+ import { Base } from "../../base/types.js";
6
7
  const relationshipDetailsLogic = setup({
7
8
  types: {
8
9
  context: {},
@@ -38,7 +39,9 @@ const relationshipDetailsLogic = setup({
38
39
  context: ({ input }) => ({
39
40
  ...input,
40
41
  initialized: false,
41
- xyflow: null
42
+ xyflow: null,
43
+ xynodes: [],
44
+ xyedges: []
42
45
  }),
43
46
  states: {
44
47
  "opening": {
@@ -61,17 +64,66 @@ const relationshipDetailsLogic = setup({
61
64
  params: { duration: 0 }
62
65
  },
63
66
  on: {
64
- // 'xyflow.nodeClick': {
65
- // actions: enqueueActions(({ event, enqueue }) => {
66
- // if ('fqn' in event.node.data) {
67
- // const fqn = event.node.data.fqn
68
- // enqueue.assign({
69
- // subject: fqn,
70
- // })
71
- // enqueue.raise({ type: 'fitDiagram' }, { delay: 50 })
72
- // }
73
- // }),
74
- // },
67
+ "xyflow.edgeMouseEnter": {
68
+ actions: [
69
+ assign({
70
+ xyedges: ({ context, event }) => {
71
+ const hasDimmed = context.xyedges.some((edge) => edge.data.dimmed === true);
72
+ return context.xyedges.map((edge) => {
73
+ if (edge.id === event.edge.id) {
74
+ return Base.setData(edge, {
75
+ hovered: true,
76
+ dimmed: false
77
+ });
78
+ }
79
+ return hasDimmed && !edge.data.dimmed && !edge.selected ? Base.setDimmed(edge, "immediate") : edge;
80
+ });
81
+ }
82
+ }),
83
+ cancel("undim.edges"),
84
+ cancel("dim.nonhovered.edges"),
85
+ raise({ type: "dim.nonhovered.edges" }, { id: "dim.nonhovered.edges", delay: 100 })
86
+ ]
87
+ },
88
+ "xyflow.edgeMouseLeave": {
89
+ actions: [
90
+ assign({
91
+ xyedges: ({ context, event }) => context.xyedges.map((edge) => {
92
+ if (edge.id === event.edge.id) {
93
+ return Base.setHovered(edge, false);
94
+ }
95
+ return edge;
96
+ })
97
+ }),
98
+ cancel("dim.nonhovered.edges"),
99
+ raise({ type: "undim.edges" }, { id: "undim.edges", delay: 400 })
100
+ ]
101
+ },
102
+ "dim.nonhovered.edges": {
103
+ actions: assign({
104
+ xyedges: ({ context }) => context.xyedges.map((edge) => Base.setDimmed(edge, edge.data.hovered !== true))
105
+ })
106
+ },
107
+ "undim.edges": {
108
+ actions: assign({
109
+ xyedges: ({ context }) => {
110
+ const hasSelected = context.xyedges.some((edge) => edge.selected === true);
111
+ if (hasSelected) {
112
+ return context.xyedges.map(
113
+ (edge) => Base.setDimmed(edge, edge.selected !== true ? edge.data.dimmed || "immediate" : false)
114
+ );
115
+ }
116
+ return context.xyedges.map(Base.setDimmed(false));
117
+ }
118
+ })
119
+ },
120
+ "xyflow.selectionChange": {
121
+ actions: enqueueActions(({ event, context, enqueue }) => {
122
+ if (event.edges.length === 0 && context.xyedges.some((e) => e.data.dimmed) && !context.xyedges.some((e) => e.data.hovered)) {
123
+ enqueue.raise({ type: "undim.edges" });
124
+ }
125
+ })
126
+ },
75
127
  "navigate.to": {
76
128
  actions: [
77
129
  assign({
@@ -105,6 +157,26 @@ const relationshipDetailsLogic = setup({
105
157
  cancel("fitDiagram"),
106
158
  raise({ type: "fitDiagram" }, { id: "fitDiagram", delay: 200 })
107
159
  ]
160
+ },
161
+ "update.xydata": {
162
+ actions: assign({
163
+ xynodes: ({ event }) => event.xynodes,
164
+ xyedges: ({ event }) => event.xyedges
165
+ })
166
+ },
167
+ "xyflow.applyNodeChanges": {
168
+ actions: assign({
169
+ xynodes: ({ context, event }) => {
170
+ return applyNodeChanges(event.changes, context.xynodes);
171
+ }
172
+ })
173
+ },
174
+ "xyflow.applyEdgeChanges": {
175
+ actions: assign({
176
+ xyedges: ({ context, event }) => {
177
+ return applyEdgeChanges(event.changes, context.xyedges);
178
+ }
179
+ })
108
180
  }
109
181
  }
110
182
  });
@@ -7,6 +7,7 @@ export declare const RelationshipDetailsActorContext: Context<RelationshipDetail
7
7
  export declare function useRelationshipDetailsActor(): RelationshipDetailsActorRef;
8
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
+ actor: RelationshipDetailsActorRef;
10
11
  getState: () => Readonly< Input & {
11
12
  xyflow: ( GeneralHelpers<import("./_types").RelationshipDetailsTypes.Node, {
12
13
  id: string;
@@ -51,6 +52,8 @@ export declare function useRelationshipDetails(): {
51
52
  viewportInitialized: boolean;
52
53
  }) | null;
53
54
  initialized: boolean;
55
+ xynodes: import("./_types").RelationshipDetailsTypes.Node[];
56
+ xyedges: import("./_types").RelationshipDetailsTypes.Edge[];
54
57
  }>;
55
58
  send: (event: Events) => void;
56
59
  navigateTo: (edgeId: EdgeId) => void;
@@ -16,6 +16,7 @@ function useRelationshipDetails() {
16
16
  const actor = useRelationshipDetailsActor();
17
17
  const [, startTransition] = useTransition();
18
18
  return useMemo(() => ({
19
+ actor,
19
20
  getState: () => actor.getSnapshot().context,
20
21
  send: actor.send,
21
22
  navigateTo: (edgeId) => {
@@ -32,9 +33,12 @@ function useRelationshipDetails() {
32
33
  });
33
34
  },
34
35
  close: () => {
35
- startTransition(() => {
36
+ var _a;
37
+ if (actor._parent) {
38
+ (_a = actor._parent) == null ? void 0 : _a.send({ type: "close", actorId: actor.id });
39
+ } else {
36
40
  actor.send({ type: "close" });
37
- });
41
+ }
38
42
  }
39
43
  }), [actor]);
40
44
  }
@@ -205,9 +205,9 @@ function layoutRelationshipsView(data) {
205
205
  if (node.isCompound) {
206
206
  continue;
207
207
  }
208
- const edgeCount = Math.max(((_a = g.inEdges(nodeId)) == null ? void 0 : _a.length) ?? 0, ((_b = g.outEdges(nodeId)) == null ? void 0 : _b.length) ?? 0);
209
- if (edgeCount > 2) {
210
- node.height = node.height + (edgeCount - 3) * 14;
208
+ const edgeCount2 = Math.max(((_a = g.inEdges(nodeId)) == null ? void 0 : _a.length) ?? 0, ((_b = g.outEdges(nodeId)) == null ? void 0 : _b.length) ?? 0);
209
+ if (edgeCount2 > 2) {
210
+ node.height = node.height + (edgeCount2 - 3) * 14;
211
211
  }
212
212
  }
213
213
  const nodeIds = [
@@ -255,6 +255,15 @@ function layoutRelationshipsView(data) {
255
255
  portId: id
256
256
  });
257
257
  }
258
+ const edgeCount = g.edgeCount();
259
+ if (edgeCount > 10) {
260
+ for (const edge of g.edges()) {
261
+ g.setEdge(edge, {
262
+ ...Sizes.edgeLabel,
263
+ width: edgeCount > 30 ? 800 : 400
264
+ });
265
+ }
266
+ }
258
267
  const dagreBounds = applyDagreLayout(g);
259
268
  const _calculatedNodeBounds = pipe(
260
269
  nodeIds,