@likec4/diagram 1.27.1 → 1.27.2

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 (115) hide show
  1. package/bundle/index.js +2147 -6730
  2. package/dist/LikeC4Diagram.js +1 -1
  3. package/dist/LikeC4Diagram.props.d.ts +5 -3
  4. package/dist/StaticLikeC4Diagram.js +1 -1
  5. package/dist/_virtual/set.js +5 -0
  6. package/dist/_virtual/set2.js +3 -0
  7. package/dist/base/BaseXYFlow.d.ts +2 -1
  8. package/dist/base/primitives/compound/CompoundNodeContainer.d.ts +1 -1
  9. package/dist/base/primitives/compound/CompoundNodeContainer.js +0 -1
  10. package/dist/base/primitives/element/ElementActionButtons.js +5 -10
  11. package/dist/base/primitives/element/ElementNodeContainer.css.js +4 -4
  12. package/dist/base/updateEdges.js +1 -9
  13. package/dist/base/updateNodes.js +1 -12
  14. package/dist/bundle/LikeC4View.js +2 -2
  15. package/dist/bundle/LikeC4View.props.d.ts +7 -7
  16. package/dist/hooks/useCurrentViewId.js +8 -2
  17. package/dist/hooks/useDiagram.d.ts +1517 -684
  18. package/dist/hooks/useDiagram.js +16 -8
  19. package/dist/likec4diagram/ui/notation/NotationPanel.css.js +3 -3
  20. package/dist/likec4diagram/ui/notation/NotationPanel.js +10 -4
  21. package/dist/node_modules/.pnpm/mnemonist@0.40.3_patch_hash_5c12abb43fcff6fd81e53021b89075d53249640d165a2556cc1e58d71201a13a/node_modules/mnemonist/set.js +369 -0
  22. package/dist/overlays/Overlays.js +3 -1
  23. package/dist/overlays/element-details/actor.d.ts +2769 -173
  24. package/dist/overlays/overlay/Overlay.css.d.ts +26 -2
  25. package/dist/overlays/overlay/Overlay.css.js +66 -18
  26. package/dist/overlays/overlay/Overlay.d.ts +7 -6
  27. package/dist/overlays/overlay/Overlay.js +12 -8
  28. package/dist/overlays/overlaysActor.js +2 -2
  29. package/dist/overlays/relationship-details/RelationshipDetails.js +113 -70
  30. package/dist/overlays/relationship-details/SelectEdge.d.ts +1 -1
  31. package/dist/overlays/relationship-details/SelectEdge.js +15 -9
  32. package/dist/overlays/relationship-details/_types.d.ts +4 -1
  33. package/dist/overlays/relationship-details/actor.d.ts +415 -128
  34. package/dist/overlays/relationship-details/actor.js +162 -53
  35. package/dist/overlays/relationship-details/compute.d.ts +12 -0
  36. package/dist/overlays/relationship-details/compute.js +139 -0
  37. package/dist/overlays/relationship-details/custom/ElementActions.js +27 -5
  38. package/dist/overlays/relationship-details/custom/edgeTypes.js +1 -1
  39. package/dist/overlays/relationship-details/custom/nodeTypes.d.ts +2 -1
  40. package/dist/overlays/relationship-details/hooks.d.ts +336 -6
  41. package/dist/overlays/relationship-details/hooks.js +9 -7
  42. package/dist/overlays/relationship-details/layout-to-xyflow.d.ts +12 -0
  43. package/dist/overlays/relationship-details/layout-to-xyflow.js +149 -0
  44. package/dist/overlays/relationship-details/layout.d.ts +22 -0
  45. package/dist/overlays/relationship-details/layout.js +314 -0
  46. package/dist/overlays/relationships-browser/RelationshipsBrowser.d.ts +3 -1
  47. package/dist/overlays/relationships-browser/RelationshipsBrowser.js +2 -2
  48. package/dist/overlays/relationships-browser/_types.d.ts +2 -0
  49. package/dist/overlays/relationships-browser/actor.d.ts +130 -14
  50. package/dist/overlays/relationships-browser/actor.js +30 -16
  51. package/dist/overlays/relationships-browser/custom/ElementActions.js +5 -3
  52. package/dist/overlays/relationships-browser/custom/edgeTypes.d.ts +3 -1
  53. package/dist/overlays/relationships-browser/custom/edgeTypes.js +1 -1
  54. package/dist/overlays/relationships-browser/hooks.d.ts +3 -1
  55. package/dist/overlays/relationships-browser/hooks.js +1 -1
  56. package/dist/overlays/relationships-browser/{-useRelationshipsView.d.ts → layout.d.ts} +2 -0
  57. package/dist/overlays/relationships-browser/{-useRelationshipsView.js → layout.js} +19 -2
  58. package/dist/overlays/relationships-browser/useViewToNodesEdges.d.ts +1 -1
  59. package/dist/overlays/relationships-browser/useViewToNodesEdges.js +3 -1
  60. package/dist/packages/core/src/compute-view/relationships-view/compute.js +1 -1
  61. package/dist/packages/core/src/model/connection/model/ConnectionModel.js +121 -0
  62. package/dist/packages/core/src/model/connection/model/find.js +39 -0
  63. package/dist/packages/core/src/model/connection/ops.js +7 -0
  64. package/dist/packages/core/src/utils/const.js +3 -0
  65. package/dist/packages/core/src/utils/fqn.js +9 -1
  66. package/dist/packages/core/src/utils/iterable/find.js +21 -0
  67. package/dist/packages/core/src/utils/iterable/some.js +21 -0
  68. package/dist/packages/core/src/utils/set.js +48 -0
  69. package/dist/packages/core/src/utils/string-hash.js +13 -0
  70. package/dist/state/diagram-machine.d.ts +1552 -703
  71. package/dist/state/diagram-machine.js +39 -3
  72. package/dist/state/syncManualLayoutActor.d.ts +1 -1
  73. package/dist/styles.css.js +1 -1
  74. package/package.json +11 -11
  75. package/src/LikeC4Diagram.props.ts +7 -3
  76. package/src/LikeC4Diagram.tsx +1 -1
  77. package/src/StaticLikeC4Diagram.tsx +1 -1
  78. package/src/base/BaseXYFlow.tsx +2 -1
  79. package/src/base/primitives/compound/CompoundNodeContainer.tsx +0 -2
  80. package/src/base/primitives/element/ElementActionButtons.tsx +6 -7
  81. package/src/base/primitives/element/ElementNodeContainer.css.ts +4 -4
  82. package/src/base/updateEdges.ts +8 -8
  83. package/src/base/updateNodes.ts +11 -11
  84. package/src/bundle/LikeC4View.props.ts +7 -7
  85. package/src/bundle/LikeC4View.tsx +2 -2
  86. package/src/hooks/useCurrentViewId.ts +4 -2
  87. package/src/hooks/useDiagram.ts +18 -9
  88. package/src/likec4diagram/ui/notation/NotationPanel.css.ts +2 -2
  89. package/src/likec4diagram/ui/notation/NotationPanel.tsx +8 -6
  90. package/src/overlays/Overlays.tsx +3 -1
  91. package/src/overlays/overlay/Overlay.css.ts +67 -17
  92. package/src/overlays/overlay/Overlay.tsx +26 -21
  93. package/src/overlays/overlaysActor.ts +1 -1
  94. package/src/overlays/relationship-details/RelationshipDetails.tsx +131 -68
  95. package/src/overlays/relationship-details/SelectEdge.tsx +19 -12
  96. package/src/overlays/relationship-details/_types.ts +3 -1
  97. package/src/overlays/relationship-details/actor.ts +217 -68
  98. package/src/overlays/relationship-details/compute.ts +186 -0
  99. package/src/overlays/relationship-details/custom/ElementActions.tsx +27 -18
  100. package/src/overlays/relationship-details/custom/edgeTypes.tsx +1 -1
  101. package/src/overlays/relationship-details/hooks.ts +10 -8
  102. package/src/overlays/relationship-details/layout-to-xyflow.ts +195 -0
  103. package/src/overlays/relationship-details/layout.ts +470 -0
  104. package/src/overlays/relationships-browser/RelationshipsBrowser.tsx +2 -2
  105. package/src/overlays/relationships-browser/_types.ts +2 -0
  106. package/src/overlays/relationships-browser/actor.ts +39 -14
  107. package/src/overlays/relationships-browser/custom/ElementActions.tsx +5 -3
  108. package/src/overlays/relationships-browser/custom/edgeTypes.tsx +1 -1
  109. package/src/overlays/relationships-browser/hooks.ts +2 -2
  110. package/src/overlays/relationships-browser/{-useRelationshipsView.ts → layout.ts} +23 -13
  111. package/src/overlays/relationships-browser/useViewToNodesEdges.ts +3 -1
  112. package/src/state/diagram-machine.ts +51 -6
  113. package/dist/overlays/relationship-details/useLayoutedDetails.d.ts +0 -19
  114. package/dist/overlays/relationship-details/useLayoutedDetails.js +0 -267
  115. package/src/overlays/relationship-details/useLayoutedDetails.ts +0 -376
@@ -1,10 +1,26 @@
1
1
  import { invariant } from '@likec4/core';
2
- import { applyEdgeChanges, applyNodeChanges } from '@xyflow/react';
3
- import { prop } from 'remeda';
4
- import { setup, assign, cancel, raise, enqueueActions } from 'xstate';
2
+ import { getViewportForBounds, applyEdgeChanges, applyNodeChanges } from '@xyflow/react';
3
+ import { shallowEqual } from 'fast-equals';
4
+ import { prop, isString } from 'remeda';
5
+ import { setup, assign, assertEvent, cancel, raise, enqueueActions } from 'xstate';
5
6
  import { MinZoom } from '../../base/const.js';
7
+ import { updateEdges } from '../../base/updateEdges.js';
8
+ import { updateNodes } from '../../base/updateNodes.js';
9
+ import { layoutResultToXYFlow } from './layout-to-xyflow.js';
6
10
  import { Base } from '../../base/types.js';
7
11
 
12
+ function inputToSubject(input) {
13
+ if ("edgeId" in input) {
14
+ invariant(isString(input.edgeId), "edgeId is required");
15
+ return {
16
+ edgeId: input.edgeId
17
+ };
18
+ }
19
+ return {
20
+ source: input.source,
21
+ target: input.target
22
+ };
23
+ }
8
24
  const relationshipDetailsLogic = setup({
9
25
  types: {
10
26
  context: {},
@@ -13,16 +29,18 @@ const relationshipDetailsLogic = setup({
13
29
  },
14
30
  actions: {
15
31
  "xyflow:fitDiagram": ({ context }, params) => {
16
- const {
17
- duration = 450,
18
- bounds
19
- } = params ?? {};
20
- const { xyflow } = context;
32
+ let { duration, bounds } = params ?? {};
33
+ duration ??= 450;
34
+ const { xyflow, xystore } = context;
21
35
  invariant(xyflow, "xyflow is not initialized");
36
+ invariant(xystore, "xystore is not initialized");
37
+ bounds ??= context.bounds;
38
+ const maxZoom = Math.max(xyflow.getZoom(), 1);
22
39
  if (bounds) {
23
- xyflow.fitBounds(bounds, duration > 0 ? { duration } : void 0);
40
+ const { width, height } = xystore.getState();
41
+ const viewport = getViewportForBounds(bounds, width, height, MinZoom, maxZoom, 0.1);
42
+ xyflow.setViewport(viewport, duration > 0 ? { duration } : void 0);
24
43
  } else {
25
- const maxZoom = Math.max(xyflow.getZoom(), 1);
26
44
  xyflow.fitView({
27
45
  minZoom: MinZoom,
28
46
  maxZoom,
@@ -30,46 +48,117 @@ const relationshipDetailsLogic = setup({
30
48
  ...duration > 0 && { duration }
31
49
  });
32
50
  }
33
- }
51
+ },
52
+ "xyflow:updateNodeInternals": ({ context }) => {
53
+ invariant(context.xystore, "xystore is not initialized");
54
+ const { domNode, updateNodeInternals } = context.xystore.getState();
55
+ const nodeIds = new Set(context.xyedges.flatMap((e) => [e.source, e.target]));
56
+ if (nodeIds.size === 0 || !domNode) {
57
+ return;
58
+ }
59
+ const updates = /* @__PURE__ */ new Map();
60
+ for (const updateId of nodeIds) {
61
+ const nodeElement = domNode.querySelector(`.react-flow__node[data-id="${updateId}"]`);
62
+ if (nodeElement) {
63
+ updates.set(updateId, { id: updateId, nodeElement, force: true });
64
+ }
65
+ }
66
+ updateNodeInternals(updates, { triggerFitView: false });
67
+ },
68
+ "updateXYFlow": assign(({ context, event }) => {
69
+ assertEvent(event, "xyflow.init");
70
+ let initialized = context.initialized;
71
+ if (!initialized.xyflow) {
72
+ initialized = {
73
+ ...initialized,
74
+ xyflow: true
75
+ };
76
+ }
77
+ return {
78
+ initialized,
79
+ xyflow: event.instance,
80
+ xystore: event.store
81
+ };
82
+ }),
83
+ "updateLayoutData": assign(({ context, event }) => {
84
+ assertEvent(event, "update.layoutData");
85
+ const { xynodes, xyedges, bounds } = layoutResultToXYFlow(event.data);
86
+ let initialized = context.initialized;
87
+ if (!initialized.xydata) {
88
+ initialized = {
89
+ ...initialized,
90
+ xydata: true
91
+ };
92
+ }
93
+ return {
94
+ initialized,
95
+ xynodes: updateNodes(context.xynodes, xynodes),
96
+ xyedges: updateEdges(context.xyedges, xyedges),
97
+ bounds: shallowEqual(context.bounds, bounds) ? context.bounds : bounds
98
+ };
99
+ })
34
100
  },
35
101
  guards: {
102
+ "isReady": ({ context }) => context.initialized.xydata && context.initialized.xyflow,
36
103
  "enable: navigate.to": () => true
37
104
  }
38
105
  }).createMachine({
39
- initial: "opening",
106
+ initial: "initializing",
40
107
  context: ({ input }) => ({
41
- ...input,
42
- initialized: false,
108
+ subject: inputToSubject(input),
109
+ viewId: input.viewId,
110
+ bounds: {
111
+ x: 0,
112
+ y: 0,
113
+ width: 200,
114
+ height: 200
115
+ },
116
+ initialized: {
117
+ xydata: false,
118
+ xyflow: false
119
+ },
43
120
  xyflow: null,
121
+ xystore: null,
44
122
  xynodes: [],
45
123
  xyedges: []
46
124
  }),
47
125
  states: {
48
- "opening": {
126
+ "initializing": {
49
127
  on: {
50
128
  "xyflow.init": {
51
- actions: assign({
52
- initialized: true,
53
- xyflow: ({ event }) => event.instance
54
- }),
55
- target: "active"
129
+ actions: "updateXYFlow",
130
+ target: "isReady"
131
+ },
132
+ "update.layoutData": {
133
+ actions: "updateLayoutData",
134
+ target: "isReady"
56
135
  },
57
136
  "close": {
58
137
  target: "closed"
59
138
  }
60
139
  }
61
140
  },
62
- "active": {
63
- entry: {
64
- type: "xyflow:fitDiagram",
65
- params: { duration: 0 }
66
- },
141
+ "isReady": {
142
+ always: [{
143
+ guard: "isReady",
144
+ actions: [
145
+ { type: "xyflow:fitDiagram", params: { duration: 0 } },
146
+ raise({ type: "xyflow.updateNodeInternals" }, { delay: 50 })
147
+ ],
148
+ target: "ready"
149
+ }, {
150
+ target: "initializing"
151
+ }]
152
+ },
153
+ "ready": {
67
154
  on: {
68
155
  "xyflow.edgeMouseEnter": {
69
156
  actions: [
70
157
  assign({
71
158
  xyedges: ({ context, event }) => {
72
- const hasDimmed = context.xyedges.some((edge) => edge.data.dimmed === true);
159
+ const hasDimmed = context.xyedges.some(
160
+ (edge) => edge.data.dimmed === true || edge.data.dimmed === "immediate"
161
+ );
73
162
  return context.xyedges.map((edge) => {
74
163
  if (edge.id === event.edge.id) {
75
164
  return Base.setData(edge, {
@@ -77,7 +166,7 @@ const relationshipDetailsLogic = setup({
77
166
  dimmed: false
78
167
  });
79
168
  }
80
- return hasDimmed && !edge.data.dimmed && !edge.selected ? Base.setDimmed(edge, "immediate") : edge;
169
+ return hasDimmed && !edge.selected ? Base.setDimmed(edge, "immediate") : edge;
81
170
  });
82
171
  }
83
172
  }),
@@ -125,21 +214,58 @@ const relationshipDetailsLogic = setup({
125
214
  }
126
215
  })
127
216
  },
128
- "navigate.to": {
217
+ "update.layoutData": {
129
218
  actions: [
130
- assign({
131
- edgeId: ({ event }) => event.edgeId
132
- }),
133
- raise({ type: "fitDiagram" }, { delay: 50 })
219
+ "updateLayoutData",
220
+ cancel("fitDiagram"),
221
+ {
222
+ type: "xyflow:fitDiagram",
223
+ params: {
224
+ duration: 0
225
+ }
226
+ },
227
+ raise({ type: "xyflow.updateNodeInternals" }, { delay: 75 })
134
228
  ]
135
229
  },
230
+ "xyflow.init": {
231
+ actions: "updateXYFlow"
232
+ },
233
+ "xyflow.applyNodeChanges": {
234
+ actions: assign({
235
+ xynodes: ({ context, event }) => {
236
+ return applyNodeChanges(event.changes, context.xynodes);
237
+ }
238
+ })
239
+ },
240
+ "xyflow.applyEdgeChanges": {
241
+ actions: assign({
242
+ xyedges: ({ context, event }) => {
243
+ return applyEdgeChanges(event.changes, context.xyedges);
244
+ }
245
+ })
246
+ },
247
+ "xyflow.paneDblClick": {
248
+ actions: "xyflow:fitDiagram"
249
+ },
250
+ "navigate.to": {
251
+ actions: assign({
252
+ subject: ({ event }) => inputToSubject(event.params),
253
+ viewId: ({ context, event }) => event.params.viewId ?? context.viewId
254
+ })
255
+ },
136
256
  "close": {
137
257
  target: "closed"
138
258
  }
139
259
  },
140
260
  exit: assign({
141
- initialized: false,
142
- xyflow: null
261
+ xyedges: [],
262
+ xynodes: [],
263
+ initialized: {
264
+ xydata: false,
265
+ xyflow: false
266
+ },
267
+ xyflow: null,
268
+ xystore: null
143
269
  })
144
270
  },
145
271
  "closed": {
@@ -159,25 +285,8 @@ const relationshipDetailsLogic = setup({
159
285
  raise({ type: "fitDiagram" }, { id: "fitDiagram", delay: 200 })
160
286
  ]
161
287
  },
162
- "update.xydata": {
163
- actions: assign({
164
- xynodes: ({ event }) => event.xynodes,
165
- xyedges: ({ event }) => event.xyedges
166
- })
167
- },
168
- "xyflow.applyNodeChanges": {
169
- actions: assign({
170
- xynodes: ({ context, event }) => {
171
- return applyNodeChanges(event.changes, context.xynodes);
172
- }
173
- })
174
- },
175
- "xyflow.applyEdgeChanges": {
176
- actions: assign({
177
- xyedges: ({ context, event }) => {
178
- return applyEdgeChanges(event.changes, context.xyedges);
179
- }
180
- })
288
+ "xyflow.updateNodeInternals": {
289
+ actions: "xyflow:updateNodeInternals"
181
290
  }
182
291
  }
183
292
  });
@@ -0,0 +1,12 @@
1
+ import { EdgeId, NonEmptyReadonlyArray } from '@likec4/core';
2
+ import { AnyAux, ElementModel, LikeC4ViewModel, RelationshipModel } from '@likec4/core/model';
3
+ export interface RelationshipDetailsViewData {
4
+ sources: ReadonlySet<ElementModel>;
5
+ relationships: ReadonlySet<RelationshipModel>;
6
+ targets: ReadonlySet<ElementModel>;
7
+ }
8
+ export declare function computeEdgeDetailsViewData(edges: NonEmptyReadonlyArray<EdgeId>, view: LikeC4ViewModel<AnyAux>): RelationshipDetailsViewData;
9
+ export declare function computeRelationshipDetailsViewData({ source, target, }: {
10
+ source: ElementModel;
11
+ target: ElementModel;
12
+ }): RelationshipDetailsViewData;
@@ -0,0 +1,139 @@
1
+ import { hasAtLeast, invariant, isAncestor, sortParentsFirst } from '@likec4/core';
2
+ import { treeFromElements } from '../../packages/core/src/compute-view/relationships-view/utils.js';
3
+ import { findConnection } from '../../packages/core/src/model/connection/model/find.js';
4
+
5
+ const finalize = (elements, explicits) => {
6
+ if (elements.size > 2 && explicits.size !== elements.size) {
7
+ return new Set(sortParentsFirst([
8
+ ...treeFromElements(elements).flatten(),
9
+ ...explicits
10
+ ]));
11
+ }
12
+ if (elements.size > 1) {
13
+ return new Set(sortParentsFirst([...elements]));
14
+ }
15
+ return elements;
16
+ };
17
+ function computeEdgeDetailsViewData(edges, view) {
18
+ const sources = /* @__PURE__ */ new Set();
19
+ const relationships = /* @__PURE__ */ new Set();
20
+ const targets = /* @__PURE__ */ new Set();
21
+ const explicit = {
22
+ sources: /* @__PURE__ */ new Set(),
23
+ targets: /* @__PURE__ */ new Set()
24
+ };
25
+ const addExplicit = (el, type) => {
26
+ if (type === "source") {
27
+ sources.add(el);
28
+ explicit.sources.add(el);
29
+ } else {
30
+ targets.add(el);
31
+ explicit.targets.add(el);
32
+ }
33
+ };
34
+ for (const edgeId of edges) {
35
+ const edge = view.findEdge(edgeId);
36
+ const _relationships = edge ? [...edge.relationships("model")] : [];
37
+ if (!edge || !hasAtLeast(_relationships, 1) || !edge.source.hasElement() || !edge.target.hasElement()) {
38
+ continue;
39
+ }
40
+ const source = edge.source.element;
41
+ const target = edge.target.element;
42
+ addExplicit(source, "source");
43
+ addExplicit(target, "target");
44
+ for (const relationship of _relationships) {
45
+ relationships.add(relationship);
46
+ if (relationship.source !== source) {
47
+ addExplicit(relationship.source, "source");
48
+ for (const parent of relationship.source.ancestors()) {
49
+ if (parent === source) {
50
+ break;
51
+ }
52
+ sources.add(parent);
53
+ }
54
+ }
55
+ if (relationship.target !== target) {
56
+ addExplicit(relationship.target, "target");
57
+ for (const parent of relationship.target.ancestors()) {
58
+ if (parent === target) {
59
+ break;
60
+ }
61
+ targets.add(parent);
62
+ }
63
+ }
64
+ }
65
+ }
66
+ return {
67
+ sources: finalize(sources, explicit.sources),
68
+ targets: finalize(targets, explicit.targets),
69
+ relationships
70
+ };
71
+ }
72
+ function computeRelationshipDetailsViewData({
73
+ // relationships: _relationships,
74
+ source,
75
+ target
76
+ }) {
77
+ const sources = /* @__PURE__ */ new Set();
78
+ const relationships = /* @__PURE__ */ new Set();
79
+ const targets = /* @__PURE__ */ new Set();
80
+ const explicit = {
81
+ sources: /* @__PURE__ */ new Set(),
82
+ targets: /* @__PURE__ */ new Set()
83
+ };
84
+ const addExplicit = (el, type) => {
85
+ if (type === "source") {
86
+ sources.add(el);
87
+ explicit.sources.add(el);
88
+ } else {
89
+ targets.add(el);
90
+ explicit.targets.add(el);
91
+ }
92
+ };
93
+ if (source) {
94
+ addExplicit(source, "source");
95
+ }
96
+ if (target) {
97
+ addExplicit(target, "target");
98
+ }
99
+ const [connection] = findConnection(source, target, "directed");
100
+ if (!connection) {
101
+ return {
102
+ sources,
103
+ targets,
104
+ relationships
105
+ };
106
+ }
107
+ for (const relationship of connection.relations) {
108
+ const relationSource = relationship.source;
109
+ const relationTarget = relationship.target;
110
+ addExplicit(relationSource, "source");
111
+ addExplicit(relationTarget, "target");
112
+ relationships.add(relationship);
113
+ if (source !== relationSource) {
114
+ invariant(isAncestor(source, relationSource), `${source.id} is not an ancestor of ${relationSource.id}`);
115
+ for (const parent of relationSource.ancestors()) {
116
+ if (parent === source) {
117
+ break;
118
+ }
119
+ sources.add(parent);
120
+ }
121
+ }
122
+ if (target !== relationTarget) {
123
+ invariant(isAncestor(target, relationTarget), `${target.id} is not an ancestor of ${relationTarget.id}`);
124
+ for (const parent of relationTarget.ancestors()) {
125
+ if (parent === target) {
126
+ break;
127
+ }
128
+ targets.add(parent);
129
+ }
130
+ }
131
+ }
132
+ return {
133
+ sources: finalize(sources, explicit.sources),
134
+ targets: finalize(targets, explicit.targets),
135
+ relationships
136
+ };
137
+ }
138
+
139
+ export { computeEdgeDetailsViewData, computeRelationshipDetailsViewData };
@@ -1,15 +1,17 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
- import { IconZoomScan } from '@tabler/icons-react';
3
- import { useEnabledFeature } from '../../../context/DiagramFeatures.js';
2
+ import { IconZoomScan, IconTransform, IconFileSymlink } from '@tabler/icons-react';
3
+ import { useEnabledFeatures } from '../../../context/DiagramFeatures.js';
4
+ import { useCurrentViewId } from '../../../hooks/useCurrentViewId.js';
4
5
  import { useDiagram } from '../../../hooks/useDiagram.js';
5
6
  import { ElementActionButtons } from '../../../base/primitives/element/ElementActionButtons.js';
6
7
 
7
8
  const ElementActions = (props) => {
8
- const { enableNavigateTo } = useEnabledFeature("NavigateTo");
9
+ const { enableNavigateTo, enableVscode } = useEnabledFeatures();
9
10
  const diagram = useDiagram();
11
+ const currentViewId = useCurrentViewId();
10
12
  const buttons = [];
11
- const { navigateTo } = props.data;
12
- if (navigateTo && enableNavigateTo) {
13
+ const { navigateTo, fqn } = props.data;
14
+ if (navigateTo && enableNavigateTo && currentViewId !== navigateTo) {
13
15
  buttons.push({
14
16
  key: "navigate",
15
17
  icon: /* @__PURE__ */ jsx(IconZoomScan, {}),
@@ -19,6 +21,26 @@ const ElementActions = (props) => {
19
21
  }
20
22
  });
21
23
  }
24
+ if (fqn) {
25
+ buttons.push({
26
+ key: "relationships",
27
+ icon: /* @__PURE__ */ jsx(IconTransform, {}),
28
+ onClick: (e) => {
29
+ e.stopPropagation();
30
+ diagram.openRelationshipsBrowser(fqn);
31
+ }
32
+ });
33
+ }
34
+ if (fqn && enableVscode) {
35
+ buttons.push({
36
+ key: "goToSource",
37
+ icon: /* @__PURE__ */ jsx(IconFileSymlink, {}),
38
+ onClick: (e) => {
39
+ e.stopPropagation();
40
+ diagram.openSource({ element: fqn });
41
+ }
42
+ });
43
+ }
22
44
  return /* @__PURE__ */ jsx(
23
45
  ElementActionButtons,
24
46
  {
@@ -30,7 +30,7 @@ const RelationshipEdge = customEdge((props) => {
30
30
  translate: "translate(-50%, 0)"
31
31
  },
32
32
  style: {
33
- maxWidth: Math.abs(props.targetX - props.sourceX - 70)
33
+ maxWidth: Math.abs(props.targetX - props.sourceX - 100)
34
34
  },
35
35
  children: /* @__PURE__ */ jsx(EdgeLabel, { edgeProps: props, children: enableNavigateTo && navigateTo && /* @__PURE__ */ jsx(
36
36
  EdgeActionButton,
@@ -12,6 +12,7 @@ export declare const ElementNode: NamedExoticComponent<NodeProps<{
12
12
  color: Color;
13
13
  shape: "rectangle" | "person" | "browser" | "mobile" | "cylinder" | "storage" | "queue";
14
14
  navigateTo: ViewId<string> | null | undefined;
15
+ column: RelationshipDetailsTypes.Column;
15
16
  fqn: Fqn;
16
17
  icon: IconUrl;
17
18
  ports: RelationshipDetailsTypes.Ports;
@@ -22,7 +23,7 @@ export declare const CompoundNode: NamedExoticComponent<NodeProps<{
22
23
  style: ElementStyle;
23
24
  title: string;
24
25
  color: Color;
25
- shape: "rectangle" | "person" | "browser" | "mobile" | "cylinder" | "storage" | "queue";
26
+ column: RelationshipDetailsTypes.Column;
26
27
  fqn: Fqn;
27
28
  depth: number;
28
29
  icon?: IconUrl;