@likec4/diagram 1.0.0-next.2 → 1.0.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.
- package/dist/LikeC4Diagram.css.d.ts +0 -1
- package/dist/LikeC4Diagram.d.ts +1 -2
- package/dist/LikeC4Diagram.props.d.ts +24 -25
- package/dist/StaticLikeC4Diagram.d.ts +1 -2
- package/dist/globals.css.d.ts +1 -0
- package/dist/hooks/index.d.ts +2 -3
- package/dist/hooks/useSetState.d.ts +3 -0
- package/dist/hooks/useUpdateEffect.d.ts +4 -0
- package/dist/icons/Clipboard.d.ts +0 -1
- package/dist/icons/Link.d.ts +0 -1
- package/dist/icons/ZoomIn.d.ts +0 -1
- package/dist/icons/index.d.ts +0 -1
- package/dist/index.d.ts +1 -4
- package/dist/index.mjs +9388 -0
- package/dist/mantine/EnsureMantine.d.ts +2 -4
- package/dist/mantine/theme.d.ts +3 -0
- package/dist/mantine.css.d.ts +1 -0
- package/dist/state/DiagramContext.d.ts +386 -0
- package/dist/state/WhenInitialized.d.ts +3 -0
- package/dist/state/diagramStore.d.ts +428 -0
- package/dist/state/index.d.ts +4 -2
- package/dist/state/useDiagramStore.d.ts +387 -0
- package/dist/style.css +1212 -1
- package/dist/theme.css.d.ts +16 -2
- package/dist/ui/Camera.d.ts +0 -1
- package/dist/ui/KeepAspectRatio.css.d.ts +0 -1
- package/dist/ui/KeepAspectRatio.d.ts +0 -1
- package/dist/ui/OptionsPanel.css.d.ts +0 -1
- package/dist/ui/OptionsPanel.d.ts +0 -1
- package/dist/ui/index.d.ts +0 -1
- package/dist/ui/options/NodeOptions.d.ts +0 -1
- package/dist/xyflow/FitviewOnDiagramChange.d.ts +1 -5
- package/dist/xyflow/SyncWithDiagram.d.ts +5 -0
- package/dist/xyflow/XYFlowBackground.d.ts +0 -1
- package/dist/xyflow/XYFlowEvents.d.ts +5 -15
- package/dist/xyflow/XYFlowWrapper.d.ts +4 -5
- package/dist/xyflow/const.d.ts +1 -2
- package/dist/xyflow/diagram-to-xyflow.d.ts +4 -2
- package/dist/xyflow/edges/RelationshipEdge.d.ts +7 -7
- package/dist/xyflow/edges/edges.css.d.ts +2 -1
- package/dist/xyflow/edges/utils.d.ts +3 -4
- package/dist/xyflow/hooks/index.d.ts +2 -37
- package/dist/xyflow/hooks/{use-layout-/321/201onstraints.d.ts → useLayoutConstraints.d.ts} +2 -4
- package/dist/xyflow/hooks/useXYFlow.d.ts +266 -0
- package/dist/xyflow/nodes/compound/CompoundNode.css.d.ts +0 -1
- package/dist/xyflow/nodes/compound/CompoundNode.d.ts +0 -1
- package/dist/xyflow/nodes/compound/index.d.ts +0 -1
- package/dist/xyflow/nodes/element/ElementIcon.d.ts +1 -2
- package/dist/xyflow/nodes/element/ElementLink.css.d.ts +0 -1
- package/dist/xyflow/nodes/element/ElementLink.d.ts +1 -2
- package/dist/xyflow/nodes/element/ElementNode.d.ts +1 -3
- package/dist/xyflow/nodes/element/ElementShapeSvg.d.ts +2 -3
- package/dist/xyflow/nodes/element/element.css.d.ts +0 -1
- package/dist/xyflow/nodes/element/index.d.ts +1 -2
- package/dist/xyflow/nodes/shared/NavigateToBtn.d.ts +0 -1
- package/dist/xyflow/types.d.ts +20 -8
- package/dist/xyflow/utils.d.ts +0 -1
- package/package.json +36 -34
- package/src/LikeC4Diagram.css.ts +31 -0
- package/src/LikeC4Diagram.props.ts +154 -0
- package/src/LikeC4Diagram.tsx +119 -0
- package/src/StaticLikeC4Diagram.tsx +39 -0
- package/src/globals.css.ts +128 -0
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useSetState.ts +19 -0
- package/src/hooks/useUpdateEffect.ts +28 -0
- package/src/icons/Clipboard.tsx +24 -0
- package/src/icons/Link.tsx +23 -0
- package/src/icons/ZoomIn.tsx +25 -0
- package/src/icons/index.ts +3 -0
- package/src/index.ts +11 -0
- package/src/mantine/EnsureMantine.tsx +14 -0
- package/src/mantine/theme.ts +19 -0
- package/src/mantine.css.ts +4 -0
- package/src/reset.d.ts +2 -0
- package/src/state/DiagramContext.tsx +42 -0
- package/src/state/WhenInitialized.tsx +24 -0
- package/src/state/diagramStore.ts +358 -0
- package/src/state/index.ts +4 -0
- package/src/state/useDiagramStore.ts +39 -0
- package/src/theme.css.ts +64 -0
- package/src/ui/Camera.tsx +174 -0
- package/src/ui/KeepAspectRatio.css.ts +7 -0
- package/src/ui/KeepAspectRatio.tsx +35 -0
- package/src/ui/OptionsPanel.css.ts +10 -0
- package/src/ui/OptionsPanel.tsx +65 -0
- package/src/ui/index.ts +3 -0
- package/src/ui/options/NodeOptions.tsx +253 -0
- package/src/xyflow/FitviewOnDiagramChange.tsx +93 -0
- package/src/xyflow/SyncWithDiagram.tsx +113 -0
- package/src/xyflow/XYFlowBackground.tsx +33 -0
- package/src/xyflow/XYFlowEvents.tsx +100 -0
- package/src/xyflow/XYFlowWrapper.tsx +166 -0
- package/src/xyflow/const.ts +7 -0
- package/src/xyflow/diagram-to-xyflow.ts +197 -0
- package/src/xyflow/edges/RelationshipEdge.tsx +288 -0
- package/src/xyflow/edges/edges.css.ts +124 -0
- package/src/xyflow/edges/utils.ts +110 -0
- package/src/xyflow/hooks/index.ts +2 -0
- package/src/xyflow/hooks/useLayoutConstraints.ts +289 -0
- package/src/xyflow/hooks/useXYFlow.ts +20 -0
- package/src/xyflow/nodes/compound/CompoundNode.css.ts +191 -0
- package/src/xyflow/nodes/compound/CompoundNode.tsx +105 -0
- package/src/xyflow/nodes/compound/index.ts +1 -0
- package/src/xyflow/nodes/element/ElementIcon.tsx +44 -0
- package/src/xyflow/nodes/element/ElementLink.css.ts +67 -0
- package/src/xyflow/nodes/element/ElementLink.tsx +94 -0
- package/src/xyflow/nodes/element/ElementNode.tsx +225 -0
- package/src/xyflow/nodes/element/ElementShapeSvg.tsx +181 -0
- package/src/xyflow/nodes/element/element.css.ts +261 -0
- package/src/xyflow/nodes/element/index.ts +1 -0
- package/src/xyflow/nodes/shared/NavigateToBtn.tsx +25 -0
- package/src/xyflow/types.ts +100 -0
- package/src/xyflow/utils.ts +12 -0
- package/dist/EmbeddedLikeC4Diagram.css.d.ts +0 -6
- package/dist/EmbeddedLikeC4Diagram.d.ts +0 -18
- package/dist/bundle.d.ts +0 -3
- package/dist/hooks/use-set-state.d.ts +0 -2
- package/dist/hooks/use-update-effect.d.ts +0 -4
- package/dist/index.css.d.ts +0 -2
- package/dist/index.js +0 -13043
- package/dist/mantine/index.d.ts +0 -3
- package/dist/state/DiagramState.d.ts +0 -162
- package/dist/xyflow/UpdateOnDiagramChange.d.ts +0 -10
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { type ElementShape, nonexhaustive } from '@likec4/core'
|
|
2
|
+
import { toDomPrecision } from '../../utils'
|
|
3
|
+
import { fillElementFill, fillMixStroke } from './element.css'
|
|
4
|
+
|
|
5
|
+
function cylinderSVGPath(diameter: number, height: number, tilt = 0.0725) {
|
|
6
|
+
const radius = Math.round(diameter / 2)
|
|
7
|
+
// const tiltAdjustedHeight = height * Math.cos((tilt * Math.PI) / 2)
|
|
8
|
+
const rx = radius
|
|
9
|
+
const ry = toDomPrecision(tilt * radius)
|
|
10
|
+
const tiltAdjustedHeight = height - 2 * ry
|
|
11
|
+
|
|
12
|
+
const path = ` M ${diameter},${ry}
|
|
13
|
+
a ${rx},${ry} 0,0,0 ${-diameter} 0
|
|
14
|
+
l 0,${tiltAdjustedHeight}
|
|
15
|
+
a ${rx},${ry} 0,0,0 ${diameter} 0
|
|
16
|
+
l 0,${-tiltAdjustedHeight}
|
|
17
|
+
z
|
|
18
|
+
`
|
|
19
|
+
.replace(/\s+/g, ' ')
|
|
20
|
+
.trim()
|
|
21
|
+
return {
|
|
22
|
+
path,
|
|
23
|
+
ry,
|
|
24
|
+
rx
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function queueSVGPath(width: number, height: number, tilt = 0.2) {
|
|
29
|
+
const diameter = height
|
|
30
|
+
const radius = Math.round(diameter / 2)
|
|
31
|
+
const ry = radius
|
|
32
|
+
const rx = toDomPrecision((diameter / 2) * tilt)
|
|
33
|
+
const tiltAdjustedWidth = width - 2 * rx
|
|
34
|
+
|
|
35
|
+
const path = `
|
|
36
|
+
M ${rx},0
|
|
37
|
+
a ${rx},${ry} 0,0,0 0 ${diameter}
|
|
38
|
+
l ${tiltAdjustedWidth},0
|
|
39
|
+
a ${rx},${ry} 0,0,0 0 ${-diameter}
|
|
40
|
+
z`
|
|
41
|
+
.replace(/\s+/g, ' ')
|
|
42
|
+
.trim()
|
|
43
|
+
return {
|
|
44
|
+
path,
|
|
45
|
+
ry,
|
|
46
|
+
rx
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const PersonIcon = {
|
|
51
|
+
width: 115,
|
|
52
|
+
height: 120,
|
|
53
|
+
path:
|
|
54
|
+
`M57.9197 0C10.9124 0 33.5766 54.75 33.5766 54.75C38.6131 62.25 45.3285 60.75 45.3285 66C45.3285 70.5 39.4526 72 33.5766 72.75C24.3431 72.75 15.9489 71.25 7.55474 84.75C2.51825 93 0 120 0 120H115C115 120 112.482 93 108.285 84.75C99.8905 70.5 91.4963 72.75 82.2628 72C76.3869 71.25 70.5109 69.75 70.5109 65.25C70.5109 60.75 77.2263 62.25 82.2628 54C82.2628 54.75 104.927 0 57.9197 0V0Z`
|
|
55
|
+
} as const
|
|
56
|
+
|
|
57
|
+
type ElementShapeSvgProps = {
|
|
58
|
+
shape: ElementShape
|
|
59
|
+
w: number
|
|
60
|
+
h: number
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function ElementShapeSvg({ shape, w, h }: ElementShapeSvgProps) {
|
|
64
|
+
switch (shape) {
|
|
65
|
+
case 'mobile': {
|
|
66
|
+
return (
|
|
67
|
+
<>
|
|
68
|
+
<rect
|
|
69
|
+
width={w}
|
|
70
|
+
height={h}
|
|
71
|
+
rx={6}
|
|
72
|
+
className={fillMixStroke}
|
|
73
|
+
strokeWidth={0}
|
|
74
|
+
/>
|
|
75
|
+
<g className={fillElementFill} strokeWidth={0}>
|
|
76
|
+
<circle cx={17} cy={h / 2} r={12} />
|
|
77
|
+
<rect x={33} y={12} width={w - 44} height={h - 24} rx={5} />
|
|
78
|
+
</g>
|
|
79
|
+
</>
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
case 'browser': {
|
|
83
|
+
return (
|
|
84
|
+
<>
|
|
85
|
+
<rect
|
|
86
|
+
width={w}
|
|
87
|
+
height={h}
|
|
88
|
+
rx={6}
|
|
89
|
+
className={fillMixStroke}
|
|
90
|
+
strokeWidth={0}
|
|
91
|
+
/>
|
|
92
|
+
<g className={fillElementFill} strokeWidth={0}>
|
|
93
|
+
<circle cx={16} cy={17} r={7} />
|
|
94
|
+
<circle cx={36} cy={17} r={7} />
|
|
95
|
+
<circle cx={56} cy={17} r={7} />
|
|
96
|
+
<rect x={70} y={8} width={w - 80} height={17} rx={4} />
|
|
97
|
+
<rect x={10} y={32} width={w - 20} height={h - 42} rx={4} />
|
|
98
|
+
</g>
|
|
99
|
+
</>
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
case 'person': {
|
|
103
|
+
return (
|
|
104
|
+
<>
|
|
105
|
+
<rect
|
|
106
|
+
width={w}
|
|
107
|
+
height={h}
|
|
108
|
+
rx={6}
|
|
109
|
+
strokeWidth={0} />
|
|
110
|
+
<svg
|
|
111
|
+
x={w - PersonIcon.width - 6}
|
|
112
|
+
y={h - PersonIcon.height}
|
|
113
|
+
width={PersonIcon.width}
|
|
114
|
+
height={PersonIcon.height}
|
|
115
|
+
viewBox={`0 0 ${PersonIcon.width} ${PersonIcon.height}`}
|
|
116
|
+
className={fillMixStroke}
|
|
117
|
+
>
|
|
118
|
+
<path
|
|
119
|
+
strokeWidth={0}
|
|
120
|
+
d={PersonIcon.path} />
|
|
121
|
+
</svg>
|
|
122
|
+
</>
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
case 'queue': {
|
|
126
|
+
const { path, rx, ry } = queueSVGPath(w, h)
|
|
127
|
+
return (
|
|
128
|
+
<>
|
|
129
|
+
<path d={path} strokeWidth={2} />
|
|
130
|
+
<ellipse cx={rx} cy={ry} ry={ry - 0.75} rx={rx} className={fillMixStroke} strokeWidth={2} />
|
|
131
|
+
</>
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
case 'storage':
|
|
135
|
+
case 'cylinder': {
|
|
136
|
+
const { path, rx, ry } = cylinderSVGPath(w, h)
|
|
137
|
+
return (
|
|
138
|
+
<>
|
|
139
|
+
<path d={path} strokeWidth={2} />
|
|
140
|
+
<ellipse cx={rx} cy={ry} ry={ry} rx={rx - 0.75} className={fillMixStroke} strokeWidth={2} />
|
|
141
|
+
</>
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
case 'rectangle': {
|
|
145
|
+
return (
|
|
146
|
+
<rect
|
|
147
|
+
width={w}
|
|
148
|
+
height={h}
|
|
149
|
+
rx={6}
|
|
150
|
+
strokeWidth={0} />
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
default: {
|
|
154
|
+
return nonexhaustive(shape)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* When element is selected, this component is used to render the indicator
|
|
161
|
+
*/
|
|
162
|
+
export function SelectedIndicator({ shape, w, h }: ElementShapeSvgProps) {
|
|
163
|
+
switch (shape) {
|
|
164
|
+
case 'queue':
|
|
165
|
+
return <path d={queueSVGPath(w, h).path} />
|
|
166
|
+
case 'storage':
|
|
167
|
+
case 'cylinder': {
|
|
168
|
+
return <path d={cylinderSVGPath(w, h).path} />
|
|
169
|
+
}
|
|
170
|
+
default: {
|
|
171
|
+
return (
|
|
172
|
+
<rect
|
|
173
|
+
x={-1}
|
|
174
|
+
y={-1}
|
|
175
|
+
width={w + 2}
|
|
176
|
+
height={h + 2}
|
|
177
|
+
rx={6} />
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { rem } from '@mantine/core'
|
|
2
|
+
import { createVar, fallbackVar, generateIdentifier, globalKeyframes, globalStyle, style } from '@vanilla-extract/css'
|
|
3
|
+
import { mantine } from '../../../mantine.css'
|
|
4
|
+
import { vars } from '../../../theme.css'
|
|
5
|
+
|
|
6
|
+
export const stokeFillMix = createVar('stroke-fill-mix')
|
|
7
|
+
const elPadding = createVar('el-padding')
|
|
8
|
+
// assignVars
|
|
9
|
+
|
|
10
|
+
export const container = style({
|
|
11
|
+
position: 'relative',
|
|
12
|
+
width: '100%',
|
|
13
|
+
height: '100%',
|
|
14
|
+
padding: 0,
|
|
15
|
+
margin: 0,
|
|
16
|
+
vars: {
|
|
17
|
+
[stokeFillMix]: `color-mix(in srgb, ${vars.element.stroke} 90%, ${vars.element.fill})`
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
const indicatorKeyframes = generateIdentifier('indicator')
|
|
22
|
+
globalKeyframes(indicatorKeyframes, {
|
|
23
|
+
'from': {
|
|
24
|
+
opacity: 0.6
|
|
25
|
+
},
|
|
26
|
+
'to': {
|
|
27
|
+
opacity: 0.4
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const outlineColor = fallbackVar(
|
|
32
|
+
mantine.colors.primaryColors.outline,
|
|
33
|
+
mantine.colors.primaryColors.filled,
|
|
34
|
+
vars.element.stroke
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
export const indicator = style({
|
|
38
|
+
stroke: vars.element.loContrast,
|
|
39
|
+
transformOrigin: 'center center',
|
|
40
|
+
strokeWidth: 6,
|
|
41
|
+
animationDuration: '800ms',
|
|
42
|
+
animationName: indicatorKeyframes,
|
|
43
|
+
animationIterationCount: 'infinite',
|
|
44
|
+
animationDirection: 'alternate',
|
|
45
|
+
visibility: 'hidden',
|
|
46
|
+
selectors: {
|
|
47
|
+
':where(.react-flow__node.selected) &': {
|
|
48
|
+
visibility: 'visible'
|
|
49
|
+
},
|
|
50
|
+
[`:where(.react-flow__node:focus-visible, ${container}:focus-visible) &`]: {
|
|
51
|
+
animationName: 'none',
|
|
52
|
+
strokeWidth: 10,
|
|
53
|
+
stroke: outlineColor,
|
|
54
|
+
visibility: 'visible'
|
|
55
|
+
},
|
|
56
|
+
':where([data-likec4-shape="queue"], [data-likec4-shape="cylinder"], [data-likec4-shape="storage"]) &': {
|
|
57
|
+
strokeWidth: 10
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
export const fillElementFill = style({
|
|
63
|
+
fill: vars.element.fill
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
export const fillElementStroke = style({
|
|
67
|
+
fill: vars.element.stroke
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
export const fillMixStroke = style({
|
|
71
|
+
fill: stokeFillMix
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
export const title = style({
|
|
75
|
+
flex: '0 0 auto',
|
|
76
|
+
fontFamily: vars.element.font,
|
|
77
|
+
fontOpticalSizing: 'auto',
|
|
78
|
+
fontStyle: 'normal',
|
|
79
|
+
textAlign: 'center',
|
|
80
|
+
fontWeight: 500,
|
|
81
|
+
fontSize: rem(19),
|
|
82
|
+
lineHeight: 1.2,
|
|
83
|
+
textWrap: 'balance',
|
|
84
|
+
color: vars.element.hiContrast
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
export const description = style({
|
|
88
|
+
flex: '0 1 auto',
|
|
89
|
+
fontFamily: vars.element.font,
|
|
90
|
+
fontOpticalSizing: 'auto',
|
|
91
|
+
fontStyle: 'normal',
|
|
92
|
+
fontWeight: 400,
|
|
93
|
+
fontSize: rem(14),
|
|
94
|
+
lineHeight: 1.125,
|
|
95
|
+
textAlign: 'center',
|
|
96
|
+
textWrap: 'pretty',
|
|
97
|
+
color: vars.element.loContrast,
|
|
98
|
+
whiteSpaceCollapse: 'preserve-breaks',
|
|
99
|
+
textOverflow: 'ellipsis',
|
|
100
|
+
overflow: 'hidden'
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
export const technology = style({
|
|
104
|
+
flex: '0 0 auto',
|
|
105
|
+
fontFamily: vars.element.font,
|
|
106
|
+
fontOpticalSizing: 'auto',
|
|
107
|
+
fontStyle: 'normal',
|
|
108
|
+
fontWeight: 400,
|
|
109
|
+
fontSize: rem(12),
|
|
110
|
+
lineHeight: 1.125,
|
|
111
|
+
textAlign: 'center',
|
|
112
|
+
textWrap: 'balance',
|
|
113
|
+
opacity: 0.92,
|
|
114
|
+
color: vars.element.loContrast,
|
|
115
|
+
selectors: {
|
|
116
|
+
[`${container}:hover &`]: {
|
|
117
|
+
opacity: 1
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
export const cssElement = style({
|
|
123
|
+
position: 'relative',
|
|
124
|
+
width: '100%',
|
|
125
|
+
height: '100%',
|
|
126
|
+
padding: elPadding,
|
|
127
|
+
display: 'flex',
|
|
128
|
+
flexDirection: 'column',
|
|
129
|
+
alignItems: 'stretch',
|
|
130
|
+
justifyContent: 'center',
|
|
131
|
+
overflow: 'hidden',
|
|
132
|
+
gap: rem(8),
|
|
133
|
+
'vars': {
|
|
134
|
+
[elPadding]: rem(24)
|
|
135
|
+
},
|
|
136
|
+
selectors: {
|
|
137
|
+
[`&:has(${description}):has(${technology})`]: {
|
|
138
|
+
gap: rem(6)
|
|
139
|
+
},
|
|
140
|
+
':where([data-likec4-shape="queue"], [data-likec4-shape="mobile"]) &': {
|
|
141
|
+
paddingLeft: rem(40),
|
|
142
|
+
paddingRight: rem(20)
|
|
143
|
+
},
|
|
144
|
+
':where([data-likec4-shape="cylinder"], [data-likec4-shape="storage"]) &': {
|
|
145
|
+
paddingTop: rem(34)
|
|
146
|
+
},
|
|
147
|
+
':where([data-likec4-shape="browser"]) &': {
|
|
148
|
+
paddingTop: rem(32),
|
|
149
|
+
paddingBottom: rem(28)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
export const iconMaxH = createVar('max-h')
|
|
155
|
+
|
|
156
|
+
export const elementIcon = style({
|
|
157
|
+
flex: `0 1 ${iconMaxH}`,
|
|
158
|
+
maxHeight: iconMaxH,
|
|
159
|
+
marginTop: -8,
|
|
160
|
+
// display: 'flex',
|
|
161
|
+
// justifyContent: 'flex-end',
|
|
162
|
+
// flexDirection: 'column',
|
|
163
|
+
// alignItems: 'center',
|
|
164
|
+
position: 'relative',
|
|
165
|
+
userSelect: 'none',
|
|
166
|
+
pointerEvents: 'none',
|
|
167
|
+
overflow: 'visible',
|
|
168
|
+
mixBlendMode: 'hard-light',
|
|
169
|
+
'vars': {
|
|
170
|
+
[iconMaxH]: '30px'
|
|
171
|
+
}
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
globalStyle(`${elementIcon} img`, {
|
|
175
|
+
position: 'absolute',
|
|
176
|
+
left: 0,
|
|
177
|
+
bottom: 0,
|
|
178
|
+
width: '100%',
|
|
179
|
+
height: 'auto',
|
|
180
|
+
objectFit: 'contain',
|
|
181
|
+
maxHeight: `calc(${iconMaxH} + 16px)`
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
const filterShadow = createVar('filter-shadow')
|
|
185
|
+
const filterShadowHovered = createVar('filter-shadow-hovered')
|
|
186
|
+
|
|
187
|
+
export const cssShapeSvg = style({
|
|
188
|
+
top: '0px',
|
|
189
|
+
left: '0px',
|
|
190
|
+
position: 'absolute',
|
|
191
|
+
pointerEvents: 'none',
|
|
192
|
+
fill: vars.element.fill,
|
|
193
|
+
stroke: vars.element.stroke,
|
|
194
|
+
overflow: 'visible',
|
|
195
|
+
filter: fallbackVar(filterShadowHovered, filterShadow),
|
|
196
|
+
transition: 'filter 300ms ease-out',
|
|
197
|
+
transitionDelay: '0ms',
|
|
198
|
+
vars: {
|
|
199
|
+
[filterShadow]: `
|
|
200
|
+
drop-shadow(0 2px 1px rgba(0, 0, 0, 0.21))
|
|
201
|
+
drop-shadow(0 1px 1px color-mix(in srgb, ${vars.element.stroke} 40%, transparent))
|
|
202
|
+
drop-shadow(0 5px 3px rgba(0, 0, 0, 0.1))
|
|
203
|
+
`
|
|
204
|
+
},
|
|
205
|
+
selectors: {
|
|
206
|
+
[`:where(.react-flow__node.selected, .react-flow__node:focus-visible, ${container}:focus-visible) &`]: {
|
|
207
|
+
filter: 'none'
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
export const cssNavigateBtn = style({
|
|
213
|
+
zIndex: 'calc(var(--layer-overlays, 1) + 1)',
|
|
214
|
+
position: 'absolute',
|
|
215
|
+
pointerEvents: 'all',
|
|
216
|
+
left: '50%',
|
|
217
|
+
bottom: 0,
|
|
218
|
+
color: vars.element.loContrast,
|
|
219
|
+
cursor: 'pointer',
|
|
220
|
+
transformOrigin: '50% 65%',
|
|
221
|
+
opacity: 0.75,
|
|
222
|
+
transition: 'all 150ms ease-out',
|
|
223
|
+
transform: 'translate(-50%, 0)',
|
|
224
|
+
transitionDelay: '0ms',
|
|
225
|
+
backgroundColor: 'var(--ai-bg)',
|
|
226
|
+
'vars': {
|
|
227
|
+
'--ai-bg': `color-mix(in srgb , ${vars.element.fill}, transparent 99%)`,
|
|
228
|
+
'--ai-bg-hover': `color-mix(in srgb , ${vars.element.fill} 65%, ${vars.element.stroke})`,
|
|
229
|
+
'--ai-hover': `color-mix(in srgb , ${vars.element.fill} 50%, ${vars.element.stroke})`
|
|
230
|
+
},
|
|
231
|
+
selectors: {
|
|
232
|
+
[`:where([data-likec4-shape='browser']) &`]: {
|
|
233
|
+
bottom: 3
|
|
234
|
+
},
|
|
235
|
+
[`:where(.react-flow__node:not(.dragging) ${container}:hover) &:not(:hover)`]: {
|
|
236
|
+
boxShadow: mantine.shadows.lg,
|
|
237
|
+
transform: 'translate(-50%, 0) scale(1.185)',
|
|
238
|
+
opacity: 1,
|
|
239
|
+
transitionDelay: '250ms',
|
|
240
|
+
vars: {
|
|
241
|
+
'--ai-bg': 'var(--ai-bg-hover)'
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
':hover': {
|
|
246
|
+
boxShadow: mantine.shadows.lg,
|
|
247
|
+
transform: 'translate(-50%, 0) scale(1.35)',
|
|
248
|
+
opacity: 1,
|
|
249
|
+
transitionDelay: '0'
|
|
250
|
+
},
|
|
251
|
+
':active': {
|
|
252
|
+
transform: 'translate(-50%, 0) scale(1.02)',
|
|
253
|
+
opacity: 1,
|
|
254
|
+
transitionDelay: '0'
|
|
255
|
+
}
|
|
256
|
+
})
|
|
257
|
+
globalStyle(`${cssNavigateBtn} svg.icon`, {
|
|
258
|
+
width: '65%',
|
|
259
|
+
height: '65%',
|
|
260
|
+
strokeWidth: '1.5'
|
|
261
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { ElementNode } from './ElementNode'
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ActionIcon } from '@mantine/core'
|
|
2
|
+
import clsx from 'clsx'
|
|
3
|
+
import { ZoomIn } from '../../../icons'
|
|
4
|
+
import { useDiagramStoreApi } from '../../../state'
|
|
5
|
+
|
|
6
|
+
export type NavigateToBtnProps = {
|
|
7
|
+
xynodeId: string
|
|
8
|
+
className?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function NavigateToBtn({ xynodeId, className }: NavigateToBtnProps) {
|
|
12
|
+
const diagramApi = useDiagramStoreApi()
|
|
13
|
+
return (
|
|
14
|
+
<ActionIcon
|
|
15
|
+
className={clsx('nodrag nopan', className)}
|
|
16
|
+
radius="xl"
|
|
17
|
+
onClick={event => {
|
|
18
|
+
diagramApi.getState().triggerOnNavigateTo(xynodeId, event)
|
|
19
|
+
event.stopPropagation()
|
|
20
|
+
}}
|
|
21
|
+
>
|
|
22
|
+
<ZoomIn />
|
|
23
|
+
</ActionIcon>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { BBox, DiagramEdge, DiagramNode, Fqn, Point } from '@likec4/core'
|
|
2
|
+
import type {
|
|
3
|
+
Edge,
|
|
4
|
+
EdgeMouseHandler,
|
|
5
|
+
InternalNode,
|
|
6
|
+
Node,
|
|
7
|
+
NodeMouseHandler,
|
|
8
|
+
OnMoveEnd,
|
|
9
|
+
OnMoveStart,
|
|
10
|
+
ReactFlowInstance,
|
|
11
|
+
ReactFlowState
|
|
12
|
+
} from '@xyflow/react'
|
|
13
|
+
import { isNode } from '@xyflow/react'
|
|
14
|
+
import type { MouseEvent as ReactMouseEvent } from 'react'
|
|
15
|
+
import { isTruthy } from 'remeda'
|
|
16
|
+
import type { SetReadonly, SetRequired, Simplify } from 'type-fest'
|
|
17
|
+
|
|
18
|
+
export type ElementNodeData = {
|
|
19
|
+
fqn: Fqn
|
|
20
|
+
element: DiagramNode
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
type TypedXYFlowNode<D extends Record<string, unknown>, T extends string> = SetReadonly<
|
|
24
|
+
SetRequired<Node<D, T>, 'type'>,
|
|
25
|
+
'id' | 'type'
|
|
26
|
+
>
|
|
27
|
+
|
|
28
|
+
export type ElementXYFlowNode = TypedXYFlowNode<ElementNodeData, 'element'>
|
|
29
|
+
|
|
30
|
+
export type CompoundNodeData = {
|
|
31
|
+
fqn: Fqn
|
|
32
|
+
element: DiagramNode
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type CompoundXYFlowNode = TypedXYFlowNode<CompoundNodeData, 'compound'>
|
|
36
|
+
|
|
37
|
+
export type XYFlowNode = ElementXYFlowNode | CompoundXYFlowNode
|
|
38
|
+
|
|
39
|
+
export type InternalXYFlowNode = InternalNode<XYFlowNode>
|
|
40
|
+
|
|
41
|
+
export namespace XYFlowNode {
|
|
42
|
+
export type Data = ElementNodeData | CompoundNodeData
|
|
43
|
+
|
|
44
|
+
export function isCompound(node: unknown): node is CompoundXYFlowNode {
|
|
45
|
+
return isNode(node) && node.type === 'compound'
|
|
46
|
+
}
|
|
47
|
+
export function isElement(node: Node): node is ElementXYFlowNode {
|
|
48
|
+
return isNode(node) && node.type === 'element'
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const is = (node: Node): node is XYFlowNode => isCompound(node) || isElement(node)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type RelationshipData = {
|
|
55
|
+
edge: DiagramEdge
|
|
56
|
+
controlPoints: Point[]
|
|
57
|
+
headPoint: Point | null
|
|
58
|
+
tailPoint: Point | null
|
|
59
|
+
type: 'bezier' | 'poly'
|
|
60
|
+
label: null | {
|
|
61
|
+
bbox: BBox
|
|
62
|
+
text: string
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export type RelationshipEdge = Simplify<
|
|
67
|
+
SetRequired<
|
|
68
|
+
Omit<Edge<RelationshipData>, 'type'>,
|
|
69
|
+
'data'
|
|
70
|
+
> & {
|
|
71
|
+
type: 'relationship'
|
|
72
|
+
}
|
|
73
|
+
>
|
|
74
|
+
|
|
75
|
+
export type XYFlowEdge = RelationshipEdge
|
|
76
|
+
|
|
77
|
+
export namespace XYFlowEdge {
|
|
78
|
+
export type Data = RelationshipData
|
|
79
|
+
|
|
80
|
+
export const isRelationship = (e: Edge): e is RelationshipEdge => e.type === 'relationship' && isTruthy(e.data)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export type XYFlowInstance = ReactFlowInstance<XYFlowNode, XYFlowEdge>
|
|
84
|
+
export type XYFlowState = ReactFlowState<XYFlowNode, XYFlowEdge>
|
|
85
|
+
|
|
86
|
+
export type XYFlowData = {
|
|
87
|
+
nodes: XYFlowNode[]
|
|
88
|
+
edges: XYFlowEdge[]
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export type XYFlowEventHandlers = {
|
|
92
|
+
onPanelClick: (event: ReactMouseEvent) => void
|
|
93
|
+
onNodeContextMenu: NodeMouseHandler<XYFlowNode>
|
|
94
|
+
onEdgeContextMenu: EdgeMouseHandler<XYFlowEdge>
|
|
95
|
+
onPaneContextMenu: (event: ReactMouseEvent | MouseEvent) => void
|
|
96
|
+
onNodeClick: NodeMouseHandler<XYFlowNode>
|
|
97
|
+
onEdgeClick: EdgeMouseHandler<XYFlowEdge>
|
|
98
|
+
onMoveStart: OnMoveStart
|
|
99
|
+
onMoveEnd: OnMoveEnd
|
|
100
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { XYPosition } from '@xyflow/react'
|
|
2
|
+
|
|
3
|
+
export function toDomPrecision(v: number | null) {
|
|
4
|
+
if (v === null) {
|
|
5
|
+
return 0.01
|
|
6
|
+
}
|
|
7
|
+
return Math.round(v * 100) / 100
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function distance(a: XYPosition, b: XYPosition) {
|
|
11
|
+
return Math.sqrt(Math.pow(b.x - a.x, 2) + Math.pow(b.y - a.y, 2))
|
|
12
|
+
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
export declare const modalContent: string;
|
|
2
|
-
export declare const modalBody: string;
|
|
3
|
-
export declare const modalHeader: string;
|
|
4
|
-
export declare const modalCloseButtonBox: string;
|
|
5
|
-
export declare const modalCloseButton: string;
|
|
6
|
-
//# sourceMappingURL=EmbeddedLikeC4Diagram.css.d.ts.map
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { LikeC4ColorMode } from './LikeC4Diagram.props';
|
|
2
|
-
import { DiagramView } from '@likec4/core';
|
|
3
|
-
|
|
4
|
-
export type EmbeddedLikeC4DiagramProps = {
|
|
5
|
-
viewId: string;
|
|
6
|
-
views: Record<string, DiagramView>;
|
|
7
|
-
/**
|
|
8
|
-
* Controls color scheme used for styling the flow
|
|
9
|
-
* By default inherits from system or surrounding MantineProvider
|
|
10
|
-
*
|
|
11
|
-
* @example 'light' | 'dark'
|
|
12
|
-
*/
|
|
13
|
-
colorMode?: LikeC4ColorMode | undefined;
|
|
14
|
-
};
|
|
15
|
-
declare function EmbeddedLikeC4DiagramCmp({ viewId, views, colorMode }: EmbeddedLikeC4DiagramProps): JSX.Element;
|
|
16
|
-
export declare const EmbeddedLikeC4Diagram: typeof EmbeddedLikeC4DiagramCmp;
|
|
17
|
-
export {};
|
|
18
|
-
//# sourceMappingURL=EmbeddedLikeC4Diagram.d.ts.map
|
package/dist/bundle.d.ts
DELETED
package/dist/index.css.d.ts
DELETED