@likec4/diagram 1.12.2 → 1.13.0

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 (49) hide show
  1. package/package.json +8 -8
  2. package/src/LikeC4CustomColors.tsx +2 -2
  3. package/src/LikeC4Diagram.props.ts +15 -7
  4. package/src/LikeC4Diagram.tsx +23 -14
  5. package/src/StaticLikeC4Diagram.tsx +2 -0
  6. package/src/components/Overlay.css.ts +78 -0
  7. package/src/components/Overlay.tsx +102 -0
  8. package/src/components/OverlayContext.tsx +11 -0
  9. package/src/components/index.ts +2 -0
  10. package/src/likec4model/index.ts +1 -0
  11. package/src/likec4model/useLikeC4ElementsTree.ts +44 -0
  12. package/src/overlays/edge-details/EdgeDetailsOverlay.css.ts +13 -0
  13. package/src/overlays/edge-details/EdgeDetailsOverlay.tsx +37 -0
  14. package/src/overlays/index.tsx +33 -0
  15. package/src/overlays/relationships-of/RelationshipsOverlay.tsx +33 -0
  16. package/src/overlays/relationships-of/RelationshipsXYFlow.tsx +359 -0
  17. package/src/overlays/relationships-of/SelectElement.css.ts +18 -0
  18. package/src/overlays/relationships-of/SelectElement.tsx +133 -0
  19. package/src/overlays/relationships-of/_types.ts +56 -0
  20. package/src/overlays/relationships-of/styles.css.ts +34 -0
  21. package/src/overlays/relationships-of/use-layouted-relationships.ts +607 -0
  22. package/src/overlays/relationships-of/xyflow/CompoundNode.tsx +76 -0
  23. package/src/overlays/relationships-of/xyflow/ElementNode.tsx +130 -0
  24. package/src/overlays/relationships-of/xyflow/EmptyNode.tsx +22 -0
  25. package/src/overlays/relationships-of/xyflow/RelationshipEdge.tsx +59 -0
  26. package/src/overlays/relationships-of/xyflow/styles.css.ts +170 -0
  27. package/src/state/diagram-to-xyflow.ts +1 -1
  28. package/src/state/diagramStore.ts +119 -27
  29. package/src/ui/top-left/ChangeAutoLayoutButton.tsx +1 -1
  30. package/src/ui/top-left/ManualLayoutToolsButton.tsx +110 -0
  31. package/src/ui/top-left/TopLeftPanel.tsx +15 -31
  32. package/src/ui/top-left/_shared.ts +2 -2
  33. package/src/xyflow/EdgeMarkers.tsx +19 -0
  34. package/src/xyflow/XYFlowEvents.tsx +21 -24
  35. package/src/xyflow/XYFlowInner.tsx +6 -10
  36. package/src/xyflow/edges/EdgeLabel.tsx +6 -5
  37. package/src/xyflow/edges/RelationshipEdge.tsx +4 -3
  38. package/src/xyflow/edges/RelationshipsDropdownMenu.tsx +16 -0
  39. package/src/xyflow/edges/edges.css.ts +5 -11
  40. package/src/xyflow/nodes/compound/CompoundNode.css.ts +17 -40
  41. package/src/xyflow/nodes/compound/CompoundNode.tsx +219 -28
  42. package/src/xyflow/nodes/element/ElementLink.tsx +11 -8
  43. package/src/xyflow/nodes/element/ElementNode.tsx +211 -140
  44. package/src/xyflow/nodes/element/element.css.ts +22 -33
  45. package/src/xyflow/nodes/shared/Toolbar.tsx +81 -27
  46. package/src/xyflow/nodes/use-animate-variants.tsx +64 -0
  47. package/src/xyflow/useLayoutConstraints.ts +95 -125
  48. package/src/xyflow/utils.ts +4 -0
  49. package/src/xyflow/nodes/shared/NavigateToBtn.tsx +0 -32
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@likec4/diagram",
3
- "version": "1.12.2",
3
+ "version": "1.13.0",
4
4
  "license": "MIT",
5
5
  "bugs": "https://github.com/likec4/likec4/issues",
6
6
  "homepage": "https://likec4.dev",
@@ -30,15 +30,15 @@
30
30
  "clean": "rm -r -f -g 'dist/*' .tsbuildinfo"
31
31
  },
32
32
  "dependencies": {
33
- "@likec4/core": "1.12.2",
34
- "@lume/kiwi": "^0.4.4",
33
+ "@dagrejs/dagre": "^1.1.4",
34
+ "@likec4/core": "1.13.0",
35
35
  "@mantine/vanilla-extract": "7.13.2",
36
36
  "@react-hookz/web": "^24.0.4",
37
37
  "@tabler/icons-react": "3.17.0",
38
38
  "@vanilla-extract/css": "^1.16.0",
39
39
  "@vanilla-extract/css-utils": "^0.1.4",
40
40
  "@vanilla-extract/dynamic": "^2.1.2",
41
- "@xyflow/react": "12.3.1",
41
+ "@xyflow/react": "12.3.2",
42
42
  "@xyflow/system": "0.0.43",
43
43
  "bezier-js": "^6.1.4",
44
44
  "clsx": "^2.1.1",
@@ -58,16 +58,16 @@
58
58
  "react-dom": "^18.3.1"
59
59
  },
60
60
  "devDependencies": {
61
- "@likec4/tsconfig": "1.12.2",
61
+ "@likec4/tsconfig": "1.13.0",
62
62
  "@mantine/core": "7.13.2",
63
63
  "@mantine/hooks": "7.13.2",
64
64
  "@types/bezier-js": "^4.1.3",
65
65
  "@types/d3-shape": "^3.1.6",
66
- "@types/react": "^18.3.4",
67
- "@types/react-dom": "^18.3.0",
66
+ "@types/react": "^18.3.11",
67
+ "@types/react-dom": "^18.3.1",
68
68
  "execa": "^9.3.1",
69
69
  "react": "^18.3.1",
70
70
  "react-dom": "^18.3.1",
71
- "typescript": "^5.6.2"
71
+ "typescript": "^5.6.3"
72
72
  }
73
73
  }
@@ -1,8 +1,8 @@
1
1
  import { type ThemeColorValues } from '@likec4/core'
2
+ import { useMantineStyleNonce } from '@mantine/core'
2
3
  import { entries } from 'remeda'
3
4
  import type { LikeC4CustomColorsProperties } from './LikeC4CustomColors.props'
4
5
  import { vars } from './theme-vars'
5
- import { useMantineStyleNonce } from '@mantine/core'
6
6
 
7
7
  type CSSVarFunction = `var(--${string})` | `var(--${string}, ${string | number} )`
8
8
 
@@ -29,7 +29,7 @@ export function LikeC4CustomColors({ customColors }: LikeC4CustomColorsPropertie
29
29
  const styles = entries(customColors)
30
30
  .map(([name, color]) => toStyle(name, color))
31
31
  .join('\n')
32
-
32
+
33
33
  const nonce = useMantineStyleNonce()?.()
34
34
 
35
35
  return (
@@ -61,11 +61,6 @@ export interface LikeC4DiagramProperties {
61
61
 
62
62
  className?: string | undefined
63
63
 
64
- /**
65
- * Show/hide ReactFlow controls menu
66
- * @default false
67
- */
68
- controls?: boolean | undefined
69
64
  /**
70
65
  * Enable/disable panning
71
66
  * @default true
@@ -81,6 +76,12 @@ export interface LikeC4DiagramProperties {
81
76
  * @default true
82
77
  */
83
78
  readonly?: boolean | undefined
79
+ /**
80
+ * Show/hide panel with top left controls,
81
+ *
82
+ * @default true if not readonly
83
+ */
84
+ controls?: boolean | undefined
84
85
  /**
85
86
  * If set, initial viewport will show all nodes & edges
86
87
  * @default true
@@ -130,7 +131,7 @@ export interface LikeC4DiagramProperties {
130
131
  showDiagramTitle?: boolean | undefined
131
132
 
132
133
  /**
133
- * Show back/forward navigation buttons
134
+ * Show back/forward history navigation buttons
134
135
  * @default true if `onNavigateTo` is set
135
136
  */
136
137
  showNavigationButtons?: undefined | boolean
@@ -149,10 +150,17 @@ export interface LikeC4DiagramProperties {
149
150
 
150
151
  /**
151
152
  * If double click on a node should enable focus mode, i.e. highlight incoming/outgoing edges
152
- * @default true
153
+ * @default false
153
154
  */
154
155
  enableFocusMode?: boolean | undefined
155
156
 
157
+ /**
158
+ * Experimental feature to browse relationships
159
+ *
160
+ * @default false
161
+ */
162
+ enableRelationshipsBrowser?: boolean | undefined
163
+
156
164
  /**
157
165
  * If Walkthrough for dynamic views should be enabled
158
166
  * @default false
@@ -11,6 +11,7 @@ import * as css from './LikeC4Diagram.css'
11
11
  import { type LikeC4DiagramEventHandlers, type LikeC4DiagramProperties } from './LikeC4Diagram.props'
12
12
  import { useLikeC4Model } from './likec4model'
13
13
  import { EnsureMantine } from './mantine/EnsureMantine'
14
+ import { Overlays } from './overlays'
14
15
  import { DiagramContextProvider } from './state/DiagramContext'
15
16
  import { FitViewOnDiagramChange } from './xyflow/FitviewOnDiagramChange'
16
17
  import { SelectEdgesOnNodeFocus } from './xyflow/SelectEdgesOnNodeFocus'
@@ -30,13 +31,14 @@ export function LikeC4Diagram({
30
31
  nodesSelectable = !readonly,
31
32
  nodesDraggable = !readonly,
32
33
  background = 'dots',
33
- controls = false,
34
+ controls = !readonly,
34
35
  showElementLinks = true,
35
36
  showDiagramTitle = true,
36
37
  showNotations = true,
37
38
  showRelationshipDetails = true,
38
39
  enableDynamicViewWalkthrough = false,
39
40
  enableFocusMode = false,
41
+ enableRelationshipsBrowser = false,
40
42
  initialWidth,
41
43
  initialHeight,
42
44
  keepAspectRatio = false,
@@ -69,10 +71,16 @@ export function LikeC4Diagram({
69
71
  console.warn('where filter is only supported in readonly mode')
70
72
  }
71
73
  useEffect(() => {
72
- if (showRelationshipDetails && !hasLikec4model) {
73
- console.warn('Relationship details require LikeC4ModelProvider')
74
+ if (hasLikec4model) {
75
+ return
74
76
  }
75
- }, [showRelationshipDetails, hasLikec4model])
77
+ if (showRelationshipDetails) {
78
+ console.warn('Invalid showRelationshipDetails=true, requires LikeC4ModelProvider')
79
+ }
80
+ if (enableRelationshipsBrowser) {
81
+ console.warn('Invalid enableRelationshipsBrowser=true, requires LikeC4ModelProvider')
82
+ }
83
+ }, [])
76
84
 
77
85
  return (
78
86
  <EnsureMantine>
@@ -89,10 +97,12 @@ export function LikeC4Diagram({
89
97
  readonly={readonly}
90
98
  pannable={pannable}
91
99
  zoomable={zoomable}
100
+ hasLikeC4Model={hasLikec4model}
92
101
  fitViewEnabled={fitView}
93
102
  fitViewPadding={fitViewPadding}
103
+ controls={controls}
94
104
  showElementLinks={showElementLinks}
95
- showNavigationButtons={showNavigationButtons}
105
+ showNavigationButtons={showNavigationButtons && !!onNavigateTo}
96
106
  showNotations={showNotations}
97
107
  showRelationshipDetails={showRelationshipDetails && hasLikec4model}
98
108
  nodesDraggable={nodesDraggable}
@@ -100,7 +110,8 @@ export function LikeC4Diagram({
100
110
  experimentalEdgeEditing={experimentalEdgeEditing}
101
111
  enableDynamicViewWalkthrough={enableDynamicViewWalkthrough}
102
112
  enableFocusMode={enableFocusMode}
103
- whereFilter={where ?? null}
113
+ enableRelationshipsBrowser={enableRelationshipsBrowser && hasLikec4model}
114
+ whereFilter={readonly !== true ? (where ?? null) : null}
104
115
  renderIcon={renderIcon ?? null}
105
116
  onCanvasClick={onCanvasClick ?? null}
106
117
  onCanvasContextMenu={onCanvasContextMenu ?? null}
@@ -117,7 +128,6 @@ export function LikeC4Diagram({
117
128
  onBurgerMenuClick={onBurgerMenuClick ?? null}
118
129
  >
119
130
  <LikeC4DiagramInnerMemo
120
- controls={controls}
121
131
  background={background}
122
132
  showDiagramTitle={showDiagramTitle}
123
133
  />
@@ -131,25 +141,24 @@ LikeC4Diagram.displayName = 'LikeC4Diagram'
131
141
 
132
142
  type LikeC4DiagramInnerProps = {
133
143
  background: NonNullable<LikeC4DiagramProperties['background']>
134
- controls: boolean
135
144
  showDiagramTitle: boolean
136
145
  }
137
146
  const LikeC4DiagramInnerMemo = /* @__PURE__ */ memo<LikeC4DiagramInnerProps>(function LikeC4DiagramInner({
138
147
  background,
139
- controls,
140
148
  showDiagramTitle
141
149
  }) {
142
150
  const {
143
151
  isInitialized,
144
152
  pannable,
145
153
  fitView,
146
- enableFocusMode
154
+ enableFocusMode,
155
+ enableOverlays
147
156
  } = useDiagramState(s => ({
148
157
  isInitialized: s.initialized,
149
- zoomable: s.zoomable,
150
158
  pannable: s.pannable,
151
159
  fitView: s.fitViewEnabled,
152
- enableFocusMode: s.enableFocusMode
160
+ enableFocusMode: s.enableFocusMode,
161
+ enableOverlays: s.enableRelationshipsBrowser
153
162
  }))
154
163
 
155
164
  return (
@@ -158,7 +167,7 @@ const LikeC4DiagramInnerMemo = /* @__PURE__ */ memo<LikeC4DiagramInnerProps>(fun
158
167
  className={clsx(
159
168
  'likec4-diagram',
160
169
  css.cssReactFlow,
161
- controls === false && css.cssNoControls,
170
+ css.cssNoControls,
162
171
  pannable !== true && css.cssDisablePan,
163
172
  background === 'transparent' && css.cssTransparentBg
164
173
  )}
@@ -166,9 +175,9 @@ const LikeC4DiagramInnerMemo = /* @__PURE__ */ memo<LikeC4DiagramInnerProps>(fun
166
175
  <XYFlowInner
167
176
  showDiagramTitle={showDiagramTitle}
168
177
  background={background}
169
- controls={controls}
170
178
  />
171
179
  </XYFlow>
180
+ {enableOverlays && <Overlays />}
172
181
  {isInitialized && (
173
182
  <>
174
183
  {fitView && <FitViewOnDiagramChange />}
@@ -37,6 +37,8 @@ export function StaticLikeC4Diagram({
37
37
  showNotations={false}
38
38
  showRelationshipDetails={false}
39
39
  enableDynamicViewWalkthrough={false}
40
+ enableRelationshipsBrowser={false}
41
+ experimentalEdgeEditing={false}
40
42
  enableFocusMode={false}
41
43
  nodesSelectable={false}
42
44
  nodesDraggable={false}
@@ -0,0 +1,78 @@
1
+ import { createVar, keyframes, style } from '@vanilla-extract/css'
2
+ import { whereDark } from '../theme-vars'
3
+
4
+ const dialogFadeIn = keyframes({
5
+ '0%': {
6
+ opacity: 0,
7
+ display: 'none'
8
+ },
9
+ '100%': {
10
+ opacity: 1,
11
+ display: 'block'
12
+ }
13
+ })
14
+
15
+ const dialogFadeOut = keyframes({
16
+ '0%': {
17
+ opacity: 1,
18
+ transform: 'scale(1)',
19
+ display: 'block'
20
+ },
21
+ '100%': {
22
+ opacity: 0,
23
+ transform: 'scale(0.98)',
24
+ display: 'none'
25
+ }
26
+ })
27
+
28
+ const backdropRgb = createVar('backdrop-rgb')
29
+
30
+ const dialogBackdropFadeIn = keyframes({
31
+ '0%': {
32
+ WebkitBackdropFilter: 'blur(1px)',
33
+ backdropFilter: 'blur(1px)',
34
+ backgroundColor: `rgb(${backdropRgb} / 30%)`
35
+ },
36
+ '100%': {
37
+ WebkitBackdropFilter: 'blur(8px)',
38
+ backdropFilter: 'blur(8px)',
39
+ backgroundColor: `rgb(${backdropRgb} / 85%)`
40
+ }
41
+ })
42
+
43
+ export const dialog = style({
44
+ top: 0,
45
+ left: 0,
46
+ padding: 0,
47
+ margin: 0,
48
+ boxSizing: 'border-box',
49
+ border: '0 solid transparent',
50
+ width: '100%',
51
+ maxWidth: '100dvw',
52
+ height: '100%',
53
+ maxHeight: '100dvh',
54
+ background: 'transparent',
55
+ animation: `${dialogFadeOut} 130ms cubic-bezier(0.50, 0, 1, 1)`,
56
+ transformOrigin: '50% 20%',
57
+ vars: {
58
+ [backdropRgb]: '255 255 255'
59
+ },
60
+ selectors: {
61
+ [`${whereDark} &`]: {
62
+ vars: {
63
+ [backdropRgb]: '36 36 36'
64
+ }
65
+ },
66
+ '&[open]': {
67
+ animation: `${dialogFadeIn} 200ms cubic-bezier(0, 0, 0.40, 1) forwards`
68
+ },
69
+ '&::backdrop': {
70
+ WebkitBackdropFilter: 'blur(1px)',
71
+ backdropFilter: 'blur(1px)',
72
+ backgroundColor: `rgb(${backdropRgb} / 30%)`
73
+ },
74
+ '&[open]::backdrop': {
75
+ animation: `${dialogBackdropFadeIn} 350ms cubic-bezier(0, 0, 0.40, 1) forwards`
76
+ }
77
+ }
78
+ })
@@ -0,0 +1,102 @@
1
+ import { ActionIcon, Box } from '@mantine/core'
2
+ import { useMergedRef, useTimeout } from '@mantine/hooks'
3
+ import { useDebouncedEffect, useSyncedRef } from '@react-hookz/web'
4
+ import { IconX } from '@tabler/icons-react'
5
+ import clsx from 'clsx'
6
+ import { forwardRef, type HTMLAttributes, type ReactNode, useMemo, useRef, useState } from 'react'
7
+ import { useDiagramStoreApi } from '../hooks/useDiagramState'
8
+ import * as css from './Overlay.css'
9
+ import { OverlayContext, useOverlayDialog } from './OverlayContext'
10
+
11
+ type OverlayDialogProps = Pick<HTMLAttributes<HTMLDialogElement>, 'style' | 'className'> & {
12
+ onClose?: (() => void) | undefined
13
+ children: (renderProps: {
14
+ opened: boolean
15
+ close: () => void
16
+ }) => ReactNode
17
+ }
18
+
19
+ export const OverlayDialog = forwardRef<HTMLDialogElement, OverlayDialogProps>(({
20
+ className,
21
+ children,
22
+ onClose,
23
+ ...props
24
+ }, forwardedRef) => {
25
+ const onCloseRef = useSyncedRef(onClose)
26
+ const api = useDiagramStoreApi()
27
+ const [opened, setOpened] = useState(false)
28
+ const dialogRef = useRef<HTMLDialogElement>(null)
29
+ const ref = useMergedRef(dialogRef, forwardedRef)
30
+
31
+ useDebouncedEffect(
32
+ () => {
33
+ dialogRef.current?.showModal()
34
+ },
35
+ [],
36
+ 30
37
+ )
38
+
39
+ useDebouncedEffect(
40
+ () => {
41
+ setOpened(true)
42
+ },
43
+ [],
44
+ 80
45
+ )
46
+
47
+ const { start: triggerOnClose } = useTimeout(() => {
48
+ onCloseRef.current?.()
49
+ }, 300)
50
+
51
+ const ctxValue = useMemo(() => ({
52
+ openOverlay: api.getState().openOverlay,
53
+ close: () => {
54
+ dialogRef.current?.close()
55
+ }
56
+ }), [api])
57
+
58
+ return (
59
+ <OverlayContext.Provider value={ctxValue}>
60
+ <dialog
61
+ aria-modal="true"
62
+ ref={ref}
63
+ className={clsx(css.dialog, className)}
64
+ onClick={e => {
65
+ if ((e.target as any)?.nodeName?.toUpperCase() === 'DIALOG') {
66
+ e.stopPropagation()
67
+ dialogRef.current?.close()
68
+ }
69
+ }}
70
+ onClose={e => {
71
+ e.stopPropagation()
72
+ triggerOnClose()
73
+ }}
74
+ {...props}
75
+ >
76
+ {children({
77
+ opened,
78
+ ...ctxValue
79
+ })}
80
+ </dialog>
81
+ </OverlayContext.Provider>
82
+ )
83
+ })
84
+
85
+ export const OverlayDialogCloseButton = () => {
86
+ const { close } = useOverlayDialog()
87
+ return (
88
+ <Box pos={'absolute'} top={'1rem'} right={'1rem'}>
89
+ <ActionIcon
90
+ variant="default"
91
+ // color="gray"
92
+ size={'lg'}
93
+ autoFocus
94
+ onClick={(e) => {
95
+ e.stopPropagation()
96
+ close()
97
+ }}>
98
+ <IconX />
99
+ </ActionIcon>
100
+ </Box>
101
+ )
102
+ }
@@ -0,0 +1,11 @@
1
+ import { createContext, useContext } from 'react'
2
+ import type { DiagramState } from '../hooks'
3
+
4
+ export const OverlayContext = createContext({} as {
5
+ openOverlay: DiagramState['openOverlay']
6
+ close: () => void
7
+ })
8
+
9
+ export function useOverlayDialog() {
10
+ return useContext(OverlayContext)
11
+ }
@@ -0,0 +1,2 @@
1
+ export { OverlayDialog, OverlayDialogCloseButton } from './Overlay'
2
+ export { useOverlayDialog } from './OverlayContext'
@@ -1 +1,2 @@
1
+ export * from './useLikeC4ElementsTree'
1
2
  export * from './useLikeC4Model'
@@ -0,0 +1,44 @@
1
+ import {
2
+ compareNatural,
3
+ type Fqn,
4
+ type LikeC4DiagramModel,
5
+ type LikeC4Model,
6
+ type LikeC4ViewModel,
7
+ type ViewID
8
+ } from '@likec4/core'
9
+ import { useMemo } from 'react'
10
+ import { useLikeC4Model } from './useLikeC4Model'
11
+
12
+ interface LikeC4ModelTreeNodeData {
13
+ label: string
14
+ value: Fqn
15
+ children: LikeC4ModelTreeNodeData[]
16
+ }
17
+
18
+ const sortByLabel = (a: LikeC4ModelTreeNodeData, b: LikeC4ModelTreeNodeData) => compareNatural(a.label, b.label)
19
+
20
+ function buildNode(
21
+ element: LikeC4Model.ElementModel | LikeC4DiagramModel.Element | LikeC4ViewModel.Element
22
+ ): LikeC4ModelTreeNodeData {
23
+ return {
24
+ label: element.title,
25
+ value: element.id,
26
+ children: element.children().map(buildNode).sort(sortByLabel)
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Returns a tree of elements in the model.
32
+ * If `viewId` is provided, returns the tree of elements in the view.
33
+ */
34
+ export function useLikeC4ElementsTree(viewId?: ViewID): LikeC4ModelTreeNodeData[] {
35
+ const model = useLikeC4Model(true)
36
+ return useMemo(() => {
37
+ if (viewId) {
38
+ const view = model.view(viewId)
39
+ return view.roots().map(buildNode).sort(sortByLabel)
40
+ } else {
41
+ return model.roots().map(buildNode).sort(sortByLabel)
42
+ }
43
+ }, [model, viewId ?? null])
44
+ }
@@ -0,0 +1,13 @@
1
+ import { style } from '@vanilla-extract/css'
2
+
3
+ //
4
+ export const dependencyViewContainer = style({
5
+ width: '100%',
6
+ height: '100%'
7
+ // position: 'absolute',
8
+ // top: 20,
9
+ // left: 20,
10
+ // width: 'calc(100% - 40px)',
11
+ // height: 'calc(100% - 40px)',
12
+ // overflow: 'hidden',
13
+ })
@@ -0,0 +1,37 @@
1
+ import { type EdgeId, invariant } from '@likec4/core'
2
+ import { ActionIcon, Code, Paper } from '@mantine/core'
3
+ import { IconX } from '@tabler/icons-react'
4
+ import { OverlayDialog } from '../../components'
5
+ import { useDiagramState, useDiagramStoreApi } from '../../hooks'
6
+ import * as css from './EdgeDetailsOverlay.css'
7
+
8
+ export function EdgeDetailsOverlay({ edgeId }: { edgeId: EdgeId }) {
9
+ const diagramApi = useDiagramStoreApi()
10
+ const edge = useDiagramState(s => s.xyedges.find(e => e.id === edgeId))
11
+ invariant(edge, `Edge with id ${edgeId} not found`)
12
+ return (
13
+ <OverlayDialog
14
+ onClose={() => {
15
+ diagramApi.getState().closeOverlay()
16
+ }}
17
+ data-likec4-color="gray"
18
+ >
19
+ {({ close }) => (
20
+ <Paper className={css.dependencyViewContainer}>
21
+ <div>edge</div>
22
+ <Code block>{JSON.stringify(edge, null, 2)}</Code>
23
+ <ActionIcon
24
+ variant="default"
25
+ color="gray"
26
+ autoFocus
27
+ onClick={(e) => {
28
+ e.stopPropagation()
29
+ close()
30
+ }}>
31
+ <IconX />
32
+ </ActionIcon>
33
+ </Paper>
34
+ )}
35
+ </OverlayDialog>
36
+ )
37
+ }
@@ -0,0 +1,33 @@
1
+ import { memo } from 'react'
2
+ import { useDiagramState } from '../hooks/useDiagramState'
3
+ import { EdgeDetailsOverlay } from './edge-details/EdgeDetailsOverlay'
4
+ import { RelationshipsOfOverlay } from './relationships-of/RelationshipsOverlay'
5
+
6
+ export const Overlays = memo(() => {
7
+ const {
8
+ hasLikeC4Model,
9
+ activeOverlay
10
+ } = useDiagramState(s => ({
11
+ hasLikeC4Model: s.hasLikeC4Model,
12
+ activeOverlay: s.activeOverlay
13
+ }))
14
+
15
+ if (!activeOverlay) {
16
+ return null
17
+ }
18
+
19
+ if (!hasLikeC4Model) {
20
+ console.warn('LikeC4Model is not available, but overlays are active')
21
+ return null
22
+ }
23
+
24
+ if (activeOverlay.relationshipsOf) {
25
+ return <RelationshipsOfOverlay />
26
+ }
27
+
28
+ if (activeOverlay.edgeDetails) {
29
+ return <EdgeDetailsOverlay edgeId={activeOverlay.edgeDetails} />
30
+ }
31
+
32
+ return null
33
+ })
@@ -0,0 +1,33 @@
1
+ import { ReactFlowProvider as XYFlowProvider } from '@xyflow/react'
2
+ import { memo } from 'react'
3
+ import { OverlayDialog, OverlayDialogCloseButton } from '../../components'
4
+ import { useDiagramStoreApi } from '../../hooks'
5
+ import { RelationshipsXYFlow } from './RelationshipsXYFlow'
6
+ import * as css from './styles.css'
7
+
8
+ export const RelationshipsOfOverlay = memo(() => {
9
+ const diagramApi = useDiagramStoreApi()
10
+
11
+ return (
12
+ <OverlayDialog
13
+ className={css.overlay}
14
+ onClose={() => {
15
+ diagramApi.getState().closeOverlay()
16
+ }}
17
+ data-likec4-color="gray"
18
+ >
19
+ {({ opened }) => (
20
+ <>
21
+ {opened && (
22
+ <XYFlowProvider
23
+ defaultNodes={[]}
24
+ defaultEdges={[]}>
25
+ <RelationshipsXYFlow />
26
+ </XYFlowProvider>
27
+ )}
28
+ <OverlayDialogCloseButton />
29
+ </>
30
+ )}
31
+ </OverlayDialog>
32
+ )
33
+ })