@likec4/diagram 1.27.3 → 1.28.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 (164) hide show
  1. package/bundle/index.js +974 -1036
  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.js +2 -2
  14. package/dist/base/primitives/customNode.js +2 -2
  15. package/dist/base/primitives/edge/EdgeActionButton.d.ts +1 -2
  16. package/dist/base/primitives/edge/EdgeContainer.js +1 -1
  17. package/dist/base/primitives/edge/EdgeLabel.js +1 -1
  18. package/dist/base/primitives/edge/EdgeLabelContainer.js +1 -1
  19. package/dist/base/primitives/edge/EdgePath.d.ts +10 -42
  20. package/dist/base/primitives/edge/EdgePath.js +23 -16
  21. package/dist/base/primitives/edge/edge.css.js +1 -1
  22. package/dist/base/primitives/element/ElementActionButtons.d.ts +1 -1
  23. package/dist/base/primitives/element/ElementActionButtons.js +6 -5
  24. package/dist/base/primitives/element/ElementDetailsButton.js +5 -2
  25. package/dist/base/primitives/element/ElementNodeContainer.js +3 -3
  26. package/dist/base/updateEdges.js +7 -2
  27. package/dist/base/updateNodes.js +11 -3
  28. package/dist/bundle/LikeC4View.js +3 -3
  29. package/dist/bundle/ReactLikeC4.js +1 -1
  30. package/dist/bundle/ShadowRoot.js +3 -3
  31. package/dist/bundle/styles.css.js +4 -4
  32. package/dist/context/ControlsCustomLayout.js +1 -1
  33. package/dist/context/DiagramEventHandlers.js +5 -5
  34. package/dist/context/DiagramFeatures.js +7 -7
  35. package/dist/context/EnsureMantine.js +2 -2
  36. package/dist/context/FramerMotionConfig.js +2 -2
  37. package/dist/context/IconRenderer.js +4 -4
  38. package/dist/context/ReduceGraphics.js +2 -2
  39. package/dist/context/RootContainer.js +5 -9
  40. package/dist/hooks/safeContext.js +3 -3
  41. package/dist/hooks/useCurrentViewId.js +1 -1
  42. package/dist/hooks/useDiagram.d.ts +3 -7
  43. package/dist/hooks/useDiagram.js +2 -2
  44. package/dist/hooks/useMantinePortalProps.js +2 -2
  45. package/dist/hooks/useSetState.js +3 -3
  46. package/dist/hooks/useUpdateEffect.js +1 -1
  47. package/dist/likec4diagram/DiagramUI.js +2 -2
  48. package/dist/likec4diagram/DiagramXYFlow.js +2 -2
  49. package/dist/likec4diagram/custom/edges/NotePopover.js +2 -2
  50. package/dist/likec4diagram/custom/edges/RelationshipEdge.js +17 -7
  51. package/dist/likec4diagram/custom/edges/RelationshipsDropdownMenu.js +6 -6
  52. package/dist/likec4diagram/custom/nodes/toolbar/ColorButton.js +2 -2
  53. package/dist/likec4diagram/custom/nodes/toolbar/CompoundToolbar.js +3 -3
  54. package/dist/likec4diagram/custom/nodes/toolbar/_shared.js +2 -2
  55. package/dist/likec4diagram/ui/controls/ChangeAutoLayoutButton.js +4 -4
  56. package/dist/likec4diagram/ui/controls/Controls.js +6 -6
  57. package/dist/likec4diagram/ui/controls/NavigationButtons.js +1 -1
  58. package/dist/likec4diagram/ui/diagram-title/DiagramTitlePanel.js +1 -1
  59. package/dist/likec4diagram/ui/notation/NotationPanel.js +4 -5
  60. package/dist/likec4diagram/ui/search/ElementsColumn.js +88 -77
  61. package/dist/likec4diagram/ui/search/LikeC4Search.d.ts +2 -1
  62. package/dist/likec4diagram/ui/search/LikeC4Search.js +27 -24
  63. package/dist/likec4diagram/ui/search/SearchInput.js +1 -1
  64. package/dist/likec4diagram/ui/search/ViewsColum.js +3 -2
  65. package/dist/likec4diagram/ui/search/state.js +11 -9
  66. package/dist/likec4diagram/ui/walkthrough/DynamicViewWalkthrough.js +1 -1
  67. package/dist/likec4diagram/useLayoutConstraints.js +3 -3
  68. package/dist/likec4model/LikeC4ModelContext.js +2 -2
  69. package/dist/likec4model/useLikeC4ElementsTree.js +2 -2
  70. package/dist/likec4model/useLikeC4Model.js +2 -2
  71. package/dist/node_modules/.pnpm/react@19.0.0/node_modules/react/cjs/react.production.js +548 -0
  72. package/dist/node_modules/.pnpm/react@19.0.0/node_modules/react/index.js +15 -0
  73. package/dist/overlays/Overlays.js +8 -8
  74. package/dist/overlays/element-details/ElementDetailsCard.js +5 -5
  75. package/dist/overlays/element-details/TabPanelDeployments.js +7 -4
  76. package/dist/overlays/element-details/TabPanelRelationships.js +2 -2
  77. package/dist/overlays/element-details/TabPanelStructure.js +6 -3
  78. package/dist/overlays/element-details/actor.d.ts +301 -301
  79. package/dist/overlays/element-details/actorContext.js +3 -3
  80. package/dist/overlays/overlay/Overlay.css.d.ts +2 -25
  81. package/dist/overlays/overlay/Overlay.css.js +0 -40
  82. package/dist/overlays/overlay/Overlay.d.ts +1 -1
  83. package/dist/overlays/overlay/Overlay.js +99 -89
  84. package/dist/overlays/overlaysActor.js +4 -1
  85. package/dist/overlays/relationship-details/RelationshipDetails.js +28 -23
  86. package/dist/overlays/relationship-details/SelectEdge.js +3 -6
  87. package/dist/overlays/relationship-details/actor.d.ts +2 -0
  88. package/dist/overlays/relationship-details/custom/edgeTypes.js +1 -1
  89. package/dist/overlays/relationship-details/custom/nodeTypes.js +1 -1
  90. package/dist/overlays/relationship-details/hooks.d.ts +3 -1
  91. package/dist/overlays/relationship-details/hooks.js +4 -4
  92. package/dist/overlays/relationships-browser/RelationshipsBrowser.js +9 -11
  93. package/dist/overlays/relationships-browser/SelectElement.js +8 -5
  94. package/dist/overlays/relationships-browser/actor.d.ts +1 -1
  95. package/dist/overlays/relationships-browser/actor.js +6 -12
  96. package/dist/overlays/relationships-browser/const.d.ts +2 -1
  97. package/dist/overlays/relationships-browser/const.js +6 -1
  98. package/dist/overlays/relationships-browser/custom/edgeTypes.js +3 -4
  99. package/dist/overlays/relationships-browser/custom/nodeTypes.js +2 -2
  100. package/dist/overlays/relationships-browser/hooks.d.ts +1 -1
  101. package/dist/overlays/relationships-browser/hooks.js +4 -4
  102. package/dist/overlays/relationships-browser/layout.js +2 -2
  103. package/dist/overlays/relationships-browser/useViewToNodesEdges.js +1 -2
  104. package/dist/state/DiagramActorProvider.js +5 -3
  105. package/dist/state/diagram-machine.d.ts +2 -2
  106. package/dist/state/diagram-machine.js +3 -4
  107. package/dist/state/hotkeyActor.js +12 -3
  108. package/dist/styles.css.js +1 -1
  109. package/package.json +21 -17
  110. package/src/base/BaseXYFlow.tsx +2 -2
  111. package/src/base/primitives/compound/CompoundActionButton.tsx +1 -1
  112. package/src/base/primitives/compound/CompoundDetailsButton.tsx +1 -1
  113. package/src/base/primitives/compound/CompoundNodeContainer.css.ts +0 -3
  114. package/src/base/primitives/compound/CompoundNodeContainer.tsx +1 -1
  115. package/src/base/primitives/edge/EdgeActionButton.tsx +1 -2
  116. package/src/base/primitives/edge/EdgePath.tsx +22 -14
  117. package/src/base/primitives/edge/edge.css.ts +1 -1
  118. package/src/base/primitives/element/ElementActionButtons.tsx +7 -5
  119. package/src/base/primitives/element/ElementDetailsButton.tsx +5 -1
  120. package/src/base/primitives/element/ElementNodeContainer.css.ts +0 -18
  121. package/src/base/primitives/element/ElementNodeContainer.tsx +1 -1
  122. package/src/base/updateEdges.ts +6 -9
  123. package/src/base/updateNodes.ts +10 -14
  124. package/src/bundle/styles.css.ts +7 -2
  125. package/src/components/SearchControl.css.ts +4 -8
  126. package/src/context/FramerMotionConfig.tsx +1 -1
  127. package/src/context/RootContainer.tsx +0 -4
  128. package/src/hooks/useDiagram.ts +1 -1
  129. package/src/likec4diagram/custom/edges/RelationshipEdge.tsx +8 -2
  130. package/src/likec4diagram/custom/edges/RelationshipsDropdownMenu.tsx +1 -1
  131. package/src/likec4diagram/custom/edges/edges.css.ts +0 -2
  132. package/src/likec4diagram/ui/controls/Controls.tsx +53 -55
  133. package/src/likec4diagram/ui/controls/NavigationButtons.tsx +1 -1
  134. package/src/likec4diagram/ui/diagram-title/DiagramTitlePanel.tsx +1 -1
  135. package/src/likec4diagram/ui/notation/NotationPanel.css.ts +0 -4
  136. package/src/likec4diagram/ui/notation/NotationPanel.tsx +1 -2
  137. package/src/likec4diagram/ui/search/ElementsColumn.tsx +94 -71
  138. package/src/likec4diagram/ui/search/LikeC4Search.tsx +48 -55
  139. package/src/likec4diagram/ui/search/ViewsColum.tsx +2 -2
  140. package/src/likec4diagram/ui/search/ViewsColumn.css.ts +0 -4
  141. package/src/likec4diagram/ui/search/state.ts +8 -6
  142. package/src/likec4diagram/ui/walkthrough/DynamicViewWalkthrough.tsx +1 -1
  143. package/src/overlays/Overlays.tsx +4 -4
  144. package/src/overlays/element-details/ElementDetailsCard.tsx +1 -1
  145. package/src/overlays/element-details/TabPanelDeployments.tsx +2 -0
  146. package/src/overlays/element-details/TabPanelStructure.tsx +2 -1
  147. package/src/overlays/overlay/Overlay.css.ts +0 -40
  148. package/src/overlays/overlay/Overlay.tsx +109 -99
  149. package/src/overlays/overlaysActor.ts +5 -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/relationship-details/custom/nodeTypes.tsx +1 -1
  155. package/src/overlays/relationships-browser/RelationshipsBrowser.tsx +1 -3
  156. package/src/overlays/relationships-browser/SelectElement.tsx +2 -0
  157. package/src/overlays/relationships-browser/actor.ts +8 -13
  158. package/src/overlays/relationships-browser/const.ts +8 -1
  159. package/src/overlays/relationships-browser/custom/edgeTypes.tsx +2 -3
  160. package/src/overlays/relationships-browser/custom/nodeTypes.tsx +2 -2
  161. package/src/overlays/relationships-browser/useViewToNodesEdges.ts +1 -2
  162. package/src/state/DiagramActorProvider.tsx +4 -1
  163. package/src/state/diagram-machine.ts +2 -3
  164. package/src/state/hotkeyActor.ts +13 -3
@@ -61,22 +61,6 @@ const dialog = css.raw({
61
61
  xl: {
62
62
  [offset]: '4rem',
63
63
  },
64
- // inset: '[calc(2rem + var(--_level, 0) * 1rem), 2rem, 2rem, 2rem]',
65
- // width: 'calc(100vw - 4rem)',
66
- // height: 'calc(100vh - 4rem - var(--_level, 0) * 1rem)',
67
- // },
68
- // lg: {
69
- // inset: '[calc(4rem + var(--_level, 0) * 1rem), 4rem, 4rem, 4rem]',
70
- // width: 'calc(100vw - 8rem)',
71
- // height: 'calc(100vh - 8rem)',
72
- // },
73
- // xl: {
74
- // [borderRadius]: '8px',
75
- // padding: '8px',
76
- // inset: '[calc(5rem + var(--_level, 0) * 1rem), 5rem, 5rem, 5rem]',
77
- // width: 'calc(100vw - 10rem)',
78
- // height: 'calc(100vh - 10rem - var(--_level, 0) * 1rem)',
79
- // },
80
64
  })
81
65
 
82
66
  const body = css.raw({
@@ -99,28 +83,4 @@ export const overlay = sva({
99
83
  dialog: dialog,
100
84
  body: body,
101
85
  },
102
- variants: {
103
- level: {
104
- 0: {
105
- dialog: {
106
- [level]: '0',
107
- },
108
- },
109
- 1: {
110
- dialog: {
111
- [level]: '1',
112
- },
113
- },
114
- 2: {
115
- dialog: {
116
- [level]: '2',
117
- },
118
- },
119
- 3: {
120
- dialog: {
121
- [level]: '3',
122
- },
123
- },
124
- },
125
- },
126
86
  })
@@ -5,13 +5,13 @@ 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'
9
- import { type PropsWithChildren, forwardRef, useLayoutEffect, useRef, useState } from 'react'
8
+ import { m, useReducedMotionConfig } from 'motion/react'
9
+ import { type PropsWithChildren, forwardRef, useEffect, useRef, useState } from 'react'
10
10
  import { stopPropagation } from '../../utils'
11
- import { backdropBlur, backdropOpacity, overlay as overlayCVA } from './Overlay.css'
11
+ import { backdropBlur, backdropOpacity, level as cssVarLevel, overlay as overlayCVA } from './Overlay.css'
12
12
 
13
13
  type OverlayProps = PropsWithChildren<{
14
- overlayLevel?: 0 | 1 | 2 | 3
14
+ overlayLevel?: number
15
15
  className?: string
16
16
  classes?: {
17
17
  dialog?: string
@@ -21,108 +21,118 @@ type OverlayProps = PropsWithChildren<{
21
21
  onClick?: never
22
22
  }>
23
23
 
24
- export const Overlay = forwardRef<HTMLDialogElement, OverlayProps>(
25
- ({ children, onClose, className, classes, overlayLevel = 0, ...rest }, ref) => {
26
- const [opened, setOpened] = useState(false)
27
- const dialogRef = useRef<HTMLDialogElement>(null)
28
- const isClosingRef = useRef(false)
24
+ export const Overlay = forwardRef<HTMLDialogElement, OverlayProps>(({
25
+ onClose,
26
+ className,
27
+ classes,
28
+ overlayLevel = 0,
29
+ children,
30
+ ...rest
31
+ }, ref) => {
32
+ const [opened, setOpened] = useState(false)
33
+ const dialogRef = useRef<HTMLDialogElement>(null)
34
+ const isClosingRef = useRef(false)
29
35
 
30
- const motionNotReduced = useReducedMotionConfig() !== true
36
+ const motionNotReduced = useReducedMotionConfig() !== true
31
37
 
32
- const onCloseRef = useSyncedRef(onClose)
33
- const close = useDebouncedCallback(
34
- () => {
35
- if (isClosingRef.current) return
36
- isClosingRef.current = true
37
- onCloseRef.current()
38
- },
39
- [],
40
- 50,
41
- )
38
+ const onCloseRef = useSyncedRef(onClose)
39
+ const close = useDebouncedCallback(
40
+ () => {
41
+ if (isClosingRef.current) return
42
+ isClosingRef.current = true
43
+ onCloseRef.current()
44
+ },
45
+ [],
46
+ 50,
47
+ )
42
48
 
43
- useLayoutEffect(() => {
44
- const cancel = (e: Event) => {
45
- e.preventDefault()
46
- close()
47
- }
48
- dialogRef.current?.addEventListener('cancel', cancel, { capture: true })
49
- return () => {
50
- dialogRef.current?.removeEventListener('cancel', cancel, { capture: true })
51
- }
52
- }, [])
49
+ useEffect(() => {
50
+ const cancel = (e: Event) => {
51
+ e.preventDefault()
52
+ e.stopPropagation()
53
+ close()
54
+ }
55
+ dialogRef.current?.addEventListener('cancel', cancel, { capture: true })
56
+ return () => {
57
+ dialogRef.current?.removeEventListener('cancel', cancel, { capture: true })
58
+ }
59
+ }, [])
53
60
 
54
- useTimeoutEffect(() => {
55
- if (!dialogRef.current?.open) {
56
- // Move dialog to the top of the DOM
57
- dialogRef.current?.showModal()
58
- }
59
- }, 20)
61
+ useTimeoutEffect(() => {
62
+ if (!dialogRef.current?.open) {
63
+ // Move dialog to the top of the DOM
64
+ dialogRef.current?.showModal()
65
+ }
66
+ }, 30)
60
67
 
61
- useTimeoutEffect(() => {
62
- setOpened(true)
63
- }, 120)
68
+ useTimeoutEffect(() => {
69
+ setOpened(true)
70
+ }, 120)
64
71
 
65
- const styles = overlayCVA({
66
- level: overlayLevel as 0 | 1 | 2 | 3,
67
- })
72
+ const styles = overlayCVA({
73
+ level: overlayLevel as 0 | 1 | 2 | 3,
74
+ })
68
75
 
69
- return (
70
- <m.dialog
71
- ref={useMergedRef(dialogRef, ref)}
72
- className={cx(RemoveScroll.classNames.fullWidth, styles.dialog, classes?.dialog, className)}
73
- layout
74
- {...motionNotReduced
75
- ? ({
76
- initial: {
77
- [backdropBlur]: '0px',
78
- [backdropOpacity]: '0%',
79
- scale: overlayLevel > 0 ? 0.9 : 1.075,
80
- opacity: 0,
81
- },
82
- animate: {
83
- [backdropBlur]: overlayLevel > 0 ? '4px' : '8px',
84
- [backdropOpacity]: overlayLevel > 0 ? '50%' : '60%',
85
- scale: 1,
86
- opacity: 1,
87
- transition: {
88
- delay: 0.075,
89
- },
90
- },
91
- exit: {
92
- scale: 1.2,
93
- opacity: 0,
94
- [backdropBlur]: '0px',
95
- [backdropOpacity]: '0%',
76
+ return (
77
+ <m.dialog
78
+ ref={useMergedRef(dialogRef, ref)}
79
+ className={cx(RemoveScroll.classNames.fullWidth, styles.dialog, classes?.dialog, className)}
80
+ layout
81
+ style={{
82
+ // @ts-ignore
83
+ [cssVarLevel]: overlayLevel,
84
+ }}
85
+ {...motionNotReduced
86
+ ? ({
87
+ initial: {
88
+ [backdropBlur]: '0px',
89
+ [backdropOpacity]: '0%',
90
+ scale: overlayLevel > 0 ? 0.9 : 1.075,
91
+ opacity: 0,
92
+ },
93
+ animate: {
94
+ [backdropBlur]: overlayLevel > 0 ? '4px' : '8px',
95
+ [backdropOpacity]: overlayLevel > 0 ? '50%' : '60%',
96
+ scale: 1,
97
+ opacity: 1,
98
+ transition: {
99
+ delay: 0.075,
96
100
  },
97
- })
98
- : {
99
- initial: {
100
- [backdropBlur]: '8px',
101
- [backdropOpacity]: '60%',
102
- },
103
- }}
104
- onClick={e => {
105
- e.stopPropagation()
106
- if ((e.target as any)?.nodeName?.toUpperCase() === 'DIALOG') {
107
- dialogRef.current?.close()
108
- return
109
- }
110
- }}
111
- onDoubleClick={stopPropagation}
112
- onPointerDown={stopPropagation}
113
- onClose={e => {
114
- e.stopPropagation()
115
- close()
101
+ },
102
+ exit: {
103
+ scale: 1.2,
104
+ opacity: 0,
105
+ [backdropBlur]: '0px',
106
+ [backdropOpacity]: '0%',
107
+ },
108
+ })
109
+ : {
110
+ initial: {
111
+ [backdropBlur]: '8px',
112
+ [backdropOpacity]: '60%',
113
+ },
116
114
  }}
117
- {...rest}
118
- >
119
- <RemoveScroll forwardProps removeScrollBar={false}>
120
- <Box className={cx(styles.body, 'overlay-body', classes?.body)}>
121
- {opened && <>{children}</>}
122
- </Box>
123
- </RemoveScroll>
124
- </m.dialog>
125
- )
126
- },
127
- )
115
+ onClick={e => {
116
+ e.stopPropagation()
117
+ if ((e.target as any)?.nodeName?.toUpperCase() === 'DIALOG') {
118
+ dialogRef.current?.close()
119
+ return
120
+ }
121
+ }}
122
+ onDoubleClick={stopPropagation}
123
+ onPointerDown={stopPropagation}
124
+ onClose={e => {
125
+ e.stopPropagation()
126
+ close()
127
+ }}
128
+ {...rest}
129
+ >
130
+ <RemoveScroll forwardProps removeScrollBar={false}>
131
+ <Box className={cx(styles.body, 'overlay-body', classes?.body)}>
132
+ {opened && <>{children}</>}
133
+ </Box>
134
+ </RemoveScroll>
135
+ </m.dialog>
136
+ )
137
+ })
128
138
  Overlay.displayName = 'Overlay'
@@ -1,5 +1,6 @@
1
1
  import type { Fqn } from '@likec4/core'
2
2
  import { getHotkeyHandler } from '@mantine/hooks'
3
+ import type { KeyboardEvent } from 'react'
3
4
  import { isString, last, reverse } from 'remeda'
4
5
  import {
5
6
  type ActorLogicFrom,
@@ -45,7 +46,10 @@ const hotkeyLogic = fromCallback(({ sendBack }: {
45
46
  sendBack: (event: HotKeyEvent) => void
46
47
  }) => {
47
48
  const handler = getHotkeyHandler([
48
- ['Escape', () => sendBack({ type: 'close' }), {
49
+ ['Escape', (event: KeyboardEvent) => {
50
+ event.stopPropagation()
51
+ sendBack({ type: 'close' })
52
+ }, {
49
53
  preventDefault: true,
50
54
  }],
51
55
  ])
@@ -1,4 +1,4 @@
1
- import { type DiagramEdge, type DiagramView, ifind, invariant, isAncestor, nonNullable } from '@likec4/core'
1
+ import { type DiagramEdge, type DiagramView, invariant, isAncestor, nonNullable } from '@likec4/core'
2
2
  import { ActionIcon, Group } from '@mantine/core'
3
3
  import { useCallbackRef, useStateHistory } from '@mantine/hooks'
4
4
  import { IconChevronLeft, IconChevronRight, IconX } from '@tabler/icons-react'
@@ -6,9 +6,9 @@ import { useSelector } from '@xstate/react'
6
6
  import { Panel, ReactFlowProvider, useReactFlow, useStoreApi } from '@xyflow/react'
7
7
  import clsx from 'clsx'
8
8
  import { deepEqual } from 'fast-equals'
9
- import { AnimatePresence, LayoutGroup, m } from 'framer-motion'
9
+ import { AnimatePresence, LayoutGroup, m } from 'motion/react'
10
10
  import { memo, useEffect, useMemo, useRef } from 'react'
11
- import { first } from 'remeda'
11
+ import { find, isTruthy } from 'remeda'
12
12
  import { BaseXYFlow } from '../../base/BaseXYFlow'
13
13
  import { useLikeC4Model } from '../../likec4model/useLikeC4Model'
14
14
  import type { RelationshipDetailsTypes, RelationshipDetailsTypes as Types } from './_types'
@@ -66,10 +66,10 @@ export function RelationshipDetails({ actorRef }: RelationshipDetailsProps) {
66
66
  <ReactFlowProvider {...initialRef.current}>
67
67
  <LayoutGroup id={actorRef.sessionId} inherit={false}>
68
68
  <AnimatePresence>
69
- <RelationshipDetailsInner />
69
+ <RelationshipDetailsInner key="xyflow" />
70
+ <SyncRelationshipDetailsXYFlow key="sync" />
70
71
  </AnimatePresence>
71
72
  </LayoutGroup>
72
- <SyncRelationshipDetailsXYFlow />
73
73
  </ReactFlowProvider>
74
74
  </RelationshipDetailsActorContext.Provider>
75
75
  )
@@ -80,22 +80,24 @@ const selectSubject = (state: RelationshipDetailsSnapshot) => ({
80
80
  viewId: state.context.viewId,
81
81
  })
82
82
 
83
- function SyncRelationshipDetailsXYFlow() {
83
+ const SyncRelationshipDetailsXYFlow = memo(() => {
84
84
  const actor = useRelationshipDetailsActor()
85
85
  const subject = useSelector(actor, selectSubject, deepEqual)
86
86
  const likec4model = useLikeC4Model(true)
87
87
  const view = likec4model.findView(subject.viewId) ?? null
88
88
  const data = useMemo(() => {
89
89
  let data: RelationshipDetailsViewData
90
- if ('edgeId' in subject) {
90
+ if ('edgeId' in subject && isTruthy(subject.edgeId)) {
91
91
  invariant(view, `view ${subject.viewId} not found`)
92
92
  const edge = nonNullable(view.findEdge(subject.edgeId), `edge ${subject.edgeId} not found in ${subject.viewId}`)
93
93
  data = computeEdgeDetailsViewData([edge.id], view)
94
- } else {
94
+ } else if (!!subject.source && !!subject.target) {
95
95
  data = computeRelationshipDetailsViewData({
96
96
  source: likec4model.element(subject.source),
97
97
  target: likec4model.element(subject.target),
98
98
  })
99
+ } else {
100
+ return null
99
101
  }
100
102
  return layoutRelationshipDetails(data, view)
101
103
  }, [
@@ -114,11 +116,13 @@ function SyncRelationshipDetailsXYFlow() {
114
116
  }, [store, instance.viewportInitialized, actor])
115
117
 
116
118
  useEffect(() => {
117
- actor.send({ type: 'update.layoutData', data })
119
+ if (data !== null) {
120
+ actor.send({ type: 'update.layoutData', data })
121
+ }
118
122
  }, [data, actor])
119
123
 
120
124
  return null
121
- }
125
+ })
122
126
 
123
127
  const selector = ({ context }: RelationshipDetailsSnapshot) => ({
124
128
  // subject: context.subject,
@@ -227,13 +231,18 @@ const TopLeftPanel = memo(() => {
227
231
  return null
228
232
  }
229
233
 
230
- let edge = ('edgeId' in subject)
231
- ? view.findEdge(subject.edgeId)
232
- : ifind(view.edges(), (e) => e.source.element?.id === subject.source && e.target.element?.id === subject.target)
233
- ?? ifind(view.edges(), (e) => {
234
- return (e.source.element?.id === subject.source || isAncestor(e.source.element?.id ?? '--', subject.source)) &&
235
- (e.target.element?.id === subject.target || isAncestor(e.target.element?.id ?? '', subject.target))
234
+ const edges = [...view.edges()]
235
+
236
+ let edge = ('edgeId' in subject && isTruthy(subject.edgeId))
237
+ ? edges.find(e => e.id === subject.edgeId)
238
+ : (
239
+ find(edges, (e) => e.source.element?.id === subject.source && e.target.element?.id === subject.target)
240
+ || find(edges, (e) => {
241
+ return (e.source.element?.id === subject.source ||
242
+ isAncestor(e.source.element?.id ?? '--', subject.source ?? '__')) &&
243
+ (e.target.element?.id === subject.target || isAncestor(e.target.element?.id ?? '', subject.target ?? '__'))
236
244
  })
245
+ )
237
246
  if (!edge) {
238
247
  return null
239
248
  }
@@ -26,20 +26,6 @@ export const SelectEdge = ({
26
26
  }) => {
27
27
  const browser = useRelationshipDetails()
28
28
  const viewport = useRef<HTMLDivElement>(null)
29
- const portalProps = useMantinePortalProps()
30
- // const data = useLikeC4ElementsTree(scope === 'view' ? viewId : undefined)
31
- // const tree = useTree({
32
- // multiple: false
33
- // })
34
-
35
- // useEffect(() => {
36
- // ancestorsFqn(subject.id).reverse().forEach(id => {
37
- // tree.expand(id)
38
- // })
39
- // tree.select(subject.id)
40
- // }, [subject.id])
41
-
42
- const theme = useComputedColorScheme()
43
29
 
44
30
  const edgeSource = view.nodes.find(n => n.id === edge.source)
45
31
  const edgeTarget = view.nodes.find(n => n.id === edge.target)
@@ -1,6 +1,5 @@
1
1
  import {
2
2
  type BBox,
3
- type DiagramView,
4
3
  type EdgeId,
5
4
  type ExclusiveUnion,
6
5
  type Fqn,
@@ -55,6 +54,8 @@ export type Input = ExclusiveUnion<{
55
54
 
56
55
  type Subject = {
57
56
  edgeId: EdgeId
57
+ source?: never
58
+ target?: never
58
59
  // relationships: null
59
60
  } | {
60
61
  source: Fqn
@@ -22,7 +22,7 @@ export const RelationshipEdge = customEdge<RelationshipDetailsTypes.EdgeData>((p
22
22
  const diagram = useDiagram()
23
23
  return (
24
24
  <EdgeContainer {...props}>
25
- <EdgePath {...props} svgPath={svgPath} />
25
+ <EdgePath edgeProps={props} svgPath={svgPath} />
26
26
  <EdgeLabelContainer
27
27
  edgeProps={props}
28
28
  labelPosition={{
@@ -33,7 +33,7 @@ export const ElementNode = customNode<RelationshipDetailsTypes.ElementNodeData>(
33
33
  return (
34
34
  <ElementNodeContainer nodeProps={props}>
35
35
  <ElementShape {...props} />
36
- <ElementTitle {...props} iconSize={40} />
36
+ <ElementTitle {...props} />
37
37
  <ElementDetailsButtonWithHandler {...props} />
38
38
  <ElementActions {...props} />
39
39
  <ElementPorts {...props} />
@@ -4,13 +4,12 @@ import { useCallbackRef, useStateHistory } from '@mantine/hooks'
4
4
  import { IconChevronLeft, IconChevronRight, IconX } from '@tabler/icons-react'
5
5
  import { Panel, ReactFlowProvider, useReactFlow, useStoreApi } from '@xyflow/react'
6
6
  import { shallowEqual } from 'fast-equals'
7
- import { AnimatePresence, LayoutGroup, m } from 'framer-motion'
7
+ import { AnimatePresence, LayoutGroup, m } from 'motion/react'
8
8
  import { memo, useEffect, useRef } from 'react'
9
9
  import type { SnapshotFrom } from 'xstate'
10
10
  import { BaseXYFlow } from '../../base/BaseXYFlow'
11
11
  import type { RelationshipsBrowserTypes, RelationshipsBrowserTypes as Types } from './_types'
12
12
  import type { RelationshipsBrowserActorRef } from './actor'
13
- import { ViewPadding } from './const'
14
13
  import { CompoundNode, ElementNode, EmptyNode, RelationshipEdge } from './custom'
15
14
  import {
16
15
  RelationshipsBrowserActorContext,
@@ -101,7 +100,6 @@ const RelationshipsBrowserXYFlow = memo(() => {
101
100
  nodeTypes={nodeTypes}
102
101
  edgeTypes={edgeTypes}
103
102
  fitView={false}
104
- fitViewPadding={ViewPadding}
105
103
  onNodeClick={useCallbackRef((e, node) => {
106
104
  browser.send({ type: 'xyflow.nodeClick', node })
107
105
  })}
@@ -33,6 +33,7 @@ const selector2 = (state: RelationshipsBrowserSnapshot) => {
33
33
  })
34
34
  }
35
35
 
36
+ const setHoveredNode = () => {}
36
37
  export const SelectElement = memo(() => {
37
38
  const browser = useRelationshipsBrowser()
38
39
  const {
@@ -52,6 +53,7 @@ export const SelectElement = memo(() => {
52
53
  const tree = useTree({
53
54
  multiple: false,
54
55
  })
56
+ tree.setHoveredNode = setHoveredNode
55
57
 
56
58
  useEffect(() => {
57
59
  ancestorsFqn(subjectId).reverse().forEach(id => {
@@ -7,7 +7,7 @@ import {
7
7
  applyNodeChanges,
8
8
  useStoreApi,
9
9
  } from '@xyflow/react'
10
- import { type InternalNodeUpdate, getNodeDimensions, getViewportForBounds } from '@xyflow/system'
10
+ import { type InternalNodeUpdate, getViewportForBounds } from '@xyflow/system'
11
11
  import { isNullish, omit, prop } from 'remeda'
12
12
  import {
13
13
  type ActorLogicFrom,
@@ -109,8 +109,8 @@ export const layouter = fromPromise<{
109
109
  const currentSubjectCenter = centerXYInternalNode(currentSubjectInternalNode)
110
110
 
111
111
  // Move to center of existing node
112
- const existingInternalNode = xyflow.getInternalNode(existingNode.id)!
113
- const existingDimensions = getNodeDimensions(existingInternalNode)
112
+ // const existingInternalNode = xyflow.getInternalNode(existingNode.id)!
113
+ // const existingDimensions = getNodeDimensions(existingInternalNode)
114
114
 
115
115
  // Dim all nodes except the existing node
116
116
  // Hide nested nodes
@@ -146,8 +146,8 @@ export const layouter = fromPromise<{
146
146
  return {
147
147
  ...omit(n, ['parentId']),
148
148
  position: {
149
- x: currentSubjectCenter.x - existingDimensions.width / 2,
150
- y: currentSubjectCenter.y - existingDimensions.height / 2,
149
+ x: currentSubjectCenter.x - n.initialWidth / 2,
150
+ y: currentSubjectCenter.y - n.initialHeight / 2,
151
151
  },
152
152
  zIndex: ZIndexes.Max,
153
153
  hidden: false,
@@ -164,19 +164,14 @@ export const layouter = fromPromise<{
164
164
  xyedges: [],
165
165
  })
166
166
 
167
- // Pick the smaller zoom level
168
- zoom = Math.min(
169
- zoom,
170
- nextviewport.zoom,
171
- )
172
167
  // allow framer to render
173
- await delay(175)
168
+ await delay(120)
174
169
  next.xynodes = next.xynodes.map(Base.setDimmed(false))
175
170
 
176
171
  if (signal.aborted) {
177
172
  return updateXYData()
178
173
  }
179
- await xyflow.setCenter(currentSubjectCenter.x, currentSubjectCenter.y, { zoom, duration: 350 })
174
+ await xyflow.setCenter(currentSubjectCenter.x, currentSubjectCenter.y, { zoom, duration: 300 })
180
175
  await xyflow.setCenter(nextSubjectCenter.x, nextSubjectCenter.y, { zoom })
181
176
  return updateXYData()
182
177
  })
@@ -551,7 +546,7 @@ export const relationshipsBrowserLogic = setup({
551
546
  xyedges: event.output.xyedges,
552
547
  navigateFromNode: null,
553
548
  })
554
- enqueue.raise({ type: 'fitDiagram' }, { id: 'fitDiagram', delay: 50 })
549
+ enqueue.raise({ type: 'fitDiagram', duration: 200 }, { id: 'fitDiagram', delay: 50 })
555
550
  for (let i = 0; i < 6; i++) {
556
551
  enqueue.raise({ type: 'xyflow.updateNodeInternals' }, { delay: 100 + i * 100 })
557
552
  }
@@ -1 +1,8 @@
1
- export const ViewPadding = 0.1
1
+ import type { Padding } from '@xyflow/system'
2
+
3
+ export const ViewPadding: Padding = {
4
+ top: '40px',
5
+ bottom: '16px',
6
+ left: '16px',
7
+ right: '16px',
8
+ }
@@ -77,10 +77,9 @@ export const RelationshipEdge = customEdge<RelationshipsBrowserTypes.EdgeData>((
77
77
  }
78
78
 
79
79
  return (
80
- <EdgeContainer key={id} {...edgeProps}>
80
+ <EdgeContainer {...edgeProps}>
81
81
  <EdgePath
82
- key={id}
83
- {...edgeProps}
82
+ edgeProps={edgeProps}
84
83
  svgPath={svgPath}
85
84
  {...markOrange && {
86
85
  strokeWidth: 5,
@@ -1,6 +1,6 @@
1
1
  import { Handle } from '@xyflow/react'
2
2
  import { Position } from '@xyflow/system'
3
- import { m } from 'framer-motion'
3
+ import * as m from 'motion/react-m'
4
4
  import type { NodeProps } from '../../../base'
5
5
  import {
6
6
  CompoundNodeContainer,
@@ -36,7 +36,7 @@ export const ElementNode = customNode<RelationshipsBrowserTypes.ElementNodeData>
36
36
  return (
37
37
  <ElementNodeContainer key={props.id} component={m.div} layoutId={props.id} nodeProps={props}>
38
38
  <ElementShape {...props} />
39
- <ElementTitle {...props} iconSize={40} />
39
+ <ElementTitle {...props} />
40
40
  <ElementDetailsButtonWithHandler {...props} />
41
41
  <ElementActions {...props} />
42
42
  <ElementPorts {...props} />
@@ -70,7 +70,6 @@ export function viewToNodesEdge(
70
70
  draggable: false,
71
71
  selectable: true,
72
72
  focusable: true,
73
- deletable: false,
74
73
  position,
75
74
  zIndex: isCompound ? ZIndexes.Compound : ZIndexes.Element,
76
75
  style: {
@@ -181,7 +180,7 @@ export function viewToNodesEdge(
181
180
  zIndex: ZIndexes.Edge,
182
181
  // selectable: selectable,
183
182
  // hidden: !visiblePredicate(edge),
184
- deletable: false,
183
+ // deletable: false,
185
184
  data: {
186
185
  sourceFqn: edge.sourceFqn,
187
186
  targetFqn: edge.targetFqn,
@@ -34,7 +34,10 @@ export function DiagramActorProvider({
34
34
  diagramMachine.provide({
35
35
  actions: {
36
36
  'trigger:NavigateTo': ((_, { viewId }) => {
37
- handlersRef.current.onNavigateTo?.(viewId as ViewId)
37
+ // this delay allows tap animations to finish
38
+ setTimeout(() => {
39
+ handlersRef.current.onNavigateTo?.(viewId as ViewId)
40
+ }, 30)
38
41
  }),
39
42
 
40
43
  'trigger:OnChange': ((_, params) => {