@likec4/diagram 1.21.0 → 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 (129) hide show
  1. package/dist/LikeC4Diagram.js +9 -13
  2. package/dist/base/BaseXYFlow.js +0 -1
  3. package/dist/base/index.d.ts +0 -1
  4. package/dist/base/primitives/compound/CompoundNodeContainer.js +2 -2
  5. package/dist/base/primitives/edge/EdgeLabel.js +2 -2
  6. package/dist/base/primitives/element/ElementNodeContainer.d.ts +6 -1
  7. package/dist/base/primitives/element/ElementNodeContainer.js +31 -13
  8. package/dist/base/primitives/element/ElementTitle.js +7 -3
  9. package/dist/base/primitives/element/styles.css.js +1 -1
  10. package/dist/{base → components}/Link.css.js +3 -3
  11. package/dist/components/SearchControl.css.d.ts +2 -0
  12. package/dist/components/SearchControl.css.js +7 -0
  13. package/dist/components/SearchControl.d.ts +5 -0
  14. package/dist/components/SearchControl.js +17 -0
  15. package/dist/context/IconRenderer.d.ts +10 -0
  16. package/dist/context/IconRenderer.js +34 -0
  17. package/dist/hooks/index.d.ts +0 -1
  18. package/dist/hooks/useDiagram.d.ts +4 -4
  19. package/dist/index.d.ts +1 -1
  20. package/dist/likec4diagram/DiagramUI.js +1 -4
  21. package/dist/likec4diagram/XYFlow.d.ts +6 -1
  22. package/dist/likec4diagram/XYFlow.js +28 -29
  23. package/dist/likec4diagram/custom/edges/RelationshipsDropdownMenu.js +3 -1
  24. package/dist/likec4diagram/state/actorContext.d.ts +16 -16
  25. package/dist/likec4diagram/state/assign.js +1 -1
  26. package/dist/likec4diagram/state/machine.d.ts +7 -7
  27. package/dist/likec4diagram/state/machine.js +10 -7
  28. package/dist/likec4diagram/ui/controls/Controls.js +63 -48
  29. package/dist/likec4diagram/ui/controls/NavigationButtons.js +44 -69
  30. package/dist/likec4diagram/ui/controls/_shared.js +1 -1
  31. package/dist/likec4diagram/ui/diagram-title/DiagramTitlePanel.js +5 -4
  32. package/dist/likec4diagram/ui/index.d.ts +2 -0
  33. package/dist/likec4diagram/ui/search/ElementsColumn.css.d.ts +15 -0
  34. package/dist/likec4diagram/ui/search/ElementsColumn.css.js +30 -0
  35. package/dist/likec4diagram/ui/search/ElementsColumn.d.ts +1 -0
  36. package/dist/likec4diagram/ui/search/ElementsColumn.js +243 -0
  37. package/dist/likec4diagram/ui/search/LikeC4Search.css.d.ts +8 -0
  38. package/dist/likec4diagram/ui/search/LikeC4Search.css.js +20 -0
  39. package/dist/likec4diagram/ui/search/LikeC4Search.d.ts +1 -0
  40. package/dist/likec4diagram/ui/search/LikeC4Search.js +265 -0
  41. package/dist/likec4diagram/ui/search/SearchInput.d.ts +1 -0
  42. package/dist/likec4diagram/ui/search/SearchInput.js +196 -0
  43. package/dist/likec4diagram/ui/search/ViewsColum.d.ts +9 -0
  44. package/dist/likec4diagram/ui/search/ViewsColum.js +130 -0
  45. package/dist/likec4diagram/ui/search/ViewsColumn.css.d.ts +5 -0
  46. package/dist/likec4diagram/ui/search/ViewsColumn.css.js +12 -0
  47. package/dist/likec4diagram/ui/search/_shared.css.d.ts +16 -0
  48. package/dist/likec4diagram/ui/search/_shared.css.js +5 -0
  49. package/dist/likec4diagram/ui/search/state.d.ts +19 -0
  50. package/dist/likec4diagram/ui/search/state.js +72 -0
  51. package/dist/likec4diagram/ui/search/utils.d.ts +3 -0
  52. package/dist/likec4diagram/ui/search/utils.js +23 -0
  53. package/dist/likec4diagram/useLayoutConstraints.js +4 -5
  54. package/dist/likec4model/useLikeC4ElementsTree.d.ts +1 -0
  55. package/dist/likec4model/useLikeC4ElementsTree.js +1 -0
  56. package/dist/overlays/Overlays.js +10 -10
  57. package/dist/overlays/element-details/ElementDetailsCard.js +20 -13
  58. package/dist/overlays/element-details/TabPanelDeployments.js +4 -2
  59. package/dist/overlays/overlay/Overlay.d.ts +6 -2
  60. package/dist/overlays/overlay/Overlay.js +18 -29
  61. package/dist/overlays/relationship-details/SelectEdge.js +1 -3
  62. package/dist/overlays/relationships-browser/RelationshipsBrowser.js +6 -5
  63. package/dist/overlays/relationships-browser/SelectElement.js +10 -6
  64. package/dist/overlays/relationships-browser/actor.d.ts +2 -2
  65. package/dist/overlays/relationships-browser/actor.js +8 -8
  66. package/dist/overlays/relationships-browser/custom/nodeTypes.js +2 -2
  67. package/dist/style.css +1 -1
  68. package/package.json +15 -14
  69. package/src/LikeC4Diagram.tsx +0 -1
  70. package/src/base/BaseXYFlow.tsx +0 -1
  71. package/src/base/index.ts +0 -2
  72. package/src/base/primitives/element/ElementNodeContainer.css.ts +4 -7
  73. package/src/base/primitives/element/ElementNodeContainer.tsx +36 -16
  74. package/src/base/primitives/element/ElementTitle.css.ts +12 -9
  75. package/src/base/primitives/element/ElementTitle.tsx +6 -2
  76. package/src/base/primitives/element/styles.css.ts +18 -40
  77. package/src/{base → components}/Link.css.ts +3 -3
  78. package/src/components/SearchControl.css.ts +69 -0
  79. package/src/components/SearchControl.tsx +33 -0
  80. package/src/context/IconRenderer.tsx +54 -1
  81. package/src/hooks/index.ts +0 -1
  82. package/src/index.ts +15 -1
  83. package/src/likec4diagram/DiagramUI.tsx +1 -8
  84. package/src/likec4diagram/XYFlow.tsx +30 -30
  85. package/src/likec4diagram/custom/edges/RelationshipEdge.tsx +0 -1
  86. package/src/likec4diagram/custom/edges/RelationshipsDropdownMenu.css.ts +1 -0
  87. package/src/likec4diagram/custom/edges/RelationshipsDropdownMenu.tsx +3 -1
  88. package/src/likec4diagram/state/assign.ts +1 -1
  89. package/src/likec4diagram/state/machine.ts +12 -10
  90. package/src/likec4diagram/ui/controls/Controls.tsx +35 -25
  91. package/src/likec4diagram/ui/controls/NavigationButtons.tsx +36 -58
  92. package/src/likec4diagram/ui/controls/styles.css.ts +3 -1
  93. package/src/likec4diagram/ui/diagram-title/DiagramTitlePanel.tsx +111 -108
  94. package/src/likec4diagram/ui/index.ts +2 -0
  95. package/src/likec4diagram/ui/notation/NotationPanel.tsx +2 -3
  96. package/src/likec4diagram/ui/search/ElementsColumn.css.ts +119 -0
  97. package/src/likec4diagram/ui/search/ElementsColumn.tsx +291 -0
  98. package/src/likec4diagram/ui/search/LikeC4Search.css.ts +117 -0
  99. package/src/likec4diagram/ui/search/LikeC4Search.tsx +312 -0
  100. package/src/likec4diagram/ui/search/SearchInput.tsx +235 -0
  101. package/src/likec4diagram/ui/search/ViewsColum.tsx +158 -0
  102. package/src/likec4diagram/ui/search/ViewsColumn.css.ts +37 -0
  103. package/src/likec4diagram/ui/search/_shared.css.ts +104 -0
  104. package/src/likec4diagram/ui/search/state.ts +76 -0
  105. package/src/likec4diagram/ui/search/utils.ts +20 -0
  106. package/src/likec4diagram/useLayoutConstraints.ts +4 -5
  107. package/src/likec4model/useLikeC4ElementsTree.ts +4 -4
  108. package/src/overlays/Overlays.tsx +32 -26
  109. package/src/overlays/element-details/ElementDetailsCard.css.ts +4 -4
  110. package/src/overlays/element-details/ElementDetailsCard.tsx +246 -244
  111. package/src/overlays/element-details/TabPanelDeployments.tsx +4 -4
  112. package/src/overlays/overlay/Overlay.css.ts +10 -9
  113. package/src/overlays/overlay/Overlay.tsx +38 -26
  114. package/src/overlays/relationship-details/SelectEdge.css.ts +1 -0
  115. package/src/overlays/relationship-details/SelectEdge.tsx +2 -4
  116. package/src/overlays/relationships-browser/RelationshipsBrowser.tsx +10 -5
  117. package/src/overlays/relationships-browser/SelectElement.tsx +88 -82
  118. package/src/overlays/relationships-browser/actor.ts +10 -10
  119. package/src/overlays/relationships-browser/custom/nodeTypes.tsx +2 -2
  120. package/dist/LikeC4Search.css.d.ts +0 -1
  121. package/dist/LikeC4Search.css.js +0 -5
  122. package/dist/LikeC4Search.d.ts +0 -2
  123. package/dist/LikeC4Search.js +0 -88
  124. package/src/LikeC4Search.css.ts +0 -12
  125. package/src/LikeC4Search.tsx +0 -91
  126. /package/dist/{base → components}/Link.css.d.ts +0 -0
  127. /package/dist/{base → components}/Link.d.ts +0 -0
  128. /package/dist/{base → components}/Link.js +0 -0
  129. /package/src/{base → components}/Link.tsx +0 -0
@@ -2,7 +2,7 @@ import type { Fqn, LikeC4Model } from '@likec4/core'
2
2
  import { Alert, Box, Button, Group, Menu, rem, Text, ThemeIcon, Tree, useTree } from '@mantine/core'
3
3
  import { IconInfoCircle, IconTarget, IconZoomScan } from '@tabler/icons-react'
4
4
  import { type ReactNode, memo, useEffect, useMemo } from 'react'
5
- import { useCurrentViewId } from '../../hooks/useCurrentViewId'
5
+ import { useDiagram } from '../../hooks/useDiagram'
6
6
  import { useLikeC4Model } from '../../likec4model/useLikeC4Model'
7
7
  import { stopPropagation } from '../../utils'
8
8
  import * as css from './TabPanelDeployments.css'
@@ -36,8 +36,8 @@ const DeployedInstanceRenderer = (
36
36
  instance: LikeC4Model.DeployedInstance
37
37
  },
38
38
  ) => {
39
- const currentViewId = useCurrentViewId()
40
- // const diagramApi = useDiagramStoreApi()
39
+ const diagram = useDiagram()
40
+ const currentViewId = diagram.currentView().id
41
41
  const views = [...instance.views()]
42
42
  return (
43
43
  (
@@ -91,7 +91,7 @@ const DeployedInstanceRenderer = (
91
91
  }}
92
92
  onClick={e => {
93
93
  e.stopPropagation()
94
- // diagramApi.getState().onNavigateTo?.(view.id)
94
+ diagram.navigateTo(view.id)
95
95
  }}>
96
96
  {view.title}
97
97
  </Menu.Item>
@@ -4,19 +4,14 @@ import { mantine, whereLight } from '../../theme-vars'
4
4
 
5
5
  const borderRadius = createVar('border-radius')
6
6
  export const dialog = style({
7
- containerName: 'overlay-dialog',
8
- containerType: 'size',
9
7
  boxSizing: 'border-box',
10
8
  margin: 0,
11
9
  position: 'fixed',
12
10
  inset: '5rem 4rem 4rem 4rem',
13
- display: 'flex',
14
- flexDirection: 'column',
15
- alignItems: 'stretch',
16
11
  width: 'auto',
17
12
  height: 'auto',
18
- maxWidth: '100%',
19
- maxHeight: '100%',
13
+ maxWidth: '100vw',
14
+ maxHeight: '100vh',
20
15
  background: `color-mix(in srgb, ${mantine.colors.defaultBorder}, transparent 50%)`,
21
16
  boxShadow: mantine.shadows.xl,
22
17
  border: `0 solid transparent`,
@@ -46,21 +41,27 @@ export const dialog = style({
46
41
  [mantine.smallerThan('md')]: {
47
42
  borderRadius: mantine.radius.sm,
48
43
  inset: '2rem',
44
+ width: 'calc(100vw - 4rem)',
45
+ maxWidth: 'calc(100vw - 4rem)',
49
46
  },
50
47
  [mantine.smallerThan('sm')]: {
51
48
  border: 'none',
52
- inset: '0px',
49
+ inset: 0,
50
+ width: '100vw',
51
+ height: '100vh',
53
52
  },
54
53
  },
55
54
  })
56
55
 
57
56
  export const body = style({
57
+ position: 'relative',
58
+ containerName: 'overlay-dialog',
59
+ containerType: 'size',
58
60
  border: `0 solid transparent`,
59
61
  borderRadius: calc(borderRadius).subtract('2px').toString(),
60
62
  // borderRadius: borderRadius,
61
63
  backgroundColor: mantine.colors.body,
62
64
  overflow: 'hidden',
63
- flex: 1,
64
65
  width: '100%',
65
66
  height: '100%',
66
67
  // visibility: 'hidden',
@@ -2,27 +2,28 @@ import {
2
2
  Box,
3
3
  RemoveScroll,
4
4
  } from '@mantine/core'
5
- import { useMergedRef } from '@mantine/hooks'
6
- import { useDebouncedCallback, useSyncedRef } from '@react-hookz/web'
5
+ import { useDebouncedCallback, useSyncedRef, useTimeoutEffect } from '@react-hookz/web'
7
6
  import clsx from 'clsx'
8
7
  import { type HTMLMotionProps, m } from 'framer-motion'
9
- import { type PropsWithChildren, forwardRef, useLayoutEffect, useRef, useState } from 'react'
8
+ import { type PropsWithChildren, useRef, useState } from 'react'
10
9
  import * as css from './Overlay.css'
11
10
 
12
11
  type OverlayProps = PropsWithChildren<
13
12
  HTMLMotionProps<'dialog'> & {
13
+ classes?: {
14
+ dialog?: string
15
+ body?: string
16
+ }
14
17
  onClose: () => void
15
18
  onClick?: never
16
19
  }
17
20
  >
18
21
 
19
- export const Overlay = forwardRef<HTMLDialogElement, OverlayProps>(({ children, onClose, className, ...rest }, ref) => {
22
+ export function Overlay({ children, onClose, className, classes, ...rest }: OverlayProps) {
20
23
  const [opened, setOpened] = useState(false)
21
24
  const dialogRef = useRef<HTMLDialogElement>(null)
22
25
 
23
26
  // Move dialog to the top of the DOM
24
- // useLayoutEffect(() => dialogRef.current?.showModal(), [])
25
-
26
27
  const onCloseRef = useSyncedRef(onClose)
27
28
  const close = useDebouncedCallback(
28
29
  () => {
@@ -31,22 +32,37 @@ export const Overlay = forwardRef<HTMLDialogElement, OverlayProps>(({ children,
31
32
  [],
32
33
  50,
33
34
  )
35
+ // const close = useCallbackRef(() => {
36
+ // if (isOpenedRef.current) {
37
+ // isOpenedRef.current = false
38
+ // requestAnimationFrame(() => {
39
+ // onClose()
40
+ // })
41
+ // }
42
+ // })
34
43
 
35
- useLayoutEffect(() => {
36
- const cancel = (e: Event) => {
37
- e.preventDefault()
38
- onCloseRef.current()
39
- }
40
- dialogRef.current?.addEventListener('cancel', cancel, { capture: true })
41
- return () => {
42
- dialogRef.current?.removeEventListener('cancel', cancel, { capture: true })
44
+ // useLayoutEffect(() => {
45
+ // const cancel = (e: Event) => {
46
+ // e.preventDefault()
47
+ // close()
48
+ // }
49
+ // dialogRef.current?.addEventListener('cancel', cancel, { capture: true })
50
+ // return () => {
51
+ // dialogRef.current?.removeEventListener('cancel', cancel, { capture: true })
52
+ // }
53
+ // }, [])
54
+
55
+ useTimeoutEffect(() => {
56
+ if (!dialogRef.current?.open) {
57
+ dialogRef.current?.showModal()
43
58
  }
44
- }, [])
59
+ setOpened(true)
60
+ }, 30)
45
61
 
46
62
  return (
47
63
  <m.dialog
48
- ref={useMergedRef(ref, dialogRef)}
49
- className={clsx(css.dialog, className)}
64
+ ref={dialogRef}
65
+ className={clsx(css.dialog, classes?.dialog, className)}
50
66
  initial={{
51
67
  '--backdrop-blur': '0px',
52
68
  '--backdrop-opacity': '5%',
@@ -63,12 +79,8 @@ export const Overlay = forwardRef<HTMLDialogElement, OverlayProps>(({ children,
63
79
  // delay: 0.25,
64
80
  // }
65
81
  }}
66
- onAnimationStart={() => {
67
- dialogRef.current?.showModal()
68
- setOpened(true)
69
- }}
70
82
  exit={{
71
- opacity: 0.1,
83
+ opacity: 0,
72
84
  translateY: -10,
73
85
  '--backdrop-blur': '0px',
74
86
  '--backdrop-opacity': '0%',
@@ -79,21 +91,21 @@ export const Overlay = forwardRef<HTMLDialogElement, OverlayProps>(({ children,
79
91
  onClick={e => {
80
92
  if ((e.target as any)?.nodeName?.toUpperCase() === 'DIALOG') {
81
93
  e.stopPropagation()
82
- close()
94
+ dialogRef.current?.close()
95
+ return
83
96
  }
84
97
  }}
85
98
  onClose={e => {
86
- e.preventDefault()
87
99
  e.stopPropagation()
88
100
  close()
89
101
  }}
90
102
  {...rest}
91
103
  >
92
104
  <RemoveScroll forwardProps>
93
- <Box className={css.body}>
105
+ <Box className={clsx(css.body, classes?.body)}>
94
106
  {opened && <>{children}</>}
95
107
  </Box>
96
108
  </RemoveScroll>
97
109
  </m.dialog>
98
110
  )
99
- })
111
+ }
@@ -32,6 +32,7 @@ export const edgeLabel = style({
32
32
  borderBottom: `1px solid ${mantine.colors.defaultBorder}`,
33
33
  marginBottom: '0px',
34
34
  padding: '0 4px 5px 4px',
35
+ width: '100%',
35
36
  // gridArea: '2 / 1 / 3 / 3',
36
37
  // selectors: {
37
38
  // '&:last-child': {
@@ -104,9 +104,9 @@ export const SelectEdge = ({
104
104
  </Box>
105
105
  </Button>
106
106
  </PopoverTarget>
107
- <PopoverDropdown p={0} miw={250} maw={420}>
107
+ <PopoverDropdown p={0} miw={250} maw={400}>
108
108
  <ScrollAreaAutosize className={css.scrollArea} scrollbars="y" type="never" viewportRef={viewport}>
109
- <Box className={css.edgeGrid} p="xs">
109
+ <Box className={css.edgeGrid} p="xs" maw={400}>
110
110
  {edges.map(e => (
111
111
  <div
112
112
  key={e.id}
@@ -118,7 +118,6 @@ export const SelectEdge = ({
118
118
  }}>
119
119
  <Box
120
120
  className={css.edgeSource}
121
- maw={160}
122
121
  mod={{
123
122
  'edge-id': e.id,
124
123
  'likec4-color': e.source.color,
@@ -133,7 +132,6 @@ export const SelectEdge = ({
133
132
  </Box>
134
133
  <Box
135
134
  className={css.edgeTarget}
136
- maw={160}
137
135
  mod={{
138
136
  'likec4-color': e.target.color,
139
137
  }}
@@ -5,8 +5,8 @@ import { IconChevronLeft, IconChevronRight, IconX } from '@tabler/icons-react'
5
5
  import { Panel, ReactFlowProvider, useStoreApi } from '@xyflow/react'
6
6
  import clsx from 'clsx'
7
7
  import { shallowEqual } from 'fast-equals'
8
- import { AnimatePresence, m } from 'framer-motion'
9
- import { memo, useEffect, useRef } from 'react'
8
+ import { AnimatePresence, LayoutGroup, m } from 'framer-motion'
9
+ import { memo, useEffect, useId, useRef } from 'react'
10
10
  import type { SnapshotFrom } from 'xstate'
11
11
  import { BaseXYFlow } from '../../base/BaseXYFlow'
12
12
  import { useRelationshipsView } from './-useRelationshipsView'
@@ -26,6 +26,7 @@ export type RelationshipsBrowserProps = {
26
26
  actorRef: RelationshipsBrowserActorRef
27
27
  }
28
28
  export function RelationshipsBrowser({ actorRef }: RelationshipsBrowserProps) {
29
+ const id = useId()
29
30
  // const actorRef = useDiagramActorState(s => s.children.relationshipsBrowser)
30
31
  // if (actorRef == null) {
31
32
  // return null
@@ -45,7 +46,11 @@ export function RelationshipsBrowser({ actorRef }: RelationshipsBrowserProps) {
45
46
  return (
46
47
  <RelationshipsBrowserActorContext.Provider value={actorRef}>
47
48
  <ReactFlowProvider {...initialRef.current}>
48
- <RelationshipsBrowserXYFlow />
49
+ <AnimatePresence>
50
+ <LayoutGroup id={id}>
51
+ <RelationshipsBrowserXYFlow />
52
+ </LayoutGroup>
53
+ </AnimatePresence>
49
54
  </ReactFlowProvider>
50
55
  </RelationshipsBrowserActorContext.Provider>
51
56
  )
@@ -114,10 +119,10 @@ const RelationshipsBrowserXYFlow = memo(() => {
114
119
  browser.send({ type: 'xyflow.resized' })
115
120
  })}
116
121
  onNodesChange={useCallbackRef((changes) => {
117
- browser.send({ type: 'xyflow.applyNodeChages', changes })
122
+ browser.send({ type: 'xyflow.applyNodeChanges', changes })
118
123
  })}
119
124
  onEdgesChange={useCallbackRef((changes) => {
120
- browser.send({ type: 'xyflow.applyEdgeChages', changes })
125
+ browser.send({ type: 'xyflow.applyEdgeChanges', changes })
121
126
  })}
122
127
  nodesDraggable={false}
123
128
  fitView={false}
@@ -47,88 +47,94 @@ export const SelectElement = ({
47
47
  const theme = useComputedColorScheme()
48
48
 
49
49
  return (
50
- <Popover
51
- position="bottom"
52
- shadow="md"
53
- keepMounted
54
- withinPortal={false}
55
- closeOnClickOutside
56
- clickOutsideEvents={['pointerdown', 'mousedown', 'click']}
57
- onOpen={() => {
58
- setTimeout(() => {
59
- const item = viewport.current?.querySelector(`[data-value="${subjectId}"]`)
60
- item?.scrollIntoView({ behavior: 'instant', block: 'nearest' })
61
- }, 100)
62
- }}
63
- >
64
- <PopoverTarget>
65
- <Button
66
- size="xs"
67
- variant="light"
68
- color={theme === 'light' ? 'dark' : 'gray'}
69
- fw={'500'}
70
- maw={250}
71
- style={{ padding: '0.25rem 0.75rem' }}
72
- rightSection={<IconSelector size={16} />}
73
- >
74
- {subject?.title ?? '???'}
75
- </Button>
76
- </PopoverTarget>
77
- <PopoverDropdown p={0} miw={250} maw={400}>
78
- <ScrollAreaAutosize scrollbars="y" type="never" viewportRef={viewport} className={classes.scrollArea}>
79
- <Tree
80
- allowRangeSelection={false}
81
- selectOnClick={false}
82
- tree={tree}
83
- data={data}
84
- classNames={classes}
85
- levelOffset={8}
86
- styles={{
87
- label: {
88
- paddingTop: 5,
89
- paddingBottom: 6,
90
- },
91
- }}
92
- renderNode={({ node, selected, expanded, elementProps, hasChildren }) => (
93
- <Group gap={2} wrap="nowrap" {...elementProps} py="3">
94
- <ActionIcon
95
- variant="subtle"
96
- size={18}
97
- // color={theme === 'light' ? 'gray' : 'gray'}
98
- c={'dimmed'}
99
- style={{
100
- visibility: hasChildren ? 'visible' : 'hidden',
101
- }}>
102
- <IconChevronRight
103
- stroke={3.5}
50
+ <Box pos={'relative'}>
51
+ <Popover
52
+ position="bottom-start"
53
+ shadow="md"
54
+ keepMounted={false}
55
+ withinPortal={false}
56
+ closeOnClickOutside
57
+ clickOutsideEvents={['pointerdown', 'mousedown', 'click']}
58
+ onOpen={() => {
59
+ setTimeout(() => {
60
+ const item = viewport.current?.querySelector(`[data-value="${subjectId}"]`)
61
+ item?.scrollIntoView({ behavior: 'instant', block: 'nearest' })
62
+ }, 100)
63
+ }}
64
+ >
65
+ <PopoverTarget>
66
+ <Button
67
+ size="xs"
68
+ variant="light"
69
+ color={theme === 'light' ? 'dark' : 'gray'}
70
+ fw={'500'}
71
+ maw={250}
72
+ style={{ padding: '0.25rem 0.75rem' }}
73
+ rightSection={<IconSelector size={16} />}
74
+ >
75
+ {subject?.title ?? '???'}
76
+ </Button>
77
+ </PopoverTarget>
78
+ <PopoverDropdown p={0} miw={250} maw={400}>
79
+ <ScrollAreaAutosize scrollbars="y" type="never" viewportRef={viewport} className={classes.scrollArea}>
80
+ <Tree
81
+ allowRangeSelection={false}
82
+ selectOnClick={false}
83
+ tree={tree}
84
+ data={data}
85
+ classNames={classes}
86
+ levelOffset={8}
87
+ styles={{
88
+ root: {
89
+ maxWidth: 400,
90
+ overflow: 'hidden',
91
+ },
92
+ label: {
93
+ paddingTop: 5,
94
+ paddingBottom: 6,
95
+ },
96
+ }}
97
+ renderNode={({ node, selected, expanded, elementProps, hasChildren }) => (
98
+ <Group gap={2} wrap="nowrap" {...elementProps} py="3">
99
+ <ActionIcon
100
+ variant="subtle"
101
+ size={18}
102
+ // color={theme === 'light' ? 'gray' : 'gray'}
103
+ c={'dimmed'}
104
104
  style={{
105
- transition: 'transform 150ms ease',
106
- transform: `rotate(${expanded ? '90deg' : '0'})`,
107
- width: '80%',
108
- }} />
109
- </ActionIcon>
110
- <Box
111
- flex={'1 1 100%'}
112
- w={'100%'}
113
- // gap={3}
114
- onClick={e => {
115
- e.stopPropagation()
116
- onSelect(node.value as Fqn)
117
- tree.select(node.value)
118
- tree.expand(node.value)
119
- }}>
120
- <Text
121
- fz="sm"
122
- fw={selected ? '600' : '400'}
123
- truncate="end">
124
- {node.label}
125
- </Text>
126
- </Box>
127
- </Group>
128
- )}
129
- />
130
- </ScrollAreaAutosize>
131
- </PopoverDropdown>
132
- </Popover>
105
+ visibility: hasChildren ? 'visible' : 'hidden',
106
+ }}>
107
+ <IconChevronRight
108
+ stroke={3.5}
109
+ style={{
110
+ transition: 'transform 150ms ease',
111
+ transform: `rotate(${expanded ? '90deg' : '0'})`,
112
+ width: '80%',
113
+ }} />
114
+ </ActionIcon>
115
+ <Box
116
+ flex={'1 1 100%'}
117
+ w={'100%'}
118
+ // gap={3}
119
+ onClick={e => {
120
+ e.stopPropagation()
121
+ onSelect(node.value as Fqn)
122
+ tree.select(node.value)
123
+ tree.expand(node.value)
124
+ }}>
125
+ <Text
126
+ fz="sm"
127
+ fw={selected ? '600' : '400'}
128
+ truncate="end">
129
+ {node.label}
130
+ </Text>
131
+ </Box>
132
+ </Group>
133
+ )}
134
+ />
135
+ </ScrollAreaAutosize>
136
+ </PopoverDropdown>
137
+ </Popover>
138
+ </Box>
133
139
  )
134
140
  }
@@ -72,8 +72,8 @@ export type Events =
72
72
  | { type: 'xyflow.init'; instance: XYFLowInstance; store: XYStoreApi }
73
73
  | { type: 'xyflow.nodeClick'; node: RelationshipsBrowserTypes.Node }
74
74
  | { type: 'xyflow.edgeClick'; edge: RelationshipsBrowserTypes.Edge }
75
- | { type: 'xyflow.applyNodeChages'; changes: NodeChange<RelationshipsBrowserTypes.Node>[] }
76
- | { type: 'xyflow.applyEdgeChages'; changes: EdgeChange<RelationshipsBrowserTypes.Edge>[] }
75
+ | { type: 'xyflow.applyNodeChanges'; changes: NodeChange<RelationshipsBrowserTypes.Node>[] }
76
+ | { type: 'xyflow.applyEdgeChanges'; changes: EdgeChange<RelationshipsBrowserTypes.Edge>[] }
77
77
  | { type: 'xyflow.paneClick' }
78
78
  | { type: 'xyflow.paneDblClick' }
79
79
  | { type: 'xyflow.resized' }
@@ -198,7 +198,7 @@ export const relationshipsBrowserActor = setup({
198
198
  !nextSubjectNode || !existingNode || nextSubjectNode.type === 'empty' || !currentSubjectNode ||
199
199
  nextSubjectNode.data.fqn === currentSubjectNode.data.fqn
200
200
  ) {
201
- xyflow.setViewport(nextviewport)
201
+ await xyflow.setViewport(nextviewport)
202
202
  return updateXYData()
203
203
  }
204
204
 
@@ -280,7 +280,7 @@ export const relationshipsBrowserActor = setup({
280
280
  return updateXYData()
281
281
  }
282
282
  await xyflow.setCenter(currentSubjectCenter.x, currentSubjectCenter.y, { zoom, duration: 350 })
283
- xyflow.setCenter(nextSubjectCenter.x, nextSubjectCenter.y, { zoom })
283
+ await xyflow.setCenter(nextSubjectCenter.x, nextSubjectCenter.y, { zoom })
284
284
  return updateXYData()
285
285
  }),
286
286
  },
@@ -372,11 +372,11 @@ export const relationshipsBrowserActor = setup({
372
372
  xyedges: ({ event }) => event.xyedges,
373
373
  }),
374
374
  },
375
- 'xyflow.applyEdgeChages': {
376
- // actions: log('layouting: ignore xyflow.applyEdgeChages'),
375
+ 'xyflow.applyEdgeChanges': {
376
+ // actions: log('layouting: ignore xyflow.applyEdgeChanges'),
377
377
  },
378
- 'xyflow.applyNodeChages': {
379
- // actions: log('layouting: ignore xyflow.applyNodeChages'),
378
+ 'xyflow.applyNodeChanges': {
379
+ // actions: log('layouting: ignore xyflow.applyNodeChanges'),
380
380
  },
381
381
  },
382
382
  },
@@ -447,14 +447,14 @@ export const relationshipsBrowserActor = setup({
447
447
  },
448
448
  },
449
449
  on: {
450
- 'xyflow.applyNodeChages': {
450
+ 'xyflow.applyNodeChanges': {
451
451
  actions: assign({
452
452
  xynodes: ({ context, event }) => {
453
453
  return applyNodeChanges(event.changes, context.xynodes)
454
454
  },
455
455
  }),
456
456
  },
457
- 'xyflow.applyEdgeChages': {
457
+ 'xyflow.applyEdgeChanges': {
458
458
  actions: assign({
459
459
  xyedges: ({ context, event }) => {
460
460
  return applyEdgeChanges(event.changes, context.xyedges)
@@ -17,7 +17,7 @@ import { EmptyNode } from './EmptyNode'
17
17
  export const nodeTypes = {
18
18
  element: customNode<RelationshipsBrowserTypes.ElementNodeData>((props) => {
19
19
  return (
20
- <ElementNodeContainer component={m.div} layoutId={props.id} nodeProps={props}>
20
+ <ElementNodeContainer key={props.id} component={m.div} layoutId={props.id} nodeProps={props}>
21
21
  <ElementShape {...props} />
22
22
  <ElementTitle {...props} iconSize={40} />
23
23
  <ElementActions {...props} />
@@ -28,7 +28,7 @@ export const nodeTypes = {
28
28
 
29
29
  compound: customNode<RelationshipsBrowserTypes.CompoundNodeData>((props) => {
30
30
  return (
31
- <CompoundNodeContainer component={m.div} layoutId={props.id} nodeProps={props}>
31
+ <CompoundNodeContainer key={props.id} component={m.div} layoutId={props.id} nodeProps={props}>
32
32
  <CompoundTitle {...props} />
33
33
  <CompoundPorts {...props} />
34
34
  </CompoundNodeContainer>
@@ -1 +0,0 @@
1
- export declare const actionSection: string;
@@ -1,5 +0,0 @@
1
- /* empty css */
2
- var actionSection = "_16mg60z0";
3
- export {
4
- actionSection
5
- };
@@ -1,2 +0,0 @@
1
- import { MemoExoticComponent } from 'react';
2
- export declare const LikeC4Search: MemoExoticComponent<() => import("react/jsx-runtime").JSX.Element>;
@@ -1,88 +0,0 @@
1
- import { jsx } from "react/jsx-runtime";
2
- import { rem } from "@mantine/core";
3
- import { Spotlight } from "@mantine/spotlight";
4
- import { IconSearch, IconRectangularPrism, IconStack2, IconSitemap } from "@tabler/icons-react";
5
- import { memo, useMemo } from "react";
6
- import { pipe, filter, map } from "remeda";
7
- import { useDiagram } from "./hooks/useDiagram.js";
8
- import { useDiagramContext } from "./hooks/useDiagramContext.js";
9
- import { actionSection } from "./LikeC4Search.css.js";
10
- import { useLikeC4Model } from "./likec4model/useLikeC4Model.js";
11
- import { useMantinePortalProps } from "./hooks/useMantinePortalProps.js";
12
- const LikeC4Search = memo(() => {
13
- const view = useDiagramContext((s) => s.view);
14
- const model = useLikeC4Model(true);
15
- const diagram = useDiagram();
16
- const getNodeActionsData = () => {
17
- return pipe(
18
- view.nodes,
19
- filter((n) => !!n.title),
20
- map((n) => ({
21
- id: n.id,
22
- label: n.title,
23
- ...n.description && {
24
- description: n.description
25
- },
26
- keywords: [
27
- n.title,
28
- ...(n.tags ?? []).map((t) => `#${t}`),
29
- ...n.description ? [n.description] : []
30
- ].filter((k) => k.toLowerCase()),
31
- onClick: () => diagram.focusNode(n.id),
32
- leftSection: /* @__PURE__ */ jsx(IconRectangularPrism, {})
33
- }))
34
- );
35
- };
36
- const getViewActionsData = () => {
37
- const views = [...model.views()];
38
- return map(views, (v) => ({
39
- id: v.id,
40
- label: v.title ?? v.id,
41
- ...v.$view.description && {
42
- description: v.$view.description
43
- },
44
- keywords: [
45
- v.title ?? v.id,
46
- ...v.tags ?? [],
47
- ...v.$view.description ? [v.$view.description] : []
48
- ],
49
- onClick: () => {
50
- diagram.navigateTo(v.id);
51
- },
52
- leftSection: v.isDeploymentView() ? /* @__PURE__ */ jsx(IconStack2, {}) : /* @__PURE__ */ jsx(IconSitemap, {})
53
- }));
54
- };
55
- const actions = useMemo(() => [
56
- {
57
- group: "Elements",
58
- actions: getNodeActionsData()
59
- },
60
- {
61
- group: "Views",
62
- actions: getViewActionsData()
63
- }
64
- ], [model, diagram, view]);
65
- const portalProps = useMantinePortalProps();
66
- return /* @__PURE__ */ jsx(
67
- Spotlight,
68
- {
69
- ...portalProps,
70
- actions,
71
- nothingFound: "Nothing found...",
72
- shortcut: ["mod + f", "mod + k"],
73
- scrollable: true,
74
- highlightQuery: true,
75
- maxHeight: 350,
76
- classNames: {
77
- actionSection
78
- },
79
- searchProps: {
80
- leftSection: /* @__PURE__ */ jsx(IconSearch, { style: { width: rem(18), height: rem(18) }, stroke: 1.5 }),
81
- placeholder: "Search elements and views..."
82
- }
83
- }
84
- );
85
- });
86
- export {
87
- LikeC4Search
88
- };
@@ -1,12 +0,0 @@
1
- import { rem } from '@mantine/core'
2
- import { globalStyle, style } from '@vanilla-extract/css'
3
-
4
- export const actionSection = style({
5
- marginInlineEnd: 4
6
- })
7
-
8
- globalStyle(`:where(${actionSection}) .tabler-icon`, {
9
- opacity: 0.8,
10
- width: '70%',
11
- height: '70%'
12
- })