@likec4/diagram 1.28.0 → 1.29.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 (162) hide show
  1. package/bundle/index.js +1040 -1190
  2. package/dist/LikeC4CustomColors.js +2 -2
  3. package/dist/LikeC4Diagram.js +2 -2
  4. package/dist/LikeC4ModelProvider.js +1 -1
  5. package/dist/_virtual/index.js +5 -0
  6. package/dist/_virtual/index2.js +3 -0
  7. package/dist/_virtual/react.production.js +3 -0
  8. package/dist/base/BaseXYFlow.js +5 -5
  9. package/dist/base/primitives/compound/CompoundActionButton.js +1 -1
  10. package/dist/base/primitives/compound/CompoundDetailsButton.js +1 -1
  11. package/dist/base/primitives/compound/CompoundNodeContainer.d.ts +1 -1
  12. package/dist/base/primitives/compound/CompoundNodeContainer.js +2 -2
  13. package/dist/base/primitives/customEdge.d.ts +1 -1
  14. package/dist/base/primitives/customEdge.js +3 -3
  15. package/dist/base/primitives/customNode.js +2 -2
  16. package/dist/base/primitives/edge/EdgeContainer.js +1 -1
  17. package/dist/base/primitives/edge/EdgeLabel.css.d.ts +3 -0
  18. package/dist/base/primitives/edge/EdgeLabel.css.js +3 -0
  19. package/dist/base/primitives/edge/EdgeLabel.d.ts +1 -1
  20. package/dist/base/primitives/edge/EdgeLabel.js +3 -2
  21. package/dist/base/primitives/edge/EdgeLabelContainer.js +1 -1
  22. package/dist/base/primitives/edge/EdgePath.d.ts +10 -42
  23. package/dist/base/primitives/edge/EdgePath.js +23 -16
  24. package/dist/base/primitives/edge/edge.css.js +1 -1
  25. package/dist/base/primitives/element/ElementActionButtons.js +1 -2
  26. package/dist/base/primitives/element/ElementDetailsButton.js +1 -2
  27. package/dist/base/primitives/element/ElementNodeContainer.js +3 -3
  28. package/dist/base/updateEdges.js +7 -2
  29. package/dist/base/updateNodes.js +11 -3
  30. package/dist/bundle/LikeC4View.js +3 -3
  31. package/dist/bundle/ReactLikeC4.js +1 -1
  32. package/dist/bundle/ShadowRoot.js +3 -3
  33. package/dist/bundle/styles.css.js +3 -3
  34. package/dist/context/ControlsCustomLayout.js +1 -1
  35. package/dist/context/DiagramEventHandlers.js +5 -5
  36. package/dist/context/DiagramFeatures.js +7 -7
  37. package/dist/context/EnsureMantine.js +2 -2
  38. package/dist/context/FramerMotionConfig.js +2 -2
  39. package/dist/context/IconRenderer.js +4 -4
  40. package/dist/context/ReduceGraphics.js +2 -2
  41. package/dist/context/RootContainer.js +5 -9
  42. package/dist/hooks/safeContext.d.ts +2 -5
  43. package/dist/hooks/safeContext.js +5 -4
  44. package/dist/hooks/useCurrentViewId.js +1 -1
  45. package/dist/hooks/useDiagram.d.ts +715 -150
  46. package/dist/hooks/useDiagram.js +2 -2
  47. package/dist/hooks/useMantinePortalProps.js +2 -2
  48. package/dist/hooks/useSetState.js +3 -3
  49. package/dist/hooks/useUpdateEffect.js +1 -1
  50. package/dist/hooks/useXYFlow.d.ts +2 -2
  51. package/dist/likec4diagram/DiagramUI.js +2 -2
  52. package/dist/likec4diagram/DiagramXYFlow.js +2 -2
  53. package/dist/likec4diagram/custom/edges/NotePopover.js +2 -2
  54. package/dist/likec4diagram/custom/edges/RelationshipEdge.d.ts +2 -2
  55. package/dist/likec4diagram/custom/edges/RelationshipEdge.js +7 -7
  56. package/dist/likec4diagram/custom/edges/RelationshipsDropdownMenu.js +6 -6
  57. package/dist/likec4diagram/custom/index.d.ts +1 -2
  58. package/dist/likec4diagram/custom/nodes/toolbar/ColorButton.js +2 -2
  59. package/dist/likec4diagram/custom/nodes/toolbar/CompoundToolbar.js +3 -3
  60. package/dist/likec4diagram/custom/nodes/toolbar/_shared.js +2 -2
  61. package/dist/likec4diagram/ui/controls/ChangeAutoLayoutButton.js +4 -4
  62. package/dist/likec4diagram/ui/controls/Controls.js +9 -9
  63. package/dist/likec4diagram/ui/controls/NavigationButtons.js +1 -1
  64. package/dist/likec4diagram/ui/diagram-title/DiagramTitlePanel.js +1 -1
  65. package/dist/likec4diagram/ui/notation/NotationPanel.js +4 -4
  66. package/dist/likec4diagram/ui/search/ElementsColumn.js +88 -77
  67. package/dist/likec4diagram/ui/search/LikeC4Search.d.ts +2 -1
  68. package/dist/likec4diagram/ui/search/LikeC4Search.js +27 -24
  69. package/dist/likec4diagram/ui/search/SearchInput.js +1 -1
  70. package/dist/likec4diagram/ui/search/ViewsColum.js +3 -2
  71. package/dist/likec4diagram/ui/search/state.js +11 -9
  72. package/dist/likec4diagram/ui/walkthrough/DynamicViewWalkthrough.js +1 -1
  73. package/dist/likec4diagram/useLayoutConstraints.js +3 -3
  74. package/dist/likec4model/LikeC4ModelContext.js +2 -2
  75. package/dist/likec4model/useLikeC4ElementsTree.js +2 -2
  76. package/dist/likec4model/useLikeC4Model.js +2 -2
  77. package/dist/node_modules/.pnpm/react@19.0.0/node_modules/react/cjs/react.production.js +548 -0
  78. package/dist/node_modules/.pnpm/react@19.0.0/node_modules/react/index.js +15 -0
  79. package/dist/overlays/Overlays.js +6 -6
  80. package/dist/overlays/element-details/ElementDetails.js +1 -1
  81. package/dist/overlays/element-details/ElementDetailsCard.js +5 -5
  82. package/dist/overlays/element-details/TabPanelDeployments.js +7 -4
  83. package/dist/overlays/element-details/TabPanelRelationships.js +2 -2
  84. package/dist/overlays/element-details/TabPanelStructure.js +6 -3
  85. package/dist/overlays/element-details/actor.d.ts +2 -2
  86. package/dist/overlays/element-details/actorContext.js +3 -3
  87. package/dist/overlays/overlay/Overlay.js +8 -7
  88. package/dist/overlays/relationship-details/RelationshipDetails.d.ts +1 -1
  89. package/dist/overlays/relationship-details/RelationshipDetails.js +28 -23
  90. package/dist/overlays/relationship-details/SelectEdge.js +3 -6
  91. package/dist/overlays/relationship-details/actor.d.ts +4 -1
  92. package/dist/overlays/relationship-details/custom/edgeTypes.d.ts +1 -1
  93. package/dist/overlays/relationship-details/custom/edgeTypes.js +1 -1
  94. package/dist/overlays/relationship-details/hooks.d.ts +4 -1
  95. package/dist/overlays/relationship-details/hooks.js +4 -4
  96. package/dist/overlays/relationships-browser/RelationshipsBrowser.d.ts +1 -1
  97. package/dist/overlays/relationships-browser/RelationshipsBrowser.js +9 -11
  98. package/dist/overlays/relationships-browser/SelectElement.js +8 -5
  99. package/dist/overlays/relationships-browser/actor.d.ts +2 -1
  100. package/dist/overlays/relationships-browser/actor.js +6 -12
  101. package/dist/overlays/relationships-browser/const.d.ts +2 -1
  102. package/dist/overlays/relationships-browser/const.js +6 -1
  103. package/dist/overlays/relationships-browser/custom/edgeTypes.d.ts +1 -1
  104. package/dist/overlays/relationships-browser/custom/edgeTypes.js +3 -4
  105. package/dist/overlays/relationships-browser/custom/nodeTypes.js +1 -1
  106. package/dist/overlays/relationships-browser/hooks.d.ts +2 -1
  107. package/dist/overlays/relationships-browser/hooks.js +4 -4
  108. package/dist/overlays/relationships-browser/layout.js +2 -2
  109. package/dist/overlays/relationships-browser/useViewToNodesEdges.js +1 -2
  110. package/dist/state/DiagramActorProvider.js +7 -5
  111. package/dist/state/diagram-machine.d.ts +719 -146
  112. package/dist/state/diagram-machine.js +51 -10
  113. package/dist/styles.css.js +1 -1
  114. package/package.json +20 -16
  115. package/src/LikeC4Diagram.tsx +0 -1
  116. package/src/base/BaseXYFlow.tsx +2 -2
  117. package/src/base/primitives/compound/CompoundActionButton.tsx +1 -1
  118. package/src/base/primitives/compound/CompoundDetailsButton.tsx +1 -1
  119. package/src/base/primitives/compound/CompoundNodeContainer.tsx +1 -1
  120. package/src/base/primitives/customEdge.ts +1 -0
  121. package/src/base/primitives/edge/EdgeLabel.css.ts +3 -0
  122. package/src/base/primitives/edge/EdgeLabel.tsx +2 -1
  123. package/src/base/primitives/edge/EdgePath.tsx +22 -14
  124. package/src/base/primitives/edge/edge.css.ts +1 -1
  125. package/src/base/primitives/element/ElementActionButtons.tsx +2 -2
  126. package/src/base/primitives/element/ElementDetailsButton.tsx +2 -2
  127. package/src/base/primitives/element/ElementNodeContainer.tsx +1 -1
  128. package/src/base/updateEdges.ts +6 -9
  129. package/src/base/updateNodes.ts +10 -14
  130. package/src/context/FramerMotionConfig.tsx +1 -1
  131. package/src/context/RootContainer.tsx +0 -4
  132. package/src/hooks/safeContext.tsx +3 -1
  133. package/src/likec4diagram/custom/edges/RelationshipEdge.tsx +2 -2
  134. package/src/likec4diagram/custom/edges/RelationshipsDropdownMenu.tsx +1 -1
  135. package/src/likec4diagram/ui/controls/Controls.tsx +56 -58
  136. package/src/likec4diagram/ui/controls/NavigationButtons.tsx +1 -1
  137. package/src/likec4diagram/ui/diagram-title/DiagramTitlePanel.tsx +1 -1
  138. package/src/likec4diagram/ui/notation/NotationPanel.tsx +1 -1
  139. package/src/likec4diagram/ui/search/ElementsColumn.tsx +94 -71
  140. package/src/likec4diagram/ui/search/LikeC4Search.tsx +48 -55
  141. package/src/likec4diagram/ui/search/ViewsColum.tsx +2 -2
  142. package/src/likec4diagram/ui/search/state.ts +8 -6
  143. package/src/likec4diagram/ui/walkthrough/DynamicViewWalkthrough.tsx +1 -1
  144. package/src/overlays/Overlays.tsx +2 -2
  145. package/src/overlays/element-details/ElementDetails.tsx +2 -2
  146. package/src/overlays/element-details/ElementDetailsCard.tsx +1 -1
  147. package/src/overlays/element-details/TabPanelDeployments.tsx +2 -0
  148. package/src/overlays/element-details/TabPanelStructure.tsx +2 -1
  149. package/src/overlays/overlay/Overlay.tsx +2 -1
  150. package/src/overlays/relationship-details/RelationshipDetails.tsx +25 -16
  151. package/src/overlays/relationship-details/SelectEdge.tsx +0 -14
  152. package/src/overlays/relationship-details/actor.ts +2 -1
  153. package/src/overlays/relationship-details/custom/edgeTypes.tsx +1 -1
  154. package/src/overlays/relationships-browser/RelationshipsBrowser.tsx +1 -3
  155. package/src/overlays/relationships-browser/SelectElement.tsx +2 -0
  156. package/src/overlays/relationships-browser/actor.ts +8 -13
  157. package/src/overlays/relationships-browser/const.ts +8 -1
  158. package/src/overlays/relationships-browser/custom/edgeTypes.tsx +2 -3
  159. package/src/overlays/relationships-browser/custom/nodeTypes.tsx +1 -1
  160. package/src/overlays/relationships-browser/useViewToNodesEdges.ts +1 -2
  161. package/src/state/DiagramActorProvider.tsx +7 -4
  162. package/src/state/diagram-machine.ts +50 -8
@@ -23,8 +23,8 @@ import {
23
23
  import { useCallbackRef } from '@mantine/hooks'
24
24
  import { IconChevronRight } from '@tabler/icons-react'
25
25
  import clsx from 'clsx'
26
- import { m } from 'framer-motion'
27
- import { useEffect, useMemo } from 'react'
26
+ import * as m from 'motion/react-m'
27
+ import { type KeyboardEventHandler, useCallback, useEffect, useMemo } from 'react'
28
28
  import { first, indexBy, isEmpty, only, partition, pipe, prop, reduce } from 'remeda'
29
29
  import { IconOrShapeRenderer } from '../../../context/IconRenderer'
30
30
  import { sortByLabel } from '../../../likec4model/useLikeC4ElementsTree'
@@ -61,11 +61,8 @@ const btn = buttonsva()
61
61
  export function ElementsColumn() {
62
62
  const search = useNormalizedSearch()
63
63
  const model = useLikeC4Model(true)
64
- const {
65
- all,
66
- byid,
67
- roots: data,
68
- } = useMemo(() => {
64
+
65
+ const data = useMemo(() => {
69
66
  const searchTerms = search.split('.')
70
67
  let elements
71
68
  if (isEmpty(search) || search === 'kind:') {
@@ -82,9 +79,9 @@ export function ElementsColumn() {
82
79
  .toLocaleLowerCase().includes(search)
83
80
  })
84
81
  }
82
+ const byid = {} as Record<Fqn, LikeC4ModelTreeNodeData>
85
83
  const { all, roots } = pipe(
86
- elements,
87
- toArray(),
84
+ [...elements],
88
85
  sortParentsFirst,
89
86
  reduce((acc, element) => {
90
87
  const treeItem: LikeC4ModelTreeNodeData = {
@@ -94,6 +91,7 @@ export function ElementsColumn() {
94
91
  searchTerms,
95
92
  children: [],
96
93
  }
94
+ byid[treeItem.value] = treeItem
97
95
  const parent = acc.all.findLast((root) => isAncestor(root.value, treeItem.value))
98
96
  if (parent) {
99
97
  parent.children.push(treeItem)
@@ -112,14 +110,42 @@ export function ElementsColumn() {
112
110
  )
113
111
  return {
114
112
  all,
115
- byid: indexBy(all, prop('value')),
113
+ byid,
116
114
  roots: roots.sort(sortByLabel),
117
115
  }
118
116
  }, [model, search])
119
117
 
118
+ const handleClick = useHandleElementSelection()
119
+
120
+ if (data.all.length === 0) {
121
+ return <NothingFound />
122
+ }
123
+
124
+ return <ElementsTree data={data} handleClick={handleClick} />
125
+ }
126
+
127
+ const setHoveredNode = () => {}
128
+
129
+ function ElementsTree({
130
+ data: {
131
+ all,
132
+ byid,
133
+ roots,
134
+ },
135
+ handleClick,
136
+ }: {
137
+ data: {
138
+ all: LikeC4ModelTreeNodeData[]
139
+ byid: Record<Fqn, LikeC4ModelTreeNodeData>
140
+ roots: LikeC4ModelTreeNodeData[]
141
+ }
142
+ handleClick: (element: LikeC4Model.Element) => void
143
+ }) {
120
144
  const tree = useTree({
121
145
  multiple: false,
122
146
  })
147
+ tree.setHoveredNode = setHoveredNode
148
+
123
149
  useEffect(() => {
124
150
  tree.collapseAllNodes()
125
151
  for (const nd of all) {
@@ -129,66 +155,63 @@ export function ElementsColumn() {
129
155
  }
130
156
  }, [all])
131
157
 
132
- const handleClick = useHandleElementSelection()
158
+ const onKeyDownCapture: KeyboardEventHandler = useCallbackRef((e) => {
159
+ const target = e.target as HTMLElement
160
+ const id = target.getAttribute('data-value')
161
+ const node = !!id && byid[id as Fqn]
162
+ if (!node) return
163
+ if (e.key === 'ArrowUp') {
164
+ if (id === roots[0]?.value) {
165
+ stopAndPrevent(e)
166
+ moveFocusToSearchInput()
167
+ }
168
+ return
169
+ }
170
+ if (e.key === 'ArrowRight') {
171
+ const hasChildren = node.children.length > 0
172
+ if (hasChildren && tree.expandedState[id] === false) {
173
+ return
174
+ }
175
+ const label = (e.target as HTMLLIElement).querySelector<HTMLLIElement>('.mantine-Tree-label') ?? target
176
+ const maxY = label.getBoundingClientRect().y
177
+ const viewButtons = [...document.querySelectorAll<HTMLButtonElement>(
178
+ `[data-likec4-search-views] .${styles.focusable}`,
179
+ )]
180
+ let view = viewButtons.length > 1
181
+ ? viewButtons.find((el, i, all) => centerY(el) > maxY || i === all.length - 1)
182
+ : null
183
+ view ??= first(viewButtons)
184
+ if (view) {
185
+ stopAndPrevent(e)
186
+ view.focus()
187
+ }
188
+ return
189
+ }
190
+ if (e.key === ' ' || e.key === 'Enter') {
191
+ stopAndPrevent(e)
192
+ handleClick(node.element)
193
+ return
194
+ }
195
+ })
133
196
 
134
197
  return (
135
- <>
136
- {data.length === 0 && <NothingFound />}
137
- <Tree
138
- data-likec4-search-elements
139
- allowRangeSelection={false}
140
- clearSelectionOnOutsideClick
141
- selectOnClick={false}
142
- tree={tree}
143
- data={data}
144
- levelOffset={'lg'}
145
- classNames={{
146
- root: styles.treeRoot,
147
- node: cx(styles.focusable, styles.treeNode),
148
- label: styles.treeLabel,
149
- subtree: styles.treeSubtree,
150
- }}
151
- onKeyDownCapture={(e) => {
152
- const target = e.target as HTMLElement
153
- const id = target.getAttribute('data-value')
154
- const node = !!id && byid[id as Fqn]
155
- if (!node) return
156
- if (e.key === 'ArrowUp') {
157
- if (id === data[0]?.value) {
158
- stopAndPrevent(e)
159
- moveFocusToSearchInput()
160
- }
161
- return
162
- }
163
- if (e.key === 'ArrowRight') {
164
- const hasChildren = node.children.length > 0
165
- if (hasChildren && tree.expandedState[id] === false) {
166
- return
167
- }
168
- const label = (e.target as HTMLLIElement).querySelector<HTMLLIElement>('.mantine-Tree-label') ?? target
169
- const maxY = label.getBoundingClientRect().y
170
- const viewButtons = [...document.querySelectorAll<HTMLButtonElement>(
171
- `[data-likec4-search-views] .${styles.focusable}`,
172
- )]
173
- let view = viewButtons.length > 1
174
- ? viewButtons.find((el, i, all) => centerY(el) > maxY || i === all.length - 1)
175
- : null
176
- view ??= first(viewButtons)
177
- if (view) {
178
- stopAndPrevent(e)
179
- view.focus()
180
- }
181
- return
182
- }
183
- if (e.key === ' ' || e.key === 'Enter') {
184
- stopAndPrevent(e)
185
- handleClick(node.element)
186
- return
187
- }
188
- }}
189
- renderNode={ElementTreeNode}
190
- />
191
- </>
198
+ <Tree
199
+ data-likec4-search-elements
200
+ allowRangeSelection={false}
201
+ clearSelectionOnOutsideClick
202
+ selectOnClick={false}
203
+ tree={tree}
204
+ data={roots}
205
+ levelOffset={'lg'}
206
+ classNames={{
207
+ root: styles.treeRoot,
208
+ node: cx(styles.focusable, styles.treeNode),
209
+ label: styles.treeLabel,
210
+ subtree: styles.treeSubtree,
211
+ }}
212
+ onKeyDownCapture={onKeyDownCapture}
213
+ renderNode={ElementTreeNode}
214
+ />
192
215
  )
193
216
  }
194
217
 
@@ -212,7 +235,7 @@ function ElementTreeNode(
212
235
  const key = `@tree.${node.value}`
213
236
 
214
237
  return (
215
- <m.div layoutId={key} key={key} {...elementProps as any}>
238
+ <m.div layoutId={key} {...elementProps as any}>
216
239
  <ActionIcon
217
240
  variant="transparent"
218
241
  size={16}
@@ -278,7 +301,7 @@ function ElementTreeNode(
278
301
  function useHandleElementSelection() {
279
302
  const navigateTo = useCloseSearchAndNavigateTo()
280
303
 
281
- return useCallbackRef((element: LikeC4Model.Element) => {
304
+ return useCallback((element: LikeC4Model.Element) => {
282
305
  const views = [...element.views()]
283
306
  if (views.length === 0) {
284
307
  return
@@ -294,5 +317,5 @@ function useHandleElementSelection() {
294
317
  scoped,
295
318
  others,
296
319
  })
297
- })
320
+ }, [setPickView, navigateTo])
298
321
  }
@@ -1,11 +1,11 @@
1
+ import { css } from '@likec4/styles/css'
2
+ import { Box } from '@likec4/styles/jsx'
1
3
  import {
2
4
  ActionIcon,
3
- Box,
4
5
  Grid,
5
6
  GridCol,
6
7
  Group,
7
8
  Portal,
8
- RemoveScroll,
9
9
  ScrollArea,
10
10
  ScrollAreaAutosize,
11
11
  Stack,
@@ -20,24 +20,25 @@ import {
20
20
  useWindowEvent,
21
21
  } from '@mantine/hooks'
22
22
  import { IconX } from '@tabler/icons-react'
23
- import { AnimatePresence, LayoutGroup, m } from 'framer-motion'
24
- import { useRef } from 'react'
23
+ import { AnimatePresence, LayoutGroup, m } from 'motion/react'
24
+ import { memo, useRef } from 'react'
25
25
  import { SearchControl } from '../../../components/SearchControl'
26
26
  import { useMantinePortalProps } from '../../../hooks'
27
27
  import { ElementsColumn } from './ElementsColumn'
28
- import * as css from './LikeC4Search.css'
28
+ import * as styles from './LikeC4Search.css'
29
29
  import { LikeC4SearchInput } from './SearchInput'
30
30
  import {
31
31
  LikeC4SearchContext,
32
32
  setPickView,
33
33
  useCloseSearch,
34
+ useIsPickViewActive,
34
35
  usePickView,
35
36
  wasResetPickView,
36
37
  } from './state'
37
38
  import { stopAndPrevent } from './utils'
38
39
  import { ViewButton, ViewsColumn } from './ViewsColum'
39
40
 
40
- export function LikeC4Search() {
41
+ export const LikeC4Search = memo(() => {
41
42
  const [searchOpened, searchOps] = useDisclosure(false)
42
43
  useHotkeys([
43
44
  ['mod+k', () => searchOps.toggle(), {
@@ -69,41 +70,38 @@ export function LikeC4Search() {
69
70
  e.stopPropagation()
70
71
  searchOps.toggle()
71
72
  }} />
72
- <Portal {...portalProps}>
73
- <RemoveScroll enabled={searchOpened}>
74
- <AnimatePresence onExitComplete={onExitComplete}>
75
- {searchOpened && (
76
- <>
77
- <m.div
78
- key={'backdrop'}
79
- className={css.backdrop}
80
- initial={{ opacity: 0.7 }}
81
- animate={{
82
- opacity: 1,
83
- transition: {
84
- duration: 0.1,
85
- },
86
- }}
87
- exit={{
88
- opacity: 0,
89
- transition: {
90
- duration: 0.075,
91
- },
92
- }}>
93
- </m.div>
94
- <LikeC4SearchOverlay key="overlay" />
95
- </>
96
- )}
97
- </AnimatePresence>
98
- </RemoveScroll>
73
+ <Portal {...portalProps} reuseTargetNode>
74
+ <AnimatePresence onExitComplete={onExitComplete}>
75
+ {searchOpened && (
76
+ <m.div
77
+ key={'backdrop'}
78
+ className={styles.backdrop}
79
+ initial={{ opacity: 0.7 }}
80
+ animate={{
81
+ opacity: 1,
82
+ transition: {
83
+ duration: 0.13,
84
+ },
85
+ }}
86
+ exit={{
87
+ opacity: 0,
88
+ transition: {
89
+ duration: 0.13,
90
+ },
91
+ }}>
92
+ </m.div>
93
+ )}
94
+ {searchOpened && <LikeC4SearchOverlay key="overlay" />}
95
+ </AnimatePresence>
99
96
  </Portal>
100
97
  </LikeC4SearchContext>
101
98
  )
102
- }
99
+ })
100
+ LikeC4Search.displayName = 'LikeC4Search'
103
101
 
104
- function LikeC4SearchOverlay() {
102
+ const LikeC4SearchOverlay = () => {
105
103
  const close = useCloseSearch()
106
- const ref = useFocusTrap()
104
+ const pickViewActive = useIsPickViewActive()
107
105
 
108
106
  useWindowEvent(
109
107
  'keydown',
@@ -120,14 +118,9 @@ function LikeC4SearchOverlay() {
120
118
 
121
119
  return (
122
120
  <m.div
123
- ref={ref}
124
- className={css.root}
125
- data-likec4-search
126
- // initial={{
127
- // opacity: 0,
128
- // // scale: 0.9,
129
- // translateY: -20,
130
- // }}
121
+ className={styles.root}
122
+ data-likec4-search="true"
123
+ initial={false}
131
124
  animate={{
132
125
  opacity: 1,
133
126
  scale: 1,
@@ -136,8 +129,9 @@ function LikeC4SearchOverlay() {
136
129
  exit={{
137
130
  opacity: 0,
138
131
  scale: 0.9,
132
+ translateY: -30,
139
133
  transition: {
140
- duration: 0.1,
134
+ duration: 0.13,
141
135
  },
142
136
  }}
143
137
  >
@@ -166,16 +160,16 @@ function LikeC4SearchOverlay() {
166
160
  </GridCol>
167
161
  </Grid>
168
162
  <Grid
169
- style={{
163
+ className={css({
170
164
  containerName: 'likec4-search-elements',
171
165
  containerType: 'size',
172
166
  overflow: 'hidden',
173
167
  flexGrow: 1,
174
- }}>
168
+ })}>
175
169
  <GridCol span={6}>
176
170
  <ScrollArea
177
171
  type="hover"
178
- className={css.scrollArea}
172
+ className={styles.scrollArea}
179
173
  pr="xs"
180
174
  scrollbars="y">
181
175
  <AnimatePresence>
@@ -188,7 +182,7 @@ function LikeC4SearchOverlay() {
188
182
  <GridCol span={6}>
189
183
  <ScrollArea
190
184
  type="hover"
191
- className={css.scrollArea}
185
+ className={styles.scrollArea}
192
186
  pr="xs"
193
187
  scrollbars="y">
194
188
  <AnimatePresence>
@@ -199,8 +193,7 @@ function LikeC4SearchOverlay() {
199
193
  </ScrollArea>
200
194
  </GridCol>
201
195
  </Grid>
202
- <Box></Box>
203
- <PickView />
196
+ {pickViewActive && <PickView />}
204
197
  </m.div>
205
198
  )
206
199
  }
@@ -220,7 +213,7 @@ function PickView() {
220
213
  <>
221
214
  <m.div
222
215
  key="backdrop"
223
- className={css.pickviewBackdrop}
216
+ className={styles.pickviewBackdrop}
224
217
  onClick={e => {
225
218
  e.stopPropagation()
226
219
  setPickView(null)
@@ -248,7 +241,7 @@ function PickView() {
248
241
  duration: 0.1,
249
242
  },
250
243
  }}
251
- className={css.pickview}
244
+ className={styles.pickview}
252
245
  data-likec4-search-views
253
246
  ref={focusTrapRef}>
254
247
  <Group px="sm" py="md" justify="space-between">
@@ -266,7 +259,7 @@ function PickView() {
266
259
 
267
260
  <ScrollAreaAutosize mah={'calc(100vh - 8rem - 50px)'} type="never">
268
261
  {pickview.scoped.length > 0 && (
269
- <Stack gap={'sm'} px={'sm'} className={css.pickviewGroup}>
262
+ <Stack gap={'sm'} px={'sm'} className={styles.pickviewGroup}>
270
263
  <Title order={6} c={'dimmed'}>scoped views of the element</Title>
271
264
  {pickview.scoped.map((view, i) => (
272
265
  <ViewButton
@@ -283,7 +276,7 @@ function PickView() {
283
276
  )}
284
277
 
285
278
  {pickview.others.length > 0 && (
286
- <Stack gap={'sm'} px={'sm'} className={css.pickviewGroup}>
279
+ <Stack gap={'sm'} px={'sm'} className={styles.pickviewGroup}>
287
280
  <Title order={6} c={'dimmed'}>views including this element</Title>
288
281
  {pickview.others.map((view, i) => (
289
282
  <ViewButton
@@ -4,11 +4,11 @@ import {
4
4
  isDeploymentView,
5
5
  } from '@likec4/core'
6
6
  import { cx } from '@likec4/styles/css'
7
+ import { Box } from '@likec4/styles/jsx'
7
8
  import {
8
9
  type ElementProps,
9
10
  type UnstyledButtonProps,
10
11
  Badge,
11
- Box,
12
12
  createScopedKeydownHandler,
13
13
  Group,
14
14
  Highlight,
@@ -18,7 +18,7 @@ import {
18
18
  VisuallyHidden,
19
19
  } from '@mantine/core'
20
20
  import { IconStack2, IconZoomScan } from '@tabler/icons-react'
21
- import { m } from 'framer-motion'
21
+ import * as m from 'motion/react-m'
22
22
  import { first } from 'remeda'
23
23
  import { useCurrentViewId } from '../../../hooks/useCurrentViewId'
24
24
  import { useLikeC4Model } from '../../../likec4model/useLikeC4Model'
@@ -3,7 +3,7 @@ import { createSafeContext } from '@mantine/core'
3
3
  import { useCallbackRef } from '@mantine/hooks'
4
4
  import { useStore } from '@nanostores/react'
5
5
  import { atom, computed, onMount } from 'nanostores'
6
- import { useDeferredValue } from 'react'
6
+ import { useCallback, useDeferredValue } from 'react'
7
7
  import { useDiagram } from '../../../hooks/useDiagram'
8
8
 
9
9
  const $search = atom('')
@@ -59,18 +59,20 @@ export function useIsPickViewActive() {
59
59
  export function useCloseSearchAndNavigateTo() {
60
60
  const diagram = useDiagram()
61
61
  const close = useCloseSearch()
62
- return useCallbackRef((viewId: ViewId, fromElementFqn?: Fqn) => {
62
+ return useCallback((viewId: ViewId, fromElementFqn?: Fqn) => {
63
63
  close(() => {
64
64
  fromElementFqn ??= $pickView.get()?.elementFqn
65
65
  setPickView(null)
66
66
  const fromNode = fromElementFqn
67
67
  ? diagram.getContext().view.nodes.find(n => DiagramNode.modelRef(n) === fromElementFqn)?.id
68
68
  : undefined
69
- if (diagram.currentView.id === viewId && fromNode) {
70
- diagram.focusNode(fromNode)
69
+ if (diagram.currentView.id !== viewId) {
70
+ diagram.navigateTo(viewId, fromNode)
71
71
  return
72
72
  }
73
- diagram.navigateTo(viewId, fromNode)
73
+ if (fromNode) {
74
+ diagram.focusNode(fromNode)
75
+ }
74
76
  })
75
- })
77
+ }, [close, diagram])
76
78
  }
@@ -6,7 +6,7 @@ import {
6
6
  IconPlayerStopFilled,
7
7
  } from '@tabler/icons-react'
8
8
  import clsx from 'clsx'
9
- import { AnimatePresence, m } from 'framer-motion'
9
+ import { AnimatePresence, m } from 'motion/react'
10
10
  import { isNonNull, isTruthy } from 'remeda'
11
11
  import { useXYStore } from '../../../hooks'
12
12
  import { useDiagram, useDiagramContext } from '../../../hooks/useDiagram'
@@ -2,8 +2,8 @@ import { nonexhaustive } from '@likec4/core'
2
2
  import { Box, Button, Code, Group, Notification, ScrollAreaAutosize } from '@mantine/core'
3
3
  import { IconX } from '@tabler/icons-react'
4
4
  import { useSelector } from '@xstate/react'
5
- import { AnimatePresence, LayoutGroup, useReducedMotionConfig } from 'framer-motion'
6
- import { animate } from 'framer-motion/dom'
5
+ import { animate } from 'motion'
6
+ import { AnimatePresence, LayoutGroup, useReducedMotionConfig } from 'motion/react'
7
7
  import { memo, useEffect, useMemo } from 'react'
8
8
  import { type FallbackProps, ErrorBoundary } from 'react-error-boundary'
9
9
  import { isNonNullish } from 'remeda'
@@ -24,10 +24,10 @@ export function ElementDetails({
24
24
  shallowEqual,
25
25
  )
26
26
  return (
27
- <ElementDetailsActorContext value={actorRef}>
27
+ <ElementDetailsActorContext.Provider value={actorRef}>
28
28
  <ElementDetailsCard
29
29
  onClose={onClose}
30
30
  {...props} />
31
- </ElementDetailsActorContext>
31
+ </ElementDetailsActorContext.Provider>
32
32
  )
33
33
  }
@@ -38,7 +38,7 @@ import { useSessionStorage, useViewportSize } from '@mantine/hooks'
38
38
  import { useDebouncedCallback, useSyncedRef, useTimeoutEffect } from '@react-hookz/web'
39
39
  import { IconExternalLink, IconFileSymlink, IconStack2, IconZoomScan } from '@tabler/icons-react'
40
40
  import type { Rect } from '@xyflow/system'
41
- import { type PanInfo, m, useDragControls, useMotionValue } from 'framer-motion'
41
+ import { type PanInfo, m, useDragControls, useMotionValue } from 'motion/react'
42
42
  import { type PropsWithChildren, useCallback, useRef, useState } from 'react'
43
43
  import { clamp, isNullish, map, only, partition, pipe } from 'remeda'
44
44
  import { Link } from '../../components/Link'
@@ -105,6 +105,7 @@ const DeployedInstanceRenderer = (
105
105
  )
106
106
  }
107
107
 
108
+ const setHoveredNode = () => {}
108
109
  export const TabPanelDeployments = memo<TabPanelDeploymentsProps>(({ elementFqn }) => {
109
110
  const element = useLikeC4Model(true).element(elementFqn)
110
111
  const deployments = [...element.deployments()]
@@ -112,6 +113,7 @@ export const TabPanelDeployments = memo<TabPanelDeploymentsProps>(({ elementFqn
112
113
  const tree = useTree({
113
114
  multiple: false,
114
115
  })
116
+ tree.setHoveredNode = setHoveredNode
115
117
 
116
118
  const data = useMemo(() => {
117
119
  let roots = [] as TreeNodeData[]
@@ -37,13 +37,14 @@ export const ElementLabel = ({
37
37
  // node
38
38
  // }: RenderTreeNodePayload) => {
39
39
  // }
40
-
40
+ const setHoveredNode = () => {}
41
41
  export function TabPanelStructure({
42
42
  element,
43
43
  }: TabPanelStructureProps) {
44
44
  const tree = useTree({
45
45
  multiple: false,
46
46
  })
47
+ tree.setHoveredNode = setHoveredNode
47
48
 
48
49
  const data = useMemo(() => {
49
50
  let seq = 1
@@ -5,7 +5,7 @@ import {
5
5
  } from '@mantine/core'
6
6
  import { useMergedRef } from '@mantine/hooks'
7
7
  import { useDebouncedCallback, useSyncedRef, useTimeoutEffect } from '@react-hookz/web'
8
- import { m, useReducedMotionConfig } from 'framer-motion'
8
+ import { m, useReducedMotionConfig } from 'motion/react'
9
9
  import { type PropsWithChildren, forwardRef, useEffect, useRef, useState } from 'react'
10
10
  import { stopPropagation } from '../../utils'
11
11
  import { backdropBlur, backdropOpacity, level as cssVarLevel, overlay as overlayCVA } from './Overlay.css'
@@ -49,6 +49,7 @@ export const Overlay = forwardRef<HTMLDialogElement, OverlayProps>(({
49
49
  useEffect(() => {
50
50
  const cancel = (e: Event) => {
51
51
  e.preventDefault()
52
+ e.stopPropagation()
52
53
  close()
53
54
  }
54
55
  dialogRef.current?.addEventListener('cancel', cancel, { capture: true })