@likec4/diagram 1.27.1 → 1.27.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.
- package/bundle/index.js +2152 -6757
- package/dist/LikeC4Diagram.js +1 -1
- package/dist/LikeC4Diagram.props.d.ts +5 -3
- package/dist/StaticLikeC4Diagram.js +1 -1
- package/dist/_virtual/set.js +5 -0
- package/dist/_virtual/set2.js +3 -0
- package/dist/base/BaseXYFlow.d.ts +2 -1
- package/dist/base/primitives/compound/CompoundNodeContainer.d.ts +1 -1
- package/dist/base/primitives/compound/CompoundNodeContainer.js +0 -1
- package/dist/base/primitives/edge/EdgeLabelContainer.js +3 -2
- package/dist/base/primitives/element/ElementActionButtons.js +8 -11
- package/dist/base/primitives/element/ElementNodeContainer.css.js +4 -4
- package/dist/base/updateEdges.js +1 -9
- package/dist/base/updateNodes.js +1 -12
- package/dist/bundle/LikeC4View.js +2 -2
- package/dist/bundle/LikeC4View.props.d.ts +7 -7
- package/dist/hooks/useCurrentViewId.js +8 -2
- package/dist/hooks/useDiagram.d.ts +1522 -685
- package/dist/hooks/useDiagram.js +21 -24
- package/dist/likec4diagram/DiagramXYFlow.js +6 -6
- package/dist/likec4diagram/ui/notation/NotationPanel.css.js +3 -3
- package/dist/likec4diagram/ui/notation/NotationPanel.js +10 -4
- package/dist/node_modules/.pnpm/mnemonist@0.40.3_patch_hash_5c12abb43fcff6fd81e53021b89075d53249640d165a2556cc1e58d71201a13a/node_modules/mnemonist/set.js +369 -0
- package/dist/overlays/Overlays.js +3 -1
- package/dist/overlays/element-details/actor.d.ts +2769 -173
- package/dist/overlays/overlay/Overlay.css.d.ts +26 -2
- package/dist/overlays/overlay/Overlay.css.js +66 -18
- package/dist/overlays/overlay/Overlay.d.ts +7 -6
- package/dist/overlays/overlay/Overlay.js +12 -10
- package/dist/overlays/overlaysActor.js +2 -2
- package/dist/overlays/relationship-details/RelationshipDetails.js +113 -70
- package/dist/overlays/relationship-details/SelectEdge.d.ts +1 -1
- package/dist/overlays/relationship-details/SelectEdge.js +15 -9
- package/dist/overlays/relationship-details/_types.d.ts +4 -1
- package/dist/overlays/relationship-details/actor.d.ts +425 -128
- package/dist/overlays/relationship-details/actor.js +157 -53
- package/dist/overlays/relationship-details/compute.d.ts +12 -0
- package/dist/overlays/relationship-details/compute.js +139 -0
- package/dist/overlays/relationship-details/custom/ElementActions.js +27 -5
- package/dist/overlays/relationship-details/custom/edgeTypes.js +1 -1
- package/dist/overlays/relationship-details/custom/nodeTypes.d.ts +2 -1
- package/dist/overlays/relationship-details/hooks.d.ts +336 -6
- package/dist/overlays/relationship-details/hooks.js +11 -12
- package/dist/overlays/relationship-details/layout-to-xyflow.d.ts +12 -0
- package/dist/overlays/relationship-details/layout-to-xyflow.js +145 -0
- package/dist/overlays/relationship-details/layout.d.ts +22 -0
- package/dist/overlays/relationship-details/layout.js +314 -0
- package/dist/overlays/relationships-browser/RelationshipsBrowser.d.ts +3 -1
- package/dist/overlays/relationships-browser/RelationshipsBrowser.js +2 -2
- package/dist/overlays/relationships-browser/_types.d.ts +2 -0
- package/dist/overlays/relationships-browser/actor.d.ts +130 -14
- package/dist/overlays/relationships-browser/actor.js +30 -16
- package/dist/overlays/relationships-browser/custom/ElementActions.js +5 -3
- package/dist/overlays/relationships-browser/custom/edgeTypes.d.ts +3 -1
- package/dist/overlays/relationships-browser/custom/edgeTypes.js +1 -1
- package/dist/overlays/relationships-browser/hooks.d.ts +3 -1
- package/dist/overlays/relationships-browser/hooks.js +6 -9
- package/dist/overlays/relationships-browser/{-useRelationshipsView.d.ts → layout.d.ts} +3 -1
- package/dist/overlays/relationships-browser/{-useRelationshipsView.js → layout.js} +21 -8
- package/dist/overlays/relationships-browser/useViewToNodesEdges.d.ts +1 -1
- package/dist/overlays/relationships-browser/useViewToNodesEdges.js +3 -1
- package/dist/packages/core/src/compute-view/relationships-view/compute.js +1 -1
- package/dist/packages/core/src/model/connection/model/ConnectionModel.js +121 -0
- package/dist/packages/core/src/model/connection/model/find.js +39 -0
- package/dist/packages/core/src/model/connection/ops.js +7 -0
- package/dist/packages/core/src/utils/const.js +3 -0
- package/dist/packages/core/src/utils/fqn.js +9 -1
- package/dist/packages/core/src/utils/iterable/find.js +21 -0
- package/dist/packages/core/src/utils/iterable/some.js +21 -0
- package/dist/packages/core/src/utils/set.js +48 -0
- package/dist/packages/core/src/utils/string-hash.js +13 -0
- package/dist/state/diagram-machine.d.ts +1552 -703
- package/dist/state/diagram-machine.js +39 -3
- package/dist/state/syncManualLayoutActor.d.ts +1 -1
- package/dist/styles.css.js +1 -1
- package/dist/utils/index.d.ts +0 -1
- package/package.json +11 -11
- package/src/LikeC4Diagram.props.ts +7 -3
- package/src/LikeC4Diagram.tsx +1 -1
- package/src/StaticLikeC4Diagram.tsx +1 -1
- package/src/base/BaseXYFlow.tsx +2 -1
- package/src/base/primitives/compound/CompoundNodeContainer.tsx +0 -2
- package/src/base/primitives/edge/EdgeLabelContainer.tsx +7 -5
- package/src/base/primitives/element/ElementActionButtons.tsx +10 -11
- package/src/base/primitives/element/ElementNodeContainer.css.ts +4 -4
- package/src/base/updateEdges.ts +8 -8
- package/src/base/updateNodes.ts +11 -11
- package/src/bundle/LikeC4View.props.ts +7 -7
- package/src/bundle/LikeC4View.tsx +2 -2
- package/src/hooks/useCurrentViewId.ts +4 -2
- package/src/hooks/useDiagram.ts +22 -24
- package/src/likec4diagram/DiagramXYFlow.tsx +6 -6
- package/src/likec4diagram/ui/notation/NotationPanel.css.ts +2 -2
- package/src/likec4diagram/ui/notation/NotationPanel.tsx +8 -6
- package/src/overlays/Overlays.tsx +4 -2
- package/src/overlays/overlay/Overlay.css.ts +67 -17
- package/src/overlays/overlay/Overlay.tsx +27 -24
- package/src/overlays/overlaysActor.ts +1 -1
- package/src/overlays/relationship-details/RelationshipDetails.tsx +131 -68
- package/src/overlays/relationship-details/SelectEdge.tsx +19 -12
- package/src/overlays/relationship-details/_types.ts +3 -1
- package/src/overlays/relationship-details/actor.ts +212 -68
- package/src/overlays/relationship-details/compute.ts +186 -0
- package/src/overlays/relationship-details/custom/ElementActions.tsx +27 -18
- package/src/overlays/relationship-details/custom/edgeTypes.tsx +1 -1
- package/src/overlays/relationship-details/hooks.ts +12 -13
- package/src/overlays/relationship-details/layout-to-xyflow.ts +172 -0
- package/src/overlays/relationship-details/layout.ts +470 -0
- package/src/overlays/relationships-browser/RelationshipsBrowser.tsx +2 -2
- package/src/overlays/relationships-browser/_types.ts +2 -0
- package/src/overlays/relationships-browser/actor.ts +39 -14
- package/src/overlays/relationships-browser/custom/ElementActions.tsx +5 -3
- package/src/overlays/relationships-browser/custom/edgeTypes.tsx +1 -1
- package/src/overlays/relationships-browser/hooks.ts +7 -10
- package/src/overlays/relationships-browser/{-useRelationshipsView.ts → layout.ts} +26 -33
- package/src/overlays/relationships-browser/useViewToNodesEdges.ts +3 -1
- package/src/state/diagram-machine.ts +51 -6
- package/src/utils/index.ts +0 -1
- package/dist/overlays/relationship-details/useLayoutedDetails.d.ts +0 -19
- package/dist/overlays/relationship-details/useLayoutedDetails.js +0 -267
- package/dist/utils/types.d.ts +0 -8
- package/src/overlays/relationship-details/useLayoutedDetails.ts +0 -376
- package/src/utils/types.ts +0 -6
|
@@ -6,6 +6,7 @@ import { setup, assign, fromPromise, assertEvent, enqueueActions, cancel, raise
|
|
|
6
6
|
import { MinZoom, ZIndexes } from '../../base/const.js';
|
|
7
7
|
import { updateEdges } from '../../base/updateEdges.js';
|
|
8
8
|
import { updateNodes } from '../../base/updateNodes.js';
|
|
9
|
+
import { typedSystem } from '../../state/utils.js';
|
|
9
10
|
import { ViewPadding } from './const.js';
|
|
10
11
|
import { viewToNodesEdge } from './useViewToNodesEdges.js';
|
|
11
12
|
import { Base } from '../../base/types.js';
|
|
@@ -129,6 +130,7 @@ const relationshipsBrowserLogic = setup({
|
|
|
129
130
|
layouter
|
|
130
131
|
},
|
|
131
132
|
guards: {
|
|
133
|
+
hasViewId: ({ context }) => context.viewId !== null,
|
|
132
134
|
isReady: ({ context }) => context.xyflow !== null && context.xystore !== null && context.layouted !== null,
|
|
133
135
|
anotherSubject: ({ context, event }) => {
|
|
134
136
|
if (event.type === "update.view") {
|
|
@@ -271,10 +273,24 @@ const relationshipsBrowserLogic = setup({
|
|
|
271
273
|
}
|
|
272
274
|
})
|
|
273
275
|
},
|
|
276
|
+
"xyflow.edgeClick": {
|
|
277
|
+
guard: "hasViewId",
|
|
278
|
+
actions: enqueueActions(({ event, context, system, enqueue }) => {
|
|
279
|
+
if (event.edge.selected || event.edge.data.relations.length > 1) {
|
|
280
|
+
enqueue.sendTo(typedSystem(system).overlaysActorRef, {
|
|
281
|
+
type: "open.relationshipDetails",
|
|
282
|
+
viewId: context.viewId,
|
|
283
|
+
source: event.edge.data.sourceFqn,
|
|
284
|
+
target: event.edge.data.targetFqn
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
})
|
|
288
|
+
},
|
|
274
289
|
"navigate.to": {
|
|
275
290
|
actions: [
|
|
276
291
|
assign({
|
|
277
292
|
subject: ({ event }) => event.subject,
|
|
293
|
+
viewId: ({ event, context }) => event.viewId ?? context.viewId ?? null,
|
|
278
294
|
navigateFromNode: ({ event }) => event.fromNode ?? null
|
|
279
295
|
})
|
|
280
296
|
]
|
|
@@ -321,7 +337,7 @@ const relationshipsBrowserLogic = setup({
|
|
|
321
337
|
actions: [
|
|
322
338
|
assign({
|
|
323
339
|
xyedges: ({ context, event }) => {
|
|
324
|
-
const hasDimmed = context.xyedges.some((edge) => edge.data.dimmed
|
|
340
|
+
const hasDimmed = context.xyedges.some((edge) => edge.data.dimmed !== false || edge.selected);
|
|
325
341
|
return context.xyedges.map((edge) => {
|
|
326
342
|
if (edge.id === event.edge.id) {
|
|
327
343
|
return Base.setData(edge, {
|
|
@@ -329,24 +345,26 @@ const relationshipsBrowserLogic = setup({
|
|
|
329
345
|
dimmed: false
|
|
330
346
|
});
|
|
331
347
|
}
|
|
332
|
-
return hasDimmed && !edge.
|
|
348
|
+
return hasDimmed && !edge.selected ? Base.setDimmed(edge, "immediate") : edge;
|
|
333
349
|
});
|
|
334
350
|
}
|
|
335
351
|
}),
|
|
336
352
|
cancel("undim.edges"),
|
|
337
353
|
cancel("dim.nonhovered.edges"),
|
|
338
|
-
raise({ type: "dim.nonhovered.edges" }, { id: "dim.nonhovered.edges", delay:
|
|
354
|
+
raise({ type: "dim.nonhovered.edges" }, { id: "dim.nonhovered.edges", delay: 200 })
|
|
339
355
|
]
|
|
340
356
|
},
|
|
341
357
|
"xyflow.edgeMouseLeave": {
|
|
342
358
|
actions: [
|
|
343
359
|
assign({
|
|
344
|
-
xyedges: ({ context, event }) =>
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
360
|
+
xyedges: ({ context, event }) => {
|
|
361
|
+
return context.xyedges.map((edge) => {
|
|
362
|
+
if (edge.id === event.edge.id) {
|
|
363
|
+
return Base.setHovered(edge, false);
|
|
364
|
+
}
|
|
365
|
+
return edge;
|
|
366
|
+
});
|
|
367
|
+
}
|
|
350
368
|
}),
|
|
351
369
|
cancel("dim.nonhovered.edges"),
|
|
352
370
|
raise({ type: "undim.edges" }, { id: "undim.edges", delay: 400 })
|
|
@@ -354,18 +372,14 @@ const relationshipsBrowserLogic = setup({
|
|
|
354
372
|
},
|
|
355
373
|
"dim.nonhovered.edges": {
|
|
356
374
|
actions: assign({
|
|
357
|
-
xyedges: ({ context }) => context.xyedges.map(
|
|
375
|
+
xyedges: ({ context }) => context.xyedges.map(
|
|
376
|
+
(edge) => edge.data.hovered ? edge : Base.setDimmed(edge, edge.data.dimmed === "immediate" ? "immediate" : true)
|
|
377
|
+
)
|
|
358
378
|
})
|
|
359
379
|
},
|
|
360
380
|
"undim.edges": {
|
|
361
381
|
actions: assign({
|
|
362
382
|
xyedges: ({ context }) => {
|
|
363
|
-
const hasSelected = context.xyedges.some((edge) => edge.selected === true);
|
|
364
|
-
if (hasSelected) {
|
|
365
|
-
return context.xyedges.map(
|
|
366
|
-
(edge) => Base.setDimmed(edge, edge.selected !== true ? edge.data.dimmed || "immediate" : false)
|
|
367
|
-
);
|
|
368
|
-
}
|
|
369
383
|
return context.xyedges.map(Base.setDimmed(false));
|
|
370
384
|
}
|
|
371
385
|
})
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { IconZoomScan, IconTransform, IconFileSymlink } from '@tabler/icons-react';
|
|
3
|
-
import {
|
|
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 { useRelationshipsBrowser, useRelationshipsBrowserState } from '../hooks.js';
|
|
6
7
|
import { ElementActionButtons } from '../../../base/primitives/element/ElementActionButtons.js';
|
|
7
8
|
|
|
8
9
|
const ElementActions = (props) => {
|
|
9
|
-
const { enableNavigateTo, enableVscode } =
|
|
10
|
+
const { enableNavigateTo, enableVscode } = useEnabledFeatures();
|
|
10
11
|
const diagram = useDiagram();
|
|
12
|
+
const currentViewId = useCurrentViewId();
|
|
11
13
|
const browser = useRelationshipsBrowser();
|
|
12
14
|
const subject = useRelationshipsBrowserState((s) => s.context.subject);
|
|
13
15
|
const buttons = [];
|
|
14
16
|
const { navigateTo, fqn } = props.data;
|
|
15
|
-
if (navigateTo && enableNavigateTo) {
|
|
17
|
+
if (navigateTo && enableNavigateTo && currentViewId !== navigateTo) {
|
|
16
18
|
buttons.push({
|
|
17
19
|
key: "navigate",
|
|
18
20
|
icon: /* @__PURE__ */ jsx(IconZoomScan, {}),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { NamedExoticComponent, CSSProperties, ReactNode } from 'react';
|
|
2
2
|
import { Position } from '@xyflow/system';
|
|
3
|
-
import { NonEmptyArray, RelationId, Color, ViewId, RelationshipLineType } from '@likec4/core';
|
|
3
|
+
import { Fqn, NonEmptyArray, RelationId, Color, ViewId, RelationshipLineType } from '@likec4/core';
|
|
4
4
|
export declare const RelationshipEdge: NamedExoticComponent<{
|
|
5
5
|
id: string;
|
|
6
6
|
selected?: boolean;
|
|
@@ -33,6 +33,8 @@ export declare const RelationshipEdge: NamedExoticComponent<{
|
|
|
33
33
|
active?: boolean;
|
|
34
34
|
dimmed?: import("../../../base").Base.Dimmed;
|
|
35
35
|
} & {
|
|
36
|
+
sourceFqn: Fqn;
|
|
37
|
+
targetFqn: Fqn;
|
|
36
38
|
relations: NonEmptyArray<RelationId>;
|
|
37
39
|
color: Color | undefined;
|
|
38
40
|
label: string | null;
|
|
@@ -61,7 +61,7 @@ const RelationshipEdge = customEdge((props) => {
|
|
|
61
61
|
c: "black",
|
|
62
62
|
label: "This relationship is not included in the current view",
|
|
63
63
|
portalProps: {
|
|
64
|
-
target: `#${browser.rootElementId
|
|
64
|
+
target: `#${browser.rootElementId}`
|
|
65
65
|
},
|
|
66
66
|
openDelay: 800,
|
|
67
67
|
children: label
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Fqn, ViewId, NonEmptyArray, RelationId, Color, RelationshipLineType } from '@likec4/core';
|
|
2
|
-
import { LayoutRelationshipsViewResult } from './-useRelationshipsView';
|
|
3
2
|
import { RelationshipsBrowserActorRef, RelationshipsBrowserSnapshot, XYStoreApi, Events } from './actor';
|
|
3
|
+
import { LayoutRelationshipsViewResult } from './layout';
|
|
4
4
|
import { Context, ReactNode, CSSProperties } from 'react';
|
|
5
5
|
import { GeneralHelpers, ViewportHelperFunctions } from '@xyflow/react';
|
|
6
6
|
import { EdgeMarkerType, HandleType } from '@xyflow/system';
|
|
@@ -45,6 +45,8 @@ export declare function useRelationshipsBrowser(): {
|
|
|
45
45
|
focusable?: boolean;
|
|
46
46
|
type: "relationship";
|
|
47
47
|
data: {
|
|
48
|
+
sourceFqn: Fqn;
|
|
49
|
+
targetFqn: Fqn;
|
|
48
50
|
relations: NonEmptyArray<RelationId>;
|
|
49
51
|
color: Color | undefined;
|
|
50
52
|
label: string | null;
|
|
@@ -2,7 +2,7 @@ import { nonNullable } from '@likec4/core';
|
|
|
2
2
|
import { useCallbackRef } from '@mantine/hooks';
|
|
3
3
|
import { useSelector } from '@xstate/react';
|
|
4
4
|
import { shallowEqual } from 'fast-equals';
|
|
5
|
-
import { createContext,
|
|
5
|
+
import { createContext, useMemo, useContext } from 'react';
|
|
6
6
|
|
|
7
7
|
const RelationshipsBrowserActorContext = createContext(null);
|
|
8
8
|
function useRelationshipsBrowserActor() {
|
|
@@ -14,11 +14,10 @@ function useRelationshipsBrowserState(selector, compare = shallowEqual) {
|
|
|
14
14
|
}
|
|
15
15
|
function useRelationshipsBrowser() {
|
|
16
16
|
const actor = useRelationshipsBrowserActor();
|
|
17
|
-
const [, startTransition] = useTransition();
|
|
18
17
|
return useMemo(() => ({
|
|
19
18
|
actor,
|
|
20
19
|
get rootElementId() {
|
|
21
|
-
return `relationships-browser-${actor.sessionId}`;
|
|
20
|
+
return `relationships-browser-${actor.sessionId.replaceAll(":", "_")}`;
|
|
22
21
|
},
|
|
23
22
|
getState: () => actor.getSnapshot().context,
|
|
24
23
|
send: actor.send,
|
|
@@ -35,12 +34,10 @@ function useRelationshipsBrowser() {
|
|
|
35
34
|
});
|
|
36
35
|
},
|
|
37
36
|
navigateTo: (subject, fromNode) => {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
fromNode
|
|
43
|
-
});
|
|
37
|
+
actor.send({
|
|
38
|
+
type: "navigate.to",
|
|
39
|
+
subject,
|
|
40
|
+
fromNode
|
|
44
41
|
});
|
|
45
42
|
},
|
|
46
43
|
fitDiagram: () => {
|
|
@@ -11,12 +11,14 @@ export type LayoutRelationshipsViewResult = {
|
|
|
11
11
|
};
|
|
12
12
|
export declare namespace LayoutRelationshipsViewResult {
|
|
13
13
|
const Empty: ElementKind;
|
|
14
|
-
type Node = DiagramNode & {
|
|
14
|
+
type Node = Omit<DiagramNode, 'deploymentRef' | 'inEdges' | 'outEdges'> & {
|
|
15
15
|
column: RelationshipsBrowserTypes.Column;
|
|
16
16
|
ports: RelationshipsBrowserTypes.Ports;
|
|
17
17
|
existsInCurrentView: boolean;
|
|
18
18
|
};
|
|
19
19
|
type Edge = DiagramEdge & {
|
|
20
|
+
sourceFqn: Fqn;
|
|
21
|
+
targetFqn: Fqn;
|
|
20
22
|
sourceHandle: string;
|
|
21
23
|
targetHandle: string;
|
|
22
24
|
existsInCurrentView: boolean;
|
|
@@ -13,7 +13,7 @@ const Sizes = {
|
|
|
13
13
|
edgesep: 25
|
|
14
14
|
},
|
|
15
15
|
edgeLabel: {
|
|
16
|
-
width:
|
|
16
|
+
width: 140,
|
|
17
17
|
height: 10,
|
|
18
18
|
minlen: 1,
|
|
19
19
|
weight: 1
|
|
@@ -128,6 +128,8 @@ function layoutRelationshipsView(data, scope) {
|
|
|
128
128
|
toArray(data.incoming),
|
|
129
129
|
map((r) => ({
|
|
130
130
|
id: r.source.id,
|
|
131
|
+
sourceFqn: r.source.id,
|
|
132
|
+
targetFqn: r.target.id,
|
|
131
133
|
source: incomers.byId(r.source.id).graph,
|
|
132
134
|
target: subjects.byId(r.target.id).graph,
|
|
133
135
|
relation: r
|
|
@@ -137,6 +139,8 @@ function layoutRelationshipsView(data, scope) {
|
|
|
137
139
|
toArray(data.outgoing),
|
|
138
140
|
map((r) => ({
|
|
139
141
|
id: r.target.id,
|
|
142
|
+
sourceFqn: r.source.id,
|
|
143
|
+
targetFqn: r.target.id,
|
|
140
144
|
source: subjects.byId(r.source.id).graph,
|
|
141
145
|
target: outgoers.byId(r.target.id).graph,
|
|
142
146
|
relation: r
|
|
@@ -160,6 +164,8 @@ function layoutRelationshipsView(data, scope) {
|
|
|
160
164
|
}, name);
|
|
161
165
|
edges.push({
|
|
162
166
|
name,
|
|
167
|
+
sourceFqn: grouped[0].sourceFqn,
|
|
168
|
+
targetFqn: grouped[0].targetFqn,
|
|
163
169
|
source: source.id,
|
|
164
170
|
sourceHandle: source.id + "_out" + (g.node(source.id).outPorts.length - 1),
|
|
165
171
|
target: target.id,
|
|
@@ -229,7 +235,7 @@ function layoutRelationshipsView(data, scope) {
|
|
|
229
235
|
for (const edge of g.edges()) {
|
|
230
236
|
g.setEdge(edge, {
|
|
231
237
|
...Sizes.edgeLabel,
|
|
232
|
-
width: edgeCount >
|
|
238
|
+
width: edgeCount > 25 ? 800 : 400
|
|
233
239
|
});
|
|
234
240
|
}
|
|
235
241
|
}
|
|
@@ -340,8 +346,6 @@ function layoutRelationshipsView(data, scope) {
|
|
|
340
346
|
width,
|
|
341
347
|
height
|
|
342
348
|
},
|
|
343
|
-
inEdges: [],
|
|
344
|
-
outEdges: [],
|
|
345
349
|
children: [],
|
|
346
350
|
width,
|
|
347
351
|
height,
|
|
@@ -357,7 +361,7 @@ function layoutRelationshipsView(data, scope) {
|
|
|
357
361
|
const children = (g.children(id) ?? []).filter((c) => !c.endsWith(PortSuffix));
|
|
358
362
|
minX = Math.min(minX, position.x);
|
|
359
363
|
minY = Math.min(minY, position.y);
|
|
360
|
-
const navigateTo = element.
|
|
364
|
+
const navigateTo = scope ? ifind(element.scopedViews(), (v) => v.id !== scope.id)?.id ?? null : null;
|
|
361
365
|
const inheritFromNode = scope?.findNodeWithElement(element.id);
|
|
362
366
|
const scopedAncestor = scope && !inheritFromNode ? ifind(element.ancestors(), (a) => !!scope.findNodeWithElement(a.id))?.id : null;
|
|
363
367
|
const inheritFromNodeOrAncestor = inheritFromNode ?? (scopedAncestor && scope?.findNodeWithElement(scopedAncestor));
|
|
@@ -387,8 +391,6 @@ function layoutRelationshipsView(data, scope) {
|
|
|
387
391
|
...element.$element.style
|
|
388
392
|
},
|
|
389
393
|
navigateTo,
|
|
390
|
-
inEdges: [],
|
|
391
|
-
outEdges: [],
|
|
392
394
|
...children.length > 0 && { depth: nodeDepth(id) },
|
|
393
395
|
children,
|
|
394
396
|
width,
|
|
@@ -416,15 +418,26 @@ function layoutRelationshipsView(data, scope) {
|
|
|
416
418
|
if (!ename) {
|
|
417
419
|
return acc;
|
|
418
420
|
}
|
|
419
|
-
const {
|
|
421
|
+
const {
|
|
422
|
+
name,
|
|
423
|
+
source,
|
|
424
|
+
sourceFqn,
|
|
425
|
+
target,
|
|
426
|
+
targetFqn,
|
|
427
|
+
relations,
|
|
428
|
+
sourceHandle,
|
|
429
|
+
targetHandle
|
|
430
|
+
} = find(edges, (e2) => e2.name === ename);
|
|
420
431
|
const onlyRelation = only(relations);
|
|
421
432
|
const label = onlyRelation?.title ?? "untitled";
|
|
422
433
|
const isMultiple = relations.length > 1;
|
|
423
434
|
const navigateTo = only(unique(relations.flatMap((r) => r.navigateTo?.id ? r.navigateTo.id : [])));
|
|
424
435
|
acc.push({
|
|
425
436
|
id: name,
|
|
437
|
+
sourceFqn,
|
|
426
438
|
source,
|
|
427
439
|
sourceHandle,
|
|
440
|
+
targetFqn,
|
|
428
441
|
target,
|
|
429
442
|
targetHandle,
|
|
430
443
|
label: isMultiple ? `${relations.length} relationships` : label,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { LayoutRelationshipsViewResult } from './-useRelationshipsView';
|
|
2
1
|
import { RelationshipsBrowserTypes } from './_types';
|
|
2
|
+
import { LayoutRelationshipsViewResult } from './layout';
|
|
3
3
|
export declare function viewToNodesEdge(view: Pick<LayoutRelationshipsViewResult, 'nodes' | 'edges'>): {
|
|
4
4
|
xynodes: RelationshipsBrowserTypes.Node[];
|
|
5
5
|
xyedges: RelationshipsBrowserTypes.Edge[];
|
|
@@ -2,7 +2,7 @@ import { Queue, ElementKind, nonNullable, DiagramNode, invariant } from '@likec4
|
|
|
2
2
|
import '@react-hookz/web';
|
|
3
3
|
import { hasAtLeast } from 'remeda';
|
|
4
4
|
import { ZIndexes } from '../../base/const.js';
|
|
5
|
-
import { LayoutRelationshipsViewResult } from '
|
|
5
|
+
import { LayoutRelationshipsViewResult } from './layout.js';
|
|
6
6
|
|
|
7
7
|
function viewToNodesEdge(view) {
|
|
8
8
|
const xynodes = [], xyedges = [], nodeLookup = /* @__PURE__ */ new Map();
|
|
@@ -142,6 +142,8 @@ function viewToNodesEdge(view) {
|
|
|
142
142
|
// hidden: !visiblePredicate(edge),
|
|
143
143
|
deletable: false,
|
|
144
144
|
data: {
|
|
145
|
+
sourceFqn: edge.sourceFqn,
|
|
146
|
+
targetFqn: edge.targetFqn,
|
|
145
147
|
relations: edge.relations,
|
|
146
148
|
color: edge.color ?? "gray",
|
|
147
149
|
label: edge.label,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { pipe } from 'remeda';
|
|
2
2
|
import { invariant } from '../../errors/index.js';
|
|
3
|
-
import {
|
|
3
|
+
import { isDescendantOf, sortParentsFirst } from '../../utils/fqn.js';
|
|
4
4
|
import { treeFromElements } from './utils.js';
|
|
5
5
|
import { toSet, toArray } from '../../utils/iterable/to.js';
|
|
6
6
|
import { ifilter } from '../../utils/iterable/filter.js';
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { isNot } from 'remeda';
|
|
2
|
+
import { invariant } from '../../../errors/index.js';
|
|
3
|
+
import { customInspectSymbol } from '../../../utils/const.js';
|
|
4
|
+
import { union, difference, intersection, equals } from '../../../utils/set.js';
|
|
5
|
+
import { stringHash } from '../../../utils/string-hash.js';
|
|
6
|
+
import { hasSameSourceTarget } from '../ops.js';
|
|
7
|
+
import { findConnection } from './find.js';
|
|
8
|
+
import { isome } from '../../../utils/iterable/some.js';
|
|
9
|
+
import { ifilter } from '../../../utils/iterable/filter.js';
|
|
10
|
+
|
|
11
|
+
class ConnectionModel {
|
|
12
|
+
constructor(source, target, relations = /* @__PURE__ */ new Set()) {
|
|
13
|
+
this.source = source;
|
|
14
|
+
this.target = target;
|
|
15
|
+
this.relations = relations;
|
|
16
|
+
this.id = stringHash(`model:${source.id}:${target.id}`);
|
|
17
|
+
}
|
|
18
|
+
id;
|
|
19
|
+
_boundary;
|
|
20
|
+
/**
|
|
21
|
+
* Common ancestor of the source and target elements.
|
|
22
|
+
* Represents the boundary of the connection.
|
|
23
|
+
*/
|
|
24
|
+
get boundary() {
|
|
25
|
+
return this._boundary ??= this.source.commonAncestor(this.target);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Human readable expression of the connection
|
|
29
|
+
* Mostly used for testing and debugging
|
|
30
|
+
*/
|
|
31
|
+
get expression() {
|
|
32
|
+
return `${this.source.id} -> ${this.target.id}`;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Returns true if only includes relations between the source and target elements.
|
|
36
|
+
*/
|
|
37
|
+
get isDirect() {
|
|
38
|
+
return this.nonEmpty() && !this.isImplicit;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Returns true if includes relations between nested elements of the source and target elements.
|
|
42
|
+
*/
|
|
43
|
+
get isImplicit() {
|
|
44
|
+
return this.nonEmpty() && isome(this.relations, isNot(hasSameSourceTarget(this)));
|
|
45
|
+
}
|
|
46
|
+
get directRelations() {
|
|
47
|
+
return new Set(ifilter(this.relations, hasSameSourceTarget(this)));
|
|
48
|
+
}
|
|
49
|
+
nonEmpty() {
|
|
50
|
+
return this.relations.size > 0;
|
|
51
|
+
}
|
|
52
|
+
mergeWith(other) {
|
|
53
|
+
invariant(this.source.id === other.source.id, "Cannot merge connections with different sources");
|
|
54
|
+
invariant(this.target.id === other.target.id, "Cannot merge connections with different targets");
|
|
55
|
+
return new ConnectionModel(
|
|
56
|
+
this.source,
|
|
57
|
+
this.target,
|
|
58
|
+
union(this.relations, other.relations)
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
difference(other) {
|
|
62
|
+
return new ConnectionModel(
|
|
63
|
+
this.source,
|
|
64
|
+
this.target,
|
|
65
|
+
difference(this.relations, other.relations)
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
intersect(other) {
|
|
69
|
+
invariant(other instanceof ConnectionModel, "Cannot intersect connection with different type");
|
|
70
|
+
return new ConnectionModel(
|
|
71
|
+
this.source,
|
|
72
|
+
this.target,
|
|
73
|
+
intersection(this.relations, other.relations)
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
equals(other) {
|
|
77
|
+
invariant(other instanceof ConnectionModel, "Cannot merge connection with different type");
|
|
78
|
+
return this.id === other.id && this.source.id === other.source.id && this.target.id === other.target.id && equals(this.relations, other.relations);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Returns a new instance with the updated relations.
|
|
82
|
+
*
|
|
83
|
+
* @param relations - A readonly set of `RelationshipModel` instances representing the new relations.
|
|
84
|
+
* @returns A new `ConnectionModel` instance with the updated relations.
|
|
85
|
+
*/
|
|
86
|
+
update(relations) {
|
|
87
|
+
return new ConnectionModel(
|
|
88
|
+
this.source,
|
|
89
|
+
this.target,
|
|
90
|
+
relations
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
[customInspectSymbol](depth, inspectOptions, inspect) {
|
|
94
|
+
const asString = this.toString();
|
|
95
|
+
Object.defineProperty(asString, "constructor", {
|
|
96
|
+
value: ConnectionModel,
|
|
97
|
+
enumerable: false
|
|
98
|
+
});
|
|
99
|
+
return asString;
|
|
100
|
+
}
|
|
101
|
+
toString() {
|
|
102
|
+
return [
|
|
103
|
+
this.expression,
|
|
104
|
+
this.relations.size ? " relations:" : " relations: [ ]",
|
|
105
|
+
...[...this.relations].map((c) => " " + c.expression)
|
|
106
|
+
].join("\n");
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Creates a new connection with reversed direction (target becomes source and vice versa)
|
|
110
|
+
* @param search - When true, attempts to find an existing connection between the reversed nodes
|
|
111
|
+
*/
|
|
112
|
+
reversed(search = false) {
|
|
113
|
+
if (!search) {
|
|
114
|
+
return new ConnectionModel(this.target, this.source);
|
|
115
|
+
}
|
|
116
|
+
const [found] = findConnection(this.target, this.source, "directed");
|
|
117
|
+
return found ?? new ConnectionModel(this.target, this.source, /* @__PURE__ */ new Set());
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export { ConnectionModel };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { isSameHierarchy } from '../../../utils/fqn.js';
|
|
2
|
+
import { intersection } from '../../../utils/set.js';
|
|
3
|
+
import { ConnectionModel } from './ConnectionModel.js';
|
|
4
|
+
|
|
5
|
+
function findConnection(source, target, direction = "directed") {
|
|
6
|
+
if (source === target) {
|
|
7
|
+
return [];
|
|
8
|
+
}
|
|
9
|
+
if (isSameHierarchy(source, target)) {
|
|
10
|
+
return [];
|
|
11
|
+
}
|
|
12
|
+
const directedIntersection = intersection(source.allOutgoing, target.allIncoming);
|
|
13
|
+
const directed = directedIntersection.size > 0 ? new ConnectionModel(
|
|
14
|
+
source,
|
|
15
|
+
target,
|
|
16
|
+
directedIntersection
|
|
17
|
+
) : null;
|
|
18
|
+
if (direction === "directed") {
|
|
19
|
+
return directed ? [directed] : [];
|
|
20
|
+
}
|
|
21
|
+
const reverseIntersection = intersection(source.allIncoming, target.allOutgoing);
|
|
22
|
+
const reverse = reverseIntersection.size > 0 ? new ConnectionModel(
|
|
23
|
+
target,
|
|
24
|
+
source,
|
|
25
|
+
reverseIntersection
|
|
26
|
+
) : null;
|
|
27
|
+
if (directed && reverse) {
|
|
28
|
+
return [directed, reverse];
|
|
29
|
+
}
|
|
30
|
+
if (directed) {
|
|
31
|
+
return [directed];
|
|
32
|
+
}
|
|
33
|
+
if (reverse) {
|
|
34
|
+
return [reverse];
|
|
35
|
+
}
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export { findConnection };
|
|
@@ -12,6 +12,14 @@ function isAncestor(arg1, arg2) {
|
|
|
12
12
|
return arg1Id.startsWith(ancestorId + ".");
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
|
+
function isSameHierarchy(one, another) {
|
|
16
|
+
if (!another) {
|
|
17
|
+
return (b) => isSameHierarchy(one, b);
|
|
18
|
+
}
|
|
19
|
+
const first = asString(one);
|
|
20
|
+
const second = asString(another);
|
|
21
|
+
return first === second || second.startsWith(first + ".") || first.startsWith(second + ".");
|
|
22
|
+
}
|
|
15
23
|
function isDescendantOf(descedant, ancestor) {
|
|
16
24
|
{
|
|
17
25
|
return (d) => isAncestor(descedant, d);
|
|
@@ -40,4 +48,4 @@ function sortParentsFirst(array) {
|
|
|
40
48
|
return result;
|
|
41
49
|
}
|
|
42
50
|
|
|
43
|
-
export { isAncestor, isDescendantOf, sortParentsFirst };
|
|
51
|
+
export { isAncestor, isDescendantOf, isSameHierarchy, sortParentsFirst };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { isFunction } from 'remeda';
|
|
2
|
+
import { invariant } from '../../errors/index.js';
|
|
3
|
+
|
|
4
|
+
function ifind(arg1, arg2) {
|
|
5
|
+
const pred = arg2 ?? arg1;
|
|
6
|
+
invariant(isFunction(pred));
|
|
7
|
+
function _find(iter) {
|
|
8
|
+
for (const value of iter) {
|
|
9
|
+
if (pred(value)) {
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if (!arg2) {
|
|
16
|
+
return _find;
|
|
17
|
+
}
|
|
18
|
+
return _find(arg1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { ifind };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { isFunction } from 'remeda';
|
|
2
|
+
import { invariant } from '../../errors/index.js';
|
|
3
|
+
|
|
4
|
+
function isome(arg1, arg2) {
|
|
5
|
+
const pred = arg2 ?? arg1;
|
|
6
|
+
invariant(isFunction(pred));
|
|
7
|
+
function _some(iter) {
|
|
8
|
+
for (const value of iter) {
|
|
9
|
+
if (pred(value)) {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
if (!arg2) {
|
|
16
|
+
return _some;
|
|
17
|
+
}
|
|
18
|
+
return _some(arg1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { isome };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { hasAtLeast } from 'remeda';
|
|
2
|
+
import { s as setExports } from '../../../../_virtual/set.js';
|
|
3
|
+
|
|
4
|
+
function union(...sets) {
|
|
5
|
+
let result = /* @__PURE__ */ new Set();
|
|
6
|
+
for (const set of sets) {
|
|
7
|
+
for (const value of set) {
|
|
8
|
+
result.add(value);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
return result;
|
|
12
|
+
}
|
|
13
|
+
function intersection(first, ...sets) {
|
|
14
|
+
let result = /* @__PURE__ */ new Set();
|
|
15
|
+
if (first.size === 0) {
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
let other = hasAtLeast(sets, 2) ? setExports.intersection(...sets) : sets[0];
|
|
19
|
+
if (other.size === 0) {
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
for (const value of first) {
|
|
23
|
+
if (other.has(value)) {
|
|
24
|
+
result.add(value);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
function difference(a, b) {
|
|
30
|
+
if (a.size === 0) {
|
|
31
|
+
return /* @__PURE__ */ new Set();
|
|
32
|
+
}
|
|
33
|
+
if (b.size === 0) {
|
|
34
|
+
return new Set(a);
|
|
35
|
+
}
|
|
36
|
+
let result = /* @__PURE__ */ new Set();
|
|
37
|
+
for (const value of a) {
|
|
38
|
+
if (!b.has(value)) {
|
|
39
|
+
result.add(value);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
function equals(a, b) {
|
|
45
|
+
return a.size === b.size && [...a].every((value) => b.has(value));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export { difference, equals, intersection, union };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { invariant } from '../errors/index.js';
|
|
2
|
+
|
|
3
|
+
function stringHash(str) {
|
|
4
|
+
let hash = 5381;
|
|
5
|
+
const len = str.length;
|
|
6
|
+
invariant(len > 0, "stringHash: empty string");
|
|
7
|
+
for (let i = 0; i < len; i++) {
|
|
8
|
+
hash = hash * 33 ^ str.charCodeAt(i);
|
|
9
|
+
}
|
|
10
|
+
return (hash >>> 0).toString(36);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export { stringHash };
|