@likec4/diagram 1.21.0 → 1.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/dist/LikeC4Diagram.js +9 -13
  2. package/dist/base/BaseXYFlow.js +0 -1
  3. package/dist/base/index.d.ts +0 -1
  4. package/dist/base/primitives/compound/CompoundNodeContainer.js +2 -2
  5. package/dist/base/primitives/edge/EdgeLabel.js +2 -2
  6. package/dist/base/primitives/element/ElementNodeContainer.d.ts +6 -1
  7. package/dist/base/primitives/element/ElementNodeContainer.js +31 -13
  8. package/dist/base/primitives/element/ElementTitle.js +7 -3
  9. package/dist/base/primitives/element/styles.css.js +1 -1
  10. package/dist/{base → components}/Link.css.js +3 -3
  11. package/dist/components/SearchControl.css.d.ts +2 -0
  12. package/dist/components/SearchControl.css.js +7 -0
  13. package/dist/components/SearchControl.d.ts +5 -0
  14. package/dist/components/SearchControl.js +17 -0
  15. package/dist/context/IconRenderer.d.ts +10 -0
  16. package/dist/context/IconRenderer.js +34 -0
  17. package/dist/hooks/index.d.ts +0 -1
  18. package/dist/hooks/useDiagram.d.ts +4 -4
  19. package/dist/index.d.ts +1 -1
  20. package/dist/likec4diagram/DiagramUI.js +1 -4
  21. package/dist/likec4diagram/XYFlow.d.ts +6 -1
  22. package/dist/likec4diagram/XYFlow.js +28 -29
  23. package/dist/likec4diagram/custom/edges/RelationshipsDropdownMenu.js +3 -1
  24. package/dist/likec4diagram/state/actorContext.d.ts +16 -16
  25. package/dist/likec4diagram/state/assign.js +1 -1
  26. package/dist/likec4diagram/state/machine.d.ts +7 -7
  27. package/dist/likec4diagram/state/machine.js +10 -7
  28. package/dist/likec4diagram/ui/controls/Controls.js +63 -48
  29. package/dist/likec4diagram/ui/controls/NavigationButtons.js +44 -69
  30. package/dist/likec4diagram/ui/controls/_shared.js +1 -1
  31. package/dist/likec4diagram/ui/diagram-title/DiagramTitlePanel.js +5 -4
  32. package/dist/likec4diagram/ui/index.d.ts +2 -0
  33. package/dist/likec4diagram/ui/search/ElementsColumn.css.d.ts +15 -0
  34. package/dist/likec4diagram/ui/search/ElementsColumn.css.js +30 -0
  35. package/dist/likec4diagram/ui/search/ElementsColumn.d.ts +1 -0
  36. package/dist/likec4diagram/ui/search/ElementsColumn.js +243 -0
  37. package/dist/likec4diagram/ui/search/LikeC4Search.css.d.ts +8 -0
  38. package/dist/likec4diagram/ui/search/LikeC4Search.css.js +20 -0
  39. package/dist/likec4diagram/ui/search/LikeC4Search.d.ts +1 -0
  40. package/dist/likec4diagram/ui/search/LikeC4Search.js +265 -0
  41. package/dist/likec4diagram/ui/search/SearchInput.d.ts +1 -0
  42. package/dist/likec4diagram/ui/search/SearchInput.js +196 -0
  43. package/dist/likec4diagram/ui/search/ViewsColum.d.ts +9 -0
  44. package/dist/likec4diagram/ui/search/ViewsColum.js +130 -0
  45. package/dist/likec4diagram/ui/search/ViewsColumn.css.d.ts +5 -0
  46. package/dist/likec4diagram/ui/search/ViewsColumn.css.js +12 -0
  47. package/dist/likec4diagram/ui/search/_shared.css.d.ts +16 -0
  48. package/dist/likec4diagram/ui/search/_shared.css.js +5 -0
  49. package/dist/likec4diagram/ui/search/state.d.ts +19 -0
  50. package/dist/likec4diagram/ui/search/state.js +72 -0
  51. package/dist/likec4diagram/ui/search/utils.d.ts +3 -0
  52. package/dist/likec4diagram/ui/search/utils.js +23 -0
  53. package/dist/likec4diagram/useLayoutConstraints.js +4 -5
  54. package/dist/likec4model/useLikeC4ElementsTree.d.ts +1 -0
  55. package/dist/likec4model/useLikeC4ElementsTree.js +1 -0
  56. package/dist/overlays/Overlays.js +10 -10
  57. package/dist/overlays/element-details/ElementDetailsCard.js +20 -13
  58. package/dist/overlays/element-details/TabPanelDeployments.js +4 -2
  59. package/dist/overlays/overlay/Overlay.d.ts +6 -2
  60. package/dist/overlays/overlay/Overlay.js +18 -29
  61. package/dist/overlays/relationship-details/SelectEdge.js +1 -3
  62. package/dist/overlays/relationships-browser/RelationshipsBrowser.js +6 -5
  63. package/dist/overlays/relationships-browser/SelectElement.js +10 -6
  64. package/dist/overlays/relationships-browser/actor.d.ts +2 -2
  65. package/dist/overlays/relationships-browser/actor.js +8 -8
  66. package/dist/overlays/relationships-browser/custom/nodeTypes.js +2 -2
  67. package/dist/style.css +1 -1
  68. package/package.json +15 -14
  69. package/src/LikeC4Diagram.tsx +0 -1
  70. package/src/base/BaseXYFlow.tsx +0 -1
  71. package/src/base/index.ts +0 -2
  72. package/src/base/primitives/element/ElementNodeContainer.css.ts +4 -7
  73. package/src/base/primitives/element/ElementNodeContainer.tsx +36 -16
  74. package/src/base/primitives/element/ElementTitle.css.ts +12 -9
  75. package/src/base/primitives/element/ElementTitle.tsx +6 -2
  76. package/src/base/primitives/element/styles.css.ts +18 -40
  77. package/src/{base → components}/Link.css.ts +3 -3
  78. package/src/components/SearchControl.css.ts +69 -0
  79. package/src/components/SearchControl.tsx +33 -0
  80. package/src/context/IconRenderer.tsx +54 -1
  81. package/src/hooks/index.ts +0 -1
  82. package/src/index.ts +15 -1
  83. package/src/likec4diagram/DiagramUI.tsx +1 -8
  84. package/src/likec4diagram/XYFlow.tsx +30 -30
  85. package/src/likec4diagram/custom/edges/RelationshipEdge.tsx +0 -1
  86. package/src/likec4diagram/custom/edges/RelationshipsDropdownMenu.css.ts +1 -0
  87. package/src/likec4diagram/custom/edges/RelationshipsDropdownMenu.tsx +3 -1
  88. package/src/likec4diagram/state/assign.ts +1 -1
  89. package/src/likec4diagram/state/machine.ts +12 -10
  90. package/src/likec4diagram/ui/controls/Controls.tsx +35 -25
  91. package/src/likec4diagram/ui/controls/NavigationButtons.tsx +36 -58
  92. package/src/likec4diagram/ui/controls/styles.css.ts +3 -1
  93. package/src/likec4diagram/ui/diagram-title/DiagramTitlePanel.tsx +111 -108
  94. package/src/likec4diagram/ui/index.ts +2 -0
  95. package/src/likec4diagram/ui/notation/NotationPanel.tsx +2 -3
  96. package/src/likec4diagram/ui/search/ElementsColumn.css.ts +119 -0
  97. package/src/likec4diagram/ui/search/ElementsColumn.tsx +291 -0
  98. package/src/likec4diagram/ui/search/LikeC4Search.css.ts +117 -0
  99. package/src/likec4diagram/ui/search/LikeC4Search.tsx +312 -0
  100. package/src/likec4diagram/ui/search/SearchInput.tsx +235 -0
  101. package/src/likec4diagram/ui/search/ViewsColum.tsx +158 -0
  102. package/src/likec4diagram/ui/search/ViewsColumn.css.ts +37 -0
  103. package/src/likec4diagram/ui/search/_shared.css.ts +104 -0
  104. package/src/likec4diagram/ui/search/state.ts +76 -0
  105. package/src/likec4diagram/ui/search/utils.ts +20 -0
  106. package/src/likec4diagram/useLayoutConstraints.ts +4 -5
  107. package/src/likec4model/useLikeC4ElementsTree.ts +4 -4
  108. package/src/overlays/Overlays.tsx +32 -26
  109. package/src/overlays/element-details/ElementDetailsCard.css.ts +4 -4
  110. package/src/overlays/element-details/ElementDetailsCard.tsx +246 -244
  111. package/src/overlays/element-details/TabPanelDeployments.tsx +4 -4
  112. package/src/overlays/overlay/Overlay.css.ts +10 -9
  113. package/src/overlays/overlay/Overlay.tsx +38 -26
  114. package/src/overlays/relationship-details/SelectEdge.css.ts +1 -0
  115. package/src/overlays/relationship-details/SelectEdge.tsx +2 -4
  116. package/src/overlays/relationships-browser/RelationshipsBrowser.tsx +10 -5
  117. package/src/overlays/relationships-browser/SelectElement.tsx +88 -82
  118. package/src/overlays/relationships-browser/actor.ts +10 -10
  119. package/src/overlays/relationships-browser/custom/nodeTypes.tsx +2 -2
  120. package/dist/LikeC4Search.css.d.ts +0 -1
  121. package/dist/LikeC4Search.css.js +0 -5
  122. package/dist/LikeC4Search.d.ts +0 -2
  123. package/dist/LikeC4Search.js +0 -88
  124. package/src/LikeC4Search.css.ts +0 -12
  125. package/src/LikeC4Search.tsx +0 -91
  126. /package/dist/{base → components}/Link.css.d.ts +0 -0
  127. /package/dist/{base → components}/Link.d.ts +0 -0
  128. /package/dist/{base → components}/Link.js +0 -0
  129. /package/src/{base → components}/Link.tsx +0 -0
@@ -37,10 +37,7 @@ export const description = style({
37
37
  textOverflow: 'ellipsis',
38
38
  overflow: 'hidden',
39
39
  selectors: {
40
- [`:where(${hasIcon}) &`]: {
41
- textWrap: 'wrap',
42
- },
43
- [`:where([data-likec4-shape-size="xs"], [data-likec4-shape-size="sm"]) &`]: {
40
+ [`:where([data-likec4-shape-size="xs"]) &`]: {
44
41
  display: 'none',
45
42
  },
46
43
  },
@@ -71,17 +68,22 @@ export const technology = style({
71
68
  export const elementDataContainer = style({
72
69
  flex: '1',
73
70
  height: 'fit-content',
71
+ width: 'fit-content',
72
+ margin: '0 auto',
74
73
  display: 'flex',
75
74
  alignItems: 'center',
76
75
  justifyContent: 'center',
77
76
  flexDirection: 'row',
78
- padding: paddingSize,
77
+ paddingTop: paddingSize,
78
+ paddingBottom: paddingSize,
79
+ paddingLeft: calc(paddingSize).add('8px').toString(),
80
+ paddingRight: calc(paddingSize).add('8px').toString(),
79
81
  overflow: 'hidden',
80
82
  gap: rem(12),
81
83
  selectors: {
82
84
  ':where([data-likec4-shape="queue"], [data-likec4-shape="mobile"]) &': {
83
- paddingLeft: 40,
84
- paddingRight: 20,
85
+ paddingLeft: 46,
86
+ paddingRight: 16,
85
87
  },
86
88
  ':where([data-likec4-shape="cylinder"], [data-likec4-shape="storage"]) &': {
87
89
  paddingTop: 30,
@@ -98,7 +100,8 @@ globalStyle(`:where([data-likec4-shape-size="lg"], [data-likec4-shape-size="xl"]
98
100
 
99
101
  export const elementTextData = style({
100
102
  height: 'fit-content',
101
- width: 'max-content',
103
+ width: 'fit-content',
104
+ flex: '0 1 auto',
102
105
  display: 'flex',
103
106
  flexDirection: 'column',
104
107
  alignItems: 'stretch',
@@ -114,7 +117,7 @@ export const elementTextData = style({
114
117
  gap: rem(6),
115
118
  },
116
119
  [`:where(${hasIcon}) &`]: {
117
- minWidth: `calc(100% - ${iconSize} - ${calc(paddingSize).multiply(2).toString()})`,
120
+ minWidth: `calc(50% + ${iconSize})`,
118
121
  alignItems: 'flex-start',
119
122
  'vars': {
120
123
  [textAlign]: 'left',
@@ -6,6 +6,7 @@ import { isNumber, isTruthy } from 'remeda'
6
6
  import { IconRenderer } from '../../../context/IconRenderer'
7
7
  import { hiddenIfZoomTooSmall } from '../../../LikeC4Diagram.css'
8
8
  import type { NodeProps } from '../../types'
9
+ import { nodeSizes } from './ElementNodeContainer'
9
10
  import * as css from './ElementTitle.css'
10
11
  import { iconSize as varIconSize } from './styles.css'
11
12
 
@@ -35,6 +36,9 @@ export function ElementTitle({ id, data, iconSize }: ElementTitleProps) {
35
36
  },
36
37
  className: css.elementIcon,
37
38
  })
39
+ const size = nodeSizes(data.style).size
40
+ const isSm = size === 'sm'
41
+ const isSmOrXs = isSm || size === 'xs'
38
42
  return (
39
43
  <Box
40
44
  className={clsx(
@@ -53,7 +57,7 @@ export function ElementTitle({ id, data, iconSize }: ElementTitleProps) {
53
57
  <Text
54
58
  component="h3"
55
59
  className={clsx(css.title, 'likec4-element-title')}
56
- lineClamp={3}>
60
+ lineClamp={isSmOrXs ? 2 : 3}>
57
61
  {data.title}
58
62
  </Text>
59
63
 
@@ -69,7 +73,7 @@ export function ElementTitle({ id, data, iconSize }: ElementTitleProps) {
69
73
  <Text
70
74
  component="div"
71
75
  className={clsx(css.description, 'likec4-element-description')}
72
- lineClamp={5}>
76
+ lineClamp={isSmOrXs ? 3 : 5}>
73
77
  {data.description}
74
78
  </Text>
75
79
  )}
@@ -1,11 +1,11 @@
1
1
  import { defaultTheme } from '@likec4/core'
2
2
  import { rem } from '@mantine/core'
3
- import { createVar, globalStyle } from '@vanilla-extract/css'
3
+ import { createGlobalVar, createVar, globalStyle } from '@vanilla-extract/css'
4
4
  import { container } from './ElementNodeContainer.css'
5
5
 
6
6
  export const textSize = createVar('text-size')
7
7
  export const paddingSize = createVar('padding-size')
8
- export const iconSize = createVar('icon-size')
8
+ export const iconSize = createGlobalVar('likec4-icon-size')
9
9
 
10
10
  globalStyle(`${container}`, {
11
11
  vars: {
@@ -25,50 +25,28 @@ globalStyle(`${container}[data-likec4-shape-size="sm"]`, {
25
25
  [iconSize]: '36px',
26
26
  },
27
27
  })
28
- globalStyle(`${container}[data-likec4-shape-size="lg"], ${container}[data-likec4-shape-size="xl"]`, {
28
+ globalStyle(`${container}[data-likec4-shape-size="lg"]`, {
29
29
  vars: {
30
30
  [iconSize]: '82px',
31
31
  },
32
32
  })
33
-
34
- globalStyle(`${container}[data-likec4-text-size="xs"]`, {
35
- vars: {
36
- [textSize]: rem(defaultTheme.textSizes.xs),
37
- },
38
- })
39
- globalStyle(`${container}[data-likec4-text-size="sm"]`, {
33
+ globalStyle(`${container}[data-likec4-shape-size="xl"]`, {
40
34
  vars: {
41
- [textSize]: rem(defaultTheme.textSizes.sm),
42
- },
43
- })
44
- globalStyle(`${container}[data-likec4-text-size="lg"]`, {
45
- vars: {
46
- [textSize]: rem(defaultTheme.textSizes.lg),
47
- },
48
- })
49
- globalStyle(`${container}[data-likec4-text-size="xl"]`, {
50
- vars: {
51
- [textSize]: rem(defaultTheme.textSizes.xl),
35
+ [iconSize]: '90px',
52
36
  },
53
37
  })
54
38
 
55
- globalStyle(`${container}[data-likec4-padding="xs"]`, {
56
- vars: {
57
- [paddingSize]: rem(defaultTheme.spacing.xs),
58
- },
59
- })
60
- globalStyle(`${container}[data-likec4-padding="sm"]`, {
61
- vars: {
62
- [paddingSize]: rem(defaultTheme.spacing.sm),
63
- },
64
- })
65
- globalStyle(`${container}[data-likec4-padding="lg"]`, {
66
- vars: {
67
- [paddingSize]: rem(defaultTheme.spacing.lg),
68
- },
69
- })
70
- globalStyle(`${container}[data-likec4-padding="xl"]`, {
71
- vars: {
72
- [paddingSize]: rem(defaultTheme.spacing.xl),
73
- },
39
+ const sizes = ['xs', 'sm', 'lg', 'xl'] as const
40
+ sizes.forEach((size) => {
41
+ globalStyle(`${container}[data-likec4-text-size="${size}"]`, {
42
+ vars: {
43
+ [textSize]: rem(defaultTheme.textSizes[size]),
44
+ },
45
+ })
46
+
47
+ globalStyle(`${container}[data-likec4-padding="${size}"]`, {
48
+ vars: {
49
+ [paddingSize]: rem(defaultTheme.spacing[size]),
50
+ },
51
+ })
74
52
  })
@@ -1,6 +1,6 @@
1
1
  import { style } from '@vanilla-extract/css'
2
2
  import { mantine } from '../theme-vars'
3
- import { easings, whereDark } from '../theme-vars.css'
3
+ import { easings, transitions, whereDark } from '../theme-vars.css'
4
4
 
5
5
  export const elementLink = style({
6
6
  display: 'flex',
@@ -8,7 +8,7 @@ export const elementLink = style({
8
8
  alignItems: 'center',
9
9
  gap: 3,
10
10
  justifyContent: 'stretch',
11
- // transition: transitions.fast,
11
+ transition: transitions.fast,
12
12
  border: `1px dashed ${mantine.colors.defaultBorder}`,
13
13
  borderRadius: mantine.radius.sm,
14
14
  minHeight: 30,
@@ -38,7 +38,7 @@ export const linkIcon = style({
38
38
 
39
39
  export const linkTitleBox = style({
40
40
  flex: '1 1 100%',
41
- transition: `transform 130ms ${easings.inOut}`,
41
+ transition: `transform 100ms ${easings.inOut}`,
42
42
  selectors: {
43
43
  [`${elementLink}:hover &`]: {
44
44
  transitionTimingFunction: easings.out,
@@ -0,0 +1,69 @@
1
+ import { createVar, fallbackVar, globalStyle, style } from '@vanilla-extract/css'
2
+ import { mantine, transitions, vars, whereDark, whereLight } from '../theme-vars'
3
+
4
+ const transparent = createVar('transparent')
5
+ const bgColor = createVar('bgcolor')
6
+ export const root = style({
7
+ height: '32px',
8
+ paddingLeft: mantine.spacing.sm,
9
+ paddingRight: '4px',
10
+ borderRadius: mantine.radius.sm,
11
+ color: mantine.colors.placeholder,
12
+ boxShadow: mantine.shadows.xs,
13
+ border: '1px solid',
14
+ cursor: 'pointer',
15
+ transition: transitions.fast,
16
+ WebkitBackdropFilter: fallbackVar(vars.safariAnimationHook, 'blur(8px)'),
17
+ backdropFilter: fallbackVar(vars.safariAnimationHook, 'blur(8px)'),
18
+ backgroundColor: `color-mix(in srgb, ${bgColor}, transparent ${transparent})`,
19
+ vars: {
20
+ [transparent]: '20%',
21
+ [bgColor]: mantine.colors.default,
22
+ },
23
+ selectors: {
24
+ [`${whereLight} &`]: {
25
+ borderColor: mantine.colors.gray[4],
26
+ vars: {
27
+ [bgColor]: mantine.colors.white,
28
+ },
29
+ },
30
+ [`${whereDark} &`]: {
31
+ borderColor: mantine.colors.dark[4],
32
+ vars: {
33
+ [bgColor]: mantine.colors.dark[6],
34
+ },
35
+ },
36
+ },
37
+ ':hover': {
38
+ borderColor: mantine.colors.defaultBorder,
39
+ boxShadow: mantine.shadows.sm,
40
+ vars: {
41
+ [transparent]: '10%',
42
+ [bgColor]: mantine.colors.defaultHover,
43
+ },
44
+ },
45
+ })
46
+ globalStyle(`${root} .tabler-icon`, {
47
+ color: mantine.colors.text,
48
+ })
49
+
50
+ export const shortcut = style({
51
+ fontSize: '11px',
52
+ lineHeight: 1,
53
+ padding: '4px 7px',
54
+ borderRadius: mantine.radius.sm,
55
+ border: '1px solid',
56
+ fontWeight: 'bold',
57
+ selectors: {
58
+ [`${whereLight} &`]: {
59
+ color: mantine.colors.gray[7],
60
+ borderColor: mantine.colors.gray[2],
61
+ backgroundColor: `color-mix(in srgb, ${mantine.colors.gray[2]}, transparent 20%)`,
62
+ },
63
+ [`${whereDark} &`]: {
64
+ color: mantine.colors.dark[0],
65
+ borderColor: mantine.colors.dark[7],
66
+ backgroundColor: mantine.colors.dark[8],
67
+ },
68
+ },
69
+ })
@@ -0,0 +1,33 @@
1
+ import {
2
+ type BoxProps,
3
+ type ElementProps,
4
+ type UnstyledButtonProps,
5
+ Box,
6
+ Group,
7
+ rem,
8
+ Text,
9
+ UnstyledButton,
10
+ } from '@mantine/core'
11
+ import { IconSearch } from '@tabler/icons-react'
12
+ import { isMacOs } from '@xyflow/system'
13
+ import cx from 'clsx'
14
+ import * as classes from './SearchControl.css'
15
+
16
+ interface SearchControlProps extends UnstyledButtonProps, ElementProps<'button'> {}
17
+
18
+ export function SearchControl({ className, ...others }: SearchControlProps) {
19
+ const isMac = isMacOs()
20
+ return (
21
+ <UnstyledButton {...others} className={cx(classes.root, className)}>
22
+ <Group gap="xs">
23
+ <IconSearch style={{ width: rem(15), height: rem(15) }} stroke={2} />
24
+ <Text fz="sm" fw="500" pr={50}>
25
+ Search
26
+ </Text>
27
+ <Text fw={700} className={classes.shortcut}>
28
+ {isMac ? '⌘ + K' : 'Ctrl + K'}
29
+ </Text>
30
+ </Group>
31
+ </UnstyledButton>
32
+ )
33
+ }
@@ -1,5 +1,16 @@
1
+ import type { ElementShape } from '@likec4/core'
2
+ import {
3
+ type Icon,
4
+ type IconProps,
5
+ IconBrowser,
6
+ IconCylinder,
7
+ IconDeviceMobile,
8
+ IconRectangularPrism,
9
+ IconReorder,
10
+ IconUser,
11
+ } from '@tabler/icons-react'
1
12
  import clsx from 'clsx'
2
- import { type ReactNode, createContext, useContext } from 'react'
13
+ import { type ForwardRefExoticComponent, type ReactNode, type RefAttributes, createContext, useContext } from 'react'
3
14
  import type { ElementIconRenderer } from '../LikeC4Diagram.props'
4
15
 
5
16
  const IconRendererContext = createContext<ElementIconRenderer | null>(null)
@@ -43,3 +54,45 @@ export function IconRenderer({
43
54
  </div>
44
55
  )
45
56
  }
57
+
58
+ const ShapeIcons = {
59
+ browser: IconBrowser,
60
+ cylinder: IconCylinder,
61
+ mobile: IconDeviceMobile,
62
+ person: IconUser,
63
+ queue: IconReorder,
64
+ rectangle: IconRectangularPrism,
65
+ storage: IconCylinder,
66
+ } satisfies Record<ElementShape, ForwardRefExoticComponent<IconProps & RefAttributes<Icon>>>
67
+
68
+ export function IconOrShapeRenderer({
69
+ element,
70
+ className,
71
+ }: {
72
+ element: {
73
+ id: string
74
+ title: string
75
+ shape: ElementShape
76
+ icon?: string | null | undefined
77
+ }
78
+ className: string
79
+ }) {
80
+ const icon = IconRenderer({
81
+ element,
82
+ className,
83
+ })
84
+ if (icon) {
85
+ return icon
86
+ }
87
+ const ShapeIcon = ShapeIcons[element.shape]
88
+ return (
89
+ <div
90
+ className={clsx(
91
+ className,
92
+ 'likec4-shape-icon',
93
+ )}
94
+ >
95
+ <ShapeIcon />
96
+ </div>
97
+ )
98
+ }
@@ -1,5 +1,4 @@
1
1
  export { useCurrentViewId } from './useCurrentViewId'
2
- export type { DiagramContext } from './useDiagramContext'
3
2
  export { useMantinePortalProps } from './useMantinePortalProps'
4
3
  export { useSetState } from './useSetState'
5
4
  export { useUpdateEffect } from './useUpdateEffect'
package/src/index.ts CHANGED
@@ -16,7 +16,21 @@ export { useSetState } from './hooks/useSetState'
16
16
  export { useUpdateEffect } from './hooks/useUpdateEffect'
17
17
  export { vars, xyvars } from './theme-vars'
18
18
 
19
- export type * from './LikeC4Diagram.props'
19
+ export type {
20
+ ChangeEvent,
21
+ DiagramNodeWithNavigate,
22
+ ElementIconRenderer,
23
+ LikeC4ColorScheme,
24
+ LikeC4DiagramEventHandlers,
25
+ LikeC4DiagramProperties,
26
+ OnCanvasClick,
27
+ OnChange,
28
+ OnEdgeClick,
29
+ OnNavigateTo,
30
+ OnNodeClick,
31
+ OpenSourceParams,
32
+ WhereOperator,
33
+ } from './LikeC4Diagram.props'
20
34
 
21
35
  export { StaticLikeC4Diagram } from './StaticLikeC4Diagram'
22
36
  export type { StaticLikeC4DiagramProps } from './StaticLikeC4Diagram'
@@ -1,19 +1,12 @@
1
- import { memo } from 'react'
2
1
  import { IfEnabled } from '../context'
3
- import { LikeC4Search } from '../LikeC4Search'
4
2
  import { Overlays } from '../overlays/Overlays'
5
- import { Controls, DiagramTitlePanel } from './ui'
6
- import NotationPanel from './ui/notation'
7
- import { DynamicViewWalkthrough } from './ui/walkthrough/DynamicViewWalkthrough'
3
+ import { Controls, DiagramTitlePanel, DynamicViewWalkthrough, NotationPanel } from './ui'
8
4
 
9
5
  export function DiagramUI() {
10
6
  return (
11
7
  <>
12
8
  <Controls />
13
9
  <Overlays />
14
- <IfEnabled feature="Search">
15
- <LikeC4Search />
16
- </IfEnabled>
17
10
  <IfEnabled feature="ViewTitle">
18
11
  <DiagramTitlePanel />
19
12
  </IfEnabled>
@@ -2,6 +2,7 @@ import { type EdgeId, type NodeId, nonNullable } from '@likec4/core'
2
2
  import { useCallbackRef } from '@mantine/hooks'
3
3
  import clsx from 'clsx'
4
4
  import { shallowEqual } from 'fast-equals'
5
+ import { memo } from 'react'
5
6
  import type { EnforceOptional } from 'type-fest/source/enforce-optional'
6
7
  import { BaseXYFlow } from '../base/BaseXYFlow'
7
8
  import { useDiagramEventHandlers } from '../context'
@@ -9,6 +10,7 @@ import { useDiagram } from '../hooks/useDiagram'
9
10
  import { useDiagramContext } from '../hooks/useDiagramContext'
10
11
  import type { LikeC4DiagramProperties } from '../LikeC4Diagram.props'
11
12
  import { edgeTypes, nodeTypes } from './custom'
13
+ import { DiagramUI } from './DiagramUI'
12
14
  import type { DiagramContext } from './state/machine'
13
15
  import type { Types } from './types'
14
16
  import { useLayoutConstraints } from './useLayoutConstraints'
@@ -43,7 +45,7 @@ type Picked = EnforceOptional<
43
45
  >
44
46
  export type LikeC4DiagramXYFlowProps = Required<Picked>
45
47
 
46
- export const LikeC4DiagramXYFlow = ({ background, ...rest }: LikeC4DiagramXYFlowProps) => {
48
+ export const LikeC4DiagramXYFlow = memo<LikeC4DiagramXYFlowProps>(({ background, nodesDraggable, nodesSelectable }) => {
47
49
  const diagram = useDiagram()
48
50
  const {
49
51
  initialized,
@@ -80,10 +82,10 @@ export const LikeC4DiagramXYFlow = ({ background, ...rest }: LikeC4DiagramXYFlow
80
82
  nodeTypes={nodeTypes}
81
83
  edgeTypes={edgeTypes}
82
84
  onNodesChange={useCallbackRef((changes) => {
83
- diagram.send({ type: 'xyflow.applyNodeChages', changes })
85
+ diagram.send({ type: 'xyflow.applyNodeChanges', changes })
84
86
  })}
85
87
  onEdgesChange={useCallbackRef((changes) => {
86
- diagram.send({ type: 'xyflow.applyEdgeChages', changes })
88
+ diagram.send({ type: 'xyflow.applyEdgeChanges', changes })
87
89
  })}
88
90
  background={initialized ? background : 'transparent'}
89
91
  // Fitview is handled in onInit
@@ -105,6 +107,7 @@ export const LikeC4DiagramXYFlow = ({ background, ...rest }: LikeC4DiagramXYFlow
105
107
  })}
106
108
  onDoubleClick={useCallbackRef(e => {
107
109
  e.stopPropagation()
110
+ e.preventDefault()
108
111
  diagram.send({ type: 'xyflow.paneDblClick' })
109
112
  onCanvasDblClick?.(e as any)
110
113
  })}
@@ -115,34 +118,31 @@ export const LikeC4DiagramXYFlow = ({ background, ...rest }: LikeC4DiagramXYFlow
115
118
  onInit={useCallbackRef((instance) => {
116
119
  diagram.send({ type: 'xyflow.init', instance })
117
120
  })}
118
- {...onNodeContextMenu && {
119
- onNodeContextMenu: useCallbackRef((event, node) => {
120
- const diagramNode = nonNullable(
121
- diagram.findDiagramNode(node.id as NodeId),
122
- `diagramNode ${node.id} not found`,
123
- )
124
- onNodeContextMenu(diagramNode, event)
125
- }),
126
- }}
127
- {...onEdgeContextMenu && {
128
- onEdgeContextMenu: useCallbackRef((event, edge) => {
129
- const diagramEdge = nonNullable(
130
- diagram.findDiagramEdge(edge.id as EdgeId),
131
- `diagramEdge ${edge.id} not found`,
132
- )
133
- onEdgeContextMenu(diagramEdge, event)
134
- }),
135
- }}
136
- {...onCanvasContextMenu && {
137
- onPaneContextMenu: useCallbackRef((event) => {
138
- onCanvasContextMenu(event as any)
139
- }),
140
- }}
121
+ onNodeContextMenu={useCallbackRef((event, node) => {
122
+ const diagramNode = nonNullable(
123
+ diagram.findDiagramNode(node.id as NodeId),
124
+ `diagramNode ${node.id} not found`,
125
+ )
126
+ onNodeContextMenu?.(diagramNode, event)
127
+ })}
128
+ onEdgeContextMenu={useCallbackRef((event, edge) => {
129
+ const diagramEdge = nonNullable(
130
+ diagram.findDiagramEdge(edge.id as EdgeId),
131
+ `diagramEdge ${edge.id} not found`,
132
+ )
133
+ onEdgeContextMenu?.(diagramEdge, event)
134
+ })}
135
+ onPaneContextMenu={useCallbackRef((event) => {
136
+ onCanvasContextMenu?.(event as any)
137
+ })}
141
138
  {...enableFitView && {
142
139
  onViewportResize,
143
140
  }}
144
- {...(notReadOnly && rest.nodesDraggable && layoutConstraints)}
145
- {...props}
146
- {...rest} />
141
+ nodesDraggable={nodesDraggable}
142
+ nodesSelectable={nodesSelectable}
143
+ {...(notReadOnly && nodesDraggable && layoutConstraints)}
144
+ {...props}>
145
+ <DiagramUI />
146
+ </BaseXYFlow>
147
147
  )
148
- }
148
+ }, shallowEqual)
@@ -1,6 +1,5 @@
1
1
  import {
2
2
  type EdgeId,
3
- type NodeId,
4
3
  isStepEdgeId,
5
4
  nonNullable,
6
5
  } from '@likec4/core'
@@ -2,6 +2,7 @@ import { style } from '@vanilla-extract/css'
2
2
  import { mantine, vars } from '../../../theme-vars'
3
3
 
4
4
  export const menuDropdown = style({
5
+ overflowY: 'scroll',
5
6
  minWidth: 250,
6
7
  maxWidth: 'min(90vw, 500px)',
7
8
  })
@@ -27,7 +27,7 @@ import { IconArrowRight, IconFileSymlink, IconInfoCircle, IconZoomScan } from '@
27
27
  import clsx from 'clsx'
28
28
  import { type MouseEventHandler, type PropsWithChildren, forwardRef, Fragment, useCallback } from 'react'
29
29
  import { filter, isTruthy, map, partition, pipe } from 'remeda'
30
- import { Link } from '../../../base/Link'
30
+ import { Link } from '../../../components/Link'
31
31
  import { IfEnabled, useDiagramEventHandlers, useEnabledFeature } from '../../../context'
32
32
  import { useMantinePortalProps } from '../../../hooks'
33
33
  import { useDiagram } from '../../../hooks/useDiagram'
@@ -127,9 +127,11 @@ export function RelationshipsDropdownMenu({
127
127
  closeOnClickOutside
128
128
  clickOutsideEvents={['pointerdown', 'mousedown', 'click']}
129
129
  closeOnEscape
130
+ keepMounted={false}
130
131
  closeOnItemClick={false}
131
132
  disabled={disabled}
132
133
  position="bottom-start"
134
+ middlewares={{ size: { padding: 8 } }}
133
135
  {...portalProps}
134
136
  >
135
137
  <MenuTarget>
@@ -321,7 +321,7 @@ export function resetEdgeControlPoints({ context }: ActionArg): Partial<DiagramC
321
321
  }
322
322
 
323
323
  // Edge is a loop
324
- if (source == target) {
324
+ if (source === target) {
325
325
  const loopSize = 80
326
326
  const centerOfTopBoundary = new VectorImpl(0, source.height || 0)
327
327
  .mul(-0.5)
@@ -4,7 +4,6 @@ import {
4
4
  type EdgeId,
5
5
  type Fqn,
6
6
  type NodeId,
7
- type RelationId,
8
7
  type StepEdgeId,
9
8
  type ViewChange,
10
9
  type ViewId,
@@ -136,8 +135,8 @@ export namespace DiagramContext {
136
135
  export type Events =
137
136
  | HotKeyEvent
138
137
  | { type: 'xyflow.init'; instance: ReactFlowInstance<Types.Node, Types.Edge> }
139
- | { type: 'xyflow.applyNodeChages'; changes: NodeChange<Types.Node>[] }
140
- | { type: 'xyflow.applyEdgeChages'; changes: EdgeChange<Types.Edge>[] }
138
+ | { type: 'xyflow.applyNodeChanges'; changes: NodeChange<Types.Node>[] }
139
+ | { type: 'xyflow.applyEdgeChanges'; changes: EdgeChange<Types.Edge>[] }
141
140
  | { type: 'xyflow.viewportMoved'; viewport: Viewport; manually: boolean }
142
141
  | { type: 'xyflow.nodeClick'; node: Types.Node }
143
142
  | { type: 'xyflow.edgeClick'; edge: Types.Edge }
@@ -634,7 +633,7 @@ export const diagramMachine = setup({
634
633
  pending: {
635
634
  entry: enqueueActions(({ enqueue }) => {
636
635
  enqueue.sendTo(c => c.context.syncLayoutActorRef, { type: 'stop' })
637
- enqueue.stopChild('layout')
636
+ enqueue.stopChild('syncLayout')
638
637
  enqueue({
639
638
  type: 'trigger:NavigateTo',
640
639
  params: ({ context }) => ({
@@ -657,15 +656,16 @@ export const diagramMachine = setup({
657
656
  },
658
657
  },
659
658
  })
659
+ enqueue.raise({ type: 'fitDiagram' }, { id: 'fitDiagram', delay: 80 })
660
660
  } else {
661
661
  enqueue({
662
662
  type: 'xyflow:setViewportCenter',
663
663
  params: getBBoxCenter(event.view.bounds),
664
664
  })
665
+ enqueue.raise({ type: 'fitDiagram', duration: 200 }, { id: 'fitDiagram', delay: 25 })
665
666
  }
666
667
  enqueue.assign(updateNavigationHistory)
667
668
  enqueue.assign(mergeXYNodesEdges)
668
- enqueue.raise({ type: 'fitDiagram' }, { id: 'fitDiagram', delay: 75 })
669
669
  }),
670
670
  target: 'done',
671
671
  },
@@ -765,12 +765,12 @@ export const diagramMachine = setup({
765
765
  },
766
766
  },
767
767
  on: {
768
- 'xyflow.applyNodeChages': {
768
+ 'xyflow.applyNodeChanges': {
769
769
  actions: assign({
770
770
  xynodes: ({ context, event }) => applyNodeChanges(event.changes, context.xynodes),
771
771
  }),
772
772
  },
773
- 'xyflow.applyEdgeChages': {
773
+ 'xyflow.applyEdgeChanges': {
774
774
  actions: assign({
775
775
  xyedges: ({ context, event }) => applyEdgeChanges(event.changes, context.xyedges),
776
776
  }),
@@ -838,17 +838,19 @@ export const diagramMachine = setup({
838
838
  },
839
839
  },
840
840
  })
841
+ enqueue.raise({ type: 'fitDiagram' }, { id: 'fitDiagram', delay: 80 })
841
842
  } else {
842
843
  enqueue({
843
844
  type: 'xyflow:setViewportCenter',
844
845
  params: getBBoxCenter(event.view.bounds),
845
846
  })
847
+ enqueue.raise({ type: 'fitDiagram', duration: 200 }, { id: 'fitDiagram', delay: 25 })
846
848
  }
847
849
  } else {
848
850
  enqueue.sendTo(c => c.context.syncLayoutActorRef, { type: 'synced' })
849
- }
850
- if (isAnotherView || !context.viewportChangedManually) {
851
- enqueue.raise({ type: 'fitDiagram' }, { id: 'fitDiagram', delay: 90 })
851
+ if (!context.viewportChangedManually) {
852
+ enqueue.raise({ type: 'fitDiagram' }, { id: 'fitDiagram', delay: 50 })
853
+ }
852
854
  }
853
855
  enqueue.assign(mergeXYNodesEdges)
854
856
  enqueue.assign({