@likec4/diagram 1.21.1 → 1.22.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 (109) hide show
  1. package/dist/LikeC4Diagram.js +9 -13
  2. package/dist/base/index.d.ts +0 -1
  3. package/dist/base/primitives/element/styles.css.js +1 -1
  4. package/dist/{base → components}/Link.css.js +3 -3
  5. package/dist/components/SearchControl.css.d.ts +2 -0
  6. package/dist/components/SearchControl.css.js +7 -0
  7. package/dist/components/SearchControl.d.ts +5 -0
  8. package/dist/components/SearchControl.js +17 -0
  9. package/dist/context/IconRenderer.d.ts +10 -0
  10. package/dist/context/IconRenderer.js +34 -0
  11. package/dist/hooks/useCurrentViewId.js +8 -0
  12. package/dist/hooks/useDiagram.d.ts +4 -4
  13. package/dist/likec4diagram/DiagramUI.js +0 -2
  14. package/dist/likec4diagram/XYFlow.d.ts +6 -1
  15. package/dist/likec4diagram/XYFlow.js +28 -29
  16. package/dist/likec4diagram/custom/edges/RelationshipsDropdownMenu.js +2 -1
  17. package/dist/likec4diagram/state/actorContext.d.ts +16 -16
  18. package/dist/likec4diagram/state/assign.js +1 -1
  19. package/dist/likec4diagram/state/machine.d.ts +7 -7
  20. package/dist/likec4diagram/state/machine.js +10 -7
  21. package/dist/likec4diagram/ui/controls/Controls.js +63 -48
  22. package/dist/likec4diagram/ui/controls/NavigationButtons.js +44 -69
  23. package/dist/likec4diagram/ui/controls/_shared.js +1 -1
  24. package/dist/likec4diagram/ui/diagram-title/DiagramTitlePanel.js +1 -1
  25. package/dist/likec4diagram/ui/search/ElementsColumn.css.d.ts +15 -0
  26. package/dist/likec4diagram/ui/search/ElementsColumn.css.js +30 -0
  27. package/dist/likec4diagram/ui/search/ElementsColumn.d.ts +1 -0
  28. package/dist/likec4diagram/ui/search/ElementsColumn.js +243 -0
  29. package/dist/likec4diagram/ui/search/LikeC4Search.css.d.ts +8 -0
  30. package/dist/likec4diagram/ui/search/LikeC4Search.css.js +20 -0
  31. package/dist/likec4diagram/ui/search/LikeC4Search.d.ts +1 -0
  32. package/dist/likec4diagram/ui/search/LikeC4Search.js +265 -0
  33. package/dist/likec4diagram/ui/search/SearchInput.d.ts +1 -0
  34. package/dist/likec4diagram/ui/search/SearchInput.js +196 -0
  35. package/dist/likec4diagram/ui/search/ViewsColum.d.ts +9 -0
  36. package/dist/likec4diagram/ui/search/ViewsColum.js +130 -0
  37. package/dist/likec4diagram/ui/search/ViewsColumn.css.d.ts +5 -0
  38. package/dist/likec4diagram/ui/search/ViewsColumn.css.js +12 -0
  39. package/dist/likec4diagram/ui/search/_shared.css.d.ts +16 -0
  40. package/dist/likec4diagram/ui/search/_shared.css.js +5 -0
  41. package/dist/likec4diagram/ui/search/state.d.ts +19 -0
  42. package/dist/likec4diagram/ui/search/state.js +72 -0
  43. package/dist/likec4diagram/ui/search/utils.d.ts +3 -0
  44. package/dist/likec4diagram/ui/search/utils.js +23 -0
  45. package/dist/likec4diagram/useLayoutConstraints.js +4 -5
  46. package/dist/likec4model/useLikeC4ElementsTree.d.ts +1 -0
  47. package/dist/likec4model/useLikeC4ElementsTree.js +1 -0
  48. package/dist/overlays/Overlays.js +9 -9
  49. package/dist/overlays/element-details/ElementDetailsCard.js +20 -13
  50. package/dist/overlays/overlay/Overlay.d.ts +6 -2
  51. package/dist/overlays/overlay/Overlay.js +18 -29
  52. package/dist/overlays/relationship-details/SelectEdge.js +1 -3
  53. package/dist/overlays/relationships-browser/RelationshipsBrowser.js +6 -5
  54. package/dist/overlays/relationships-browser/SelectElement.js +10 -6
  55. package/dist/overlays/relationships-browser/actor.d.ts +2 -2
  56. package/dist/overlays/relationships-browser/actor.js +8 -8
  57. package/dist/overlays/relationships-browser/custom/nodeTypes.js +2 -2
  58. package/dist/style.css +1 -1
  59. package/package.json +13 -12
  60. package/src/LikeC4Diagram.tsx +0 -1
  61. package/src/base/index.ts +0 -2
  62. package/src/base/primitives/element/styles.css.ts +2 -2
  63. package/src/{base → components}/Link.css.ts +3 -3
  64. package/src/components/SearchControl.css.ts +69 -0
  65. package/src/components/SearchControl.tsx +33 -0
  66. package/src/context/IconRenderer.tsx +54 -1
  67. package/src/globals.css.ts +0 -2
  68. package/src/likec4diagram/DiagramUI.tsx +0 -4
  69. package/src/likec4diagram/XYFlow.tsx +30 -30
  70. package/src/likec4diagram/custom/edges/RelationshipsDropdownMenu.tsx +2 -1
  71. package/src/likec4diagram/state/assign.ts +1 -1
  72. package/src/likec4diagram/state/machine.ts +12 -10
  73. package/src/likec4diagram/ui/controls/Controls.tsx +35 -25
  74. package/src/likec4diagram/ui/controls/NavigationButtons.tsx +36 -58
  75. package/src/likec4diagram/ui/controls/styles.css.ts +3 -1
  76. package/src/likec4diagram/ui/diagram-title/DiagramTitlePanel.tsx +1 -1
  77. package/src/likec4diagram/ui/search/ElementsColumn.css.ts +119 -0
  78. package/src/likec4diagram/ui/search/ElementsColumn.tsx +291 -0
  79. package/src/likec4diagram/ui/search/LikeC4Search.css.ts +117 -0
  80. package/src/likec4diagram/ui/search/LikeC4Search.tsx +312 -0
  81. package/src/likec4diagram/ui/search/SearchInput.tsx +235 -0
  82. package/src/likec4diagram/ui/search/ViewsColum.tsx +158 -0
  83. package/src/likec4diagram/ui/search/ViewsColumn.css.ts +37 -0
  84. package/src/likec4diagram/ui/search/_shared.css.ts +104 -0
  85. package/src/likec4diagram/ui/search/state.ts +76 -0
  86. package/src/likec4diagram/ui/search/utils.ts +20 -0
  87. package/src/likec4diagram/useLayoutConstraints.ts +4 -5
  88. package/src/likec4model/useLikeC4ElementsTree.ts +4 -4
  89. package/src/overlays/Overlays.tsx +31 -25
  90. package/src/overlays/element-details/ElementDetailsCard.css.ts +4 -4
  91. package/src/overlays/element-details/ElementDetailsCard.tsx +245 -240
  92. package/src/overlays/overlay/Overlay.css.ts +10 -9
  93. package/src/overlays/overlay/Overlay.tsx +38 -26
  94. package/src/overlays/relationship-details/SelectEdge.css.ts +1 -0
  95. package/src/overlays/relationship-details/SelectEdge.tsx +2 -4
  96. package/src/overlays/relationships-browser/RelationshipsBrowser.tsx +10 -5
  97. package/src/overlays/relationships-browser/SelectElement.tsx +88 -82
  98. package/src/overlays/relationships-browser/actor.ts +10 -10
  99. package/src/overlays/relationships-browser/custom/nodeTypes.tsx +2 -2
  100. package/dist/LikeC4Search.css.d.ts +0 -1
  101. package/dist/LikeC4Search.css.js +0 -5
  102. package/dist/LikeC4Search.d.ts +0 -2
  103. package/dist/LikeC4Search.js +0 -88
  104. package/src/LikeC4Search.css.ts +0 -12
  105. package/src/LikeC4Search.tsx +0 -91
  106. /package/dist/{base → components}/Link.css.d.ts +0 -0
  107. /package/dist/{base → components}/Link.d.ts +0 -0
  108. /package/dist/{base → components}/Link.js +0 -0
  109. /package/src/{base → components}/Link.tsx +0 -0
@@ -2,7 +2,7 @@ import { Box, Button, Code, Group, Notification } from '@mantine/core'
2
2
  import { useCallbackRef } from '@mantine/hooks'
3
3
  import { IconX } from '@tabler/icons-react'
4
4
  import { deepEqual } from 'fast-equals'
5
- import { useReducedMotion } from 'framer-motion'
5
+ import { AnimatePresence, useReducedMotion } from 'framer-motion'
6
6
  import { animate } from 'framer-motion/dom'
7
7
  import { memo, useEffect, useMemo } from 'react'
8
8
  import { type FallbackProps, ErrorBoundary } from 'react-error-boundary'
@@ -96,32 +96,38 @@ export const Overlays = memo(() => {
96
96
  return (
97
97
  <DiagramFeatures.Overlays>
98
98
  <ErrorBoundary FallbackComponent={Fallback} onReset={onClose}>
99
- {relationshipsBrowserActor && (
100
- <Overlay
101
- key={'relationships-browser'}
102
- onClose={onClose}>
103
- <RelationshipsBrowser actorRef={relationshipsBrowserActor} />
104
- </Overlay>
105
- )}
106
- {relationshipDetailsActor && (
107
- <Overlay
108
- key={'relationship-details'}
109
- onClose={onClose}>
110
- <RelationshipDetails actorRef={relationshipDetailsActor} />
111
- </Overlay>
112
- )}
99
+ <AnimatePresence onExitComplete={onClose}>
100
+ {relationshipsBrowserActor && (
101
+ <Overlay
102
+ key={'relationships-browser'}
103
+ onClose={() => relationshipsBrowserActor.send({ type: 'close' })}>
104
+ <RelationshipsBrowser actorRef={relationshipsBrowserActor} />
105
+ </Overlay>
106
+ )}
107
+ </AnimatePresence>
108
+ <AnimatePresence onExitComplete={onClose}>
109
+ {relationshipDetailsActor && (
110
+ <Overlay
111
+ key={'relationship-details'}
112
+ onClose={() => relationshipDetailsActor.send({ type: 'close' })}>
113
+ <RelationshipDetails actorRef={relationshipDetailsActor} />
114
+ </Overlay>
115
+ )}
116
+ </AnimatePresence>
113
117
 
114
118
  {/* TODO: Somehow ElementDetailsCard does not work properly inside AnimatePresence} */}
115
- {!!activeElementDetailsOf && (
116
- <ElementDetailsCard
117
- key={'element-details'}
118
- fqn={activeElementDetailsOf}
119
- viewId={viewId}
120
- fromNode={fromNode}
121
- rectFromNode={rectFromNode}
122
- onClose={onClose}
123
- />
124
- )}
119
+ <AnimatePresence>
120
+ {!!activeElementDetailsOf && (
121
+ <ElementDetailsCard
122
+ key={'element-details'}
123
+ fqn={activeElementDetailsOf}
124
+ viewId={viewId}
125
+ fromNode={fromNode}
126
+ rectFromNode={rectFromNode}
127
+ onClose={onClose}
128
+ />
129
+ )}
130
+ </AnimatePresence>
125
131
  </ErrorBoundary>
126
132
  </DiagramFeatures.Overlays>
127
133
  )
@@ -11,10 +11,10 @@ export const dialog = style({
11
11
  padding: 0,
12
12
  position: 'fixed',
13
13
  inset: 0,
14
- width: '100%',
15
- height: '100%',
16
- maxWidth: '100%',
17
- maxHeight: '100%',
14
+ width: '100vw',
15
+ height: '100vh',
16
+ maxWidth: '100vw',
17
+ maxHeight: '100vh',
18
18
  background: 'transparent',
19
19
  border: '0 solid transparent',
20
20
  vars: {
@@ -21,7 +21,6 @@ import {
21
21
  Divider as MantineDivider,
22
22
  Flex,
23
23
  Group,
24
- RemoveScroll,
25
24
  ScrollArea,
26
25
  Stack,
27
26
  Tabs,
@@ -34,13 +33,13 @@ import {
34
33
  UnstyledButton,
35
34
  } from '@mantine/core'
36
35
  import { useSessionStorage, useViewportSize } from '@mantine/hooks'
37
- import { useDebouncedCallback, useSyncedRef } from '@react-hookz/web'
36
+ import { useDebouncedCallback, useSyncedRef, useTimeoutEffect } from '@react-hookz/web'
38
37
  import { IconExternalLink, IconFileSymlink, IconStack2, IconZoomScan } from '@tabler/icons-react'
39
38
  import type { Rect } from '@xyflow/system'
40
39
  import { type PanInfo, m, useDragControls, useMotionValue } from 'framer-motion'
41
- import { type PropsWithChildren, useCallback, useRef } from 'react'
40
+ import { type PropsWithChildren, useCallback, useRef, useState } from 'react'
42
41
  import { clamp, isNullish, map, only, partition, pipe } from 'remeda'
43
- import { Link } from '../../base'
42
+ import { Link } from '../../components/Link'
44
43
  import { DiagramFeatures, IconRenderer, IfEnabled } from '../../context'
45
44
  import { useUpdateEffect } from '../../hooks'
46
45
  import { useDiagram } from '../../hooks/useDiagram'
@@ -101,6 +100,7 @@ export function ElementDetailsCard({
101
100
  fqn,
102
101
  onClose,
103
102
  }: ElementDetailsCardProps) {
103
+ const [opened, setOpened] = useState(false)
104
104
  const windowSize = useViewportSize()
105
105
  const windowWidth = windowSize.width || window.innerWidth || 1200,
106
106
  windowHeight = windowSize.height || window.innerHeight || 800
@@ -214,10 +214,21 @@ export function ElementDetailsCard({
214
214
  className: css.elementIcon,
215
215
  })
216
216
 
217
+ useTimeoutEffect(() => {
218
+ if (!ref.current?.open) {
219
+ ref.current?.showModal()
220
+ }
221
+ }, 30)
222
+
223
+ useTimeoutEffect(() => {
224
+ setOpened(true)
225
+ }, 120)
226
+
217
227
  return (
218
228
  <m.dialog
219
229
  ref={ref}
220
230
  className={css.dialog}
231
+ layout
221
232
  initial={{
222
233
  '--backdrop-blur': '0px',
223
234
  '--backdrop-opacity': '10%',
@@ -233,13 +244,10 @@ export function ElementDetailsCard({
233
244
  duration: 0.1,
234
245
  },
235
246
  }}
236
- onAnimationStart={() => {
237
- ref.current?.showModal()
238
- }}
239
247
  onClick={e => {
240
248
  if ((e.target as any)?.nodeName?.toUpperCase() === 'DIALOG') {
241
249
  e.stopPropagation()
242
- close()
250
+ ref.current?.close()
243
251
  }
244
252
  }}
245
253
  onClose={e => {
@@ -247,248 +255,245 @@ export function ElementDetailsCard({
247
255
  close()
248
256
  }}
249
257
  >
250
- <RemoveScroll forwardProps>
251
- <Card
252
- drag
253
- dragElastic={0}
254
- dragMomentum={false}
255
- dragListener={false}
256
- dragControls={controls}
257
- withBorder
258
- shadow="md"
259
- component={m.div}
260
- className={css.card}
261
- initial={{
262
- top,
263
- left,
264
- width: _width,
265
- height: _height,
266
- opacity: 0,
267
- originX,
268
- originY,
269
- scale: Math.max(fromScale, 0.7),
270
- }}
271
- animate={{
272
- opacity: 1,
273
- scale: 1,
274
- }}
275
- exit={{
276
- opacity: 0,
277
- scale: 0.9,
278
- translateY: -10,
279
- transition: {
280
- duration: 0.1,
281
- },
282
- }}
283
- style={{
284
- // `style` prop in Mantine doesn't accept motion values
285
- width: width as any,
286
- height: height as any,
287
- }}
288
- data-likec4-color={nodeModel?.color ?? elementModel.color}>
289
- <Box
290
- className={css.cardHeader}
291
- onPointerDown={e => controls.start(e)}>
292
- <Group align="start" justify="space-between" gap={'sm'} mb={'sm'} wrap="nowrap">
293
- <Group align="start" gap={'sm'} style={{ cursor: 'default' }} wrap="nowrap">
294
- {elementIcon}
295
- <Box>
296
- <Text
297
- component={m.div}
298
- layout="position"
299
- layoutId={`${viewId}:element:title:${fqn}`}
300
- className={css.title}>
301
- {elementModel.title}
302
- </Text>
303
- {notation && (
304
- <Text component="div" c={'dimmed'} fz={'sm'} fw={500} lh={1.3} lineClamp={1}>
305
- {notation}
306
- </Text>
307
- )}
308
- </Box>
309
- </Group>
310
- <CloseButton
311
- size={'lg'}
312
- onClick={e => {
313
- e.stopPropagation()
314
- close()
315
- }} />
316
- </Group>
317
- <Group align="baseline" gap={'sm'} wrap="nowrap">
258
+ <Card
259
+ drag
260
+ dragElastic={0}
261
+ dragMomentum={false}
262
+ dragListener={false}
263
+ dragControls={controls}
264
+ withBorder
265
+ shadow="md"
266
+ component={m.div}
267
+ className={css.card}
268
+ initial={{
269
+ top,
270
+ left,
271
+ width: _width,
272
+ height: _height,
273
+ opacity: 0,
274
+ originX,
275
+ originY,
276
+ scale: Math.max(fromScale, 0.7),
277
+ }}
278
+ animate={{
279
+ opacity: 1,
280
+ scale: 1,
281
+ }}
282
+ exit={{
283
+ opacity: 0,
284
+ scale: 0.9,
285
+ translateY: -10,
286
+ transition: {
287
+ duration: 0.1,
288
+ },
289
+ }}
290
+ style={{
291
+ // `style` prop in Mantine doesn't accept motion values
292
+ width: width as any,
293
+ height: height as any,
294
+ }}
295
+ data-likec4-color={nodeModel?.color ?? elementModel.color}>
296
+ <Box
297
+ className={css.cardHeader}
298
+ onPointerDown={e => controls.start(e)}>
299
+ <Group align="start" justify="space-between" gap={'sm'} mb={'sm'} wrap="nowrap">
300
+ <Group align="start" gap={'sm'} style={{ cursor: 'default' }} wrap="nowrap">
301
+ {elementIcon}
318
302
  <Box>
319
- <SmallLabel>kind</SmallLabel>
320
- <Badge radius={'sm'} size="sm" fw={600} color="gray">{elementModel.kind}</Badge>
321
- </Box>
322
- <Box flex={1}>
323
- <SmallLabel>tags</SmallLabel>
324
- <Flex gap={4} flex={1} mt={6}>
325
- {elementModel.tags.map((tag) => (
326
- <Badge key={tag} radius={'sm'} size="sm" fw={600} variant="gradient">#{tag}</Badge>
327
- ))}
328
- {elementModel.tags.length === 0 && <Badge radius={'sm'} size="sm" fw={600} color="gray">—</Badge>}
329
- </Flex>
303
+ <Text
304
+ component={m.div}
305
+ layout="position"
306
+ layoutId={`${viewId}:element:title:${fqn}`}
307
+ className={css.title}>
308
+ {elementModel.title}
309
+ </Text>
310
+ {notation && (
311
+ <Text component="div" c={'dimmed'} fz={'sm'} fw={500} lh={1.3} lineClamp={1}>
312
+ {notation}
313
+ </Text>
314
+ )}
330
315
  </Box>
331
- <ActionIconGroup
332
- style={{
333
- alignSelf: 'flex-end',
334
- }}>
335
- {defaultLink && (
316
+ </Group>
317
+ <CloseButton
318
+ size={'lg'}
319
+ onClick={e => {
320
+ e.stopPropagation()
321
+ close()
322
+ }} />
323
+ </Group>
324
+ <Group align="baseline" gap={'sm'} wrap="nowrap">
325
+ <Box>
326
+ <SmallLabel>kind</SmallLabel>
327
+ <Badge radius={'sm'} size="sm" fw={600} color="gray">{elementModel.kind}</Badge>
328
+ </Box>
329
+ <Box flex={1}>
330
+ <SmallLabel>tags</SmallLabel>
331
+ <Flex gap={4} flex={1} mt={6}>
332
+ {elementModel.tags.map((tag) => (
333
+ <Badge key={tag} radius={'sm'} size="sm" fw={600} variant="gradient">#{tag}</Badge>
334
+ ))}
335
+ {elementModel.tags.length === 0 && <Badge radius={'sm'} size="sm" fw={600} color="gray">—</Badge>}
336
+ </Flex>
337
+ </Box>
338
+ <ActionIconGroup
339
+ style={{
340
+ alignSelf: 'flex-end',
341
+ }}>
342
+ {defaultLink && (
343
+ <ActionIcon
344
+ component="a"
345
+ href={defaultLink.url}
346
+ target="_blank"
347
+ size="lg"
348
+ variant="default"
349
+ radius="sm"
350
+ >
351
+ <IconExternalLink stroke={1.6} style={{ width: '65%' }} />
352
+ </ActionIcon>
353
+ )}
354
+ <IfEnabled feature="Vscode">
355
+ <Tooltip label="Open source">
356
+ <ActionIcon
357
+ size="lg"
358
+ variant="default"
359
+ radius="sm"
360
+ onClick={e => {
361
+ e.stopPropagation()
362
+ diagram.openSource({
363
+ element: elementModel.id,
364
+ })
365
+ }}>
366
+ <IconFileSymlink stroke={1.8} style={{ width: '62%' }} />
367
+ </ActionIcon>
368
+ </Tooltip>
369
+ </IfEnabled>
370
+ {defaultView && (
371
+ <Tooltip label="Open default view">
336
372
  <ActionIcon
337
- component="a"
338
- href={defaultLink.url}
339
- target="_blank"
340
373
  size="lg"
341
374
  variant="default"
342
375
  radius="sm"
343
- >
344
- <IconExternalLink stroke={1.6} style={{ width: '65%' }} />
376
+ onClick={e => {
377
+ e.stopPropagation()
378
+ diagram.navigateTo(defaultView.id, fromNode ?? undefined)
379
+ }}>
380
+ <IconZoomScan style={{ width: '70%' }} />
345
381
  </ActionIcon>
382
+ </Tooltip>
383
+ )}
384
+ </ActionIconGroup>
385
+ </Group>
386
+ </Box>
387
+ <Tabs
388
+ value={activeTab}
389
+ onChange={v => setActiveTab(v as any)}
390
+ variant="none"
391
+ classNames={{
392
+ root: css.tabsRoot,
393
+ list: css.tabsList,
394
+ tab: css.tabsTab,
395
+ panel: css.tabsPanel,
396
+ }}>
397
+ <TabsList>
398
+ {TABS.map(tab => (
399
+ <TabsTab key={tab} value={tab}>
400
+ {tab}
401
+ </TabsTab>
402
+ ))}
403
+ </TabsList>
404
+
405
+ <TabsPanel value="Properties">
406
+ <ScrollArea scrollbars="y" type="auto">
407
+ <Box className={css.propertiesGrid} pt={'xs'}>
408
+ <ElementProperty title="description" emptyValue="no description">
409
+ {elementModel.description}
410
+ </ElementProperty>
411
+ {elementModel.technology && (
412
+ <ElementProperty title="technology">
413
+ {elementModel.technology}
414
+ </ElementProperty>
346
415
  )}
347
- <IfEnabled feature="Vscode">
348
- <Tooltip label="Open source">
349
- <ActionIcon
350
- size="lg"
351
- variant="default"
352
- radius="sm"
353
- onClick={e => {
354
- e.stopPropagation()
355
- diagram.openSource({
356
- element: elementModel.id,
357
- })
358
- }}>
359
- <IconFileSymlink stroke={1.8} style={{ width: '62%' }} />
360
- </ActionIcon>
361
- </Tooltip>
362
- </IfEnabled>
363
- {defaultView && (
364
- <Tooltip label="Open default view">
365
- <ActionIcon
366
- size="lg"
367
- variant="default"
368
- radius="sm"
369
- onClick={e => {
370
- e.stopPropagation()
371
- diagram.navigateTo(defaultView.id, fromNode ?? undefined)
372
- }}>
373
- <IconZoomScan style={{ width: '70%' }} />
374
- </ActionIcon>
375
- </Tooltip>
416
+ {elementModel.links.length > 0 && (
417
+ <>
418
+ <PropertyLabel>links</PropertyLabel>
419
+ <Stack gap={'xs'} align="flex-start">
420
+ {elementModel.links.map((link, i) => <Link key={i} value={link} />)}
421
+ </Stack>
422
+ </>
376
423
  )}
377
- </ActionIconGroup>
378
- </Group>
379
- </Box>
380
-
381
- <Tabs
382
- value={activeTab}
383
- onChange={v => setActiveTab(v as any)}
384
- variant="none"
385
- classNames={{
386
- root: css.tabsRoot,
387
- list: css.tabsList,
388
- tab: css.tabsTab,
389
- panel: css.tabsPanel,
390
- }}>
391
- <TabsList>
392
- {TABS.map(tab => (
393
- <TabsTab key={tab} value={tab}>
394
- {tab}
395
- </TabsTab>
396
- ))}
397
- </TabsList>
398
-
399
- <TabsPanel value="Properties">
400
- <ScrollArea scrollbars="y" type="auto">
401
- <Box className={css.propertiesGrid} pt={'xs'}>
402
- <ElementProperty title="description" emptyValue="no description">
403
- {elementModel.description}
404
- </ElementProperty>
405
- {elementModel.technology && (
406
- <ElementProperty title="technology">
407
- {elementModel.technology}
408
- </ElementProperty>
409
- )}
410
- {elementModel.links.length > 0 && (
411
- <>
412
- <PropertyLabel>links</PropertyLabel>
413
- <Stack gap={'xs'} align="flex-start">
414
- {elementModel.links.map((link, i) => <Link key={i} value={link} />)}
415
- </Stack>
416
- </>
417
- )}
418
- {elementModel.$element.metadata && <ElementMetata value={elementModel.$element.metadata} />}
419
- </Box>
420
- </ScrollArea>
421
- </TabsPanel>
422
-
423
- <TabsPanel value="Relationships">
424
- <DiagramFeatures
425
- overrides={{
426
- enableRelationshipBrowser: false,
427
- enableNavigateTo: false,
428
- }}>
429
- {!!nodeModel && activeTab === 'Relationships' && (
430
- <TabPanelRelationships
431
- element={elementModel}
432
- node={nodeModel}
433
- currentView={diagram.currentView()} />
424
+ {elementModel.$element.metadata && <ElementMetata value={elementModel.$element.metadata} />}
425
+ </Box>
426
+ </ScrollArea>
427
+ </TabsPanel>
428
+
429
+ <TabsPanel value="Relationships">
430
+ <DiagramFeatures
431
+ overrides={{
432
+ enableRelationshipBrowser: false,
433
+ enableNavigateTo: false,
434
+ }}>
435
+ {opened && !!nodeModel && activeTab === 'Relationships' && (
436
+ <TabPanelRelationships
437
+ element={elementModel}
438
+ node={nodeModel}
439
+ currentView={diagram.currentView()} />
440
+ )}
441
+ </DiagramFeatures>
442
+ </TabsPanel>
443
+
444
+ <TabsPanel value="Views">
445
+ <ScrollArea scrollbars="y" type="auto">
446
+ <Stack gap={'lg'}>
447
+ {viewsOf.length > 0 && (
448
+ <Box>
449
+ <Divider label="views of the element (scoped)" />
450
+ <Stack gap={'sm'}>
451
+ {viewsOf.map((view) => (
452
+ <ViewButton
453
+ key={view.id}
454
+ view={view}
455
+ onNavigateTo={to => diagram.navigateTo(to, fromNode ?? undefined)} />
456
+ ))}
457
+ </Stack>
458
+ </Box>
434
459
  )}
435
- </DiagramFeatures>
436
- </TabsPanel>
437
-
438
- <TabsPanel value="Views">
439
- <ScrollArea scrollbars="y" type="auto">
440
- <Stack gap={'lg'}>
441
- {viewsOf.length > 0 && (
442
- <Box>
443
- <Divider label="views of the element (scoped)" />
444
- <Stack gap={'sm'}>
445
- {viewsOf.map((view) => (
446
- <ViewButton
447
- key={view.id}
448
- view={view}
449
- onNavigateTo={to => diagram.navigateTo(to, fromNode ?? undefined)} />
450
- ))}
451
- </Stack>
452
- </Box>
453
- )}
454
- {otherViews.length > 0 && (
455
- <Box>
456
- <Divider label="views including this element" />
457
- <Stack gap={'sm'}>
458
- {otherViews.map((view) => (
459
- <ViewButton
460
- key={view.id}
461
- view={view}
462
- onNavigateTo={to => diagram.navigateTo(to, fromNode ?? undefined)} />
463
- ))}
464
- </Stack>
465
- </Box>
466
- )}
467
- </Stack>
468
- </ScrollArea>
469
- </TabsPanel>
470
-
471
- <TabsPanel value="Structure">
472
- <ScrollArea scrollbars="y" type="auto">
473
- <TabPanelStructure element={elementModel} />
474
- </ScrollArea>
475
- </TabsPanel>
476
-
477
- <TabsPanel value="Deployments">
478
- <ScrollArea scrollbars="y" type="auto">
479
- <TabPanelDeployments elementFqn={elementModel.id} />
480
- </ScrollArea>
481
- </TabsPanel>
482
- </Tabs>
483
- <m.div
484
- className={css.resizeHandle}
485
- drag
486
- dragElastic={0}
487
- dragMomentum={false}
488
- onDrag={handleDrag}
489
- dragConstraints={{ top: 0, left: 0, right: 0, bottom: 0 }} />
490
- </Card>
491
- </RemoveScroll>
460
+ {otherViews.length > 0 && (
461
+ <Box>
462
+ <Divider label="views including this element" />
463
+ <Stack gap={'sm'}>
464
+ {otherViews.map((view) => (
465
+ <ViewButton
466
+ key={view.id}
467
+ view={view}
468
+ onNavigateTo={to => diagram.navigateTo(to, fromNode ?? undefined)} />
469
+ ))}
470
+ </Stack>
471
+ </Box>
472
+ )}
473
+ </Stack>
474
+ </ScrollArea>
475
+ </TabsPanel>
476
+
477
+ <TabsPanel value="Structure">
478
+ <ScrollArea scrollbars="y" type="auto">
479
+ <TabPanelStructure element={elementModel} />
480
+ </ScrollArea>
481
+ </TabsPanel>
482
+
483
+ <TabsPanel value="Deployments">
484
+ <ScrollArea scrollbars="y" type="auto">
485
+ <TabPanelDeployments elementFqn={elementModel.id} />
486
+ </ScrollArea>
487
+ </TabsPanel>
488
+ </Tabs>
489
+ <m.div
490
+ className={css.resizeHandle}
491
+ drag
492
+ dragElastic={0}
493
+ dragMomentum={false}
494
+ onDrag={handleDrag}
495
+ dragConstraints={{ top: 0, left: 0, right: 0, bottom: 0 }} />
496
+ </Card>
492
497
  </m.dialog>
493
498
  )
494
499
  }