@likec4/diagram 1.12.1 → 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 +9 -9
  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 +35 -18
  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
@@ -1,103 +1,140 @@
1
- import { defaultTheme, type DiagramNode, type ThemeColor } from '@likec4/core'
2
- import { Text as MantineText } from '@mantine/core'
1
+ import { type DiagramNode, type ThemeColor } from '@likec4/core'
2
+ import { ActionIcon, Text as MantineText, Tooltip } from '@mantine/core'
3
3
  import { useDebouncedValue } from '@mantine/hooks'
4
+ import { IconTransform, IconZoomScan } from '@tabler/icons-react'
4
5
  import { Handle, type NodeProps, Position } from '@xyflow/react'
5
6
  import clsx from 'clsx'
6
7
  import { deepEqual as eq } from 'fast-equals'
7
8
  import { m, type Variants } from 'framer-motion'
8
- import { memo, useState } from 'react'
9
- import { isNumber, isTruthy, keys } from 'remeda'
10
- import { type DiagramStoreApi, useDiagramState, useDiagramStoreApi } from '../../../hooks/useDiagramState'
9
+ import { memo, useCallback, useState } from 'react'
10
+ import { isNumber, isTruthy } from 'remeda'
11
+ import { useDiagramState } from '../../../hooks/useDiagramState'
12
+ import type { ElementIconRenderer } from '../../../LikeC4Diagram.props'
11
13
  import type { ElementXYFlowNode } from '../../types'
12
- import { toDomPrecision } from '../../utils'
13
- import { NavigateToBtn } from '../shared/NavigateToBtn'
14
+ import { stopPropagation, toDomPrecision } from '../../utils'
14
15
  import { ElementToolbar } from '../shared/Toolbar'
16
+ import { useFramerAnimateVariants } from '../use-animate-variants'
15
17
  import * as css from './element.css'
16
18
  import { ElementLink } from './ElementLink'
17
19
  import { ElementShapeSvg, SelectedIndicator } from './ElementShapeSvg'
18
20
 
19
- const {
20
- primary,
21
- secondary,
22
- muted,
23
- ...otherColors
24
- } = defaultTheme.elements
25
-
26
- export const themedColors = [
27
- { key: 'primary', value: primary.fill },
28
- { key: 'secondary', value: secondary.fill },
29
- { key: 'muted', value: muted.fill }
30
- ] satisfies Array<{ key: ThemeColor; value: string }>
31
-
32
- export const colors = keys(otherColors).map(key => ({
33
- key,
34
- value: defaultTheme.elements[key].fill
35
- }))
36
-
37
- export type ThemeColorKey = typeof themedColors[0]['key']
38
- export type ColorKey = typeof colors[0]['key']
39
-
40
21
  const Text = MantineText.withProps({
41
22
  component: 'div'
42
23
  })
43
24
 
44
- type ElementNodeProps = NodeProps<ElementXYFlowNode>
45
-
46
25
  const selectedScale = 1.015
26
+
47
27
  // Frame-motion variants
48
- const variants = {
28
+
29
+ const VariantsRoot = {
49
30
  idle: (_, { scale }) => ({
50
31
  scale: 1,
51
- transition: {
52
- delay: isNumber(scale) && scale > selectedScale ? 0.09 : 0
53
- }
54
- }),
55
- selected: (_, { scale }) => ({
56
- scale: selectedScale,
57
- transition: {
58
- delay: isNumber(scale) && scale > selectedScale ? 0.09 : 0
59
- }
32
+ transition: isNumber(scale) && scale > selectedScale
33
+ ? {
34
+ delay: 0.1,
35
+ delayChildren: 0.06,
36
+ staggerChildren: 0.07,
37
+ staggerDirection: -1
38
+ }
39
+ : {}
60
40
  }),
61
- // dimmed: {
62
- // filter: 'brightness(0.5)',
63
- // transition: {
64
- // duration: 0.8,
65
- // ease: 'easeInOut',
66
- // }
67
- // },
68
- // dragging: {
69
- // scale: selectedScale
70
- // },
71
- // hovered: {
72
- // scale: 1.08
73
- // },
74
- // hover: (_, {scale}) => isNumber(scale) && scale < 1.02 ? ({
75
- // scale: 1.08,
76
- // }) : ({
77
- // scale: 1.08,
78
- // transition: {
79
- // delay: 1
80
- // }
81
- // }),
82
- hovered: {
83
- scale: 1.06
41
+ selected: {
42
+ scale: selectedScale
84
43
  },
44
+ hovered: (_, { scale }) => ({
45
+ scale: 1.06,
46
+ transition: !isNumber(scale) || (scale >= 1 && scale < 1.06)
47
+ ? {
48
+ // delay: 0.09,
49
+ delayChildren: 0.07,
50
+ staggerChildren: 0.1
51
+ }
52
+ : {}
53
+ }),
85
54
  tap: {
86
55
  scale: 0.975
87
56
  }
88
- // tap: {
89
- // scale: 0.9
90
- // }
91
57
  } satisfies Variants
92
58
 
59
+ const VariantsNavigate = {
60
+ idle: {
61
+ '--ai-bg': 'var(--ai-bg-idle)',
62
+ scale: 1,
63
+ opacity: 0.75,
64
+ translateX: '-50%',
65
+ originY: 0.1
66
+ },
67
+ selected: {},
68
+ hovered: {
69
+ display: 'block',
70
+ '--ai-bg': 'var(--ai-bg-hover)',
71
+ scale: 1.35,
72
+ opacity: 1
73
+ },
74
+ 'hovered:navigate': {
75
+ scale: 1.42
76
+ },
77
+ 'tap:navigate': {
78
+ scale: 1.15
79
+ }
80
+ } satisfies Variants
81
+ VariantsNavigate['selected'] = VariantsNavigate['hovered']
82
+
83
+ /**
84
+ * Variants for the relationships button (when Navigate button is visible)
85
+ */
86
+ const VariantsRelationsBtn = {
87
+ idle: {
88
+ '--ai-bg': 'var(--ai-bg-idle)',
89
+ scale: 1,
90
+ opacity: 0,
91
+ originX: 0.25,
92
+ originY: 0.1,
93
+ translateX: -8,
94
+ transitionEnd: {
95
+ display: 'none'
96
+ }
97
+ },
98
+ selected: {},
99
+ hovered: {
100
+ display: 'block',
101
+ '--ai-bg': 'var(--ai-bg-hover)',
102
+ scale: 1.25,
103
+ opacity: 1,
104
+ translateX: 0
105
+ },
106
+ 'hovered:navigate': {},
107
+ 'hovered:relations': {
108
+ scale: 1.42
109
+ },
110
+ 'tap:relations': {
111
+ scale: 1.15
112
+ }
113
+ } satisfies Variants
114
+ VariantsRelationsBtn['selected'] = VariantsRelationsBtn['hovered']
115
+
116
+ /**
117
+ * Variants for the relationships button (when Navigate button is not visible)
118
+ */
119
+ const VariantsRelationsBtnSingle = {
120
+ ...VariantsNavigate,
121
+ idle: {
122
+ ...VariantsNavigate.idle,
123
+ opacity: 0,
124
+ transitionEnd: {
125
+ display: 'none'
126
+ }
127
+ }
128
+ }
129
+
130
+ type ElementNodeProps = NodeProps<ElementXYFlowNode>
93
131
  const isEqualProps = (prev: ElementNodeProps, next: ElementNodeProps) => (
94
132
  prev.id === next.id
95
133
  && eq(prev.selected ?? false, next.selected ?? false)
96
134
  && eq(prev.dragging ?? false, next.dragging ?? false)
97
- // && eq(prev.draggable ?? false, next.draggable ?? false)
98
135
  && eq(prev.width ?? 0, next.width ?? 0)
99
136
  && eq(prev.height ?? 0, next.height ?? 0)
100
- && eq(prev.data.element, next.data.element)
137
+ && eq(prev.data, next.data)
101
138
  )
102
139
 
103
140
  export const ElementNodeMemo = memo<ElementNodeProps>(function ElementNode({
@@ -106,32 +143,46 @@ export const ElementNodeMemo = memo<ElementNodeProps>(function ElementNode({
106
143
  element
107
144
  },
108
145
  dragging,
109
- // draggable = false,
110
146
  selected = false,
111
147
  width,
112
148
  height
113
149
  }) {
114
- const store = useDiagramStoreApi()
115
- const { isEditable, isHovered, isDimmed, hasOnNavigateTo, isHovercards, isInteractive } = useDiagramState(s => ({
150
+ const {
151
+ isEditable,
152
+ isHovered,
153
+ isDimmed,
154
+ hasOnNavigateTo,
155
+ isHovercards,
156
+ isInteractive,
157
+ enableRelationshipsMode,
158
+ triggerOnNavigateTo,
159
+ openOverlay,
160
+ renderIcon
161
+ } = useDiagramState(s => ({
116
162
  isEditable: s.readonly !== true,
117
163
  isHovered: s.hoveredNodeId === id,
118
164
  isDimmed: s.dimmed.has(id),
119
- isInteractive: s.nodesDraggable || s.nodesSelectable || (!!s.onNavigateTo && !!element.navigateTo),
165
+ isInteractive: s.nodesDraggable || s.nodesSelectable || s.enableRelationshipsBrowser
166
+ || (!!s.onNavigateTo && !!element.navigateTo),
120
167
  isHovercards: s.showElementLinks,
121
- hasOnNavigateTo: !!s.onNavigateTo
122
- // hasGoToSource: !!s.onOpenSourceElement
168
+ hasOnNavigateTo: !!s.onNavigateTo,
169
+ enableRelationshipsMode: s.enableRelationshipsBrowser,
170
+ triggerOnNavigateTo: s.triggerOnNavigateTo,
171
+ openOverlay: s.openOverlay,
172
+ renderIcon: s.renderIcon
123
173
  }))
174
+
124
175
  const isNavigable = hasOnNavigateTo && !!element.navigateTo
125
- // const isIdle = useIdle(300)
126
- // const [isToolbarVisible] = useDebouncedValue(isEditable && (isHovered || (import.meta.env.DEV && selected)), isIdle ? 50 : 350)
127
- const _isToolbarVisible = isEditable && (isHovered || (import.meta.env.DEV && selected))
128
176
 
177
+ const _isToolbarVisible = isEditable && (isHovered || (import.meta.env.DEV && selected))
129
178
  const [isToolbarVisible] = useDebouncedValue(_isToolbarVisible, _isToolbarVisible ? 500 : 300)
130
179
 
131
180
  const w = toDomPrecision(width ?? element.width)
132
181
  const h = toDomPrecision(height ?? element.height)
133
182
 
134
- let animate: keyof typeof variants = 'idle'
183
+ const [animateVariants, animateHandlers] = useFramerAnimateVariants()
184
+
185
+ let animate: keyof typeof VariantsRoot
135
186
  switch (true) {
136
187
  case dragging && selected:
137
188
  animate = 'selected'
@@ -145,12 +196,27 @@ export const ElementNodeMemo = memo<ElementNodeProps>(function ElementNode({
145
196
  case selected:
146
197
  animate = 'selected'
147
198
  break
199
+ default:
200
+ animate = 'idle'
148
201
  }
149
202
 
150
- const elementIcon = ElementIcon({ node: element, store })
203
+ const elementIcon = ElementIcon({
204
+ element,
205
+ renderIcon
206
+ })
151
207
 
152
208
  const [previewColor, setPreviewColor] = useState<ThemeColor | null>(null)
153
209
 
210
+ const onNavigateTo = useCallback((e: React.MouseEvent) => {
211
+ e.stopPropagation()
212
+ triggerOnNavigateTo(id, e)
213
+ }, [triggerOnNavigateTo, id])
214
+
215
+ const onBrowseRelations = useCallback((e: React.MouseEvent) => {
216
+ e.stopPropagation()
217
+ openOverlay({ relationshipsOf: element.id })
218
+ }, [openOverlay, element.id])
219
+
154
220
  return (
155
221
  <>
156
222
  {isToolbarVisible && (
@@ -160,11 +226,6 @@ export const ElementNodeMemo = memo<ElementNodeProps>(function ElementNode({
160
226
  onColorPreview={setPreviewColor}
161
227
  />
162
228
  )}
163
- <Handle
164
- type="target"
165
- position={Position.Top}
166
- className={css.handleCenter}
167
- />
168
229
  <m.div
169
230
  id={id}
170
231
  className={clsx([
@@ -176,50 +237,22 @@ export const ElementNodeMemo = memo<ElementNodeProps>(function ElementNode({
176
237
  data-hovered={!dragging && isHovered}
177
238
  data-likec4-color={previewColor ?? element.color}
178
239
  data-likec4-shape={element.shape}
179
- variants={variants}
240
+ data-animate-target=""
241
+ variants={VariantsRoot}
180
242
  initial={false}
181
- animate={animate}
243
+ animate={(isHovered && !dragging) ? (animateVariants ?? animate) : animate}
182
244
  tabIndex={-1}
183
245
  {...(isInteractive && {
184
- whileTap: dragging ? animate : 'tap'
246
+ onTapStart: animateHandlers.onTapStart,
247
+ onTap: animateHandlers.onTap,
248
+ onTapCancel: animateHandlers.onTapCancel
185
249
  })}
186
250
  >
187
- {
188
- /* <svg
189
- className={clsx(
190
- css.cssShapeSvgMultiple
191
- )}
192
- viewBox={`0 0 ${w} ${h}`}
193
- width={w}
194
- height={h}
195
- >
196
- <ElementShapeSvg
197
- shape={element.shape}
198
- w={w}
199
- h={h}
200
- />
201
- </svg> */
202
- }
203
- <svg
204
- className={clsx(
205
- css.cssShapeSvg
206
- )}
207
- viewBox={`0 0 ${w} ${h}`}
208
- width={w}
209
- height={h}
210
- >
251
+ <svg className={clsx(css.cssShapeSvg)} viewBox={`0 0 ${w} ${h}`} width={w} height={h}>
211
252
  <g className={css.indicator}>
212
- <SelectedIndicator
213
- shape={element.shape}
214
- w={w}
215
- h={h}
216
- />
253
+ <SelectedIndicator shape={element.shape} w={w} h={h} />
217
254
  </g>
218
- <ElementShapeSvg
219
- shape={element.shape}
220
- w={w}
221
- h={h}
222
- />
255
+ <ElementShapeSvg shape={element.shape} w={w} h={h} />
223
256
  </svg>
224
257
  <div
225
258
  className={clsx(
@@ -230,54 +263,92 @@ export const ElementNodeMemo = memo<ElementNodeProps>(function ElementNode({
230
263
  >
231
264
  {elementIcon}
232
265
  <div className={clsx(css.elementTextData, 'likec4-element-main-props')}>
233
- <Text
234
- className={clsx(css.title, 'likec4-element-title')}>
266
+ <Text className={clsx(css.title, 'likec4-element-title')} lineClamp={3}>
235
267
  {element.title}
236
268
  </Text>
237
269
  {element.technology && (
238
- <Text
239
- className={clsx(css.technology, 'likec4-element-technology')}>
270
+ <Text className={clsx(css.technology, 'likec4-element-technology')}>
240
271
  {element.technology}
241
272
  </Text>
242
273
  )}
243
274
  {element.description && (
244
- <Text
245
- className={clsx(css.description, 'likec4-element-description')}>
275
+ <Text className={clsx(css.description, 'likec4-element-description')} lineClamp={5}>
246
276
  {element.description}
247
277
  </Text>
248
278
  )}
249
279
  </div>
250
280
  </div>
251
281
  {isHovercards && element.links && <ElementLink element={element} />}
252
- {isNavigable && <NavigateToBtn xynodeId={id} className={css.cssNavigateBtn} />}
282
+ {isNavigable && (
283
+ <ActionIcon
284
+ component={m.div}
285
+ variants={VariantsNavigate}
286
+ data-animate-target="navigate"
287
+ className={clsx('nodrag nopan', css.navigateBtn)}
288
+ radius="md"
289
+ style={{ zIndex: 100 }}
290
+ role="button"
291
+ onClick={onNavigateTo}
292
+ onDoubleClick={stopPropagation}
293
+ {...isInteractive && animateHandlers}
294
+ >
295
+ <IconZoomScan stroke={1.8} style={{ width: '75%' }} />
296
+ </ActionIcon>
297
+ )}
298
+ {enableRelationshipsMode && (
299
+ <Tooltip fz="xs" color="dark" label="Browse relationships" withinPortal={false} openDelay={600}>
300
+ <ActionIcon
301
+ component={m.div}
302
+ // Is is a second button, so we need to use a different variant
303
+ variants={isNavigable ? VariantsRelationsBtn : VariantsRelationsBtnSingle}
304
+ data-animate-target={isNavigable ? 'relations' : 'navigate'}
305
+ className={clsx('nodrag nopan', css.navigateBtn)}
306
+ radius="md"
307
+ style={{
308
+ left: isNavigable ? 'calc(50% + 28px)' : '50%',
309
+ zIndex: 99
310
+ }}
311
+ role="button"
312
+ onClick={onBrowseRelations}
313
+ onDoubleClick={stopPropagation}
314
+ {...isInteractive && animateHandlers}
315
+ >
316
+ <IconTransform stroke={1.8} style={{ width: '67%' }} />
317
+ </ActionIcon>
318
+ </Tooltip>
319
+ )}
253
320
  </m.div>
254
- <Handle
255
- type="source"
256
- position={Position.Top}
257
- className={css.handleCenter}
258
- />
321
+ <Handle type="target" position={Position.Top} className={css.handleCenter} />
322
+ <Handle type="source" position={Position.Top} className={css.handleCenter} />
259
323
  </>
260
324
  )
261
325
  }, isEqualProps)
262
326
 
263
- const ElementIcon = ({ node, store }: { node: DiagramNode; store: DiagramStoreApi }) => {
264
- if (!node.icon) {
327
+ const ElementIcon = (
328
+ { element, renderIcon: RenderIcon }: { element: DiagramNode; renderIcon: ElementIconRenderer | null }
329
+ ) => {
330
+ if (!element.icon) {
265
331
  return null
266
332
  }
267
- if (node.icon.startsWith('http://') || node.icon.startsWith('https://')) {
333
+ if (element.icon.startsWith('http://') || element.icon.startsWith('https://')) {
268
334
  return (
269
335
  <div className={clsx(css.elementIcon, 'likec4-element-icon')}>
270
- <img src={node.icon} alt={node.title} />
336
+ <img src={element.icon} alt={element.title} />
271
337
  </div>
272
338
  )
273
339
  }
274
- const RenderIcon = store.getState().renderIcon
275
- const icon = RenderIcon ? <RenderIcon node={node} /> : null
340
+ const icon = RenderIcon ? <RenderIcon node={element} /> : null
276
341
  if (!icon) {
277
342
  return null
278
343
  }
279
344
  return (
280
- <div className={clsx(css.elementIcon, 'likec4-element-icon')}>
345
+ <div
346
+ className={clsx(
347
+ css.elementIcon,
348
+ 'likec4-element-icon'
349
+ )}
350
+ data-likec4-icon={element.icon}
351
+ >
281
352
  {icon}
282
353
  </div>
283
354
  )
@@ -23,6 +23,16 @@ export const container = style({
23
23
  '&[data-hovered="true"]': {
24
24
  willChange: 'transform'
25
25
  }
26
+ },
27
+ ':after': {
28
+ content: ' ',
29
+ position: 'absolute',
30
+ top: '100%',
31
+ left: 0,
32
+ right: 0,
33
+ height: 16,
34
+ background: 'transparent',
35
+ pointerEvents: 'all'
26
36
  }
27
37
  })
28
38
 
@@ -110,7 +120,7 @@ export const fillElementStroke = style({
110
120
  })
111
121
 
112
122
  export const fillMixStroke = style({
113
- fill: stokeFillMix
123
+ fill: fallbackVar(stokeFillMix, `color-mix(in srgb, ${vars.element.stroke} 90%, ${vars.element.fill})`)
114
124
  })
115
125
 
116
126
  export const hasIcon = style({})
@@ -248,7 +258,12 @@ export const elementIcon = style({
248
258
  alignItems: 'center',
249
259
  justifyContent: 'center',
250
260
  mixBlendMode: 'hard-light',
251
- alignSelf: 'flex-start'
261
+ alignSelf: 'flex-start',
262
+ selectors: {
263
+ '&[data-likec4-icon^="azure:"]': {
264
+ mixBlendMode: 'normal'
265
+ }
266
+ }
252
267
  })
253
268
  globalStyle(`${elementIcon} svg, ${elementIcon} img`, {
254
269
  width: '100%',
@@ -323,7 +338,7 @@ export const cssShapeSvg = style({
323
338
  }
324
339
  })
325
340
 
326
- export const cssNavigateBtn = style({
341
+ export const navigateBtn = style({
327
342
  zIndex: 'calc(var(--layer-overlays, 1) + 1)',
328
343
  position: 'absolute',
329
344
  pointerEvents: 'all',
@@ -331,45 +346,19 @@ export const cssNavigateBtn = style({
331
346
  bottom: 0,
332
347
  color: vars.element.loContrast,
333
348
  cursor: 'pointer',
334
- transformOrigin: '50% 65%',
335
- opacity: 0.75,
336
- transition: 'all 150ms ease-out',
337
- transform: 'translate(-50%, 0)',
338
- transitionDelay: '0ms',
339
349
  backgroundColor: 'var(--ai-bg)',
340
350
  'vars': {
341
- '--ai-bg': `color-mix(in srgb , ${vars.element.fill}, transparent 99%)`,
351
+ '--ai-bg-idle': `color-mix(in srgb , ${vars.element.fill}, transparent 99%)`,
352
+ '--ai-bg': `var(--ai-bg-idle)`,
342
353
  '--ai-bg-hover': `color-mix(in srgb , ${vars.element.fill} 65%, ${vars.element.stroke})`,
343
354
  '--ai-hover': `color-mix(in srgb , ${vars.element.fill} 50%, ${vars.element.stroke})`
344
355
  },
345
356
  selectors: {
346
357
  [`:where([data-likec4-shape='browser']) &`]: {
347
- bottom: 3
348
- },
349
- [`:where(.react-flow__node:not(.dragging) ${container}:hover) &:not(:hover)`]: {
350
- boxShadow: mantine.shadows.lg,
351
- transform: 'translate(-50%, 0) scale(1.185)',
352
- opacity: 1,
353
- transitionDelay: '250ms',
354
- vars: {
355
- '--ai-bg': 'var(--ai-bg-hover)'
356
- }
358
+ bottom: 4
357
359
  }
358
360
  },
359
361
  ':hover': {
360
- boxShadow: mantine.shadows.lg,
361
- transform: 'translate(-50%, 0) scale(1.35)',
362
- opacity: 1,
363
- transitionDelay: '0'
364
- },
365
- ':active': {
366
- transform: 'translate(-50%, 0) scale(1.02)',
367
- opacity: 1,
368
- transitionDelay: '0'
362
+ boxShadow: mantine.shadows.md
369
363
  }
370
364
  })
371
- globalStyle(`${cssNavigateBtn} svg.icon`, {
372
- width: '65%',
373
- height: '65%',
374
- strokeWidth: '1.5'
375
- })