@likec4/diagram 1.8.1 → 1.12.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/package.json +21 -45
- package/src/LikeC4CustomColors.props.ts +5 -0
- package/src/LikeC4CustomColors.tsx +37 -0
- package/src/LikeC4Diagram.css.ts +1 -1
- package/src/LikeC4Diagram.props.ts +43 -95
- package/src/LikeC4Diagram.tsx +21 -2
- package/src/LikeC4ModelProvider.tsx +49 -0
- package/src/StaticLikeC4Diagram.tsx +1 -0
- package/src/globals.css.ts +2 -3
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useMantinePortalProps.ts +8 -0
- package/src/hooks/useXYFlow.ts +1 -2
- package/src/index.ts +3 -0
- package/src/likec4model/LikeC4ModelContext.ts +4 -0
- package/src/likec4model/index.ts +1 -0
- package/src/likec4model/useLikeC4Model.ts +55 -0
- package/src/mantine/EnsureMantine.tsx +1 -0
- package/src/state/DiagramContext.tsx +1 -0
- package/src/state/diagram-to-xyflow.ts +2 -3
- package/src/state/diagramStore.ts +185 -66
- package/src/{theme.css.ts → theme-vars.ts} +11 -4
- package/src/ui/DiagramTitlePanel.css.ts +2 -3
- package/src/ui/DiagramTitlePanel.tsx +17 -1
- package/src/ui/DynamicViewWalkthrough.css.ts +28 -2
- package/src/ui/DynamicViewWalkthrough.tsx +97 -52
- package/src/ui/OptionsPanel.css.ts +1 -2
- package/src/ui/OptionsPanel.tsx +1 -1
- package/src/ui/notation/NotationPanel.css.ts +2 -3
- package/src/ui/notation/NotationPanel.tsx +11 -12
- package/src/ui/options/NodeOptions.tsx +1 -1
- package/src/ui/top-left/ChangeAutoLayoutButton.tsx +20 -32
- package/src/ui/top-left/TopLeftPanel.tsx +128 -117
- package/src/ui/top-left/_shared.ts +7 -6
- package/src/ui/top-left/styles.css.ts +17 -8
- package/src/utils/vector.ts +57 -0
- package/src/xyflow/FitviewOnDiagramChange.tsx +5 -4
- package/src/xyflow/SelectEdgesOnNodeFocus.tsx +7 -5
- package/src/xyflow/XYFlow.tsx +10 -11
- package/src/xyflow/XYFlowEvents.tsx +59 -60
- package/src/xyflow/XYFlowInner.tsx +5 -5
- package/src/xyflow/edges/EdgeLabel.tsx +185 -0
- package/src/xyflow/edges/RelationshipEdge.tsx +170 -137
- package/src/xyflow/edges/RelationshipsDropdownMenu.css.ts +60 -0
- package/src/xyflow/edges/RelationshipsDropdownMenu.tsx +214 -0
- package/src/xyflow/edges/edges.css.ts +75 -7
- package/src/xyflow/nodes/compound/CompoundNode.css.ts +1 -2
- package/src/xyflow/nodes/compound/CompoundNode.tsx +4 -4
- package/src/xyflow/nodes/element/ElementLink.css.ts +1 -1
- package/src/xyflow/nodes/element/ElementLink.tsx +3 -8
- package/src/xyflow/nodes/element/ElementNode.tsx +47 -4
- package/src/xyflow/nodes/element/element.css.ts +1 -2
- package/src/xyflow/types.ts +1 -24
- package/src/xyflow/useLayoutConstraints.ts +10 -6
- package/src/xyflow/utils.ts +1 -1
- package/dist/LikeC4Diagram.css.d.ts +0 -4
- package/dist/LikeC4Diagram.d.ts +0 -4
- package/dist/LikeC4Diagram.props.d.ts +0 -185
- package/dist/StaticLikeC4Diagram.d.ts +0 -4
- package/dist/globals.css.d.ts +0 -1
- package/dist/hooks/index.d.ts +0 -4
- package/dist/hooks/useDiagramState.d.ts +0 -6
- package/dist/hooks/useSetState.d.ts +0 -3
- package/dist/hooks/useUpdateEffect.d.ts +0 -5
- package/dist/hooks/useXYFlow.d.ts +0 -267
- package/dist/index.d.ts +0 -9
- package/dist/index.mjs +0 -3651
- package/dist/mantine/EnsureMantine.d.ts +0 -5
- package/dist/mantine.css.d.ts +0 -3
- package/dist/state/DiagramContext.d.ts +0 -1189
- package/dist/state/diagram-to-xyflow.d.ts +0 -12
- package/dist/state/diagramStore.d.ts +0 -1268
- package/dist/style.css +0 -2139
- package/dist/theme.css.d.ts +0 -63
- package/dist/ui/DiagramTitlePanel.css.d.ts +0 -4
- package/dist/ui/DiagramTitlePanel.d.ts +0 -1
- package/dist/ui/DynamicViewWalkthrough.css.d.ts +0 -3
- package/dist/ui/DynamicViewWalkthrough.d.ts +0 -1
- package/dist/ui/OptionsPanel.css.d.ts +0 -1
- package/dist/ui/OptionsPanel.d.ts +0 -1
- package/dist/ui/notation/NotationPanel.css.d.ts +0 -6
- package/dist/ui/notation/NotationPanel.d.ts +0 -1
- package/dist/ui/notation/index.d.ts +0 -1
- package/dist/ui/options/EdgeOptions.d.ts +0 -19
- package/dist/ui/options/NodeOptions.d.ts +0 -19
- package/dist/ui/top-left/ChangeAutoLayoutButton.d.ts +0 -3
- package/dist/ui/top-left/TopLeftPanel.d.ts +0 -4
- package/dist/ui/top-left/_shared.d.ts +0 -5
- package/dist/ui/top-left/styles.css.d.ts +0 -3
- package/dist/xyflow/EdgeMarkers.d.ts +0 -12
- package/dist/xyflow/FitviewOnDiagramChange.d.ts +0 -4
- package/dist/xyflow/SelectEdgesOnNodeFocus.d.ts +0 -1
- package/dist/xyflow/XYFlow.d.ts +0 -9
- package/dist/xyflow/XYFlowBackground.d.ts +0 -11
- package/dist/xyflow/XYFlowEvents.d.ts +0 -161
- package/dist/xyflow/XYFlowInner.d.ts +0 -10
- package/dist/xyflow/const.d.ts +0 -7
- package/dist/xyflow/edges/RelationshipEdge.d.ts +0 -32
- package/dist/xyflow/edges/edges.css.d.ts +0 -12
- package/dist/xyflow/edges/utils.d.ts +0 -17
- package/dist/xyflow/nodes/compound/CompoundNode.css.d.ts +0 -11
- package/dist/xyflow/nodes/compound/CompoundNode.d.ts +0 -6
- package/dist/xyflow/nodes/compound/index.d.ts +0 -1
- package/dist/xyflow/nodes/element/ElementLink.css.d.ts +0 -3
- package/dist/xyflow/nodes/element/ElementLink.d.ts +0 -7
- package/dist/xyflow/nodes/element/ElementNode.d.ts +0 -6
- package/dist/xyflow/nodes/element/ElementShapeSvg.d.ts +0 -18
- package/dist/xyflow/nodes/element/element.css.d.ts +0 -19
- package/dist/xyflow/nodes/element/index.d.ts +0 -1
- package/dist/xyflow/nodes/shared/NavigateToBtn.d.ts +0 -5
- package/dist/xyflow/types.d.ts +0 -50
- package/dist/xyflow/useLayoutConstraints.d.ts +0 -9
- package/dist/xyflow/utils.d.ts +0 -18
- package/src/mantine.css.ts +0 -7
- package/src/reset.d.ts +0 -2
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { type DiagramNode, LikeC4Model, nameFromFqn } from '@likec4/core'
|
|
2
|
+
import {
|
|
3
|
+
ActionIcon,
|
|
4
|
+
Box,
|
|
5
|
+
Group,
|
|
6
|
+
Menu,
|
|
7
|
+
MenuDivider,
|
|
8
|
+
MenuDropdown,
|
|
9
|
+
MenuItem,
|
|
10
|
+
MenuLabel,
|
|
11
|
+
MenuTarget,
|
|
12
|
+
Space,
|
|
13
|
+
Stack,
|
|
14
|
+
type StackProps,
|
|
15
|
+
Text,
|
|
16
|
+
Tooltip as MantineTooltip,
|
|
17
|
+
TooltipGroup
|
|
18
|
+
} from '@mantine/core'
|
|
19
|
+
import { IconArrowRight, IconFileSymlink, IconZoomScan } from '@tabler/icons-react'
|
|
20
|
+
import clsx from 'clsx'
|
|
21
|
+
import { forwardRef, Fragment, type MouseEventHandler, type PropsWithChildren } from 'react'
|
|
22
|
+
import { filter, isTruthy, map, partition, pipe } from 'remeda'
|
|
23
|
+
import { useDiagramState, useDiagramStoreApi, useMantinePortalProps, useXYNodesData } from '../../hooks'
|
|
24
|
+
import type { RelationshipData } from '../types'
|
|
25
|
+
import * as css from './RelationshipsDropdownMenu.css'
|
|
26
|
+
|
|
27
|
+
const stopPropagation: MouseEventHandler = (e) => e.stopPropagation()
|
|
28
|
+
|
|
29
|
+
export const Tooltip = MantineTooltip.withProps({
|
|
30
|
+
color: 'gray',
|
|
31
|
+
fz: 'xs',
|
|
32
|
+
openDelay: 300,
|
|
33
|
+
closeDelay: 150,
|
|
34
|
+
label: '',
|
|
35
|
+
children: null,
|
|
36
|
+
offset: 8,
|
|
37
|
+
withinPortal: false
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
export function RelationshipsDropdownMenu({
|
|
41
|
+
edge,
|
|
42
|
+
disabled = false,
|
|
43
|
+
likec4model,
|
|
44
|
+
children
|
|
45
|
+
}: PropsWithChildren<{
|
|
46
|
+
edge: RelationshipData['edge']
|
|
47
|
+
disabled?: boolean | undefined
|
|
48
|
+
likec4model: LikeC4Model
|
|
49
|
+
}>) {
|
|
50
|
+
const portalProps = useMantinePortalProps()
|
|
51
|
+
const [sourceXYNode, targetXYNode] = useXYNodesData([edge.source, edge.target])
|
|
52
|
+
|
|
53
|
+
if (!sourceXYNode || !targetXYNode) return children
|
|
54
|
+
|
|
55
|
+
const [direct, nested] = pipe(
|
|
56
|
+
edge.relations,
|
|
57
|
+
map(id => {
|
|
58
|
+
try {
|
|
59
|
+
return likec4model.relationship(id)
|
|
60
|
+
} catch (e) {
|
|
61
|
+
// View was cached, but likec4model based on new data
|
|
62
|
+
console.error(
|
|
63
|
+
`View is cached and likec4model missing relationship ${id} from ${edge.source} -> ${edge.target}`,
|
|
64
|
+
e
|
|
65
|
+
)
|
|
66
|
+
return null
|
|
67
|
+
}
|
|
68
|
+
}),
|
|
69
|
+
filter(isTruthy),
|
|
70
|
+
partition(r => r.relationship.source === edge.source && r.relationship.target === edge.target)
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
if (direct.length + nested.length === 0) {
|
|
74
|
+
return <>{children}</>
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const renderRelationship = (relationship: LikeC4Model.ViewModel.Relationship, index: number) => (
|
|
78
|
+
<Fragment key={relationship.id}>
|
|
79
|
+
{index > 0 && <MenuDivider opacity={0.65} />}
|
|
80
|
+
<MenuItem
|
|
81
|
+
component={Relationship}
|
|
82
|
+
relationship={relationship}
|
|
83
|
+
sourceNode={sourceXYNode.data.element}
|
|
84
|
+
targetNode={targetXYNode.data.element}
|
|
85
|
+
edge={edge} />
|
|
86
|
+
</Fragment>
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<Menu
|
|
91
|
+
trigger={'click-hover'}
|
|
92
|
+
openDelay={300}
|
|
93
|
+
closeDelay={450}
|
|
94
|
+
floatingStrategy={'fixed'}
|
|
95
|
+
closeOnClickOutside
|
|
96
|
+
clickOutsideEvents={['pointerdown', 'mousedown', 'click']}
|
|
97
|
+
closeOnEscape
|
|
98
|
+
closeOnItemClick={false}
|
|
99
|
+
disabled={disabled}
|
|
100
|
+
{...portalProps}
|
|
101
|
+
>
|
|
102
|
+
<MenuTarget>
|
|
103
|
+
{children}
|
|
104
|
+
</MenuTarget>
|
|
105
|
+
<MenuDropdown
|
|
106
|
+
className={css.menuDropdown}
|
|
107
|
+
onPointerDownCapture={stopPropagation}
|
|
108
|
+
onPointerDown={stopPropagation}
|
|
109
|
+
onClick={stopPropagation}
|
|
110
|
+
onDoubleClick={stopPropagation}
|
|
111
|
+
>
|
|
112
|
+
{direct.length > 0 && (
|
|
113
|
+
<>
|
|
114
|
+
<MenuLabel>direct relationships</MenuLabel>
|
|
115
|
+
{direct.map(renderRelationship)}
|
|
116
|
+
</>
|
|
117
|
+
)}
|
|
118
|
+
{nested.length > 0 && (
|
|
119
|
+
<>
|
|
120
|
+
{direct.length > 0 && <MenuDivider />}
|
|
121
|
+
<MenuLabel>resolved from nested</MenuLabel>
|
|
122
|
+
{nested.map(renderRelationship)}
|
|
123
|
+
</>
|
|
124
|
+
)}
|
|
125
|
+
</MenuDropdown>
|
|
126
|
+
</Menu>
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const Relationship = forwardRef<
|
|
131
|
+
HTMLDivElement,
|
|
132
|
+
StackProps & {
|
|
133
|
+
relationship: LikeC4Model.ViewModel.Relationship
|
|
134
|
+
edge: RelationshipData['edge']
|
|
135
|
+
sourceNode: DiagramNode
|
|
136
|
+
targetNode: DiagramNode
|
|
137
|
+
}
|
|
138
|
+
>(({
|
|
139
|
+
className,
|
|
140
|
+
relationship: r,
|
|
141
|
+
edge,
|
|
142
|
+
sourceNode,
|
|
143
|
+
targetNode,
|
|
144
|
+
...props
|
|
145
|
+
}, ref) => {
|
|
146
|
+
const diagramApi = useDiagramStoreApi()
|
|
147
|
+
const {
|
|
148
|
+
viewId,
|
|
149
|
+
hasOnOpenSourceRelation,
|
|
150
|
+
hasOnNavigateTo
|
|
151
|
+
} = useDiagramState(s => ({
|
|
152
|
+
viewId: s.view.id,
|
|
153
|
+
hasOnOpenSourceRelation: !!s.onOpenSourceRelation,
|
|
154
|
+
hasOnNavigateTo: !!s.onNavigateTo
|
|
155
|
+
}))
|
|
156
|
+
const sourceId = nameFromFqn(edge.source) + r.source.id.slice(edge.source.length)
|
|
157
|
+
const targetId = nameFromFqn(edge.target) + r.target.id.slice(edge.target.length)
|
|
158
|
+
const navigateTo = hasOnNavigateTo && r.relationship.navigateTo !== viewId ? r.relationship.navigateTo : undefined
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<Stack ref={ref} className={clsx(css.menuItemRelationship, className)} {...props}>
|
|
162
|
+
<Group gap={4}>
|
|
163
|
+
<Text component="div" className={css.endpoint} data-likec4-color={sourceNode.color}>
|
|
164
|
+
{sourceId}
|
|
165
|
+
</Text>
|
|
166
|
+
<IconArrowRight stroke={2.5} size={11} />
|
|
167
|
+
<Text component="div" className={css.endpoint} data-likec4-color={targetNode.color}>
|
|
168
|
+
{targetId}
|
|
169
|
+
</Text>
|
|
170
|
+
{(navigateTo || hasOnOpenSourceRelation) && (
|
|
171
|
+
<TooltipGroup openDelay={100}>
|
|
172
|
+
<Space w={'xs'} />
|
|
173
|
+
{navigateTo && (
|
|
174
|
+
<Tooltip label={'Open dynamic view'}>
|
|
175
|
+
<ActionIcon
|
|
176
|
+
className={clsx('nodrag nopan')}
|
|
177
|
+
size={'sm'}
|
|
178
|
+
radius="sm"
|
|
179
|
+
variant="default"
|
|
180
|
+
onPointerDownCapture={stopPropagation}
|
|
181
|
+
onClick={event => {
|
|
182
|
+
diagramApi.getState().onNavigateTo?.(navigateTo, event)
|
|
183
|
+
}}
|
|
184
|
+
role="button"
|
|
185
|
+
>
|
|
186
|
+
<IconZoomScan size="80%" stroke={2} />
|
|
187
|
+
</ActionIcon>
|
|
188
|
+
</Tooltip>
|
|
189
|
+
)}
|
|
190
|
+
{hasOnOpenSourceRelation && (
|
|
191
|
+
<Tooltip label={'Open source'}>
|
|
192
|
+
<ActionIcon
|
|
193
|
+
className={clsx('nodrag nopan')}
|
|
194
|
+
size={'sm'}
|
|
195
|
+
radius="sm"
|
|
196
|
+
variant="default"
|
|
197
|
+
onPointerDownCapture={stopPropagation}
|
|
198
|
+
onClick={event => {
|
|
199
|
+
event.stopPropagation()
|
|
200
|
+
diagramApi.getState().onOpenSourceRelation?.(r.id)
|
|
201
|
+
}}
|
|
202
|
+
role="button"
|
|
203
|
+
>
|
|
204
|
+
<IconFileSymlink size="80%" stroke={2} />
|
|
205
|
+
</ActionIcon>
|
|
206
|
+
</Tooltip>
|
|
207
|
+
)}
|
|
208
|
+
</TooltipGroup>
|
|
209
|
+
)}
|
|
210
|
+
</Group>
|
|
211
|
+
<Box className={css.title}>{r.title || 'untitled'}</Box>
|
|
212
|
+
</Stack>
|
|
213
|
+
)
|
|
214
|
+
})
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { rem } from '@mantine/core'
|
|
2
2
|
import { createVar, fallbackVar, globalStyle, keyframes, style } from '@vanilla-extract/css'
|
|
3
|
-
import { mantine } from '../../
|
|
4
|
-
import { vars, xyvars } from '../../theme.css'
|
|
3
|
+
import { mantine, vars, xyvars } from '../../theme-vars'
|
|
5
4
|
|
|
6
5
|
const mixColor = createVar('mix-color')
|
|
7
6
|
|
|
@@ -145,7 +144,7 @@ export const cssEdgePath = style({
|
|
|
145
144
|
animationDelay: '450ms',
|
|
146
145
|
transition: 'stroke 130ms ease-out,stroke-width 130ms ease-out'
|
|
147
146
|
},
|
|
148
|
-
[`:where(${isSelected}, [data-edge-active='true']) &`]: {
|
|
147
|
+
[`:where(${isSelected}, [data-edge-active='true'], [data-edge-animated='true']) &`]: {
|
|
149
148
|
animationName: strokeKeyframes,
|
|
150
149
|
animationDelay: '0ms',
|
|
151
150
|
transition: 'stroke 130ms ease-out,stroke-width 130ms ease-out'
|
|
@@ -171,10 +170,15 @@ export const stepEdgeNumber = style({
|
|
|
171
170
|
borderTopLeftRadius: labelBorderRadius,
|
|
172
171
|
borderBottomLeftRadius: labelBorderRadius,
|
|
173
172
|
backgroundColor: `color-mix(in srgb, ${vars.relation.labelBgColor}, ${mixColor} 5%)`,
|
|
174
|
-
fontVariantNumeric: 'tabular-nums'
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
173
|
+
fontVariantNumeric: 'tabular-nums',
|
|
174
|
+
selectors: {
|
|
175
|
+
[`:where([data-mantine-color-scheme="dark"]) :where([data-likec4-color="gray"]) &`]: {
|
|
176
|
+
backgroundColor: `color-mix(in srgb, ${vars.relation.labelBgColor}, ${mixColor} 15%)`
|
|
177
|
+
},
|
|
178
|
+
[`:where([data-edge-active='true']) &`]: {
|
|
179
|
+
backgroundColor: 'transparent'
|
|
180
|
+
}
|
|
181
|
+
}
|
|
178
182
|
})
|
|
179
183
|
|
|
180
184
|
export const varLabelX = createVar('label-x')
|
|
@@ -230,3 +234,67 @@ export const edgeLabelText = style({
|
|
|
230
234
|
globalStyle(`${edgeLabel}:has(${stepEdgeNumber}) ${edgeLabelText}`, {
|
|
231
235
|
padding: '2px 5px 4px 2px'
|
|
232
236
|
})
|
|
237
|
+
|
|
238
|
+
export const edgeNoteCloseButton = style({
|
|
239
|
+
position: 'absolute',
|
|
240
|
+
top: 4,
|
|
241
|
+
right: 4,
|
|
242
|
+
zIndex: 9
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
export const edgeNoteText = style({
|
|
246
|
+
userSelect: 'all',
|
|
247
|
+
textAlign: 'left',
|
|
248
|
+
whiteSpaceCollapse: 'preserve-breaks',
|
|
249
|
+
textWrap: 'pretty',
|
|
250
|
+
lineHeight: 1.25,
|
|
251
|
+
vars: {
|
|
252
|
+
'--text-fz': mantine.fontSizes.sm
|
|
253
|
+
},
|
|
254
|
+
'@media': {
|
|
255
|
+
[mantine.largerThan('md')]: {
|
|
256
|
+
vars: {
|
|
257
|
+
'--text-fz': mantine.fontSizes.md
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
export const cssNavigateBtn = style({
|
|
264
|
+
zIndex: 'calc(var(--layer-overlays, 1) + 1)',
|
|
265
|
+
position: 'absolute',
|
|
266
|
+
pointerEvents: 'all',
|
|
267
|
+
left: '50%',
|
|
268
|
+
top: 'calc(100% + 1px)',
|
|
269
|
+
color: xyvars.edge.labelColor,
|
|
270
|
+
cursor: 'pointer',
|
|
271
|
+
transformOrigin: 'top center',
|
|
272
|
+
opacity: 0.8,
|
|
273
|
+
transition: 'all 150ms ease-in-out',
|
|
274
|
+
transform: 'translate(-50%, 0)',
|
|
275
|
+
transitionDelay: '0ms',
|
|
276
|
+
backgroundColor: 'var(--ai-bg)',
|
|
277
|
+
vars: {
|
|
278
|
+
'--ai-bg': vars.relation.labelBgColor,
|
|
279
|
+
'--ai-hover': `color-mix(in srgb , ${vars.relation.labelBgColor}, ${mixColor} 10%)`
|
|
280
|
+
},
|
|
281
|
+
selectors: {
|
|
282
|
+
[`:is(${dimmed}) &`]: {
|
|
283
|
+
display: 'none'
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
':hover': {
|
|
287
|
+
boxShadow: mantine.shadows.lg,
|
|
288
|
+
transform: 'translate(-50%, 0) scale(1.3)',
|
|
289
|
+
opacity: 1
|
|
290
|
+
},
|
|
291
|
+
':active': {
|
|
292
|
+
transform: 'translate(-50%, 0) scale(1.02)'
|
|
293
|
+
}
|
|
294
|
+
})
|
|
295
|
+
globalStyle(`${cssNavigateBtn} .tabler-icon`, {
|
|
296
|
+
width: '75%',
|
|
297
|
+
height: '75%',
|
|
298
|
+
strokeWidth: 2
|
|
299
|
+
})
|
|
300
|
+
export const cssNavigateBtnIcon = style({})
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { rem } from '@mantine/core'
|
|
2
2
|
import { createVar, fallbackVar, globalStyle, keyframes, style } from '@vanilla-extract/css'
|
|
3
3
|
import { calc } from '@vanilla-extract/css-utils'
|
|
4
|
-
import { mantine } from '../../../
|
|
5
|
-
import { vars } from '../../../theme.css'
|
|
4
|
+
import { mantine, vars } from '../../../theme-vars'
|
|
6
5
|
|
|
7
6
|
export const container = style({
|
|
8
7
|
width: '100%',
|
|
@@ -83,15 +83,15 @@ export const CompoundNodeMemo = /* @__PURE__ */ memo<CompoundNodeProps>(function
|
|
|
83
83
|
opacity < 1 && css.transparent,
|
|
84
84
|
'likec4-compound'
|
|
85
85
|
)}
|
|
86
|
-
style={
|
|
87
|
-
|
|
86
|
+
style={{
|
|
87
|
+
...(opacity < 1 && {
|
|
88
88
|
...assignInlineVars({
|
|
89
89
|
[css.varBorderTransparency]: `${borderTransparency}%`,
|
|
90
90
|
[css.varOpacity]: opacity.toFixed(2)
|
|
91
91
|
}),
|
|
92
92
|
borderStyle: style.border ?? 'dashed'
|
|
93
|
-
}
|
|
94
|
-
|
|
93
|
+
})
|
|
94
|
+
}}
|
|
95
95
|
>
|
|
96
96
|
<Text
|
|
97
97
|
component="div"
|
|
@@ -15,7 +15,7 @@ import { IconLink } from '@tabler/icons-react'
|
|
|
15
15
|
import clsx from 'clsx'
|
|
16
16
|
import { useId } from 'react'
|
|
17
17
|
import { clamp } from 'remeda'
|
|
18
|
-
import {
|
|
18
|
+
import { useMantinePortalProps } from '../../../hooks/useMantinePortalProps'
|
|
19
19
|
import { type DiagramState } from '../../../state/diagramStore'
|
|
20
20
|
import type { XYFlowNode } from '../../types'
|
|
21
21
|
import { elementLink, trigger } from './ElementLink.css'
|
|
@@ -24,17 +24,12 @@ type ElementLinkProps = {
|
|
|
24
24
|
element: XYFlowNode['data']['element']
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
const selector = (s: DiagramState) => {
|
|
28
|
-
const target = s.getContainer()
|
|
29
|
-
return target ? { target } : null
|
|
30
|
-
}
|
|
31
|
-
|
|
32
27
|
export function ElementLink({
|
|
33
28
|
element
|
|
34
29
|
}: ElementLinkProps) {
|
|
35
30
|
invariant(element.links, 'ElementLink: links are required')
|
|
36
31
|
const id = useId()
|
|
37
|
-
const portalProps =
|
|
32
|
+
const portalProps = useMantinePortalProps()
|
|
38
33
|
return (
|
|
39
34
|
<div className={elementLink} data-likec4-linkid={id}>
|
|
40
35
|
<HoverCard
|
|
@@ -47,7 +42,7 @@ export function ElementLink({
|
|
|
47
42
|
transitionProps={{
|
|
48
43
|
transition: 'pop'
|
|
49
44
|
}}
|
|
50
|
-
{...
|
|
45
|
+
{...portalProps}
|
|
51
46
|
floatingStrategy={'fixed'}
|
|
52
47
|
openDelay={350}
|
|
53
48
|
closeDelay={800}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type { DiagramNode } from '@likec4/core
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { Handle, type NodeProps, NodeToolbar, Position } from '@xyflow/react'
|
|
1
|
+
import type { DiagramNode } from '@likec4/core'
|
|
2
|
+
import { Text as MantineText } from '@mantine/core'
|
|
3
|
+
import { Handle, type NodeProps, Position } from '@xyflow/react'
|
|
5
4
|
import clsx from 'clsx'
|
|
6
5
|
import { deepEqual as eq } from 'fast-equals'
|
|
7
6
|
import { m, type Variants } from 'framer-motion'
|
|
@@ -123,6 +122,50 @@ export const ElementNodeMemo = memo<ElementNodeProps>(function ElementNode({
|
|
|
123
122
|
|
|
124
123
|
return (
|
|
125
124
|
<>
|
|
125
|
+
{
|
|
126
|
+
/* <NodeToolbar isVisible={isTollbarVisible} offset={0}>
|
|
127
|
+
<ActionIconGroup>
|
|
128
|
+
<ActionIcon
|
|
129
|
+
size={'md'}
|
|
130
|
+
variant="default"
|
|
131
|
+
color="dark"
|
|
132
|
+
onClick={e => {
|
|
133
|
+
e.stopPropagation()
|
|
134
|
+
store.getState().onOpenSourceElement?.(element.id)
|
|
135
|
+
}}>
|
|
136
|
+
<IconFileSymlink
|
|
137
|
+
// stroke={1.2}
|
|
138
|
+
style={{
|
|
139
|
+
width: '70%',
|
|
140
|
+
height: '70%'
|
|
141
|
+
}} />
|
|
142
|
+
</ActionIcon>
|
|
143
|
+
<ActionIcon
|
|
144
|
+
size={'md'}
|
|
145
|
+
variant="default"
|
|
146
|
+
color="dark"
|
|
147
|
+
>
|
|
148
|
+
<ColorSwatch
|
|
149
|
+
color={defaultTheme.elements[element.color as ThemeColor]?.fill}
|
|
150
|
+
size={16}
|
|
151
|
+
withShadow
|
|
152
|
+
style={{ color: '#fff', cursor: 'pointer' }}
|
|
153
|
+
/>
|
|
154
|
+
</ActionIcon>
|
|
155
|
+
<ActionIcon
|
|
156
|
+
size={'md'}
|
|
157
|
+
variant="default"
|
|
158
|
+
color="dark"
|
|
159
|
+
w={'fit-content'}
|
|
160
|
+
fz={'xs'}
|
|
161
|
+
>
|
|
162
|
+
{element.shape}
|
|
163
|
+
</ActionIcon>
|
|
164
|
+
|
|
165
|
+
</ActionIconGroup>
|
|
166
|
+
</NodeToolbar> */
|
|
167
|
+
}
|
|
168
|
+
|
|
126
169
|
<Handle
|
|
127
170
|
type="target"
|
|
128
171
|
position={Position.Top}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { rem } from '@mantine/core'
|
|
2
2
|
import { createVar, fallbackVar, globalStyle, keyframes, style } from '@vanilla-extract/css'
|
|
3
|
-
import { mantine } from '../../../
|
|
4
|
-
import { vars } from '../../../theme.css'
|
|
3
|
+
import { mantine, vars } from '../../../theme-vars'
|
|
5
4
|
|
|
6
5
|
export const stokeFillMix = createVar('stroke-fill-mix')
|
|
7
6
|
|
package/src/xyflow/types.ts
CHANGED
|
@@ -1,17 +1,6 @@
|
|
|
1
1
|
import type { BBox, DiagramEdge, DiagramNode, Fqn, XYPoint } 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'
|
|
2
|
+
import type { Edge, InternalNode, Node, ReactFlowInstance, ReactFlowState } from '@xyflow/react'
|
|
13
3
|
import { isNode } from '@xyflow/react'
|
|
14
|
-
import type { MouseEvent as ReactMouseEvent } from 'react'
|
|
15
4
|
import { isTruthy } from 'remeda'
|
|
16
5
|
import type { Simplify } from 'type-fest'
|
|
17
6
|
|
|
@@ -44,7 +33,6 @@ export type RelationshipData = {
|
|
|
44
33
|
edge: DiagramEdge
|
|
45
34
|
// if set - edge was changed by user
|
|
46
35
|
controlPoints: XYPoint[] | null
|
|
47
|
-
stepNum: number | null
|
|
48
36
|
label: null | {
|
|
49
37
|
bbox: BBox
|
|
50
38
|
text: string
|
|
@@ -69,14 +57,3 @@ export namespace XYFlowEdge {
|
|
|
69
57
|
|
|
70
58
|
export type XYFlowInstance = ReactFlowInstance<XYFlowNode, XYFlowEdge>
|
|
71
59
|
export type XYFlowState = ReactFlowState<XYFlowNode, XYFlowEdge>
|
|
72
|
-
|
|
73
|
-
export type XYFlowEventHandlers = {
|
|
74
|
-
onPanelClick: (event: ReactMouseEvent) => void
|
|
75
|
-
onNodeContextMenu: NodeMouseHandler<XYFlowNode>
|
|
76
|
-
onEdgeContextMenu: EdgeMouseHandler<XYFlowEdge>
|
|
77
|
-
onPaneContextMenu: (event: ReactMouseEvent | MouseEvent) => void
|
|
78
|
-
onNodeClick: NodeMouseHandler<XYFlowNode>
|
|
79
|
-
onEdgeClick: EdgeMouseHandler<XYFlowEdge>
|
|
80
|
-
onMoveStart: OnMoveStart
|
|
81
|
-
onMoveEnd: OnMoveEnd
|
|
82
|
-
}
|
|
@@ -181,12 +181,16 @@ function createLayoutConstraints(
|
|
|
181
181
|
}
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
+
const draggingNodes = new Set(nodeLookup.values()
|
|
185
|
+
.filter(x => x.dragging === true || x.id === draggingNodeId || x.selected)
|
|
186
|
+
.map(x => x.id))
|
|
187
|
+
|
|
184
188
|
while (traverse.length > 0) {
|
|
185
189
|
const { xynode, parent } = traverse.shift()!
|
|
186
|
-
const isDragging =
|
|
190
|
+
const isDragging = draggingNodes.has(xynode.id)
|
|
187
191
|
|
|
188
192
|
// Traverse children if the node is a compound, not dragging, and is an ancestor of the dragging node
|
|
189
|
-
const shouldTraverse = !isDragging && xynode.type === 'compound' && isAncestor(xynode.id,
|
|
193
|
+
const shouldTraverse = !isDragging && xynode.type === 'compound' && draggingNodes.values().some(x => isAncestor(xynode.id, x))
|
|
190
194
|
|
|
191
195
|
const rect = shouldTraverse ? new Compound(solver, xynode, parent) : new Leaf(solver, xynode, parent, isDragging)
|
|
192
196
|
rects.set(xynode.id, rect)
|
|
@@ -203,8 +207,7 @@ function createLayoutConstraints(
|
|
|
203
207
|
|
|
204
208
|
solver.updateVariables()
|
|
205
209
|
solver.maxIterations = 1000
|
|
206
|
-
|
|
207
|
-
const rectsToUpdate = [...rects.values()].filter(r => r.id !== draggingNodeId)
|
|
210
|
+
const rectsToUpdate = [...rects.values()].filter(r => !draggingNodes.has(r.id))
|
|
208
211
|
|
|
209
212
|
function updateXYFlowNodes() {
|
|
210
213
|
solver.updateVariables()
|
|
@@ -243,8 +246,8 @@ function createLayoutConstraints(
|
|
|
243
246
|
animationFrameId = null
|
|
244
247
|
const pos = nodeLookup.get(xynode.id)!.internals.positionAbsolute
|
|
245
248
|
const rect = nonNullable(rects.get(xynode.id))
|
|
246
|
-
solver.suggestValue(rect.minX, Math.
|
|
247
|
-
solver.suggestValue(rect.minY, Math.
|
|
249
|
+
solver.suggestValue(rect.minX, Math.floor(pos.x))
|
|
250
|
+
solver.suggestValue(rect.minY, Math.floor(pos.y))
|
|
248
251
|
updateXYFlowNodes()
|
|
249
252
|
})
|
|
250
253
|
}
|
|
@@ -273,6 +276,7 @@ export function useLayoutConstraints(): LayoutConstraints {
|
|
|
273
276
|
solverRef.current?.onNodeDrag(xynode)
|
|
274
277
|
},
|
|
275
278
|
onNodeDragStop: (_event, _xynode) => {
|
|
279
|
+
solverRef.current?.updateXYFlowNodes()
|
|
276
280
|
diagramApi.getState().scheduleSaveManualLayout()
|
|
277
281
|
solverRef.current = undefined
|
|
278
282
|
}
|
package/src/xyflow/utils.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { hasAtLeast, invariant } from '@likec4/core'
|
|
2
|
-
import type { DiagramEdge, Point } from '@likec4/core
|
|
2
|
+
import type { DiagramEdge, Point } from '@likec4/core'
|
|
3
3
|
import type { Rect, XYPosition } from '@xyflow/react'
|
|
4
4
|
import { Bezier } from 'bezier-js'
|
|
5
5
|
import { isArray } from 'remeda'
|
package/dist/LikeC4Diagram.d.ts
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import { LikeC4DiagramEventHandlers, LikeC4DiagramProperties } from './LikeC4Diagram.props';
|
|
2
|
-
|
|
3
|
-
export type LikeC4DiagramProps = LikeC4DiagramProperties & LikeC4DiagramEventHandlers;
|
|
4
|
-
export declare function LikeC4Diagram({ view, className, fitView, fitViewPadding, readonly, pannable, zoomable, nodesSelectable, nodesDraggable, background, controls, showElementLinks, showDiagramTitle, showNotations, enableDynamicViewWalkthrough, enableFocusMode, initialWidth, initialHeight, keepAspectRatio, experimentalEdgeEditing, onCanvasClick, onCanvasContextMenu, onCanvasDblClick, onEdgeClick, onChange, onEdgeContextMenu, onNavigateTo, onNodeClick, onNodeContextMenu, onOpenSourceElement, onOpenSourceView, onOpenSourceRelation, onBurgerMenuClick, renderIcon, where, showNavigationButtons }: LikeC4DiagramProps): import("react/jsx-runtime").JSX.Element;
|