@likec4/diagram 1.9.0 → 1.12.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.
- package/package.json +18 -41
- package/src/LikeC4CustomColors.tsx +1 -1
- package/src/LikeC4Diagram.css.ts +1 -1
- package/src/LikeC4Diagram.props.ts +40 -94
- package/src/LikeC4Diagram.tsx +12 -2
- package/src/LikeC4ModelProvider.tsx +49 -0
- package/src/StaticLikeC4Diagram.tsx +1 -0
- package/src/globals.css.ts +2 -3
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useMantinePortalProps.ts +8 -0
- package/src/hooks/useXYFlow.ts +1 -2
- package/src/index.ts +3 -0
- package/src/likec4model/LikeC4ModelContext.ts +4 -0
- package/src/likec4model/index.ts +1 -0
- package/src/likec4model/useLikeC4Model.ts +55 -0
- package/src/state/diagram-to-xyflow.ts +1 -2
- package/src/state/diagramStore.ts +185 -66
- package/src/{theme.css.ts → theme-vars.ts} +11 -4
- package/src/ui/DiagramTitlePanel.css.ts +2 -3
- package/src/ui/DiagramTitlePanel.tsx +17 -1
- package/src/ui/DynamicViewWalkthrough.css.ts +28 -2
- package/src/ui/DynamicViewWalkthrough.tsx +97 -52
- package/src/ui/OptionsPanel.css.ts +1 -2
- package/src/ui/OptionsPanel.tsx +1 -1
- package/src/ui/notation/NotationPanel.css.ts +2 -3
- package/src/ui/notation/NotationPanel.tsx +9 -10
- package/src/ui/options/NodeOptions.tsx +1 -1
- package/src/ui/top-left/ChangeAutoLayoutButton.tsx +20 -32
- package/src/ui/top-left/TopLeftPanel.tsx +128 -117
- package/src/ui/top-left/_shared.ts +7 -6
- package/src/ui/top-left/styles.css.ts +17 -8
- package/src/utils/vector.ts +57 -0
- package/src/xyflow/FitviewOnDiagramChange.tsx +5 -4
- package/src/xyflow/SelectEdgesOnNodeFocus.tsx +7 -5
- package/src/xyflow/XYFlow.tsx +9 -11
- package/src/xyflow/XYFlowEvents.tsx +59 -60
- package/src/xyflow/XYFlowInner.tsx +5 -5
- package/src/xyflow/edges/EdgeLabel.tsx +185 -0
- package/src/xyflow/edges/RelationshipEdge.tsx +170 -137
- package/src/xyflow/edges/RelationshipsDropdownMenu.css.ts +60 -0
- package/src/xyflow/edges/RelationshipsDropdownMenu.tsx +214 -0
- package/src/xyflow/edges/edges.css.ts +75 -7
- package/src/xyflow/nodes/compound/CompoundNode.css.ts +1 -2
- package/src/xyflow/nodes/compound/CompoundNode.tsx +4 -4
- package/src/xyflow/nodes/element/ElementLink.css.ts +1 -1
- package/src/xyflow/nodes/element/ElementLink.tsx +3 -8
- package/src/xyflow/nodes/element/ElementNode.tsx +44 -0
- package/src/xyflow/nodes/element/element.css.ts +1 -2
- package/src/xyflow/types.ts +1 -24
- package/src/xyflow/useLayoutConstraints.ts +10 -6
- package/dist/LikeC4CustomColors.d.ts +0 -3
- package/dist/LikeC4CustomColors.props.d.ts +0 -5
- package/dist/LikeC4Diagram.css.d.ts +0 -4
- package/dist/LikeC4Diagram.d.ts +0 -7
- package/dist/LikeC4Diagram.props.d.ts +0 -154
- package/dist/StaticLikeC4Diagram.d.ts +0 -4
- package/dist/globals.css.d.ts +0 -1
- package/dist/hooks/index.d.ts +0 -4
- package/dist/hooks/useDiagramState.d.ts +0 -6
- package/dist/hooks/useSetState.d.ts +0 -3
- package/dist/hooks/useUpdateEffect.d.ts +0 -5
- package/dist/hooks/useXYFlow.d.ts +0 -267
- package/dist/index.d.ts +0 -9
- package/dist/index.mjs +0 -3684
- package/dist/mantine/EnsureMantine.d.ts +0 -8
- package/dist/mantine.css.d.ts +0 -3
- package/dist/state/DiagramContext.d.ts +0 -1192
- package/dist/state/diagram-to-xyflow.d.ts +0 -12
- package/dist/state/diagramStore.d.ts +0 -1268
- package/dist/style.css +0 -2139
- package/dist/theme.css.d.ts +0 -63
- package/dist/ui/DiagramTitlePanel.css.d.ts +0 -4
- package/dist/ui/DiagramTitlePanel.d.ts +0 -1
- package/dist/ui/DynamicViewWalkthrough.css.d.ts +0 -3
- package/dist/ui/DynamicViewWalkthrough.d.ts +0 -1
- package/dist/ui/OptionsPanel.css.d.ts +0 -1
- package/dist/ui/OptionsPanel.d.ts +0 -1
- package/dist/ui/notation/NotationPanel.css.d.ts +0 -6
- package/dist/ui/notation/NotationPanel.d.ts +0 -1
- package/dist/ui/notation/index.d.ts +0 -1
- package/dist/ui/options/EdgeOptions.d.ts +0 -19
- package/dist/ui/options/NodeOptions.d.ts +0 -19
- package/dist/ui/top-left/ChangeAutoLayoutButton.d.ts +0 -3
- package/dist/ui/top-left/TopLeftPanel.d.ts +0 -4
- package/dist/ui/top-left/_shared.d.ts +0 -5
- package/dist/ui/top-left/styles.css.d.ts +0 -3
- package/dist/xyflow/EdgeMarkers.d.ts +0 -12
- package/dist/xyflow/FitviewOnDiagramChange.d.ts +0 -4
- package/dist/xyflow/SelectEdgesOnNodeFocus.d.ts +0 -1
- package/dist/xyflow/XYFlow.d.ts +0 -12
- package/dist/xyflow/XYFlowBackground.d.ts +0 -11
- package/dist/xyflow/XYFlowEvents.d.ts +0 -161
- package/dist/xyflow/XYFlowInner.d.ts +0 -10
- package/dist/xyflow/const.d.ts +0 -7
- package/dist/xyflow/edges/RelationshipEdge.d.ts +0 -32
- package/dist/xyflow/edges/edges.css.d.ts +0 -12
- package/dist/xyflow/edges/utils.d.ts +0 -17
- package/dist/xyflow/nodes/compound/CompoundNode.css.d.ts +0 -11
- package/dist/xyflow/nodes/compound/CompoundNode.d.ts +0 -6
- package/dist/xyflow/nodes/compound/index.d.ts +0 -1
- package/dist/xyflow/nodes/element/ElementLink.css.d.ts +0 -3
- package/dist/xyflow/nodes/element/ElementLink.d.ts +0 -7
- package/dist/xyflow/nodes/element/ElementNode.d.ts +0 -6
- package/dist/xyflow/nodes/element/ElementShapeSvg.d.ts +0 -18
- package/dist/xyflow/nodes/element/element.css.d.ts +0 -19
- package/dist/xyflow/nodes/element/index.d.ts +0 -1
- package/dist/xyflow/nodes/shared/NavigateToBtn.d.ts +0 -5
- package/dist/xyflow/types.d.ts +0 -50
- package/dist/xyflow/useLayoutConstraints.d.ts +0 -9
- package/dist/xyflow/utils.d.ts +0 -18
- package/src/mantine.css.ts +0 -7
- package/src/reset.d.ts +0 -2
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { extractStep } from '@likec4/core'
|
|
2
1
|
import type { ReactFlowProps } from '@xyflow/react'
|
|
3
2
|
import { useMemo, useRef } from 'react'
|
|
4
3
|
import { isNonNullish, isTruthy } from 'remeda'
|
|
@@ -46,12 +45,17 @@ export function useXYFlowEvents() {
|
|
|
46
45
|
}
|
|
47
46
|
dblclickTimeout.current = window.setTimeout(() => {
|
|
48
47
|
dblclickTimeout.current = undefined
|
|
49
|
-
},
|
|
48
|
+
}, 300)
|
|
50
49
|
return false
|
|
51
50
|
}
|
|
52
51
|
|
|
53
|
-
const lastClickWasRecent = (ms =
|
|
54
|
-
|
|
52
|
+
const lastClickWasRecent = (ms = 2000) => {
|
|
53
|
+
const prevTimestamp = lastClickTimestamp.current
|
|
54
|
+
lastClickTimestamp.current = Date.now()
|
|
55
|
+
if (prevTimestamp === undefined) {
|
|
56
|
+
return false
|
|
57
|
+
}
|
|
58
|
+
return prevTimestamp + ms > Date.now()
|
|
55
59
|
}
|
|
56
60
|
|
|
57
61
|
return ({
|
|
@@ -59,13 +63,11 @@ export function useXYFlowEvents() {
|
|
|
59
63
|
const {
|
|
60
64
|
onCanvasDblClick,
|
|
61
65
|
zoomable,
|
|
62
|
-
xystore,
|
|
63
66
|
viewportChanged,
|
|
64
67
|
fitDiagram,
|
|
65
|
-
|
|
68
|
+
resetFocusAndLastClicked
|
|
66
69
|
} = diagramApi.getState()
|
|
67
|
-
|
|
68
|
-
xystore.getState().resetSelectedElements()
|
|
70
|
+
resetFocusAndLastClicked()
|
|
69
71
|
if (!onCanvasDblClick) {
|
|
70
72
|
event.stopPropagation()
|
|
71
73
|
}
|
|
@@ -75,48 +77,46 @@ export function useXYFlowEvents() {
|
|
|
75
77
|
onCanvasDblClick?.(event)
|
|
76
78
|
},
|
|
77
79
|
onPaneClick: (event) => {
|
|
80
|
+
console.log('onPaneClick')
|
|
78
81
|
if (dbclickLock()) {
|
|
79
82
|
return
|
|
80
83
|
}
|
|
81
84
|
const {
|
|
82
85
|
focusedNodeId,
|
|
83
|
-
|
|
86
|
+
activeWalkthrough,
|
|
84
87
|
fitDiagram,
|
|
85
88
|
onCanvasClick,
|
|
86
|
-
|
|
87
|
-
resetLastClicked
|
|
89
|
+
resetFocusAndLastClicked
|
|
88
90
|
} = diagramApi.getState()
|
|
89
|
-
if ((focusedNodeId ??
|
|
91
|
+
if ((focusedNodeId ?? activeWalkthrough) !== null) {
|
|
90
92
|
fitDiagram()
|
|
91
93
|
if (!onCanvasClick) {
|
|
92
94
|
event.stopPropagation()
|
|
93
95
|
}
|
|
94
96
|
}
|
|
95
|
-
|
|
96
|
-
xystore.getState().resetSelectedElements()
|
|
97
|
+
resetFocusAndLastClicked()
|
|
97
98
|
onCanvasClick?.(event)
|
|
98
99
|
},
|
|
99
100
|
onNodeContextMenu: (event, xynode) => {
|
|
100
101
|
diagramApi.getState().setLastClickedNode(xynode.id)
|
|
101
|
-
diagramApi.getState().onNodeContextMenu?.(
|
|
102
|
-
|
|
103
|
-
xynode,
|
|
102
|
+
diagramApi.getState().onNodeContextMenu?.(
|
|
103
|
+
xynode.data.element,
|
|
104
104
|
event
|
|
105
|
-
|
|
105
|
+
)
|
|
106
106
|
},
|
|
107
107
|
onPaneContextMenu: (event) => {
|
|
108
|
-
diagramApi.getState().
|
|
108
|
+
diagramApi.getState().resetFocusAndLastClicked()
|
|
109
109
|
diagramApi.getState().onCanvasContextMenu?.(event as any)
|
|
110
110
|
},
|
|
111
111
|
onEdgeContextMenu: (event, xyedge) => {
|
|
112
112
|
diagramApi.getState().setLastClickedEdge(xyedge.id)
|
|
113
|
-
diagramApi.getState().onEdgeContextMenu?.(
|
|
114
|
-
|
|
115
|
-
xyedge,
|
|
113
|
+
diagramApi.getState().onEdgeContextMenu?.(
|
|
114
|
+
xyedge.data.edge,
|
|
116
115
|
event
|
|
117
|
-
|
|
116
|
+
)
|
|
118
117
|
},
|
|
119
118
|
onNodeClick: (event, xynode) => {
|
|
119
|
+
console.log('onNodeClick')
|
|
120
120
|
const {
|
|
121
121
|
focusedNodeId,
|
|
122
122
|
fitDiagram,
|
|
@@ -140,36 +140,31 @@ export function useXYFlowEvents() {
|
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
if (enableFocusMode || nodesSelectable) {
|
|
143
|
-
let stopPropagation = true
|
|
144
143
|
switch (true) {
|
|
145
|
-
case enableFocusMode &&
|
|
146
|
-
|
|
147
|
-
|
|
144
|
+
case enableFocusMode && clickedRecently && focusedNodeId === xynode.id: {
|
|
145
|
+
focusOnNode(false)
|
|
146
|
+
fitDiagram()
|
|
148
147
|
break
|
|
149
148
|
}
|
|
150
|
-
case enableFocusMode &&
|
|
151
|
-
|
|
149
|
+
case enableFocusMode && shallChangeFocus:
|
|
150
|
+
case enableFocusMode && clickedRecently && focusedNodeId !== xynode.id: {
|
|
151
|
+
focusOnNode(xynode.id)
|
|
152
152
|
break
|
|
153
153
|
}
|
|
154
154
|
case nodesSelectable: {
|
|
155
155
|
xystore.getState().addSelectedNodes([xynode.id])
|
|
156
156
|
break
|
|
157
157
|
}
|
|
158
|
-
default: {
|
|
159
|
-
stopPropagation = false
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
if (!onNodeClick && stopPropagation) {
|
|
163
|
-
// user did not provide a custom handler, stop propagation
|
|
164
|
-
event.stopPropagation()
|
|
165
158
|
}
|
|
166
159
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
160
|
+
if (!onNodeClick) {
|
|
161
|
+
// user did not provide a custom handler, stop propagation
|
|
162
|
+
event.stopPropagation()
|
|
163
|
+
}
|
|
164
|
+
onNodeClick?.(
|
|
165
|
+
xynode.data.element,
|
|
171
166
|
event
|
|
172
|
-
|
|
167
|
+
)
|
|
173
168
|
},
|
|
174
169
|
onNodeDoubleClick: (event, xynode) => {
|
|
175
170
|
const {
|
|
@@ -196,8 +191,8 @@ export function useXYFlowEvents() {
|
|
|
196
191
|
lastClickedEdgeId,
|
|
197
192
|
isDynamicView,
|
|
198
193
|
enableDynamicViewWalkthrough,
|
|
199
|
-
|
|
200
|
-
|
|
194
|
+
activateWalkthrough,
|
|
195
|
+
activeWalkthrough,
|
|
201
196
|
focusedNodeId,
|
|
202
197
|
xystore,
|
|
203
198
|
nodesSelectable,
|
|
@@ -209,22 +204,27 @@ export function useXYFlowEvents() {
|
|
|
209
204
|
setLastClickedEdge(xyedge.id)
|
|
210
205
|
}
|
|
211
206
|
const isNotAFirstClick = lastClickedEdgeId === xyedge.id
|
|
212
|
-
const
|
|
207
|
+
const connectedToFocusedNode = isTruthy(focusedNodeId)
|
|
213
208
|
&& (focusedNodeId === xyedge.source || focusedNodeId === xyedge.target)
|
|
214
209
|
if (
|
|
215
210
|
isDynamicView && enableDynamicViewWalkthrough
|
|
216
|
-
&& (
|
|
211
|
+
&& (connectedToFocusedNode || isNotAFirstClick || isNonNullish(activeWalkthrough))
|
|
217
212
|
) {
|
|
218
|
-
const nextStep =
|
|
219
|
-
if (
|
|
220
|
-
|
|
213
|
+
const nextStep = xyedge.data.edge.id
|
|
214
|
+
if (activeWalkthrough?.stepId !== nextStep) {
|
|
215
|
+
activateWalkthrough(nextStep)
|
|
221
216
|
event.stopPropagation()
|
|
222
217
|
return
|
|
223
218
|
}
|
|
224
219
|
}
|
|
225
|
-
// if we
|
|
226
|
-
if (
|
|
227
|
-
|
|
220
|
+
// if we are in focus mode
|
|
221
|
+
if (isTruthy(focusedNodeId)) {
|
|
222
|
+
// if we focused on a node, and clicked on an edge connected to it - focus on the other node
|
|
223
|
+
if (connectedToFocusedNode) {
|
|
224
|
+
focusOnNode(focusedNodeId === xyedge.source ? xyedge.target : xyedge.source)
|
|
225
|
+
} else {
|
|
226
|
+
focusOnNode(xyedge.source)
|
|
227
|
+
}
|
|
228
228
|
if (!onEdgeClick) {
|
|
229
229
|
event.stopPropagation()
|
|
230
230
|
}
|
|
@@ -234,11 +234,10 @@ export function useXYFlowEvents() {
|
|
|
234
234
|
event.stopPropagation()
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
|
-
onEdgeClick?.(
|
|
238
|
-
|
|
239
|
-
xyedge,
|
|
237
|
+
onEdgeClick?.(
|
|
238
|
+
xyedge.data.edge,
|
|
240
239
|
event
|
|
241
|
-
|
|
240
|
+
)
|
|
242
241
|
},
|
|
243
242
|
onEdgeDoubleClick: (event, xyedge) => {
|
|
244
243
|
diagramApi.getState().setLastClickedEdge(xyedge.id)
|
|
@@ -247,14 +246,14 @@ export function useXYFlowEvents() {
|
|
|
247
246
|
isDynamicView,
|
|
248
247
|
enableDynamicViewWalkthrough,
|
|
249
248
|
focusOnNode,
|
|
250
|
-
|
|
251
|
-
|
|
249
|
+
activeWalkthrough,
|
|
250
|
+
activateWalkthrough
|
|
252
251
|
} = diagramApi.getState()
|
|
253
252
|
// if we are in dynamic view, and clicked on an edge, activate the step
|
|
254
253
|
if (isDynamicView && enableDynamicViewWalkthrough) {
|
|
255
|
-
const nextStep =
|
|
256
|
-
if (
|
|
257
|
-
|
|
254
|
+
const nextStep = xyedge.data.edge.id
|
|
255
|
+
if (activeWalkthrough?.stepId !== nextStep) {
|
|
256
|
+
activateWalkthrough(nextStep)
|
|
258
257
|
event.stopPropagation()
|
|
259
258
|
}
|
|
260
259
|
return
|
|
@@ -17,7 +17,7 @@ type XYFlowInnerProps = PropsWithChildren<{
|
|
|
17
17
|
showDiagramTitle: boolean
|
|
18
18
|
}>
|
|
19
19
|
|
|
20
|
-
export const XYFlowInner = memo(function XYFlowInnerR({
|
|
20
|
+
export const XYFlowInner = /* @__PURE__ */ memo(function XYFlowInnerR({
|
|
21
21
|
children,
|
|
22
22
|
background,
|
|
23
23
|
controls,
|
|
@@ -34,18 +34,18 @@ export const XYFlowInner = memo(function XYFlowInnerR({
|
|
|
34
34
|
isDynamicView: s.isDynamicView,
|
|
35
35
|
readonly: s.readonly,
|
|
36
36
|
enableDynamicViewWalkthrough: s.enableDynamicViewWalkthrough,
|
|
37
|
-
isDynamicViewActive: isNonNullish(s.
|
|
37
|
+
isDynamicViewActive: isNonNullish(s.activeWalkthrough)
|
|
38
38
|
}))
|
|
39
39
|
|
|
40
40
|
const isBgWithPattern = background !== 'transparent' && background !== 'solid'
|
|
41
41
|
|
|
42
|
-
const isDiagramTitleVisible = showDiagramTitle
|
|
42
|
+
const isDiagramTitleVisible = showDiagramTitle && !isDynamicViewActive
|
|
43
43
|
return (
|
|
44
44
|
<>
|
|
45
45
|
{isBgWithPattern && <XYFlowBackground background={background} />}
|
|
46
|
-
{controls && <Controls position={'
|
|
46
|
+
{controls && <Controls showInteractive={false} position={'top-center'} />}
|
|
47
47
|
<TopLeftPanel />
|
|
48
|
-
{readonly
|
|
48
|
+
{!readonly && <OptionsPanel />}
|
|
49
49
|
{isDiagramTitleVisible && <DiagramTitlePanel />}
|
|
50
50
|
{isDynamicView && enableDynamicViewWalkthrough && <DynamicViewWalkthrough />}
|
|
51
51
|
{showNotations && <NotationPanel />}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { extractStep, isStepEdgeId, type ViewID } from '@likec4/core'
|
|
2
|
+
import {
|
|
3
|
+
ActionIcon,
|
|
4
|
+
Box,
|
|
5
|
+
type BoxProps,
|
|
6
|
+
Button,
|
|
7
|
+
CloseButton,
|
|
8
|
+
Group,
|
|
9
|
+
Popover,
|
|
10
|
+
PopoverDropdown,
|
|
11
|
+
PopoverTarget,
|
|
12
|
+
ScrollAreaAutosize,
|
|
13
|
+
Stack,
|
|
14
|
+
Text
|
|
15
|
+
} from '@mantine/core'
|
|
16
|
+
import { useDebouncedEffect } from '@react-hookz/web'
|
|
17
|
+
import { IconZoomScan } from '@tabler/icons-react'
|
|
18
|
+
import { EdgeLabelRenderer } from '@xyflow/react'
|
|
19
|
+
import clsx from 'clsx'
|
|
20
|
+
import { type PropsWithChildren, type ReactNode, useState } from 'react'
|
|
21
|
+
import { isTruthy } from 'remeda'
|
|
22
|
+
import { useDiagramState, useDiagramStoreApi, useMantinePortalProps } from '../../hooks'
|
|
23
|
+
import { useLikeC4Model } from '../../likec4model/useLikeC4Model'
|
|
24
|
+
import type { RelationshipData } from '../types'
|
|
25
|
+
import * as edgesCss from './edges.css'
|
|
26
|
+
import { RelationshipsDropdownMenu } from './RelationshipsDropdownMenu'
|
|
27
|
+
|
|
28
|
+
export interface EdgeLabelProps extends Omit<BoxProps, 'label'> {
|
|
29
|
+
isDimmed: boolean
|
|
30
|
+
edgeData: RelationshipData
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const EdgeLabel = ({
|
|
34
|
+
isDimmed,
|
|
35
|
+
edgeData: {
|
|
36
|
+
label,
|
|
37
|
+
edge
|
|
38
|
+
},
|
|
39
|
+
...props
|
|
40
|
+
}: EdgeLabelProps) => {
|
|
41
|
+
const likec4model = useLikeC4Model()
|
|
42
|
+
const {
|
|
43
|
+
showRelationshipDetails,
|
|
44
|
+
isActiveWalkthroughStep,
|
|
45
|
+
hasOnNavigateTo
|
|
46
|
+
} = useDiagramState(s => ({
|
|
47
|
+
showRelationshipDetails: s.showRelationshipDetails,
|
|
48
|
+
isActiveWalkthroughStep: s.activeWalkthrough?.stepId === edge.id,
|
|
49
|
+
hasOnNavigateTo: !!s.onNavigateTo
|
|
50
|
+
}))
|
|
51
|
+
const notes = isActiveWalkthroughStep ? (edge.notes ?? null) : null
|
|
52
|
+
const stepNum = isStepEdgeId(edge.id) ? extractStep(edge.id) : null
|
|
53
|
+
const navigateTo = hasOnNavigateTo ? edge.navigateTo ?? null : null
|
|
54
|
+
|
|
55
|
+
const wrapWithPopover = (node: ReactNode) => {
|
|
56
|
+
if (isTruthy(notes)) {
|
|
57
|
+
return <NotePopover notes={notes}>{node}</NotePopover>
|
|
58
|
+
}
|
|
59
|
+
if (showRelationshipDetails && likec4model && edge.relations.length > 0) {
|
|
60
|
+
return (
|
|
61
|
+
<RelationshipsDropdownMenu
|
|
62
|
+
disabled={isDimmed}
|
|
63
|
+
likec4model={likec4model}
|
|
64
|
+
edge={edge}>
|
|
65
|
+
{node}
|
|
66
|
+
</RelationshipsDropdownMenu>
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
return node
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<EdgeLabelRenderer>
|
|
74
|
+
{wrapWithPopover(
|
|
75
|
+
<Box {...props}>
|
|
76
|
+
{stepNum !== null && (
|
|
77
|
+
<Box className={edgesCss.stepEdgeNumber}>
|
|
78
|
+
{stepNum}
|
|
79
|
+
</Box>
|
|
80
|
+
)}
|
|
81
|
+
{isTruthy(label?.text) && (
|
|
82
|
+
<Box className={edgesCss.edgeLabelText}>
|
|
83
|
+
{label.text}
|
|
84
|
+
</Box>
|
|
85
|
+
)}
|
|
86
|
+
{navigateTo && <NavigateToBtn viewId={navigateTo} />}
|
|
87
|
+
</Box>
|
|
88
|
+
)}
|
|
89
|
+
</EdgeLabelRenderer>
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const NotePopover = ({ notes, children }: PropsWithChildren<{ notes: string }>) => {
|
|
94
|
+
const {
|
|
95
|
+
nextDynamicStep,
|
|
96
|
+
hasNext,
|
|
97
|
+
hasPrevious
|
|
98
|
+
} = useDiagramState(s => ({
|
|
99
|
+
nextDynamicStep: s.nextDynamicStep,
|
|
100
|
+
hasNext: s.activeWalkthrough?.hasNext ?? false,
|
|
101
|
+
hasPrevious: s.activeWalkthrough?.hasPrevious ?? false
|
|
102
|
+
}))
|
|
103
|
+
|
|
104
|
+
const [isOpened, setIsOpened] = useState(false)
|
|
105
|
+
const portalProps = useMantinePortalProps()
|
|
106
|
+
|
|
107
|
+
useDebouncedEffect(
|
|
108
|
+
() => {
|
|
109
|
+
setIsOpened(true)
|
|
110
|
+
},
|
|
111
|
+
[],
|
|
112
|
+
300
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<Popover
|
|
117
|
+
shadow="xs"
|
|
118
|
+
offset={16}
|
|
119
|
+
opened={isOpened}
|
|
120
|
+
closeOnClickOutside={false}
|
|
121
|
+
{...portalProps}>
|
|
122
|
+
<PopoverTarget>
|
|
123
|
+
{children}
|
|
124
|
+
</PopoverTarget>
|
|
125
|
+
<PopoverDropdown
|
|
126
|
+
component={Stack}
|
|
127
|
+
p={'xs'}
|
|
128
|
+
onPointerDownCapture={e => e.stopPropagation()}
|
|
129
|
+
onClick={e => e.stopPropagation()}
|
|
130
|
+
onDoubleClick={e => e.stopPropagation()}
|
|
131
|
+
>
|
|
132
|
+
<ScrollAreaAutosize maw={450} mah={350} type="scroll" mx={'auto'} mt={2}>
|
|
133
|
+
<Text component="div" className={edgesCss.edgeNoteText} p={4}>{notes}</Text>
|
|
134
|
+
</ScrollAreaAutosize>
|
|
135
|
+
<CloseButton
|
|
136
|
+
size={'xs'}
|
|
137
|
+
className={edgesCss.edgeNoteCloseButton}
|
|
138
|
+
onClick={() => setIsOpened(false)}
|
|
139
|
+
/>
|
|
140
|
+
{(hasPrevious || hasNext) && (
|
|
141
|
+
<Group gap={0} justify={hasPrevious ? 'flex-start' : 'flex-end'}>
|
|
142
|
+
{hasPrevious && (
|
|
143
|
+
<Button
|
|
144
|
+
variant="subtle"
|
|
145
|
+
radius={'xs'}
|
|
146
|
+
size="compact-xs"
|
|
147
|
+
onClick={() => nextDynamicStep(-1)}>
|
|
148
|
+
back
|
|
149
|
+
</Button>
|
|
150
|
+
)}
|
|
151
|
+
{hasNext && (
|
|
152
|
+
<Button variant="subtle" radius={'xs'} size="compact-xs" onClick={() => nextDynamicStep()}>
|
|
153
|
+
next
|
|
154
|
+
</Button>
|
|
155
|
+
)}
|
|
156
|
+
</Group>
|
|
157
|
+
)}
|
|
158
|
+
</PopoverDropdown>
|
|
159
|
+
</Popover>
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
type NavigateToBtnProps = {
|
|
164
|
+
viewId: ViewID
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function NavigateToBtn({ viewId }: NavigateToBtnProps) {
|
|
168
|
+
const diagramApi = useDiagramStoreApi()
|
|
169
|
+
return (
|
|
170
|
+
<ActionIcon
|
|
171
|
+
className={clsx('nodrag nopan', edgesCss.cssNavigateBtn)}
|
|
172
|
+
size={'sm'}
|
|
173
|
+
radius="sm"
|
|
174
|
+
onPointerDownCapture={e => e.stopPropagation()}
|
|
175
|
+
onClick={event => {
|
|
176
|
+
event.stopPropagation()
|
|
177
|
+
diagramApi.getState().onNavigateTo?.(viewId, event)
|
|
178
|
+
}}
|
|
179
|
+
role="button"
|
|
180
|
+
onDoubleClick={event => event.stopPropagation()}
|
|
181
|
+
>
|
|
182
|
+
<IconZoomScan className={edgesCss.cssNavigateBtnIcon} />
|
|
183
|
+
</ActionIcon>
|
|
184
|
+
)
|
|
185
|
+
}
|