@likec4/diagram 1.31.0 → 1.32.1

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 (269) hide show
  1. package/bundle/{chunk-BBzMXzCg.js → chunk-DlHyKoeo.js} +1700 -1206
  2. package/bundle/custom.js +51 -47
  3. package/bundle/index.js +5495 -5428
  4. package/dist/LikeC4CustomColors.d.ts +2 -7
  5. package/dist/LikeC4CustomColors.js +17 -6
  6. package/dist/LikeC4Diagram.d.ts +3 -2
  7. package/dist/LikeC4Diagram.js +10 -14
  8. package/dist/LikeC4Diagram.props.d.ts +41 -24
  9. package/dist/LikeC4ModelProvider.d.ts +5 -4
  10. package/dist/StaticLikeC4Diagram.d.ts +3 -2
  11. package/dist/StaticLikeC4Diagram.js +2 -2
  12. package/dist/base/BaseXYFlow.js +5 -5
  13. package/dist/base/const.d.ts +1 -1
  14. package/dist/base/const.js +1 -1
  15. package/dist/base/index.d.ts +2 -1
  16. package/dist/base/primitives/compound/CompoundActionButton.js +2 -1
  17. package/dist/base/primitives/compound/CompoundDetailsButton.js +2 -1
  18. package/dist/base/primitives/compound/CompoundTitle.d.ts +1 -1
  19. package/dist/base/primitives/compound/CompoundTitle.js +2 -1
  20. package/dist/base/primitives/edge/EdgeContainer.d.ts +1 -1
  21. package/dist/base/primitives/edge/EdgeLabel.css.js +1 -0
  22. package/dist/base/primitives/edge/EdgeLabel.d.ts +3 -3
  23. package/dist/base/primitives/edge/EdgeLabel.js +6 -4
  24. package/dist/base/primitives/edge/EdgeLabelContainer.d.ts +1 -1
  25. package/dist/base/primitives/edge/EdgeMarkers.d.ts +1 -1
  26. package/dist/base/primitives/edge/EdgePath.d.ts +1 -1
  27. package/dist/base/primitives/element/ElementActionButtons.js +10 -9
  28. package/dist/base/primitives/element/ElementDetailsButton.js +2 -1
  29. package/dist/base/primitives/element/ElementShape.d.ts +1 -1
  30. package/dist/base/primitives/element/ElementTags.d.ts +14 -0
  31. package/dist/base/primitives/element/ElementTags.js +176 -0
  32. package/dist/base/primitives/element/ElementTitle.d.ts +5 -4
  33. package/dist/base/primitives/element/ElementTitle.js +6 -6
  34. package/dist/base/primitives/index.d.ts +1 -0
  35. package/dist/base/types.Base.d.ts +54 -0
  36. package/dist/base/types.Base.js +48 -0
  37. package/dist/base/types.d.ts +2 -52
  38. package/dist/bundle/LikeC4View.d.ts +2 -1
  39. package/dist/bundle/LikeC4View.js +22 -19
  40. package/dist/bundle/LikeC4View.props.d.ts +15 -4
  41. package/dist/bundle/ReactLikeC4.d.ts +4 -7
  42. package/dist/bundle/ReactLikeC4.js +10 -8
  43. package/dist/bundle/index.js +1 -1
  44. package/dist/components/ErrorFallback.d.ts +2 -0
  45. package/dist/components/ErrorFallback.js +29 -0
  46. package/dist/context/DiagramEventHandlers.d.ts +6 -5
  47. package/dist/context/DiagramFeatures.d.ts +2 -2
  48. package/dist/context/DiagramFeatures.js +3 -31
  49. package/dist/context/EnsureMantine.js +1 -1
  50. package/dist/context/IconRenderer.js +1 -4
  51. package/dist/context/RootContainer.js +5 -1
  52. package/dist/context/TagStylesContext.d.ts +7 -0
  53. package/dist/context/TagStylesContext.js +85 -0
  54. package/dist/custom/components.d.ts +1 -1
  55. package/dist/custom/customNodes.d.ts +32 -17
  56. package/dist/custom/hooks.d.ts +1 -1
  57. package/dist/hooks/index.d.ts +1 -1
  58. package/dist/hooks/useDiagram.d.ts +73 -38
  59. package/dist/hooks/useDiagram.js +7 -3
  60. package/dist/hooks/useMantinePortalProps.d.ts +2 -2
  61. package/dist/hooks/useSearchActor.d.ts +2 -0
  62. package/dist/hooks/useSearchActor.js +10 -0
  63. package/dist/hooks/useUpdateEffect.js +3 -0
  64. package/dist/hooks/useXYFlow.d.ts +39 -37
  65. package/dist/hooks/useXYFlow.js +5 -1
  66. package/dist/index.js +1 -1
  67. package/dist/likec4diagram/DiagramUI.js +7 -2
  68. package/dist/likec4diagram/DiagramXYFlow.d.ts +4 -3
  69. package/dist/likec4diagram/DiagramXYFlow.js +107 -110
  70. package/dist/likec4diagram/custom/edges/RelationshipEdge.d.ts +7 -6
  71. package/dist/likec4diagram/custom/edges/RelationshipEdge.js +8 -0
  72. package/dist/likec4diagram/custom/edges/RelationshipsDropdownMenu.js +7 -5
  73. package/dist/likec4diagram/custom/index.d.ts +7 -6
  74. package/dist/likec4diagram/custom/nodes/nodes.js +39 -26
  75. package/dist/likec4diagram/custom/nodes/toolbar/_shared.d.ts +7 -7
  76. package/dist/likec4diagram/types.d.ts +5 -5
  77. package/dist/likec4diagram/ui/controls/Controls.js +12 -4
  78. package/dist/likec4diagram/ui/notation/NotationPanel.js +1 -1
  79. package/dist/likec4diagram/useViewToNodesEdges.d.ts +5 -3
  80. package/dist/likec4diagram/useViewToNodesEdges.js +20 -30
  81. package/dist/likec4model/LikeC4ModelContext.d.ts +2 -2
  82. package/dist/likec4model/index.d.ts +1 -1
  83. package/dist/likec4model/useLikeC4ElementsTree.js +1 -1
  84. package/dist/likec4model/useLikeC4Model.d.ts +23 -10
  85. package/dist/likec4model/useLikeC4Model.js +22 -7
  86. package/dist/overlays/Overlays.js +4 -27
  87. package/dist/overlays/element-details/ElementDetailsCard.d.ts +1 -1
  88. package/dist/overlays/element-details/ElementDetailsCard.js +19 -12
  89. package/dist/overlays/element-details/TabPanelDeployments.d.ts +1 -1
  90. package/dist/overlays/element-details/TabPanelDeployments.js +1 -1
  91. package/dist/overlays/element-details/TabPanelRelationships.d.ts +1 -1
  92. package/dist/overlays/element-details/TabPanelStructure.d.ts +1 -1
  93. package/dist/overlays/element-details/actor.d.ts +288 -286
  94. package/dist/overlays/overlay/Overlay.css.d.ts +57 -2
  95. package/dist/overlays/overlay/Overlay.css.js +76 -39
  96. package/dist/overlays/overlay/Overlay.d.ts +12 -0
  97. package/dist/overlays/overlay/Overlay.js +58 -27
  98. package/dist/overlays/overlaysActor.d.ts +1 -1
  99. package/dist/overlays/relationship-details/RelationshipDetails.d.ts +3 -2
  100. package/dist/overlays/relationship-details/RelationshipDetails.js +3 -3
  101. package/dist/overlays/relationship-details/_types.d.ts +2 -2
  102. package/dist/overlays/relationship-details/actor.js +7 -7
  103. package/dist/overlays/relationship-details/custom/edgeTypes.d.ts +3 -2
  104. package/dist/overlays/relationship-details/custom/nodeTypes.js +4 -0
  105. package/dist/overlays/relationship-details/hooks.d.ts +10 -9
  106. package/dist/overlays/relationship-details/layout-to-xyflow.js +1 -0
  107. package/dist/overlays/relationship-details/layout.js +3 -1
  108. package/dist/overlays/relationships-browser/RelationshipsBrowser.d.ts +3 -2
  109. package/dist/overlays/relationships-browser/SelectElement.js +1 -1
  110. package/dist/overlays/relationships-browser/_types.d.ts +2 -2
  111. package/dist/overlays/relationships-browser/actor.d.ts +1 -1
  112. package/dist/overlays/relationships-browser/actor.js +7 -7
  113. package/dist/overlays/relationships-browser/custom/edgeTypes.d.ts +3 -2
  114. package/dist/overlays/relationships-browser/custom/nodeTypes.d.ts +8 -5
  115. package/dist/overlays/relationships-browser/custom/nodeTypes.js +4 -0
  116. package/dist/overlays/relationships-browser/layout.d.ts +2 -2
  117. package/dist/overlays/relationships-browser/layout.js +10 -6
  118. package/dist/overlays/relationships-browser/useViewToNodesEdges.js +4 -3
  119. package/dist/search/Search.d.ts +4 -0
  120. package/dist/search/Search.js +173 -0
  121. package/dist/{likec4diagram/ui/search → search/components}/ElementsColumn.css.js +0 -1
  122. package/dist/search/components/ElementsColumn.d.ts +2 -0
  123. package/dist/{likec4diagram/ui/search → search/components}/ElementsColumn.js +26 -25
  124. package/dist/search/components/PickView.d.ts +4 -0
  125. package/dist/search/components/PickView.js +135 -0
  126. package/dist/search/components/SearchByTags.d.ts +1 -0
  127. package/dist/search/components/SearchByTags.js +119 -0
  128. package/dist/search/components/SearchInput.d.ts +2 -0
  129. package/dist/{likec4diagram/ui/search → search/components}/SearchInput.js +61 -40
  130. package/dist/{likec4diagram/ui/search → search/components}/ViewsColum.d.ts +3 -2
  131. package/dist/{likec4diagram/ui/search → search/components}/ViewsColum.js +25 -17
  132. package/dist/{likec4diagram/ui/search → search/components}/_shared.css.js +26 -35
  133. package/dist/{likec4diagram/ui/search/LikeC4Search.css.d.ts → search/components/styles.css.d.ts} +0 -2
  134. package/dist/search/components/styles.css.js +60 -0
  135. package/dist/search/components/utils.d.ts +5 -0
  136. package/dist/search/components/utils.js +58 -0
  137. package/dist/search/hooks.d.ts +10 -0
  138. package/dist/search/hooks.js +35 -0
  139. package/dist/search/searchActor.d.ts +90 -0
  140. package/dist/search/searchActor.js +75 -0
  141. package/dist/state/DiagramActorProvider.d.ts +9 -7
  142. package/dist/state/DiagramActorProvider.js +24 -18
  143. package/dist/state/assign.d.ts +0 -1
  144. package/dist/state/assign.js +9 -19
  145. package/dist/state/diagram-machine.d.ts +645 -183
  146. package/dist/state/diagram-machine.js +127 -60
  147. package/dist/state/syncManualLayoutActor.d.ts +2 -2
  148. package/dist/state/types.d.ts +2 -0
  149. package/dist/state/utils.d.ts +2 -0
  150. package/dist/state/utils.js +3 -0
  151. package/dist/styles-min.css +1 -1
  152. package/dist/styles-xyflow.css +1 -1
  153. package/dist/styles.css +1 -1
  154. package/dist/styles.css2.js +1 -1
  155. package/package.json +20 -19
  156. package/panda.buildinfo.json +171 -148
  157. package/src/LikeC4CustomColors.tsx +21 -6
  158. package/src/LikeC4Diagram.props.ts +48 -32
  159. package/src/LikeC4Diagram.tsx +19 -20
  160. package/src/LikeC4ModelProvider.tsx +6 -5
  161. package/src/StaticLikeC4Diagram.tsx +8 -6
  162. package/src/base/const.ts +1 -1
  163. package/src/base/index.ts +2 -1
  164. package/src/base/primitives/compound/CompoundActionButton.tsx +2 -1
  165. package/src/base/primitives/compound/CompoundDetailsButton.tsx +2 -1
  166. package/src/base/primitives/compound/CompoundTitle.tsx +3 -2
  167. package/src/base/primitives/edge/EdgeContainer.tsx +2 -1
  168. package/src/base/primitives/edge/EdgeLabel.css.ts +1 -0
  169. package/src/base/primitives/edge/EdgeLabel.tsx +20 -15
  170. package/src/base/primitives/edge/EdgeLabelContainer.tsx +1 -1
  171. package/src/base/primitives/edge/EdgeMarkers.tsx +2 -1
  172. package/src/base/primitives/edge/EdgePath.tsx +1 -1
  173. package/src/base/primitives/element/ElementActionButtons.tsx +11 -10
  174. package/src/base/primitives/element/ElementDetailsButton.tsx +2 -1
  175. package/src/base/primitives/element/ElementShape.tsx +2 -1
  176. package/src/base/primitives/element/ElementTags.tsx +182 -0
  177. package/src/base/primitives/element/ElementTitle.tsx +21 -15
  178. package/src/base/primitives/index.ts +1 -0
  179. package/src/base/types.Base.ts +104 -0
  180. package/src/base/types.ts +2 -99
  181. package/src/bundle/LikeC4View.props.ts +17 -4
  182. package/src/bundle/LikeC4View.tsx +27 -36
  183. package/src/bundle/ReactLikeC4.tsx +16 -36
  184. package/src/components/ErrorFallback.tsx +30 -0
  185. package/src/context/DiagramEventHandlers.tsx +5 -4
  186. package/src/context/DiagramFeatures.tsx +3 -22
  187. package/src/context/EnsureMantine.tsx +1 -1
  188. package/src/context/IconRenderer.tsx +1 -4
  189. package/src/context/RootContainer.tsx +9 -3
  190. package/src/context/TagStylesContext.tsx +96 -0
  191. package/src/custom/components.ts +2 -0
  192. package/src/custom/customNodes.tsx +29 -14
  193. package/src/custom/hooks.ts +2 -0
  194. package/src/hooks/index.ts +1 -1
  195. package/src/hooks/useCurrentViewId.ts +1 -4
  196. package/src/hooks/useDiagram.ts +104 -12
  197. package/src/hooks/useMantinePortalProps.ts +10 -1
  198. package/src/hooks/useSearchActor.ts +11 -0
  199. package/src/hooks/useUpdateEffect.ts +7 -4
  200. package/src/hooks/useXYFlow.ts +5 -0
  201. package/src/likec4diagram/DiagramUI.tsx +5 -0
  202. package/src/likec4diagram/DiagramXYFlow.tsx +118 -121
  203. package/src/likec4diagram/custom/edges/RelationshipEdge.tsx +9 -0
  204. package/src/likec4diagram/custom/edges/RelationshipsDropdownMenu.tsx +10 -9
  205. package/src/likec4diagram/custom/nodes/nodes.tsx +45 -43
  206. package/src/likec4diagram/custom/nodes/toolbar/_shared.tsx +11 -3
  207. package/src/likec4diagram/types.ts +5 -3
  208. package/src/likec4diagram/ui/controls/Controls.tsx +11 -4
  209. package/src/likec4diagram/ui/notation/NotationPanel.tsx +2 -2
  210. package/src/likec4diagram/useViewToNodesEdges.ts +34 -36
  211. package/src/likec4model/LikeC4ModelContext.ts +2 -2
  212. package/src/likec4model/index.ts +2 -0
  213. package/src/likec4model/useLikeC4ElementsTree.ts +1 -1
  214. package/src/likec4model/useLikeC4Model.ts +32 -16
  215. package/src/overlays/Overlays.tsx +5 -32
  216. package/src/overlays/element-details/ElementDetailsCard.tsx +25 -18
  217. package/src/overlays/element-details/TabPanelDeployments.tsx +3 -2
  218. package/src/overlays/element-details/TabPanelRelationships.tsx +2 -1
  219. package/src/overlays/element-details/TabPanelStructure.tsx +1 -1
  220. package/src/overlays/element-details/actor.ts +2 -2
  221. package/src/overlays/overlay/Overlay.css.ts +75 -38
  222. package/src/overlays/overlay/Overlay.tsx +61 -29
  223. package/src/overlays/overlaysActor.ts +1 -1
  224. package/src/overlays/relationship-details/RelationshipDetails.tsx +4 -4
  225. package/src/overlays/relationship-details/_types.ts +2 -1
  226. package/src/overlays/relationship-details/custom/nodeTypes.tsx +4 -0
  227. package/src/overlays/relationship-details/layout-to-xyflow.ts +1 -0
  228. package/src/overlays/relationship-details/layout.ts +3 -1
  229. package/src/overlays/relationships-browser/SelectElement.tsx +1 -1
  230. package/src/overlays/relationships-browser/_types.ts +2 -1
  231. package/src/overlays/relationships-browser/custom/nodeTypes.tsx +4 -0
  232. package/src/overlays/relationships-browser/layout.ts +12 -7
  233. package/src/overlays/relationships-browser/useViewToNodesEdges.ts +4 -4
  234. package/src/search/Search.tsx +220 -0
  235. package/src/{likec4diagram/ui/search → search/components}/ElementsColumn.css.ts +0 -29
  236. package/src/{likec4diagram/ui/search → search/components}/ElementsColumn.tsx +28 -29
  237. package/src/search/components/PickView.tsx +134 -0
  238. package/src/search/components/SearchByTags.tsx +112 -0
  239. package/src/{likec4diagram/ui/search → search/components}/SearchInput.tsx +72 -42
  240. package/src/{likec4diagram/ui/search → search/components}/ViewsColum.tsx +29 -22
  241. package/src/{likec4diagram/ui/search → search/components}/_shared.css.ts +26 -37
  242. package/src/{likec4diagram/ui/search/LikeC4Search.css.ts → search/components/styles.css.ts} +9 -36
  243. package/src/search/components/utils.ts +64 -0
  244. package/src/search/hooks.ts +38 -0
  245. package/src/search/searchActor.ts +99 -0
  246. package/src/state/DiagramActorProvider.tsx +37 -25
  247. package/src/state/assign.ts +10 -10
  248. package/src/state/diagram-machine.ts +123 -58
  249. package/src/state/syncManualLayoutActor.ts +1 -1
  250. package/src/state/types.ts +2 -0
  251. package/src/state/utils.ts +4 -0
  252. package/dist/base/types.js +0 -54
  253. package/dist/likec4diagram/ui/search/ElementsColumn.d.ts +0 -1
  254. package/dist/likec4diagram/ui/search/LikeC4Search.css.js +0 -87
  255. package/dist/likec4diagram/ui/search/LikeC4Search.d.ts +0 -2
  256. package/dist/likec4diagram/ui/search/LikeC4Search.js +0 -267
  257. package/dist/likec4diagram/ui/search/SearchInput.d.ts +0 -1
  258. package/dist/likec4diagram/ui/search/state.d.ts +0 -19
  259. package/dist/likec4diagram/ui/search/state.js +0 -63
  260. package/dist/likec4diagram/ui/search/utils.d.ts +0 -3
  261. package/dist/likec4diagram/ui/search/utils.js +0 -20
  262. package/src/likec4diagram/ui/search/LikeC4Search.tsx +0 -301
  263. package/src/likec4diagram/ui/search/state.ts +0 -78
  264. package/src/likec4diagram/ui/search/utils.ts +0 -20
  265. /package/dist/{likec4diagram/ui/search → search/components}/ElementsColumn.css.d.ts +0 -0
  266. /package/dist/{likec4diagram/ui/search → search/components}/ViewsColumn.css.d.ts +0 -0
  267. /package/dist/{likec4diagram/ui/search → search/components}/ViewsColumn.css.js +0 -0
  268. /package/dist/{likec4diagram/ui/search → search/components}/_shared.css.d.ts +0 -0
  269. /package/src/{likec4diagram/ui/search → search/components}/ViewsColumn.css.ts +0 -0
@@ -23,9 +23,10 @@ import {
23
23
  DefaultMap,
24
24
  ifind,
25
25
  invariant,
26
+ nonNullable,
26
27
  toArray,
27
28
  } from '@likec4/core'
28
- import type { AnyAux, ElementModel, LikeC4ViewModel, RelationshipModel } from '@likec4/core/model'
29
+ import type { ElementModel, LikeC4ViewModel, RelationshipModel } from '@likec4/core/model'
29
30
  import {
30
31
  concat,
31
32
  filter,
@@ -216,7 +217,7 @@ export namespace LayoutRelationshipsViewResult {
216
217
 
217
218
  export function layoutRelationshipsView(
218
219
  data: RelationshipsViewData,
219
- scope: LikeC4ViewModel<AnyAux> | null,
220
+ scope: LikeC4ViewModel | null,
220
221
  ): Omit<LayoutRelationshipsViewResult, 'subject'> {
221
222
  const g = createGraph()
222
223
 
@@ -459,12 +460,14 @@ export function layoutRelationshipsView(
459
460
  return {
460
461
  id: id as NodeId,
461
462
  parent: null,
463
+ x: position.x,
464
+ y: position.y,
462
465
  position: [position.x, position.y],
463
466
  title: 'empty node',
464
467
  description: null,
465
468
  technology: null,
466
- tags: null,
467
- links: null,
469
+ tags: [],
470
+ links: [],
468
471
  color: 'muted',
469
472
  shape: 'rectangle',
470
473
  style: {
@@ -508,11 +511,13 @@ export function layoutRelationshipsView(
508
511
  return {
509
512
  id: id as NodeId,
510
513
  parent: parentId as NodeId ?? null,
514
+ x: position.x,
515
+ y: position.y,
511
516
  position: [position.x, position.y],
512
517
  title: element.title,
513
518
  description: element.description,
514
519
  technology: element.technology,
515
- tags: null,
520
+ tags: [...element.tags],
516
521
  links: null,
517
522
  color: inheritFromNodeOrAncestor?.color ?? element.color,
518
523
  shape: inheritFromNode?.shape ?? element.shape,
@@ -568,7 +573,7 @@ export function layoutRelationshipsView(
568
573
  relations,
569
574
  sourceHandle,
570
575
  targetHandle,
571
- } = find(edges, e => e.name === ename)!
576
+ } = nonNullable(find(edges, e => e.name === ename))
572
577
  const onlyRelation = only(relations)
573
578
  const label = onlyRelation?.title ?? 'untitled'
574
579
  const isMultiple = relations.length > 1
@@ -602,7 +607,7 @@ export function useRelationshipsView(
602
607
  viewId: ViewId | null,
603
608
  scope: 'global' | 'view',
604
609
  ): LayoutRelationshipsViewResult {
605
- const model = useLikeC4Model(true)
610
+ const model = useLikeC4Model()
606
611
  return useMemo(() => {
607
612
  const view = viewId ? model.findView(viewId) : null
608
613
  const data = layoutRelationshipsView(
@@ -1,7 +1,6 @@
1
1
  import {
2
2
  type Fqn,
3
- DiagramNode,
4
- ElementKind,
3
+ GroupElementKind,
5
4
  invariant,
6
5
  nonNullable,
7
6
  Queue,
@@ -47,7 +46,7 @@ export function viewToNodesEdge(
47
46
  let next: TraverseItem | undefined
48
47
  while ((next = queue.dequeue())) {
49
48
  const { node, parent } = next
50
- const isCompound = hasAtLeast(node.children, 1) || node.kind == ElementKind.Group
49
+ const isCompound = hasAtLeast(node.children, 1) || node.kind == GroupElementKind
51
50
  if (isCompound) {
52
51
  for (const child of node.children) {
53
52
  queue.enqueue({ node: nodeById(child), parent: node })
@@ -83,7 +82,7 @@ export function viewToNodesEdge(
83
82
  }),
84
83
  } satisfies Omit<RelationshipsBrowserTypes.Node, 'data' | 'type'>
85
84
 
86
- const fqn = DiagramNode.modelRef(node)
85
+ const fqn = node.modelRef ?? null
87
86
  // const deploymentRef = DiagramNode.deploymentRef(node)
88
87
  // if (!fqn) {
89
88
  // console.error('Invalid node', node)
@@ -148,6 +147,7 @@ export function viewToNodesEdge(
148
147
  ports: node.ports,
149
148
  style: node.style,
150
149
  existsInCurrentView: node.existsInCurrentView,
150
+ tags: node.tags,
151
151
  ...navigateTo,
152
152
  },
153
153
  },
@@ -0,0 +1,220 @@
1
+ import { css } from '@likec4/styles/css'
2
+ import { Box, VStack } from '@likec4/styles/jsx'
3
+ import { Grid, GridCol, Group, ScrollArea, Title } from '@mantine/core'
4
+ import {
5
+ useCallbackRef,
6
+ useHotkeys,
7
+ } from '@mantine/hooks'
8
+ import { useTimeoutEffect } from '@react-hookz/web'
9
+ import { useSelector } from '@xstate/react'
10
+ import { AnimatePresence, LayoutGroup } from 'motion/react'
11
+ import { useRef } from 'react'
12
+ import { ErrorBoundary } from 'react-error-boundary'
13
+ import { isTruthy } from 'remeda'
14
+ import { ErrorFallback } from '../components/ErrorFallback'
15
+ import { DiagramFeatures } from '../context/DiagramFeatures'
16
+ import { Overlay } from '../overlays/overlay/Overlay'
17
+ import { ElementsColumn } from './components/ElementsColumn'
18
+ import { PickView } from './components/PickView'
19
+ import { SearchByTags } from './components/SearchByTags'
20
+ import { LikeC4SearchInput } from './components/SearchInput'
21
+ import { focusToFirstFoundElement, moveFocusToSearchInput } from './components/utils'
22
+ import { ViewsColumn } from './components/ViewsColum'
23
+ import { SearchActorContext, usePickViewFor } from './hooks'
24
+ import { type SearchActorRef, type SearchActorSnapshot } from './searchActor'
25
+
26
+ const dialog = css({
27
+ backgroundColor: `[rgb(34 34 34 / var(--_opacity, 95%))]`,
28
+ _light: {
29
+ backgroundColor: `[rgb(250 250 250 / var(--_opacity, 95%))]`,
30
+ },
31
+ backdropFilter: 'auto',
32
+ backdropBlur: 'var(--_blur, 10px)',
33
+ // base: `[rgb(34 34 34 / var(${backdropOpacity}))]`,
34
+ // _light: `[rgb(255 255 255/ var(${backdropOpacity}))]`,
35
+ // },
36
+ })
37
+
38
+ const body = css({
39
+ // containerName: 'likec4-search',
40
+ // containerType: 'size',
41
+ // position: 'fixed',
42
+ // zIndex: 901,
43
+ // top: 0,
44
+ // left: 0,
45
+ width: '100%',
46
+ height: '100%',
47
+ maxHeight: '100vh',
48
+ overflow: 'hidden',
49
+ display: 'flex',
50
+ flexDirection: 'column',
51
+ justifyContent: 'stretch',
52
+ gap: 'sm',
53
+ paddingTop: 20,
54
+ paddingLeft: 16,
55
+ paddingRight: 16,
56
+ paddingBottom: 8,
57
+ background: '[transparent]',
58
+ // backgroundColor: {
59
+ // _dark: `[rgb(34 34 34 / 0.95)]`,
60
+ // _light: `[rgb(255 255 255/ 0.95)]`,
61
+ // },
62
+ })
63
+
64
+ const selectIsOpened = (s: SearchActorSnapshot) => !s.matches('inactive')
65
+
66
+ export function Search({ searchActorRef }: { searchActorRef: SearchActorRef }) {
67
+ const isOpened = useSelector(searchActorRef, selectIsOpened)
68
+
69
+ const openSearch = () => {
70
+ searchActorRef.send({ type: 'open' })
71
+ }
72
+
73
+ const close = useCallbackRef(() => {
74
+ searchActorRef.send({ type: 'close' })
75
+ })
76
+
77
+ useHotkeys([
78
+ ['mod+k', openSearch, {
79
+ preventDefault: true,
80
+ }],
81
+ ['mod+f', openSearch, {
82
+ preventDefault: true,
83
+ }],
84
+ ])
85
+
86
+ return (
87
+ <SearchActorContext value={searchActorRef}>
88
+ <DiagramFeatures.Overlays>
89
+ <ErrorBoundary FallbackComponent={ErrorFallback} onReset={close}>
90
+ <AnimatePresence>
91
+ {isOpened && (
92
+ <Overlay
93
+ fullscreen
94
+ withBackdrop={false}
95
+ backdrop={{
96
+ opacity: 0.9,
97
+ }}
98
+ classes={{
99
+ dialog,
100
+ body,
101
+ }}
102
+ openDelay={0}
103
+ onClose={close}
104
+ data-likec4-search="true"
105
+ >
106
+ <SearchOverlay searchActorRef={searchActorRef} />
107
+ </Overlay>
108
+ )}
109
+ </AnimatePresence>
110
+ </ErrorBoundary>
111
+ </DiagramFeatures.Overlays>
112
+ </SearchActorContext>
113
+ )
114
+ }
115
+
116
+ const scrollArea = css({
117
+ height: [
118
+ '100%',
119
+ '100cqh',
120
+ ],
121
+ '& .mantine-ScrollArea-viewport': {
122
+ minHeight: '100%',
123
+ '& > div': {
124
+ minHeight: '100%',
125
+ height: '100%',
126
+ },
127
+ },
128
+ })
129
+
130
+ const SearchOverlay = ({ searchActorRef }: { searchActorRef: SearchActorRef }) => {
131
+ const ref = useRef<HTMLDivElement>(null)
132
+ const pickViewFor = usePickViewFor()
133
+
134
+ useTimeoutEffect(() => {
135
+ if (isTruthy(searchActorRef.getSnapshot().context.openedWithSearch)) {
136
+ focusToFirstFoundElement(ref.current)
137
+ }
138
+ }, 150)
139
+
140
+ return (
141
+ <Box ref={ref} display={'contents'}>
142
+ <Group
143
+ className={'group'}
144
+ wrap="nowrap"
145
+ onClick={e => {
146
+ e.stopPropagation()
147
+ moveFocusToSearchInput(ref.current)
148
+ }}>
149
+ <VStack flex={1} px={'sm'}>
150
+ <LikeC4SearchInput />
151
+ <SearchByTags />
152
+ </VStack>
153
+ {
154
+ /* <Box
155
+ css={{
156
+ alignSelf: 'flex-start',
157
+ flex: '0 0 auto',
158
+ display: 'flex',
159
+ alignItems: 'center',
160
+ justifyContent: 'center',
161
+ minHeight: 50,
162
+ }}>
163
+ <ActionIcon
164
+ size={'lg'}
165
+ variant="default"
166
+ onClick={(e) => {
167
+ e.stopPropagation()
168
+ close()
169
+ }}>
170
+ <IconX />
171
+ </ActionIcon>
172
+ </Box> */
173
+ }
174
+ </Group>
175
+ <Grid>
176
+ <GridCol span={6}>
177
+ <Title component={'div'} order={6} c="dimmed" pl="sm">Elements</Title>
178
+ </GridCol>
179
+ <GridCol span={6}>
180
+ <Title component={'div'} order={6} c="dimmed" pl="sm">Views</Title>
181
+ </GridCol>
182
+ </Grid>
183
+ <Grid
184
+ className={css({
185
+ containerName: 'likec4-search-elements',
186
+ containerType: 'size',
187
+ overflow: 'hidden',
188
+ flexGrow: 1,
189
+ })}>
190
+ <GridCol span={6}>
191
+ <ScrollArea
192
+ type="scroll"
193
+ className={scrollArea}
194
+ pr="xs"
195
+ scrollbars="y">
196
+ <AnimatePresence>
197
+ <LayoutGroup id="likec4-search-elements">
198
+ <ElementsColumn />
199
+ </LayoutGroup>
200
+ </AnimatePresence>
201
+ </ScrollArea>
202
+ </GridCol>
203
+ <GridCol span={6}>
204
+ <ScrollArea
205
+ type="scroll"
206
+ className={scrollArea}
207
+ pr="xs"
208
+ scrollbars="y">
209
+ <AnimatePresence>
210
+ <LayoutGroup id="likec4-search-views">
211
+ <ViewsColumn />
212
+ </LayoutGroup>
213
+ </AnimatePresence>
214
+ </ScrollArea>
215
+ </GridCol>
216
+ </Grid>
217
+ {pickViewFor && <PickView elementFqn={pickViewFor} />}
218
+ </Box>
219
+ )
220
+ }
@@ -14,7 +14,6 @@ export const treeRoot = css(
14
14
  containerName: 'likec4-tree',
15
15
  }),
16
16
  {
17
- overflow: 'hidden',
18
17
  containerType: 'inline-size',
19
18
  height: '100%',
20
19
  },
@@ -65,40 +64,12 @@ export const elementId = css({
65
64
  },
66
65
  })
67
66
 
68
- // const iconSizeVar = `var(${iconSize}, 32px)`
69
67
  export const elementIcon = css({
70
68
  ['--likec4-icon-size']: '24px',
71
69
  [whenContainerIsNarrow]: {
72
70
  ['--likec4-icon-size']: '18px',
73
71
  },
74
72
  })
75
- // flex: `0 0 ${iconSizeVar}`,
76
- // height: iconSizeVar,
77
- // width: iconSizeVar,
78
- // display: 'flex',
79
- // alignItems: 'center',
80
- // justifyContent: 'center',
81
- // alignSelf: 'flex-start',
82
- // marginTop: 4,
83
- // [`& svg, & img`]: {
84
- // width: '100%',
85
- // height: 'auto',
86
- // maxHeight: '100%',
87
- // pointerEvents: 'none',
88
- // },
89
- // [`& img`]: {
90
- // objectFit: 'contain',
91
- // },
92
- // '&.likec4-shape-icon svg': {
93
- // color: `[var(${iconColor})]`,
94
- // width: '90%',
95
- // strokeWidth: 1.5,
96
- // },
97
- // [iconSize]: '24px',
98
- // [whenContainerIsNarrow]: {
99
- // [iconSize]: '18px',
100
- // },
101
- // })
102
73
 
103
74
  export const elementViewsCount = css({
104
75
  flex: 0,
@@ -1,11 +1,11 @@
1
1
  import {
2
- type Fqn,
3
- type LikeC4Model,
4
2
  ifilter,
5
3
  isAncestor,
6
4
  nameFromFqn,
7
5
  sortParentsFirst,
8
6
  } from '@likec4/core'
7
+ import type { LikeC4Model } from '@likec4/core/model'
8
+ import type { Fqn } from '@likec4/core/types'
9
9
  import { cx, cx as clsx } from '@likec4/styles/css'
10
10
  import {
11
11
  type RenderTreeNodePayload,
@@ -22,15 +22,16 @@ import {
22
22
  import { useCallbackRef } from '@mantine/hooks'
23
23
  import { IconChevronRight } from '@tabler/icons-react'
24
24
  import * as m from 'motion/react-m'
25
- import { type KeyboardEventHandler, useCallback, useEffect, useMemo } from 'react'
26
- import { first, isEmpty, only, partition, pipe, reduce } from 'remeda'
27
- import { IconOrShapeRenderer } from '../../../context/IconRenderer'
28
- import { sortByLabel } from '../../../likec4model/useLikeC4ElementsTree'
29
- import { useLikeC4Model } from '../../../likec4model/useLikeC4Model'
25
+ import { type KeyboardEventHandler, memo, useEffect, useMemo } from 'react'
26
+ import { first, isEmpty, only, pipe, reduce } from 'remeda'
27
+ import { IconOrShapeRenderer } from '../../context/IconRenderer'
28
+ import { useDiagram } from '../../hooks/useDiagram'
29
+ import { sortByLabel } from '../../likec4model/useLikeC4ElementsTree'
30
+ import { useLikeC4Model } from '../../likec4model/useLikeC4Model'
31
+ import { useNormalizedSearch, useSearchActor } from '../hooks'
30
32
  import { buttonsva } from './_shared.css'
31
33
  import * as styles from './ElementsColumn.css'
32
- import { setPickView, useCloseSearchAndNavigateTo, useNormalizedSearch } from './state'
33
- import { centerY, moveFocusToSearchInput, stopAndPrevent } from './utils'
34
+ import { centerY, moveFocusToSearchInput, queryAllFocusable, stopAndPrevent } from './utils'
34
35
  import { NothingFound } from './ViewsColum'
35
36
 
36
37
  interface LikeC4ModelTreeNodeData {
@@ -56,14 +57,13 @@ function buildNode(
56
57
 
57
58
  const btn = buttonsva()
58
59
 
59
- export function ElementsColumn() {
60
+ export const ElementsColumn = memo(() => {
61
+ const model = useLikeC4Model()
60
62
  const search = useNormalizedSearch()
61
- const model = useLikeC4Model(true)
62
-
63
63
  const data = useMemo(() => {
64
64
  const searchTerms = search.split('.')
65
65
  let elements
66
- if (isEmpty(search) || search === 'kind:') {
66
+ if (isEmpty(searchTerms) || searchTerms[0] === 'kind:') {
67
67
  elements = model.elements()
68
68
  } else {
69
69
  elements = ifilter(model.elements(), element => {
@@ -120,7 +120,7 @@ export function ElementsColumn() {
120
120
  }
121
121
 
122
122
  return <ElementsTree data={data} handleClick={handleClick} />
123
- }
123
+ })
124
124
 
125
125
  const setHoveredNode = () => {}
126
126
 
@@ -161,7 +161,7 @@ function ElementsTree({
161
161
  if (e.key === 'ArrowUp') {
162
162
  if (id === roots[0]?.value) {
163
163
  stopAndPrevent(e)
164
- moveFocusToSearchInput()
164
+ moveFocusToSearchInput(target)
165
165
  }
166
166
  return
167
167
  }
@@ -172,9 +172,7 @@ function ElementsTree({
172
172
  }
173
173
  const label = (e.target as HTMLLIElement).querySelector<HTMLLIElement>('.mantine-Tree-label') ?? target
174
174
  const maxY = label.getBoundingClientRect().y
175
- const viewButtons = [...document.querySelectorAll<HTMLButtonElement>(
176
- `[data-likec4-search-views] .${styles.focusable}`,
177
- )]
175
+ const viewButtons = queryAllFocusable(target, 'views')
178
176
  let view = viewButtons.length > 1
179
177
  ? viewButtons.find((el, i, all) => centerY(el) > maxY || i === all.length - 1)
180
178
  : null
@@ -254,6 +252,7 @@ function ElementTreeNode(
254
252
  component={m.button}
255
253
  layout
256
254
  tabIndex={-1}
255
+ data-value={element.id}
257
256
  className={clsx(btn.root, 'group', 'likec4-element-button')}
258
257
  {...views.length > 0 && {
259
258
  onClick: (e) => {
@@ -297,23 +296,23 @@ function ElementTreeNode(
297
296
  }
298
297
 
299
298
  function useHandleElementSelection() {
300
- const navigateTo = useCloseSearchAndNavigateTo()
301
-
302
- return useCallback((element: LikeC4Model.Element) => {
299
+ const diagram = useDiagram()
300
+ const searchActorRef = useSearchActor()
301
+ return useCallbackRef((element: LikeC4Model.Element) => {
303
302
  const views = [...element.views()]
304
303
  if (views.length === 0) {
305
304
  return
306
305
  }
307
306
  const singleView = only(views)
308
307
  if (singleView) {
309
- navigateTo(singleView.id, element.id)
308
+ searchActorRef.send({ type: 'close' })
309
+ if (singleView.id !== diagram.currentView.id) {
310
+ setTimeout(() => {
311
+ diagram.navigateTo(singleView.id)
312
+ }, 100)
313
+ }
310
314
  return
311
315
  }
312
- const [scoped, others] = partition(views, v => v.viewOf?.id === element.id)
313
- setPickView({
314
- elementFqn: element.id,
315
- scoped,
316
- others,
317
- })
318
- }, [setPickView, navigateTo])
316
+ searchActorRef.send({ type: 'pickview.open', elementFqn: element.id })
317
+ })
319
318
  }
@@ -0,0 +1,134 @@
1
+ import type { LikeC4ViewModel } from '@likec4/core/model'
2
+ import type { Fqn } from '@likec4/core/types'
3
+ import { ActionIcon, FocusTrap, Group, ScrollAreaAutosize, Stack, Title } from '@mantine/core'
4
+ import { useWindowEvent } from '@mantine/hooks'
5
+ import { IconX } from '@tabler/icons-react'
6
+ import * as m from 'motion/react-m'
7
+ import { useLikeC4Model } from '../../likec4model'
8
+ import { useSearchActor } from '../hooks'
9
+ import * as styles from './styles.css'
10
+ import { ViewButton } from './ViewsColum'
11
+
12
+ export function PickView({ elementFqn }: { elementFqn: Fqn }) {
13
+ const searchActorRef = useSearchActor()
14
+ const element = useLikeC4Model().element(elementFqn)
15
+ const scoped = [] as LikeC4ViewModel[]
16
+ const others = [] as LikeC4ViewModel[]
17
+
18
+ for (const view of element.views()) {
19
+ if (view.viewOf === element) {
20
+ scoped.push(view)
21
+ } else {
22
+ others.push(view)
23
+ }
24
+ }
25
+
26
+ const closePickView = () => {
27
+ searchActorRef.send({ type: 'pickview.close' })
28
+ }
29
+
30
+ useWindowEvent(
31
+ 'keydown',
32
+ (event) => {
33
+ try {
34
+ if (event.key === 'Escape') {
35
+ event.stopPropagation()
36
+ event.preventDefault()
37
+ closePickView()
38
+ }
39
+ } catch (e) {
40
+ console.warn(e)
41
+ }
42
+ },
43
+ {
44
+ capture: true,
45
+ },
46
+ )
47
+
48
+ return (
49
+ <>
50
+ <m.div
51
+ key="pickview-backdrop"
52
+ className={styles.pickviewBackdrop}
53
+ onClick={e => {
54
+ e.stopPropagation()
55
+ closePickView()
56
+ }}>
57
+ </m.div>
58
+ <FocusTrap>
59
+ <m.div
60
+ key="pickview"
61
+ initial={{
62
+ opacity: 0,
63
+ scale: 0.95,
64
+ originY: 0,
65
+ translateX: '-50%',
66
+ translateY: -20,
67
+ }}
68
+ animate={{
69
+ opacity: 1,
70
+ scale: 1,
71
+ translateY: 0,
72
+ }}
73
+ exit={{
74
+ opacity: 0,
75
+ scale: 0.98,
76
+ translateY: -20,
77
+ transition: {
78
+ duration: 0.1,
79
+ },
80
+ }}
81
+ className={styles.pickview}
82
+ data-likec4-search-views>
83
+ <Group px="sm" py="md" justify="space-between">
84
+ <Title order={2} lh={1}>Select view</Title>
85
+ <ActionIcon
86
+ size={'md'}
87
+ variant="default"
88
+ onClick={(e) => {
89
+ e.stopPropagation()
90
+ closePickView()
91
+ }}>
92
+ <IconX />
93
+ </ActionIcon>
94
+ </Group>
95
+ <ScrollAreaAutosize mah={'calc(100vh - 110px)'} type="never">
96
+ {scoped.length > 0 && (
97
+ <Stack gap={'sm'} px={'sm'} className={styles.pickviewGroup}>
98
+ <Title order={6} c={'dimmed'}>scoped views of the element</Title>
99
+ {scoped.map((view, i) => (
100
+ <ViewButton
101
+ key={view.id}
102
+ view={view}
103
+ search={''}
104
+ loop
105
+ mod={{
106
+ autofocus: i === 0,
107
+ }}
108
+ />
109
+ ))}
110
+ </Stack>
111
+ )}
112
+
113
+ {others.length > 0 && (
114
+ <Stack gap={'sm'} px={'sm'} className={styles.pickviewGroup}>
115
+ <Title order={6} c={'dimmed'}>views including this element</Title>
116
+ {others.map((view, i) => (
117
+ <ViewButton
118
+ key={view.id}
119
+ view={view}
120
+ search={''}
121
+ loop
122
+ mod={{
123
+ autofocus: i === 0 && scoped.length === 0,
124
+ }}
125
+ />
126
+ ))}
127
+ </Stack>
128
+ )}
129
+ </ScrollAreaAutosize>
130
+ </m.div>
131
+ </FocusTrap>
132
+ </>
133
+ )
134
+ }