@likec4/diagram 1.17.0 → 1.18.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 +24 -18
- package/src/LikeC4Diagram.props.ts +27 -12
- package/src/LikeC4Diagram.tsx +14 -9
- package/src/LikeC4Search.tsx +149 -0
- package/src/StaticLikeC4Diagram.tsx +1 -0
- package/src/hooks/useDiagramState.ts +8 -3
- package/src/hooks/useXYFlow.ts +2 -1
- package/src/likec4model/useLikeC4ElementsTree.ts +6 -13
- package/src/likec4model/useLikeC4Model.ts +21 -11
- package/src/overlays/Overlays.tsx +96 -63
- package/src/overlays/edge-details/EdgeDetailsXYFlow.tsx +3 -1
- package/src/overlays/edge-details/_types.ts +7 -3
- package/src/overlays/edge-details/use-layouted-edge-details.ts +21 -14
- package/src/overlays/edge-details/xyflow/ElementNode.tsx +4 -2
- package/src/overlays/edge-details/xyflow/RelationshipEdge.tsx +4 -4
- package/src/overlays/element-details/ElementDetailsCard.tsx +94 -81
- package/src/overlays/element-details/TabPanelDeployments.css.ts +69 -0
- package/src/overlays/element-details/TabPanelDeployments.tsx +217 -0
- package/src/overlays/element-details/TabPanelRelationships.tsx +8 -9
- package/src/overlays/element-details/TabPanelStructure.tsx +10 -10
- package/src/overlays/relationships-of/RelationshipsOverlay.tsx +1 -1
- package/src/overlays/relationships-of/RelationshipsXYFlow.tsx +3 -11
- package/src/overlays/relationships-of/SelectElement.tsx +2 -2
- package/src/overlays/relationships-of/_types.ts +2 -2
- package/src/overlays/relationships-of/use-layouted-relationships.ts +18 -19
- package/src/overlays/relationships-of/xyflow/CompoundNode.tsx +1 -3
- package/src/overlays/relationships-of/xyflow/ElementNode.tsx +12 -6
- package/src/overlays/relationships-of/xyflow/RelationshipEdge.tsx +0 -1
- package/src/state/aligners.ts +473 -0
- package/src/state/diagramStore.layout.ts +67 -0
- package/src/state/diagramStore.ts +31 -77
- package/src/ui/DiagramTitlePanel.tsx +2 -29
- package/src/ui/Link.tsx +40 -0
- package/src/ui/notation/NotationPanel.tsx +4 -2
- package/src/ui/top-left/ChangeAutoLayoutButton.tsx +2 -2
- package/src/ui/top-left/ManualLayoutToolsButton.tsx +15 -0
- package/src/ui/top-left/TopLeftPanel.tsx +16 -3
- package/src/xyflow/edges/EdgeLabel.tsx +4 -4
- package/src/xyflow/edges/RelationshipsDropdownMenu.tsx +20 -7
- package/src/xyflow/nodes/compound/CompoundNode.css.ts +28 -13
- package/src/xyflow/nodes/compound/CompoundNode.tsx +51 -19
- package/src/xyflow/nodes/element/ElementNode.tsx +153 -92
- package/src/xyflow/nodes/element/element.css.ts +53 -11
- package/src/xyflow/nodes/shared/ElementIcon.tsx +1 -1
- package/src/xyflow/nodes/shared/Toolbar.tsx +24 -8
- package/src/xyflow/useLayoutConstraints.ts +1 -1
- package/src/xyflow/useXYFlowEvents.ts +18 -5
- package/src/ui/OptionsPanel.css.ts +0 -9
- package/src/ui/OptionsPanel.tsx +0 -70
- package/src/ui/options/EdgeOptions.tsx +0 -142
- package/src/ui/options/NodeOptions.tsx +0 -362
- package/src/xyflow/nodes/element/ElementDetails.css.ts +0 -101
- package/src/xyflow/nodes/element/ElementDetails.tsx +0 -393
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { type ThemeColor } from '@likec4/core'
|
|
1
|
+
import { DiagramNode, type ThemeColor } from '@likec4/core'
|
|
2
2
|
import { ActionIcon, Box, Text, Tooltip } from '@mantine/core'
|
|
3
3
|
import { useDebouncedValue } from '@mantine/hooks'
|
|
4
|
-
import { IconId,
|
|
4
|
+
import { IconId, IconZoomScan } from '@tabler/icons-react'
|
|
5
5
|
import { assignInlineVars } from '@vanilla-extract/dynamic'
|
|
6
6
|
import { Handle, type NodeProps, Position } from '@xyflow/react'
|
|
7
7
|
import clsx from 'clsx'
|
|
8
8
|
import { deepEqual as eq } from 'fast-equals'
|
|
9
9
|
import { m, type Variants } from 'framer-motion'
|
|
10
10
|
import { memo, useCallback, useState } from 'react'
|
|
11
|
-
import { clamp
|
|
11
|
+
import { clamp } from 'remeda'
|
|
12
12
|
import { useDiagramState } from '../../../hooks/useDiagramState'
|
|
13
13
|
import type { CompoundXYFlowNode } from '../../types'
|
|
14
14
|
import { stopPropagation } from '../../utils'
|
|
@@ -30,14 +30,28 @@ const isEqualProps = (prev: CompoundNodeProps, next: CompoundNodeProps) => (
|
|
|
30
30
|
)
|
|
31
31
|
|
|
32
32
|
const VariantsRoot = {
|
|
33
|
-
idle: {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
idle: (_, { translateZ }) => ({
|
|
34
|
+
// Why? translateZ is used to determine state
|
|
35
|
+
...translateZ !== 0 && {
|
|
36
|
+
transition: {
|
|
37
|
+
delayChildren: .08
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
transitionEnd: {
|
|
41
|
+
translateZ: 0
|
|
37
42
|
}
|
|
38
|
-
},
|
|
43
|
+
}),
|
|
39
44
|
selected: {},
|
|
40
|
-
hovered: {}
|
|
45
|
+
hovered: (_, { translateZ }) => ({
|
|
46
|
+
...translateZ !== 1 && {
|
|
47
|
+
transition: {
|
|
48
|
+
delayChildren: .08
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
transitionEnd: {
|
|
52
|
+
translateZ: 1
|
|
53
|
+
}
|
|
54
|
+
}),
|
|
41
55
|
tap: {}
|
|
42
56
|
} satisfies Variants
|
|
43
57
|
|
|
@@ -61,18 +75,21 @@ const VariantsNavigate = {
|
|
|
61
75
|
'hovered:navigate': {
|
|
62
76
|
scale: 1.42
|
|
63
77
|
},
|
|
64
|
-
'hovered:
|
|
78
|
+
'hovered:details': {},
|
|
65
79
|
'tap:navigate': {
|
|
66
80
|
scale: 1.15
|
|
67
81
|
}
|
|
68
82
|
} satisfies Variants
|
|
69
|
-
VariantsNavigate['selected'] = VariantsNavigate
|
|
83
|
+
VariantsNavigate['selected'] = VariantsNavigate.hovered
|
|
84
|
+
VariantsNavigate['hovered:details'] = VariantsNavigate.idle
|
|
70
85
|
|
|
71
86
|
const VariantsDetailsBtn = {
|
|
72
87
|
idle: {
|
|
73
88
|
'--ai-bg': 'var(--ai-bg-idle)',
|
|
74
89
|
scale: 1,
|
|
75
|
-
opacity: 0.3
|
|
90
|
+
opacity: 0.3,
|
|
91
|
+
originX: 0.45,
|
|
92
|
+
originY: 0.55
|
|
76
93
|
},
|
|
77
94
|
selected: {},
|
|
78
95
|
hovered: {
|
|
@@ -100,15 +117,17 @@ export const CompoundNodeMemo = /* @__PURE__ */ memo<CompoundNodeProps>((
|
|
|
100
117
|
}
|
|
101
118
|
}
|
|
102
119
|
) => {
|
|
120
|
+
const modelRef = DiagramNode.modelRef(element)
|
|
103
121
|
const { depth, style, color } = element
|
|
104
122
|
const isNotViewGroup = !isViewGroup
|
|
105
123
|
const opacity = clamp((style.opacity ?? 100) / 100, {
|
|
106
124
|
min: 0,
|
|
107
125
|
max: 1
|
|
108
126
|
})
|
|
109
|
-
const
|
|
127
|
+
const MAX_TRANSPARENCY = 40
|
|
128
|
+
const borderTransparency = clamp(MAX_TRANSPARENCY - opacity * MAX_TRANSPARENCY, {
|
|
110
129
|
min: 0,
|
|
111
|
-
max:
|
|
130
|
+
max: MAX_TRANSPARENCY
|
|
112
131
|
})
|
|
113
132
|
|
|
114
133
|
const {
|
|
@@ -121,6 +140,7 @@ export const CompoundNodeMemo = /* @__PURE__ */ memo<CompoundNodeProps>((
|
|
|
121
140
|
isInteractive,
|
|
122
141
|
isNavigable,
|
|
123
142
|
renderIcon,
|
|
143
|
+
isInActiveOverlay,
|
|
124
144
|
enableElementDetails
|
|
125
145
|
} = useDiagramState(s => ({
|
|
126
146
|
viewId: s.view.id,
|
|
@@ -134,6 +154,7 @@ export const CompoundNodeMemo = /* @__PURE__ */ memo<CompoundNodeProps>((
|
|
|
134
154
|
// If this is a view group, we don't want to show the navigate button
|
|
135
155
|
isNavigable: isNotViewGroup && !!s.onNavigateTo && !!element.navigateTo,
|
|
136
156
|
renderIcon: s.renderIcon,
|
|
157
|
+
isInActiveOverlay: (s.activeOverlay?.elementDetails ?? s.activeOverlay?.relationshipsOf) === id,
|
|
137
158
|
enableElementDetails: isNotViewGroup && s.enableElementDetails
|
|
138
159
|
}))
|
|
139
160
|
const _isToolbarVisible = isNotViewGroup && isEditable && (isHovered || (import.meta.env.DEV && selected))
|
|
@@ -143,6 +164,9 @@ export const CompoundNodeMemo = /* @__PURE__ */ memo<CompoundNodeProps>((
|
|
|
143
164
|
|
|
144
165
|
let animate: keyof typeof VariantsRoot
|
|
145
166
|
switch (true) {
|
|
167
|
+
case isInActiveOverlay:
|
|
168
|
+
animate = 'idle'
|
|
169
|
+
break
|
|
146
170
|
case dragging && selected:
|
|
147
171
|
animate = 'selected'
|
|
148
172
|
break
|
|
@@ -169,7 +193,7 @@ export const CompoundNodeMemo = /* @__PURE__ */ memo<CompoundNodeProps>((
|
|
|
169
193
|
const onOpenDetails = useCallback((e: React.MouseEvent) => {
|
|
170
194
|
e.stopPropagation()
|
|
171
195
|
openOverlay({ elementDetails: element.id })
|
|
172
|
-
}, [openOverlay, element
|
|
196
|
+
}, [openOverlay, element])
|
|
173
197
|
|
|
174
198
|
const elementIcon = ElementIcon({
|
|
175
199
|
element,
|
|
@@ -189,6 +213,7 @@ export const CompoundNodeMemo = /* @__PURE__ */ memo<CompoundNodeProps>((
|
|
|
189
213
|
)}
|
|
190
214
|
<Box
|
|
191
215
|
component={m.div}
|
|
216
|
+
variants={VariantsRoot}
|
|
192
217
|
key={`${viewId}:element:${id}`}
|
|
193
218
|
layoutId={`${viewId}:element:${id}`}
|
|
194
219
|
className={css.containerForFramer}>
|
|
@@ -196,7 +221,7 @@ export const CompoundNodeMemo = /* @__PURE__ */ memo<CompoundNodeProps>((
|
|
|
196
221
|
component={m.div}
|
|
197
222
|
variants={VariantsRoot}
|
|
198
223
|
initial={false}
|
|
199
|
-
animate={(isHovered && !dragging) ? (animateVariants ?? animate) : animate}
|
|
224
|
+
animate={(isHovered && !dragging && !isInActiveOverlay) ? (animateVariants ?? animate) : animate}
|
|
200
225
|
className={clsx(
|
|
201
226
|
css.container,
|
|
202
227
|
'likec4-compound-node',
|
|
@@ -237,7 +262,13 @@ export const CompoundNodeMemo = /* @__PURE__ */ memo<CompoundNodeProps>((
|
|
|
237
262
|
[css.varBorderTransparency]: `${borderTransparency}%`,
|
|
238
263
|
[css.varOpacity]: opacity.toFixed(2)
|
|
239
264
|
}),
|
|
240
|
-
|
|
265
|
+
...style.border === 'none'
|
|
266
|
+
? {
|
|
267
|
+
borderColor: 'transparent'
|
|
268
|
+
}
|
|
269
|
+
: {
|
|
270
|
+
borderStyle: style.border ?? 'dashed'
|
|
271
|
+
}
|
|
241
272
|
})
|
|
242
273
|
}}
|
|
243
274
|
>
|
|
@@ -255,7 +286,7 @@ export const CompoundNodeMemo = /* @__PURE__ */ memo<CompoundNodeProps>((
|
|
|
255
286
|
className={css.title}>
|
|
256
287
|
{element.title}
|
|
257
288
|
</Text>
|
|
258
|
-
{enableElementDetails && (
|
|
289
|
+
{enableElementDetails && !!modelRef && (
|
|
259
290
|
<Tooltip
|
|
260
291
|
fz="xs"
|
|
261
292
|
color="dark"
|
|
@@ -264,6 +295,7 @@ export const CompoundNodeMemo = /* @__PURE__ */ memo<CompoundNodeProps>((
|
|
|
264
295
|
offset={2}
|
|
265
296
|
openDelay={600}>
|
|
266
297
|
<ActionIcon
|
|
298
|
+
key={`${id}details`}
|
|
267
299
|
component={m.div}
|
|
268
300
|
variants={VariantsDetailsBtn}
|
|
269
301
|
data-animate-target="details"
|
|
@@ -283,7 +315,7 @@ export const CompoundNodeMemo = /* @__PURE__ */ memo<CompoundNodeProps>((
|
|
|
283
315
|
</Box>
|
|
284
316
|
{isNavigable && (
|
|
285
317
|
<ActionIcon
|
|
286
|
-
key={
|
|
318
|
+
key={`${id}navigate`}
|
|
287
319
|
component={m.div}
|
|
288
320
|
variants={VariantsNavigate}
|
|
289
321
|
data-animate-target="navigate"
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { type ThemeColor } from '@likec4/core'
|
|
2
|
-
import { ActionIcon, Box, Text as MantineText, Tooltip } from '@mantine/core'
|
|
1
|
+
import { DiagramNode, type ThemeColor } from '@likec4/core'
|
|
2
|
+
import { ActionIcon, type ActionIconProps, Box, Text as MantineText, Tooltip } from '@mantine/core'
|
|
3
3
|
import { useDebouncedValue } from '@mantine/hooks'
|
|
4
|
-
import { IconId, IconZoomScan } from '@tabler/icons-react'
|
|
4
|
+
import { IconId, IconTransform, IconZoomScan } from '@tabler/icons-react'
|
|
5
5
|
import { Handle, type NodeProps, Position } from '@xyflow/react'
|
|
6
6
|
import clsx from 'clsx'
|
|
7
7
|
import { deepEqual as eq } from 'fast-equals'
|
|
8
|
-
import { m, type Variants } from 'framer-motion'
|
|
9
|
-
import React, { memo, useCallback, useState } from 'react'
|
|
8
|
+
import { type HTMLMotionProps, m, type Variants } from 'framer-motion'
|
|
9
|
+
import React, { memo, type PropsWithoutRef, useCallback, useState } from 'react'
|
|
10
10
|
import { isNumber, isTruthy } from 'remeda'
|
|
11
11
|
import { useDiagramState } from '../../../hooks/useDiagramState'
|
|
12
12
|
import type { ElementXYFlowNode } from '../../types'
|
|
@@ -54,35 +54,60 @@ const VariantsRoot = {
|
|
|
54
54
|
}
|
|
55
55
|
} satisfies Variants
|
|
56
56
|
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
57
|
+
const variantsBottomButton = (target: 'navigate' | 'relationships', align: 'left' | 'right' | false) => {
|
|
58
|
+
const variants = {
|
|
59
|
+
idle: {
|
|
60
|
+
'--icon-scale': 'scale(1)',
|
|
61
|
+
'--ai-bg': 'var(--ai-bg-idle)',
|
|
62
|
+
scale: 1,
|
|
63
|
+
opacity: 0.5,
|
|
64
|
+
originX: 0.5,
|
|
65
|
+
originY: 0.35,
|
|
66
|
+
translateY: 0,
|
|
67
|
+
...align === 'left' && {
|
|
68
|
+
originX: 0.75,
|
|
69
|
+
translateX: -1
|
|
70
|
+
},
|
|
71
|
+
...align === 'right' && {
|
|
72
|
+
originX: 0.25,
|
|
73
|
+
translateX: 1
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
selected: {},
|
|
77
|
+
hovered: {
|
|
78
|
+
'--icon-scale': 'scale(1)',
|
|
79
|
+
'--ai-bg': 'var(--ai-bg-hover)',
|
|
80
|
+
translateY: 3,
|
|
81
|
+
scale: 1.32,
|
|
82
|
+
opacity: 1,
|
|
83
|
+
...align === 'left' && {
|
|
84
|
+
translateX: -4
|
|
85
|
+
},
|
|
86
|
+
...align === 'right' && {
|
|
87
|
+
translateX: 4
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
'hovered:details': {},
|
|
91
|
+
[`hovered:${target}`]: {
|
|
92
|
+
'--icon-scale': 'scale(1.08)',
|
|
93
|
+
scale: 1.45
|
|
94
|
+
},
|
|
95
|
+
[`tap:${target}`]: {
|
|
96
|
+
scale: 1.15
|
|
97
|
+
}
|
|
98
|
+
} satisfies Variants
|
|
99
|
+
variants['selected'] = variants['hovered']
|
|
100
|
+
variants['hovered:details'] = variants.idle
|
|
101
|
+
return variants
|
|
102
|
+
}
|
|
80
103
|
|
|
81
104
|
const VariantsDetailsBtn = {
|
|
82
105
|
idle: {
|
|
83
106
|
'--ai-bg': 'var(--ai-bg-idle)',
|
|
84
107
|
scale: 1,
|
|
85
|
-
opacity: 0.5
|
|
108
|
+
opacity: 0.5,
|
|
109
|
+
originX: 0.45,
|
|
110
|
+
originY: 0.55
|
|
86
111
|
},
|
|
87
112
|
selected: {},
|
|
88
113
|
hovered: {
|
|
@@ -90,7 +115,7 @@ const VariantsDetailsBtn = {
|
|
|
90
115
|
opacity: 0.7
|
|
91
116
|
},
|
|
92
117
|
'hovered:details': {
|
|
93
|
-
scale: 1.
|
|
118
|
+
scale: 1.44,
|
|
94
119
|
opacity: 1
|
|
95
120
|
},
|
|
96
121
|
'tap:details': {
|
|
@@ -119,6 +144,8 @@ export const ElementNodeMemo = memo<ElementNodeProps>(function ElementNode({
|
|
|
119
144
|
width,
|
|
120
145
|
height
|
|
121
146
|
}) {
|
|
147
|
+
const modelRef = DiagramNode.modelRef(element)
|
|
148
|
+
// const deploymentRef = DiagramNode.deploymentRef(element)
|
|
122
149
|
const {
|
|
123
150
|
viewId,
|
|
124
151
|
isEditable,
|
|
@@ -127,6 +154,7 @@ export const ElementNodeMemo = memo<ElementNodeProps>(function ElementNode({
|
|
|
127
154
|
isNavigable,
|
|
128
155
|
isInteractive,
|
|
129
156
|
enableElementDetails,
|
|
157
|
+
enableRelationshipBrowser,
|
|
130
158
|
triggerOnNavigateTo,
|
|
131
159
|
openOverlay,
|
|
132
160
|
isInActiveOverlay,
|
|
@@ -136,10 +164,11 @@ export const ElementNodeMemo = memo<ElementNodeProps>(function ElementNode({
|
|
|
136
164
|
isEditable: s.readonly !== true,
|
|
137
165
|
isHovered: s.hoveredNodeId === id,
|
|
138
166
|
isDimmed: s.dimmed.has(id),
|
|
139
|
-
isInteractive: s.nodesDraggable || s.nodesSelectable || s.enableElementDetails
|
|
167
|
+
isInteractive: s.nodesDraggable || s.nodesSelectable || s.enableElementDetails || s.enableRelationshipBrowser
|
|
140
168
|
|| (!!s.onNavigateTo && !!element.navigateTo),
|
|
141
169
|
isNavigable: (!!s.onNavigateTo && !!element.navigateTo),
|
|
142
170
|
enableElementDetails: s.enableElementDetails,
|
|
171
|
+
enableRelationshipBrowser: s.enableRelationshipBrowser,
|
|
143
172
|
triggerOnNavigateTo: s.triggerOnNavigateTo,
|
|
144
173
|
openOverlay: s.openOverlay,
|
|
145
174
|
isInActiveOverlay: (s.activeOverlay?.elementDetails ?? s.activeOverlay?.relationshipsOf) === id,
|
|
@@ -153,27 +182,29 @@ export const ElementNodeMemo = memo<ElementNodeProps>(function ElementNode({
|
|
|
153
182
|
const w = toDomPrecision(width ?? element.width)
|
|
154
183
|
const h = toDomPrecision(height ?? element.height)
|
|
155
184
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
let animate: keyof typeof VariantsRoot
|
|
185
|
+
let animateVariant: keyof typeof VariantsRoot | string[]
|
|
159
186
|
switch (true) {
|
|
160
187
|
case isInActiveOverlay:
|
|
161
|
-
|
|
188
|
+
animateVariant = 'idle'
|
|
162
189
|
break
|
|
163
190
|
case dragging && selected:
|
|
164
|
-
|
|
191
|
+
animateVariant = 'selected'
|
|
165
192
|
break
|
|
166
193
|
case dragging:
|
|
167
|
-
|
|
194
|
+
animateVariant = 'idle'
|
|
168
195
|
break
|
|
169
196
|
case isInteractive && isHovered:
|
|
170
|
-
|
|
197
|
+
animateVariant = 'hovered'
|
|
171
198
|
break
|
|
172
199
|
case selected:
|
|
173
|
-
|
|
200
|
+
animateVariant = 'selected'
|
|
174
201
|
break
|
|
175
202
|
default:
|
|
176
|
-
|
|
203
|
+
animateVariant = 'idle'
|
|
204
|
+
}
|
|
205
|
+
const [animateVariants, animateHandlers] = useFramerAnimateVariants()
|
|
206
|
+
if (isHovered && !dragging && !isInActiveOverlay) {
|
|
207
|
+
animateVariant = animateVariants ?? animateVariant
|
|
177
208
|
}
|
|
178
209
|
|
|
179
210
|
const elementIcon = ElementIcon({
|
|
@@ -195,6 +226,13 @@ export const ElementNodeMemo = memo<ElementNodeProps>(function ElementNode({
|
|
|
195
226
|
openOverlay({ elementDetails: element.id })
|
|
196
227
|
}, [openOverlay, element.id])
|
|
197
228
|
|
|
229
|
+
const onOpenRelationships = useCallback((e: React.MouseEvent) => {
|
|
230
|
+
if (modelRef) {
|
|
231
|
+
e.stopPropagation()
|
|
232
|
+
openOverlay({ relationshipsOf: modelRef })
|
|
233
|
+
}
|
|
234
|
+
}, [openOverlay, modelRef])
|
|
235
|
+
|
|
198
236
|
return (
|
|
199
237
|
<>
|
|
200
238
|
{isToolbarVisible && (
|
|
@@ -209,7 +247,7 @@ export const ElementNodeMemo = memo<ElementNodeProps>(function ElementNode({
|
|
|
209
247
|
className={clsx([
|
|
210
248
|
css.container,
|
|
211
249
|
isDimmed && css.dimmed,
|
|
212
|
-
|
|
250
|
+
animateVariant !== 'idle' && css.containerAnimated,
|
|
213
251
|
'likec4-element-node'
|
|
214
252
|
])}
|
|
215
253
|
key={`${viewId}:element:${id}`}
|
|
@@ -220,7 +258,7 @@ export const ElementNodeMemo = memo<ElementNodeProps>(function ElementNode({
|
|
|
220
258
|
data-animate-target=""
|
|
221
259
|
initial={false}
|
|
222
260
|
variants={VariantsRoot}
|
|
223
|
-
animate={
|
|
261
|
+
animate={animateVariant}
|
|
224
262
|
tabIndex={-1}
|
|
225
263
|
{...(isInteractive && {
|
|
226
264
|
onTapStart: animateHandlers.onTapStart,
|
|
@@ -266,23 +304,13 @@ export const ElementNodeMemo = memo<ElementNodeProps>(function ElementNode({
|
|
|
266
304
|
</Box>
|
|
267
305
|
</Box>
|
|
268
306
|
{/* {isHovercards && element.links && <ElementLink element={element} />} */}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
style={{ zIndex: 100 }}
|
|
277
|
-
role="button"
|
|
278
|
-
onClick={onNavigateTo}
|
|
279
|
-
onDoubleClick={stopPropagation}
|
|
280
|
-
{...isInteractive && animateHandlers}
|
|
281
|
-
>
|
|
282
|
-
<IconZoomScan style={{ width: '75%' }} />
|
|
283
|
-
</ActionIcon>
|
|
284
|
-
)}
|
|
285
|
-
{enableElementDetails && (
|
|
307
|
+
<BottomButtons
|
|
308
|
+
keyPrefix={`${viewId}:element:${id}:`}
|
|
309
|
+
onNavigateTo={isNavigable && onNavigateTo}
|
|
310
|
+
onOpenRelationships={enableRelationshipBrowser && !!modelRef && onOpenRelationships}
|
|
311
|
+
{...isInteractive && animateHandlers}
|
|
312
|
+
/>
|
|
313
|
+
{enableElementDetails && !!modelRef && (
|
|
286
314
|
<Tooltip
|
|
287
315
|
fz="xs"
|
|
288
316
|
color="dark"
|
|
@@ -291,6 +319,7 @@ export const ElementNodeMemo = memo<ElementNodeProps>(function ElementNode({
|
|
|
291
319
|
offset={2}
|
|
292
320
|
openDelay={600}>
|
|
293
321
|
<ActionIcon
|
|
322
|
+
key="details"
|
|
294
323
|
component={m.div}
|
|
295
324
|
variants={VariantsDetailsBtn}
|
|
296
325
|
data-animate-target="details"
|
|
@@ -313,37 +342,69 @@ export const ElementNodeMemo = memo<ElementNodeProps>(function ElementNode({
|
|
|
313
342
|
)
|
|
314
343
|
}, isEqualProps)
|
|
315
344
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
345
|
+
type BottomButtonsProps = PropsWithoutRef<
|
|
346
|
+
ActionIconProps & HTMLMotionProps<'div'> & {
|
|
347
|
+
keyPrefix: string
|
|
348
|
+
onNavigateTo: ((e: React.MouseEvent) => void) | false
|
|
349
|
+
onOpenRelationships: ((e: React.MouseEvent) => void) | false
|
|
350
|
+
}
|
|
351
|
+
>
|
|
352
|
+
const BottomButtons = ({
|
|
353
|
+
keyPrefix,
|
|
354
|
+
onNavigateTo,
|
|
355
|
+
onOpenRelationships,
|
|
356
|
+
...props
|
|
357
|
+
}: BottomButtonsProps) => {
|
|
358
|
+
const enableRelationships = !!onOpenRelationships
|
|
359
|
+
const enableNavigate = !!onNavigateTo
|
|
360
|
+
|
|
361
|
+
if (!enableRelationships && !enableNavigate) {
|
|
362
|
+
return null
|
|
363
|
+
}
|
|
332
364
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
//
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
365
|
+
return (
|
|
366
|
+
<Box className={css.bottomButtonsContainer}>
|
|
367
|
+
{enableNavigate && (
|
|
368
|
+
<ActionIcon
|
|
369
|
+
{...props}
|
|
370
|
+
key={`${keyPrefix}navigate`}
|
|
371
|
+
data-animate-target="navigate"
|
|
372
|
+
component={m.div}
|
|
373
|
+
// Weird, but dts-bundle-generator fails on "enableRelationships && 'left'"
|
|
374
|
+
variants={variantsBottomButton('navigate', enableRelationships ? 'left' : false)}
|
|
375
|
+
className={clsx('nodrag nopan', css.navigateBtn)}
|
|
376
|
+
radius="md"
|
|
377
|
+
role="button"
|
|
378
|
+
onClick={onNavigateTo}
|
|
379
|
+
onDoubleClick={stopPropagation}
|
|
380
|
+
>
|
|
381
|
+
<IconZoomScan
|
|
382
|
+
style={{
|
|
383
|
+
width: '70%',
|
|
384
|
+
transform: 'var(--icon-scale)'
|
|
385
|
+
}} />
|
|
386
|
+
</ActionIcon>
|
|
387
|
+
)}
|
|
388
|
+
{enableRelationships && (
|
|
389
|
+
<ActionIcon
|
|
390
|
+
{...props}
|
|
391
|
+
key={`${keyPrefix}relationships`}
|
|
392
|
+
data-animate-target="relationships"
|
|
393
|
+
component={m.div}
|
|
394
|
+
variants={variantsBottomButton('relationships', enableNavigate ? 'right' : false)}
|
|
395
|
+
className={clsx('nodrag nopan', css.navigateBtn)}
|
|
396
|
+
radius="md"
|
|
397
|
+
role="button"
|
|
398
|
+
onClick={onOpenRelationships}
|
|
399
|
+
onDoubleClick={stopPropagation}
|
|
400
|
+
>
|
|
401
|
+
<IconTransform
|
|
402
|
+
style={{
|
|
403
|
+
width: '70%',
|
|
404
|
+
transform: 'var(--icon-scale)'
|
|
405
|
+
}} />
|
|
406
|
+
</ActionIcon>
|
|
407
|
+
)}
|
|
408
|
+
</Box>
|
|
409
|
+
)
|
|
410
|
+
}
|
|
@@ -141,7 +141,8 @@ export const title = style({
|
|
|
141
141
|
fontSize: 19,
|
|
142
142
|
lineHeight: 1.15,
|
|
143
143
|
textWrap: 'balance',
|
|
144
|
-
color: vars.element.hiContrast
|
|
144
|
+
color: vars.element.hiContrast,
|
|
145
|
+
whiteSpaceCollapse: 'preserve-breaks'
|
|
145
146
|
})
|
|
146
147
|
|
|
147
148
|
export const description = style({
|
|
@@ -338,6 +339,56 @@ export const shapeSvg = style({
|
|
|
338
339
|
}
|
|
339
340
|
})
|
|
340
341
|
|
|
342
|
+
export const bottomButtonsContainer = style({
|
|
343
|
+
zIndex: 100,
|
|
344
|
+
position: 'absolute',
|
|
345
|
+
left: 0,
|
|
346
|
+
width: '100%',
|
|
347
|
+
bottom: 2,
|
|
348
|
+
display: 'flex',
|
|
349
|
+
flexDirection: 'row',
|
|
350
|
+
flexWrap: 'nowrap',
|
|
351
|
+
alignItems: 'center',
|
|
352
|
+
gap: 2,
|
|
353
|
+
justifyContent: 'center',
|
|
354
|
+
pointerEvents: 'none',
|
|
355
|
+
selectors: {
|
|
356
|
+
[`:where([data-likec4-shape='browser']) &`]: {
|
|
357
|
+
bottom: 4
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
})
|
|
361
|
+
|
|
362
|
+
// --group-gap: var(--mantine-spacing-md);
|
|
363
|
+
// --group-align: center;
|
|
364
|
+
// --group-justify: flex-start;
|
|
365
|
+
// --group-wrap: wrap;
|
|
366
|
+
// gap: 0px;
|
|
367
|
+
// }
|
|
368
|
+
// <style>
|
|
369
|
+
// .element_bottomButtonsContainer__14yllojn {
|
|
370
|
+
// z-index: 100;
|
|
371
|
+
// position: absolute;
|
|
372
|
+
// left: 50%;
|
|
373
|
+
// bottom: 2px;
|
|
374
|
+
// transform: translate(-50%, 0%);
|
|
375
|
+
// }
|
|
376
|
+
|
|
377
|
+
// <style>
|
|
378
|
+
// .m_4081bf90 {
|
|
379
|
+
// display: flex
|
|
380
|
+
// ;
|
|
381
|
+
// flex-direction: row;
|
|
382
|
+
// flex-wrap: var(--group-wrap, wrap);
|
|
383
|
+
// justify-content: var(--group-justify, flex-start);
|
|
384
|
+
// align-items: var(--group-align, center);
|
|
385
|
+
// gap: var(--group-gap, var(--mantine-spacing-md));
|
|
386
|
+
// }
|
|
387
|
+
// globalStyle(`:where(.react-flow__node:hover) ${bottomButtonsContainer}`, {
|
|
388
|
+
// transitionDelay: '20ms',
|
|
389
|
+
// gap: 16
|
|
390
|
+
// })
|
|
391
|
+
|
|
341
392
|
const btn = style({
|
|
342
393
|
pointerEvents: 'all',
|
|
343
394
|
color: vars.element.loContrast,
|
|
@@ -354,16 +405,7 @@ const btn = style({
|
|
|
354
405
|
}
|
|
355
406
|
})
|
|
356
407
|
|
|
357
|
-
export const navigateBtn = style([btn, {
|
|
358
|
-
position: 'absolute',
|
|
359
|
-
left: '50%',
|
|
360
|
-
bottom: 0,
|
|
361
|
-
selectors: {
|
|
362
|
-
[`:where([data-likec4-shape='browser']) &`]: {
|
|
363
|
-
bottom: 4
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
}])
|
|
408
|
+
export const navigateBtn = style([btn, {}])
|
|
367
409
|
|
|
368
410
|
export const detailsBtn = style([btn, {
|
|
369
411
|
position: 'absolute',
|