@likec4/diagram 1.5.0 → 1.6.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 (34) hide show
  1. package/LICENSE +1 -1
  2. package/dist/LikeC4Diagram.d.ts +1 -1
  3. package/dist/LikeC4Diagram.props.d.ts +18 -3
  4. package/dist/StaticLikeC4Diagram.d.ts +1 -1
  5. package/dist/index.mjs +3097 -3090
  6. package/dist/state/DiagramContext.d.ts +18 -9
  7. package/dist/state/diagramStore.d.ts +12 -2
  8. package/dist/style.css +149 -85
  9. package/dist/xyflow/nodes/element/ElementShapeSvg.d.ts +5 -0
  10. package/dist/xyflow/nodes/element/element.css.d.ts +4 -2
  11. package/package.json +13 -13
  12. package/src/LikeC4Diagram.props.ts +21 -3
  13. package/src/LikeC4Diagram.tsx +4 -1
  14. package/src/StaticLikeC4Diagram.tsx +1 -0
  15. package/src/state/DiagramContext.tsx +1 -1
  16. package/src/state/diagramStore.ts +8 -1
  17. package/src/ui/BackwardForwardPanel.tsx +1 -1
  18. package/src/xyflow/FitviewOnDiagramChange.tsx +1 -1
  19. package/src/xyflow/SelectEdgesOnNodeFocus.tsx +1 -1
  20. package/src/xyflow/SyncWithDiagram.tsx +1 -1
  21. package/src/xyflow/XYFlow.tsx +2 -1
  22. package/src/xyflow/XYFlowEvents.tsx +1 -1
  23. package/src/xyflow/XYFlowInner.tsx +1 -1
  24. package/src/xyflow/edges/RelationshipEdge.tsx +3 -3
  25. package/src/xyflow/edges/edges.css.ts +6 -6
  26. package/src/xyflow/hooks/useLayoutConstraints.ts +1 -1
  27. package/src/xyflow/nodes/compound/CompoundNode.tsx +1 -1
  28. package/src/xyflow/nodes/element/ElementLink.tsx +2 -1
  29. package/src/xyflow/nodes/element/ElementNode.tsx +70 -22
  30. package/src/xyflow/nodes/element/ElementShapeSvg.tsx +2 -2
  31. package/src/xyflow/nodes/element/element.css.ts +126 -54
  32. package/src/xyflow/nodes/shared/NavigateToBtn.tsx +1 -1
  33. package/dist/xyflow/nodes/element/ElementIcon.d.ts +0 -7
  34. package/src/xyflow/nodes/element/ElementIcon.tsx +0 -33
@@ -1,20 +1,19 @@
1
1
  import { rem } from '@mantine/core'
2
2
  import { createVar, fallbackVar, globalStyle, keyframes, style } from '@vanilla-extract/css'
3
- import { calc } from '@vanilla-extract/css-utils'
4
3
  import { mantine } from '../../../mantine.css'
5
4
  import { vars } from '../../../theme.css'
6
5
 
7
6
  export const stokeFillMix = createVar('stroke-fill-mix')
8
- const elPadding = createVar('el-padding')
9
- // assignVars
10
7
 
11
8
  export const container = style({
12
9
  width: '100%',
13
10
  height: '100%',
14
11
  padding: 0,
15
12
  margin: 0,
13
+ flex: '1',
16
14
  display: 'flex',
17
- alignItems: 'stretch',
15
+ alignItems: 'center',
16
+ justifyContent: 'center',
18
17
  vars: {
19
18
  [stokeFillMix]: `color-mix(in srgb, ${vars.element.stroke} 90%, ${vars.element.fill})`
20
19
  },
@@ -50,10 +49,10 @@ globalStyle(`.react-flow__node-element:has(${dimmed})`, {
50
49
 
51
50
  const indicatorKeyframes = keyframes({
52
51
  'from': {
53
- opacity: 0.6
52
+ strokeOpacity: 0.8
54
53
  },
55
54
  'to': {
56
- opacity: 0.4
55
+ strokeOpacity: 0.5
57
56
  }
58
57
  })
59
58
 
@@ -70,10 +69,10 @@ export const indicator = style({
70
69
  fill: 'none',
71
70
  transformOrigin: 'center center',
72
71
  strokeWidth: 6,
73
- animationDuration: '800ms',
72
+ animationDuration: '1s',
74
73
  animationIterationCount: 'infinite',
75
74
  animationDirection: 'alternate',
76
- opacity: 0.6,
75
+ strokeOpacity: 0.8,
77
76
  visibility: 'hidden',
78
77
  vars: {
79
78
  [indicatorStroke]: vars.element.loContrast
@@ -93,7 +92,7 @@ export const indicator = style({
93
92
  },
94
93
  [`:where([data-mantine-color-scheme='light']) &`]: {
95
94
  vars: {
96
- [indicatorStroke]: `color-mix(in srgb, ${vars.element.fill} 60%, ${vars.element.loContrast})`
95
+ [indicatorStroke]: `color-mix(in srgb, ${vars.element.fill} 50%, #3c3c3c)`
97
96
  }
98
97
  },
99
98
  [`${dimmed} &`]: {
@@ -115,15 +114,20 @@ export const fillMixStroke = style({
115
114
  fill: stokeFillMix
116
115
  })
117
116
 
117
+ export const hasIcon = style({})
118
+
119
+ const textAlign = createVar('text-align')
120
+ const iconSize = createVar('icon-size')
121
+
118
122
  export const title = style({
119
123
  flex: '0 0 auto',
120
124
  fontFamily: vars.element.font,
121
125
  fontOpticalSizing: 'auto',
122
126
  fontStyle: 'normal',
123
- textAlign: 'center',
127
+ textAlign: textAlign,
124
128
  fontWeight: 500,
125
- fontSize: rem(19.5),
126
- lineHeight: 1.2,
129
+ fontSize: 19,
130
+ lineHeight: 1.15,
127
131
  textWrap: 'balance',
128
132
  color: vars.element.hiContrast
129
133
  })
@@ -134,14 +138,19 @@ export const description = style({
134
138
  fontOpticalSizing: 'auto',
135
139
  fontStyle: 'normal',
136
140
  fontWeight: 400,
137
- fontSize: rem(14),
138
- lineHeight: 1.25,
139
- textAlign: 'center',
141
+ fontSize: 14,
142
+ lineHeight: 1.2,
143
+ textAlign: textAlign,
140
144
  textWrap: 'pretty',
141
145
  color: vars.element.loContrast,
142
146
  whiteSpaceCollapse: 'preserve-breaks',
143
147
  textOverflow: 'ellipsis',
144
- overflow: 'hidden'
148
+ overflow: 'hidden',
149
+ selectors: {
150
+ [`:where(${hasIcon}) &`]: {
151
+ textWrap: 'wrap'
152
+ }
153
+ }
145
154
  })
146
155
 
147
156
  export const technology = style({
@@ -150,9 +159,9 @@ export const technology = style({
150
159
  fontOpticalSizing: 'auto',
151
160
  fontStyle: 'normal',
152
161
  fontWeight: 400,
153
- fontSize: rem(12),
162
+ fontSize: 12,
154
163
  lineHeight: 1.125,
155
- textAlign: 'center',
164
+ textAlign: textAlign,
156
165
  textWrap: 'balance',
157
166
  opacity: 0.92,
158
167
  color: vars.element.loContrast,
@@ -163,68 +172,131 @@ export const technology = style({
163
172
  }
164
173
  })
165
174
 
166
- export const element = style({
175
+ export const elementDataContainer = style({
167
176
  flex: '1',
168
- padding: elPadding,
177
+ height: 'fit-content',
169
178
  display: 'flex',
170
- flexDirection: 'column',
171
- alignItems: 'stretch',
179
+ alignItems: 'center',
172
180
  justifyContent: 'center',
181
+ flexDirection: 'row',
182
+ padding: rem(24),
173
183
  overflow: 'hidden',
174
- gap: rem(8),
175
- 'vars': {
176
- [elPadding]: rem(24)
184
+ gap: rem(10),
185
+ vars: {
186
+ [iconSize]: '48px'
177
187
  },
178
188
  selectors: {
179
- [`&:has(${description}):has(${technology})`]: {
180
- gap: rem(6)
181
- },
182
189
  ':where([data-likec4-shape="queue"], [data-likec4-shape="mobile"]) &': {
183
- paddingLeft: rem(40),
184
- paddingRight: rem(20)
190
+ paddingLeft: 40,
191
+ paddingRight: 20
185
192
  },
186
193
  ':where([data-likec4-shape="cylinder"], [data-likec4-shape="storage"]) &': {
187
- paddingTop: rem(34)
194
+ paddingTop: 30
188
195
  },
189
196
  ':where([data-likec4-shape="browser"]) &': {
190
- paddingTop: rem(32),
191
- paddingBottom: rem(28)
197
+ paddingTop: 32,
198
+ paddingBottom: 28
199
+ },
200
+ [`&:is(${hasIcon})`]: {
201
+ paddingRight: 20
202
+ },
203
+ [`${container}:not(:is([data-likec4-shape="queue"],[data-likec4-shape="mobile"])) &:is(${hasIcon})`]: {
204
+ paddingLeft: 20
205
+ },
206
+ [`&:has(${description}, ${technology})`]: {
207
+ alignItems: 'flex-start',
208
+ gap: rem(16),
209
+ vars: {
210
+ [iconSize]: '60px'
211
+ }
192
212
  }
193
213
  }
194
214
  })
195
215
 
196
- export const iconOffsetY = createVar('offset-y')
197
-
198
- const iconMaxH = createVar('max-h')
199
-
200
- export const elementIcon = style({
201
- flex: `0 1 ${iconMaxH}`,
202
- maxHeight: iconMaxH,
203
- position: 'relative',
204
- userSelect: 'none',
205
- pointerEvents: 'none',
206
- overflow: 'visible',
207
- mixBlendMode: 'hard-light',
216
+ export const elementTextData = style({
217
+ height: 'fit-content',
218
+ width: 'max-content',
219
+ display: 'flex',
220
+ flexDirection: 'column',
221
+ alignItems: 'stretch',
222
+ justifyContent: 'center',
223
+ flexWrap: 'nowrap',
224
+ overflow: 'hidden',
225
+ gap: rem(8),
208
226
  'vars': {
209
- [iconMaxH]: calc(iconOffsetY).subtract('12px').toString()
227
+ [textAlign]: 'center'
228
+ },
229
+ selectors: {
230
+ [`&:has(${description}):has(${technology})`]: {
231
+ gap: rem(6)
232
+ },
233
+ [`:where(${hasIcon}) &`]: {
234
+ minWidth: 'calc(100% - 160px)',
235
+ alignItems: 'flex-start',
236
+ 'vars': {
237
+ [textAlign]: 'left'
238
+ }
239
+ }
210
240
  }
211
241
  })
212
242
 
213
- globalStyle(`${elementIcon} img`, {
214
- position: 'absolute',
215
- left: 0,
216
- bottom: 4,
243
+ export const elementIcon = style({
244
+ flex: `0 0 ${iconSize}`,
245
+ height: iconSize,
246
+ width: iconSize,
247
+ display: 'flex',
248
+ alignItems: 'center',
249
+ justifyContent: 'center',
250
+ mixBlendMode: 'hard-light'
251
+ })
252
+ globalStyle(`${elementIcon} svg, ${elementIcon} img`, {
217
253
  width: '100%',
218
254
  height: 'auto',
219
- objectFit: 'contain',
220
- maxHeight: iconOffsetY
255
+ maxHeight: '100%',
256
+ pointerEvents: 'none',
257
+ filter: `
258
+ drop-shadow(0 0 3px rgb(0 0 0 / 12%))
259
+ drop-shadow(0 1px 8px rgb(0 0 0 / 8%))
260
+ drop-shadow(1px 1px 16px rgb(0 0 0 / 3%))
261
+ `
262
+ })
263
+ globalStyle(`${elementIcon} img`, {
264
+ objectFit: 'contain'
221
265
  })
222
266
 
223
267
  const filterShadow = createVar('filter-shadow')
224
268
 
269
+ export const cssShapeSvgMultiple = style({
270
+ top: 0,
271
+ left: 0,
272
+ position: 'absolute',
273
+ pointerEvents: 'none',
274
+ transformOrigin: '50% 50%',
275
+ fill: vars.element.fill,
276
+ stroke: 'none',
277
+ zIndex: -1,
278
+ transition: 'opacity 500ms ease-out',
279
+ transform: 'translate(8px,10px)',
280
+ opacity: 0.5,
281
+ selectors: {
282
+ [`:where(.react-flow__node.selected, .react-flow__node:focus-visible, ${container}:focus-visible) &`]: {
283
+ visibility: 'hidden'
284
+ },
285
+ ':where([data-likec4-shape="cylinder"], [data-likec4-shape="storage"]) &': {
286
+ transform: 'translate(8px,8px)'
287
+ },
288
+ ':where([data-likec4-shape="queue"]) &': {
289
+ transform: 'translate(-10px,8px)'
290
+ },
291
+ ':where([data-hovered="true"]) &': {
292
+ transition: 'opacity 300ms ease-in',
293
+ opacity: 0.2
294
+ }
295
+ }
296
+ })
225
297
  export const cssShapeSvg = style({
226
- top: '0px',
227
- left: '0px',
298
+ top: 0,
299
+ left: 0,
228
300
  position: 'absolute',
229
301
  pointerEvents: 'none',
230
302
  fill: vars.element.fill,
@@ -1,7 +1,7 @@
1
1
  import { ActionIcon } from '@mantine/core'
2
2
  import { IconZoomScan } from '@tabler/icons-react'
3
3
  import clsx from 'clsx'
4
- import { useDiagramStoreApi } from '../../../state/hooks'
4
+ import { useDiagramStoreApi } from '../../../state/useDiagramStore'
5
5
 
6
6
  export type NavigateToBtnProps = {
7
7
  xynodeId: string
@@ -1,7 +0,0 @@
1
- import { XYFlowNode } from '../../types';
2
-
3
- type ElementIconProps = {
4
- node: XYFlowNode['data']['element'];
5
- };
6
- export declare function ElementIcon({ node: { icon, title, labelBBox } }: ElementIconProps): import("react/jsx-runtime").JSX.Element | null;
7
- export {};
@@ -1,33 +0,0 @@
1
- import { assignInlineVars } from '@vanilla-extract/dynamic'
2
- import clsx from 'clsx'
3
- import type { XYFlowNode } from '../../types'
4
- import { elementIcon, iconOffsetY } from './element.css'
5
-
6
- type ElementIconProps = {
7
- node: XYFlowNode['data']['element']
8
- }
9
-
10
- export function ElementIcon({
11
- node: {
12
- icon,
13
- title,
14
- labelBBox
15
- }
16
- }: ElementIconProps) {
17
- if (!icon) {
18
- return null
19
- }
20
-
21
- return (
22
- <div
23
- className={clsx(elementIcon, 'likec4-element-icon')}
24
- style={assignInlineVars({ iconOffsetY }, {
25
- iconOffsetY: labelBBox.y + 'px'
26
- })}>
27
- <img
28
- src={icon}
29
- alt={title}
30
- />
31
- </div>
32
- )
33
- }