@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,4 +1,28 @@
1
+ import { SlotRecipeRuntimeFn } from '@likec4/styles/types';
1
2
  export declare const backdropBlur = "--_blur";
2
3
  export declare const backdropOpacity = "--_opacity";
3
- export declare const dialog: string;
4
- export declare const body: string;
4
+ export declare const level = "--_level";
5
+ export declare const overlay: SlotRecipeRuntimeFn<"body" | "dialog", {
6
+ level: {
7
+ 0: {
8
+ dialog: {
9
+ "--_level": "0";
10
+ };
11
+ };
12
+ 1: {
13
+ dialog: {
14
+ "--_level": "1";
15
+ };
16
+ };
17
+ 2: {
18
+ dialog: {
19
+ "--_level": "2";
20
+ };
21
+ };
22
+ 3: {
23
+ dialog: {
24
+ "--_level": "3";
25
+ };
26
+ };
27
+ };
28
+ }>;
@@ -1,9 +1,12 @@
1
- import { css } from '@likec4/styles/css';
1
+ import { sva, css } from '@likec4/styles/css';
2
2
 
3
3
  const backdropBlur = "--_blur";
4
4
  const backdropOpacity = "--_opacity";
5
+ const level = "--_level";
6
+ const offset = "--_offset";
7
+ const inset = "--_inset";
5
8
  const borderRadius = "--_border-radius";
6
- const dialog = css({
9
+ const dialog = css.raw({
7
10
  boxSizing: "border-box",
8
11
  margin: 0,
9
12
  position: "fixed",
@@ -17,6 +20,9 @@ const dialog = css({
17
20
  outline: "none",
18
21
  borderRadius: `var(${borderRadius})`,
19
22
  [backdropBlur]: "0px",
23
+ [level]: "0",
24
+ [offset]: "0px",
25
+ [inset]: "calc((1 + var(--_level) * 0.5) * var(--_offset))",
20
26
  [backdropOpacity]: "0%",
21
27
  _backdrop: {
22
28
  cursor: "zoom-out",
@@ -31,35 +37,46 @@ const dialog = css({
31
37
  border: "none",
32
38
  inset: 0,
33
39
  padding: 0,
40
+ // [offset]: '0px',
41
+ // [inset]: '0px',
34
42
  width: "100vw",
35
43
  height: "100vh"
36
44
  },
37
45
  sm: {
46
+ inset: "[var(--_inset) var(--_offset) var(--_offset) var(--_inset)]",
47
+ width: "calc(100vw - var(--_offset) - var(--_inset))",
48
+ height: "calc(100vh - var(--_offset) - var(--_inset))",
38
49
  [borderRadius]: "6px",
39
50
  padding: "6px",
40
- inset: "1rem",
41
- width: "calc(100vw - 2rem)",
42
- height: "calc(100vh - 2rem)"
51
+ [offset]: "1rem"
43
52
  },
44
53
  md: {
45
- inset: "2rem",
46
- width: "calc(100vw - 4rem)",
47
- height: "calc(100vh - 4rem)"
54
+ [offset]: "1rem"
48
55
  },
49
56
  lg: {
50
- inset: "4rem",
51
- width: "calc(100vw - 8rem)",
52
- height: "calc(100vh - 8rem)"
57
+ [offset]: "2rem"
53
58
  },
54
59
  xl: {
55
- [borderRadius]: "8px",
56
- padding: "8px",
57
- inset: "5rem",
58
- width: "calc(100vw - 10rem)",
59
- height: "calc(100vh - 10rem)"
60
+ [offset]: "4rem"
60
61
  }
62
+ // inset: '[calc(2rem + var(--_level, 0) * 1rem), 2rem, 2rem, 2rem]',
63
+ // width: 'calc(100vw - 4rem)',
64
+ // height: 'calc(100vh - 4rem - var(--_level, 0) * 1rem)',
65
+ // },
66
+ // lg: {
67
+ // inset: '[calc(4rem + var(--_level, 0) * 1rem), 4rem, 4rem, 4rem]',
68
+ // width: 'calc(100vw - 8rem)',
69
+ // height: 'calc(100vh - 8rem)',
70
+ // },
71
+ // xl: {
72
+ // [borderRadius]: '8px',
73
+ // padding: '8px',
74
+ // inset: '[calc(5rem + var(--_level, 0) * 1rem), 5rem, 5rem, 5rem]',
75
+ // width: 'calc(100vw - 10rem)',
76
+ // height: 'calc(100vh - 10rem - var(--_level, 0) * 1rem)',
77
+ // },
61
78
  });
62
- const body = css({
79
+ const body = css.raw({
63
80
  position: "relative",
64
81
  containerName: "overlay-dialog",
65
82
  containerType: "size",
@@ -72,5 +89,36 @@ const body = css({
72
89
  borderRadius: `calc(var(${borderRadius}) - 2px)`
73
90
  }
74
91
  });
92
+ const overlay = sva({
93
+ slots: ["dialog", "body"],
94
+ base: {
95
+ dialog,
96
+ body
97
+ },
98
+ variants: {
99
+ level: {
100
+ 0: {
101
+ dialog: {
102
+ [level]: "0"
103
+ }
104
+ },
105
+ 1: {
106
+ dialog: {
107
+ [level]: "1"
108
+ }
109
+ },
110
+ 2: {
111
+ dialog: {
112
+ [level]: "2"
113
+ }
114
+ },
115
+ 3: {
116
+ dialog: {
117
+ [level]: "3"
118
+ }
119
+ }
120
+ }
121
+ }
122
+ });
75
123
 
76
- export { backdropBlur, backdropOpacity, body, dialog };
124
+ export { backdropBlur, backdropOpacity, level, overlay };
@@ -1,12 +1,13 @@
1
- import { HTMLMotionProps } from 'framer-motion';
2
- import { PropsWithChildren, ForwardRefExoticComponent, RefAttributes } from 'react';
3
- type OverlayProps = PropsWithChildren<HTMLMotionProps<'dialog'> & {
1
+ import { ForwardRefExoticComponent, ReactNode, RefAttributes } from 'react';
2
+ export declare const Overlay: ForwardRefExoticComponent<{
3
+ overlayLevel?: 0 | 1 | 2 | 3;
4
+ className?: string;
4
5
  classes?: {
5
6
  dialog?: string;
6
7
  body?: string;
7
8
  };
8
9
  onClose: () => void;
9
10
  onClick?: never;
10
- }>;
11
- export declare const Overlay: ForwardRefExoticComponent<Omit<OverlayProps, "ref"> & RefAttributes<HTMLDialogElement>>;
12
- export {};
11
+ } & {
12
+ children?: ReactNode | undefined;
13
+ } & RefAttributes<HTMLDialogElement>>;
@@ -5,11 +5,11 @@ import { useMergedRef } from '@mantine/hooks';
5
5
  import { useSyncedRef, useDebouncedCallback, useTimeoutEffect } from '@react-hookz/web';
6
6
  import { useReducedMotionConfig, m } from 'framer-motion';
7
7
  import { forwardRef, useState, useRef, useLayoutEffect } from 'react';
8
- import { body, backdropOpacity, backdropBlur, dialog } from './Overlay.css.js';
8
+ import { overlay, backdropOpacity, backdropBlur } from './Overlay.css.js';
9
9
  import { stopPropagation } from '../../utils/xyflow.js';
10
10
 
11
11
  const Overlay = forwardRef(
12
- ({ children, onClose, className, classes, ...rest }, ref) => {
12
+ ({ children, onClose, className, classes, overlayLevel = 0, ...rest }, ref) => {
13
13
  const [opened, setOpened] = useState(false);
14
14
  const dialogRef = useRef(null);
15
15
  const isClosingRef = useRef(false);
@@ -38,28 +38,32 @@ const Overlay = forwardRef(
38
38
  if (!dialogRef.current?.open) {
39
39
  dialogRef.current?.showModal();
40
40
  }
41
- }, 10);
41
+ }, 20);
42
42
  useTimeoutEffect(() => {
43
43
  setOpened(true);
44
44
  }, 120);
45
+ const styles = overlay({
46
+ level: overlayLevel
47
+ });
45
48
  return /* @__PURE__ */ jsx(
46
49
  m.dialog,
47
50
  {
48
51
  ref: useMergedRef(dialogRef, ref),
49
- className: cx(RemoveScroll.classNames.fullWidth, dialog, classes?.dialog, className),
52
+ className: cx(RemoveScroll.classNames.fullWidth, styles.dialog, classes?.dialog, className),
50
53
  layout: true,
51
54
  ...motionNotReduced ? {
52
55
  initial: {
53
56
  [backdropBlur]: "0px",
54
57
  [backdropOpacity]: "0%",
55
- scale: 1.075,
58
+ scale: overlayLevel > 0 ? 0.9 : 1.075,
56
59
  opacity: 0
57
60
  },
58
61
  animate: {
59
- [backdropBlur]: "8px",
60
- [backdropOpacity]: "60%",
62
+ [backdropBlur]: overlayLevel > 0 ? "4px" : "8px",
63
+ [backdropOpacity]: overlayLevel > 0 ? "50%" : "60%",
61
64
  scale: 1,
62
65
  opacity: 1,
66
+ translateY: 0,
63
67
  transition: {
64
68
  delay: 0.075
65
69
  }
@@ -91,7 +95,7 @@ const Overlay = forwardRef(
91
95
  close();
92
96
  },
93
97
  ...rest,
94
- children: /* @__PURE__ */ jsx(RemoveScroll, { forwardProps: true, removeScrollBar: false, children: /* @__PURE__ */ jsx(Box, { className: cx(body, "overlay-body", classes?.body), children: opened && /* @__PURE__ */ jsx(Fragment, { children }) }) })
98
+ children: /* @__PURE__ */ jsx(RemoveScroll, { forwardProps: true, removeScrollBar: false, children: /* @__PURE__ */ jsx(Box, { className: cx(styles.body, "overlay-body", classes?.body), children: opened && /* @__PURE__ */ jsx(Fragment, { children }) }) })
95
99
  }
96
100
  );
97
101
  }
@@ -88,8 +88,8 @@ const overlaysActorLogic = setup({
88
88
  const currentOverlay = last(context.overlays);
89
89
  if (currentOverlay?.type === "relationshipDetails") {
90
90
  enqueue.sendTo(currentOverlay.id, {
91
- type: "navigate.to",
92
- edgeId: event.edgeId
91
+ ...event,
92
+ type: "navigate.to"
93
93
  });
94
94
  return;
95
95
  }
@@ -1,15 +1,21 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { invariant, nonNullable, ifind, isAncestor } from '@likec4/core';
2
3
  import { ActionIcon, Group } from '@mantine/core';
3
- import { useStateHistory, useCallbackRef } from '@mantine/hooks';
4
+ import { useCallbackRef, useStateHistory } from '@mantine/hooks';
4
5
  import { IconX, IconChevronLeft, IconChevronRight } from '@tabler/icons-react';
5
- import { ReactFlowProvider, Panel } from '@xyflow/react';
6
+ import { useSelector } from '@xstate/react';
7
+ import { ReactFlowProvider, Panel, useStoreApi, useReactFlow } from '@xyflow/react';
6
8
  import clsx from 'clsx';
9
+ import { deepEqual } from 'fast-equals';
7
10
  import { LayoutGroup, AnimatePresence, m } from 'framer-motion';
8
- import { useRef, memo, useEffect } from 'react';
11
+ import { useRef, memo, useMemo, useEffect } from 'react';
12
+ import 'remeda';
9
13
  import { BaseXYFlow } from '../../base/BaseXYFlow.js';
10
- import { RelationshipDetailsActorContext, useRelationshipDetails, useRelationshipDetailsState } from './hooks.js';
14
+ import { useLikeC4Model } from '../../likec4model/useLikeC4Model.js';
15
+ import { computeEdgeDetailsViewData, computeRelationshipDetailsViewData } from './compute.js';
16
+ import { RelationshipDetailsActorContext, useRelationshipDetails, useRelationshipDetailsState, useRelationshipDetailsActor } from './hooks.js';
17
+ import { layoutRelationshipDetails } from './layout.js';
11
18
  import { SelectEdge } from './SelectEdge.js';
12
- import { useLayoutedDetails } from './useLayoutedDetails.js';
13
19
  import { RelationshipEdge } from './custom/edgeTypes.js';
14
20
  import { CompoundNode, ElementNode } from './custom/nodeTypes.js';
15
21
 
@@ -28,50 +34,68 @@ function RelationshipDetails({ actorRef }) {
28
34
  defaultEdges: []
29
35
  };
30
36
  }
31
- 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, {}) }) }) }) });
37
+ return /* @__PURE__ */ jsx(RelationshipDetailsActorContext.Provider, { value: actorRef, children: /* @__PURE__ */ jsxs(ReactFlowProvider, { ...initialRef.current, children: [
38
+ /* @__PURE__ */ jsx(LayoutGroup, { id: actorRef.sessionId, inherit: false, children: /* @__PURE__ */ jsx(AnimatePresence, { children: /* @__PURE__ */ jsx(RelationshipDetailsInner, {}) }) }),
39
+ /* @__PURE__ */ jsx(SyncRelationshipDetailsXYFlow, {})
40
+ ] }) });
32
41
  }
33
- const selector = (state) => ({
34
- edgeId: state.context.edgeId,
35
- view: state.context.view,
36
- initialized: state.context.initialized,
37
- nodes: state.context.xynodes,
38
- edges: state.context.xyedges
42
+ const selectSubject = (state) => ({
43
+ ...state.context.subject,
44
+ viewId: state.context.viewId
45
+ });
46
+ function SyncRelationshipDetailsXYFlow() {
47
+ const actor = useRelationshipDetailsActor();
48
+ const subject = useSelector(actor, selectSubject, deepEqual);
49
+ const likec4model = useLikeC4Model(true);
50
+ const view = likec4model.findView(subject.viewId) ?? null;
51
+ const data = useMemo(() => {
52
+ let data2;
53
+ if ("edgeId" in subject) {
54
+ invariant(view, `view ${subject.viewId} not found`);
55
+ const edge = nonNullable(view.findEdge(subject.edgeId), `edge ${subject.edgeId} not found in ${subject.viewId}`);
56
+ data2 = computeEdgeDetailsViewData([edge.id], view);
57
+ } else {
58
+ data2 = computeRelationshipDetailsViewData({
59
+ source: likec4model.element(subject.source),
60
+ target: likec4model.element(subject.target)
61
+ });
62
+ }
63
+ return layoutRelationshipDetails(data2, view);
64
+ }, [
65
+ subject,
66
+ view,
67
+ likec4model
68
+ ]);
69
+ const store = useStoreApi();
70
+ const instance = useReactFlow();
71
+ useEffect(() => {
72
+ if (instance.viewportInitialized) {
73
+ actor.send({ type: "xyflow.init", instance, store });
74
+ }
75
+ }, [store, instance.viewportInitialized, actor]);
76
+ useEffect(() => {
77
+ actor.send({ type: "update.layoutData", data });
78
+ }, [data, actor]);
79
+ return null;
80
+ }
81
+ const selector = ({ context }) => ({
82
+ // subject: context.subject,
83
+ // view: state.context.view,
84
+ initialized: context.initialized.xydata && context.initialized.xyflow,
85
+ nodes: context.xynodes,
86
+ edges: context.xyedges
39
87
  });
40
88
  const RelationshipDetailsInner = memo(() => {
41
89
  const browser = useRelationshipDetails();
42
90
  const {
43
- edgeId,
44
- view,
45
91
  initialized,
46
92
  nodes,
47
93
  edges
48
- } = useRelationshipDetailsState(selector);
49
- const {
50
- edge,
51
- xynodes,
52
- xyedges,
53
- bounds
54
- } = useLayoutedDetails(edgeId, view);
55
- useEffect(() => {
56
- browser.send({ type: "update.xydata", xynodes, xyedges });
57
- }, [xynodes, xyedges]);
58
- const [historyEdgeId, historyOps, { history, current }] = useStateHistory(edgeId);
59
- useEffect(() => {
60
- if (historyEdgeId !== edgeId) {
61
- historyOps.set(edgeId);
62
- }
63
- }, [edgeId]);
64
- useEffect(() => {
65
- if (historyEdgeId !== edgeId) {
66
- browser.navigateTo(historyEdgeId);
67
- }
68
- }, [historyEdgeId]);
69
- const hasStepBack = current > 0;
70
- const hasStepForward = current + 1 < history.length;
94
+ } = useRelationshipDetailsState(selector, deepEqual);
71
95
  return /* @__PURE__ */ jsxs(
72
96
  BaseXYFlow,
73
97
  {
74
- id: `relationships-details-${browser.actor.sessionId}`,
98
+ id: browser.rootElementId,
75
99
  nodes,
76
100
  edges,
77
101
  className: clsx(initialized ? "initialized" : "not-initialized"),
@@ -84,29 +108,32 @@ const RelationshipDetailsInner = memo(() => {
84
108
  browser.send({ type: "xyflow.applyEdgeChanges", changes });
85
109
  }),
86
110
  fitViewPadding: 0.05,
87
- onInit: useCallbackRef((instance) => {
88
- browser.send({ type: "xyflow.init", instance });
89
- }),
90
111
  onNodeClick: useCallbackRef((e, node) => {
91
112
  e.stopPropagation();
92
113
  browser.send({ type: "xyflow.nodeClick", node });
93
114
  }),
94
- onEdgeClick: useCallbackRef((e, edge2) => {
115
+ onEdgeClick: useCallbackRef((e, edge) => {
95
116
  e.stopPropagation();
96
- browser.send({ type: "xyflow.edgeClick", edge: edge2 });
117
+ browser.send({ type: "xyflow.edgeClick", edge });
97
118
  }),
98
119
  onPaneClick: useCallbackRef((e) => {
99
- e.stopPropagation();
100
120
  browser.send({ type: "xyflow.paneClick" });
101
121
  }),
122
+ onDoubleClick: useCallbackRef((e) => {
123
+ browser.send({ type: "xyflow.paneDblClick" });
124
+ }),
102
125
  onViewportResize: useCallbackRef(() => {
103
126
  browser.send({ type: "xyflow.resized" });
104
127
  }),
105
- onEdgeMouseEnter: useCallbackRef((_event, edge2) => {
106
- browser.send({ type: "xyflow.edgeMouseEnter", edge: edge2 });
128
+ onEdgeMouseEnter: useCallbackRef((_event, edge) => {
129
+ if (!edge.data.hovered) {
130
+ browser.send({ type: "xyflow.edgeMouseEnter", edge });
131
+ }
107
132
  }),
108
- onEdgeMouseLeave: useCallbackRef((_event, edge2) => {
109
- browser.send({ type: "xyflow.edgeMouseLeave", edge: edge2 });
133
+ onEdgeMouseLeave: useCallbackRef((_event, edge) => {
134
+ if (edge.data.hovered) {
135
+ browser.send({ type: "xyflow.edgeMouseLeave", edge });
136
+ }
110
137
  }),
111
138
  onSelectionChange: useCallbackRef((params) => {
112
139
  browser.send({ type: "xyflow.selectionChange", ...params });
@@ -116,17 +143,7 @@ const RelationshipDetailsInner = memo(() => {
116
143
  pannable: true,
117
144
  zoomable: true,
118
145
  children: [
119
- /* @__PURE__ */ jsx(
120
- TopLeftPanel,
121
- {
122
- edge,
123
- view,
124
- hasStepBack,
125
- hasStepForward,
126
- onStepBack: () => historyOps.back(),
127
- onStepForward: () => historyOps.forward()
128
- }
129
- ),
146
+ /* @__PURE__ */ jsx(TopLeftPanel, {}),
130
147
  /* @__PURE__ */ jsx(Panel, { position: "top-right", children: /* @__PURE__ */ jsx(
131
148
  ActionIcon,
132
149
  {
@@ -143,15 +160,41 @@ const RelationshipDetailsInner = memo(() => {
143
160
  }
144
161
  );
145
162
  });
146
- const TopLeftPanel = ({
147
- edge,
148
- view,
149
- hasStepBack,
150
- hasStepForward,
151
- onStepBack,
152
- onStepForward
153
- }) => {
154
- useRelationshipDetails();
163
+ const topLeftPanelselector = ({ context }) => ({
164
+ subject: context.subject,
165
+ viewId: context.viewId
166
+ });
167
+ const TopLeftPanel = memo(() => {
168
+ const { subject, viewId } = useRelationshipDetailsState(topLeftPanelselector, deepEqual);
169
+ const likec4model = useLikeC4Model(true);
170
+ const view = likec4model.findView(viewId);
171
+ if (!view || !view.isDiagram()) {
172
+ return null;
173
+ }
174
+ let edge = "edgeId" in subject ? view.findEdge(subject.edgeId) : ifind(view.edges(), (e) => e.source.element?.id === subject.source && e.target.element?.id === subject.target) ?? ifind(view.edges(), (e) => {
175
+ return (e.source.element?.id === subject.source || isAncestor(e.source.element?.id ?? "--", subject.source)) && (e.target.element?.id === subject.target || isAncestor(e.target.element?.id ?? "", subject.target));
176
+ });
177
+ if (!edge) {
178
+ return null;
179
+ }
180
+ return /* @__PURE__ */ jsx(TopLeftPanelInner, { edge: edge.$edge, view: view.$view });
181
+ });
182
+ const TopLeftPanelInner = ({ edge, view }) => {
183
+ const browser = useRelationshipDetails();
184
+ const edgeId = edge.id;
185
+ const [historyEdgeId, historyOps, { history, current }] = useStateHistory(edge.id);
186
+ useEffect(() => {
187
+ if (historyEdgeId !== edgeId) {
188
+ historyOps.set(edgeId);
189
+ }
190
+ }, [edgeId]);
191
+ useEffect(() => {
192
+ if (historyEdgeId !== edgeId) {
193
+ browser.navigateTo(historyEdgeId);
194
+ }
195
+ }, [historyEdgeId]);
196
+ const hasStepBack = current > 0;
197
+ const hasStepForward = current + 1 < history.length;
155
198
  return /* @__PURE__ */ jsx(Panel, { position: "top-left", children: /* @__PURE__ */ jsx(Group, { gap: 4, wrap: "nowrap", children: /* @__PURE__ */ jsxs(AnimatePresence, { mode: "popLayout", children: [
156
199
  hasStepBack && /* @__PURE__ */ jsx(
157
200
  m.div,
@@ -170,7 +213,7 @@ const TopLeftPanel = ({
170
213
  color: "gray",
171
214
  onClick: (e) => {
172
215
  e.stopPropagation();
173
- onStepBack();
216
+ historyOps.back();
174
217
  },
175
218
  children: /* @__PURE__ */ jsx(IconChevronLeft, {})
176
219
  }
@@ -195,7 +238,7 @@ const TopLeftPanel = ({
195
238
  color: "gray",
196
239
  onClick: (e) => {
197
240
  e.stopPropagation();
198
- onStepForward();
241
+ historyOps.forward();
199
242
  },
200
243
  children: /* @__PURE__ */ jsx(IconChevronRight, {})
201
244
  }
@@ -2,4 +2,4 @@ import { DiagramEdge, DiagramView } from '@likec4/core';
2
2
  export declare const SelectEdge: ({ edge, view, }: {
3
3
  edge: DiagramEdge;
4
4
  view: DiagramView;
5
- }) => import("react/jsx-runtime").JSX.Element;
5
+ }) => import("react/jsx-runtime").JSX.Element | null;
@@ -11,22 +11,28 @@ const SelectEdge = ({
11
11
  edge,
12
12
  view
13
13
  }) => {
14
- const overlay = useRelationshipDetails();
14
+ const browser = useRelationshipDetails();
15
15
  const viewport = useRef(null);
16
16
  useMantinePortalProps();
17
17
  useComputedColorScheme();
18
18
  const edgeSource$1 = view.nodes.find((n) => n.id === edge.source);
19
19
  const edgeTarget$1 = view.nodes.find((n) => n.id === edge.target);
20
- const edges = view.edges.map((edge2) => {
20
+ const edges = view.edges.flatMap((edge2) => {
21
21
  const source = view.nodes.find((n) => n.id === edge2.source);
22
22
  const target = view.nodes.find((n) => n.id === edge2.target);
23
- return {
24
- id: edge2.id,
25
- source,
26
- target,
27
- label: edge2.label
28
- };
23
+ if (source && target) {
24
+ return {
25
+ id: edge2.id,
26
+ source,
27
+ target,
28
+ label: edge2.label
29
+ };
30
+ }
31
+ return [];
29
32
  });
33
+ if (!edgeSource$1 || !edgeTarget$1 || edges.length === 0) {
34
+ return null;
35
+ }
30
36
  return /* @__PURE__ */ jsxs(
31
37
  Popover,
32
38
  {
@@ -87,7 +93,7 @@ const SelectEdge = ({
87
93
  "data-selected": e.id === edge.id,
88
94
  onClick: (event) => {
89
95
  event.stopPropagation();
90
- overlay.navigateTo(e.id);
96
+ browser.navigateTo(e.id);
91
97
  },
92
98
  children: [
93
99
  /* @__PURE__ */ jsx(
@@ -1,6 +1,7 @@
1
1
  import { Color, DiagramNode, Fqn, IconUrl, RelationId, RelationshipLineType, ViewId } from '@likec4/core';
2
2
  import { Base, NonOptional, ReactFlowEdge, ReactFlowNode } from '../../base/types';
3
3
  export declare namespace RelationshipDetailsTypes {
4
+ type Column = 'sources' | 'targets';
4
5
  /**
5
6
  * The node's incoming and outgoing ports
6
7
  */
@@ -9,11 +10,13 @@ export declare namespace RelationshipDetailsTypes {
9
10
  out: string[];
10
11
  };
11
12
  type ElementNodeData = Base.NodeData<NonOptional<Pick<DiagramNode, 'title' | 'technology' | 'description' | 'color' | 'shape' | 'width' | 'height' | 'navigateTo' | 'style'>> & {
13
+ column: Column;
12
14
  fqn: Fqn;
13
15
  icon: IconUrl;
14
16
  ports: Ports;
15
17
  }>;
16
- type CompoundNodeData = Base.NodeData<NonOptional<Pick<DiagramNode, 'title' | 'color' | 'shape' | 'style'>> & {
18
+ type CompoundNodeData = Base.NodeData<NonOptional<Pick<DiagramNode, 'title' | 'color' | 'style'>> & {
19
+ column: Column;
17
20
  fqn: Fqn;
18
21
  depth: number;
19
22
  icon?: IconUrl;