@likec4/diagram 1.27.0 → 1.27.2
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/bundle/index.js +2343 -6880
- package/dist/LikeC4Diagram.js +3 -9
- package/dist/LikeC4Diagram.props.d.ts +5 -3
- package/dist/StaticLikeC4Diagram.js +1 -1
- package/dist/_virtual/set.js +5 -0
- package/dist/_virtual/set2.js +3 -0
- package/dist/base/BaseXYFlow.d.ts +2 -1
- package/dist/base/primitives/compound/CompoundNodeContainer.d.ts +1 -1
- package/dist/base/primitives/compound/CompoundNodeContainer.js +1 -2
- package/dist/base/primitives/edge/EdgeLabel.css.js +1 -4
- package/dist/base/primitives/edge/EdgeLabelContainer.js +1 -1
- package/dist/base/primitives/edge/EdgePath.js +0 -3
- package/dist/base/primitives/edge/edge.css.js +2 -0
- package/dist/base/primitives/element/ElementActionButtons.js +5 -10
- package/dist/base/primitives/element/ElementNodeContainer.css.js +5 -4
- package/dist/base/primitives/element/ElementShape.css.js +1 -12
- package/dist/base/primitives/element/ElementTitle.css.js +3 -2
- package/dist/base/types.js +2 -2
- package/dist/base/updateEdges.js +1 -9
- package/dist/base/updateNodes.js +1 -12
- package/dist/bundle/LikeC4View.js +2 -2
- package/dist/bundle/LikeC4View.props.d.ts +7 -7
- package/dist/components/Link.js +1 -1
- package/dist/hooks/useCurrentViewId.js +8 -2
- package/dist/hooks/useDiagram.d.ts +2659 -1996
- package/dist/hooks/useDiagram.js +16 -8
- package/dist/likec4diagram/DiagramXYFlow.js +10 -9
- package/dist/likec4diagram/custom/edges/RelationshipEdge.js +30 -41
- package/dist/likec4diagram/custom/nodes/toolbar/Toolbar.js +1 -1
- package/dist/likec4diagram/custom/nodes/toolbar/styles.css.js +6 -1
- package/dist/likec4diagram/ui/controls/LayoutDriftNotification.js +13 -5
- package/dist/likec4diagram/ui/notation/NotationPanel.css.js +3 -3
- package/dist/likec4diagram/ui/notation/NotationPanel.js +10 -4
- package/dist/likec4diagram/useLayoutConstraints.d.ts +11 -10
- package/dist/likec4diagram/useLayoutConstraints.js +64 -61
- package/dist/node_modules/.pnpm/mnemonist@0.40.3_patch_hash_5c12abb43fcff6fd81e53021b89075d53249640d165a2556cc1e58d71201a13a/node_modules/mnemonist/set.js +369 -0
- package/dist/overlays/Overlays.js +5 -3
- package/dist/overlays/element-details/actor.d.ts +2769 -173
- package/dist/overlays/overlay/Overlay.css.d.ts +26 -2
- package/dist/overlays/overlay/Overlay.css.js +83 -16
- package/dist/overlays/overlay/Overlay.d.ts +7 -6
- package/dist/overlays/overlay/Overlay.js +39 -32
- package/dist/overlays/overlaysActor.js +2 -2
- package/dist/overlays/relationship-details/RelationshipDetails.js +113 -70
- package/dist/overlays/relationship-details/SelectEdge.d.ts +1 -1
- package/dist/overlays/relationship-details/SelectEdge.js +15 -9
- package/dist/overlays/relationship-details/_types.d.ts +4 -1
- package/dist/overlays/relationship-details/actor.d.ts +415 -128
- package/dist/overlays/relationship-details/actor.js +162 -53
- package/dist/overlays/relationship-details/compute.d.ts +12 -0
- package/dist/overlays/relationship-details/compute.js +139 -0
- package/dist/overlays/relationship-details/custom/ElementActions.js +27 -5
- package/dist/overlays/relationship-details/custom/edgeTypes.js +1 -1
- package/dist/overlays/relationship-details/custom/nodeTypes.d.ts +2 -1
- package/dist/overlays/relationship-details/hooks.d.ts +336 -6
- package/dist/overlays/relationship-details/hooks.js +9 -7
- package/dist/overlays/relationship-details/layout-to-xyflow.d.ts +12 -0
- package/dist/overlays/relationship-details/layout-to-xyflow.js +149 -0
- package/dist/overlays/relationship-details/layout.d.ts +22 -0
- package/dist/overlays/relationship-details/layout.js +314 -0
- package/dist/overlays/relationships-browser/RelationshipsBrowser.d.ts +3 -1
- package/dist/overlays/relationships-browser/RelationshipsBrowser.js +2 -2
- package/dist/overlays/relationships-browser/SelectElement.js +4 -4
- package/dist/overlays/relationships-browser/_types.d.ts +2 -0
- package/dist/overlays/relationships-browser/actor.d.ts +130 -14
- package/dist/overlays/relationships-browser/actor.js +30 -16
- package/dist/overlays/relationships-browser/custom/ElementActions.js +5 -3
- package/dist/overlays/relationships-browser/custom/edgeTypes.d.ts +3 -1
- package/dist/overlays/relationships-browser/custom/edgeTypes.js +1 -1
- package/dist/overlays/relationships-browser/hooks.d.ts +3 -1
- package/dist/overlays/relationships-browser/hooks.js +1 -1
- package/dist/overlays/relationships-browser/{-useRelationshipsView.d.ts → layout.d.ts} +2 -0
- package/dist/overlays/relationships-browser/{-useRelationshipsView.js → layout.js} +19 -2
- package/dist/overlays/relationships-browser/useViewToNodesEdges.d.ts +1 -1
- package/dist/overlays/relationships-browser/useViewToNodesEdges.js +3 -1
- package/dist/packages/core/src/compute-view/relationships-view/compute.js +1 -1
- package/dist/packages/core/src/model/connection/model/ConnectionModel.js +121 -0
- package/dist/packages/core/src/model/connection/model/find.js +39 -0
- package/dist/packages/core/src/model/connection/ops.js +7 -0
- package/dist/packages/core/src/utils/const.js +3 -0
- package/dist/packages/core/src/utils/fqn.js +9 -1
- package/dist/packages/core/src/utils/iterable/find.js +21 -0
- package/dist/packages/core/src/utils/iterable/some.js +21 -0
- package/dist/packages/core/src/utils/set.js +48 -0
- package/dist/packages/core/src/utils/string-hash.js +13 -0
- package/dist/state/DiagramActorProvider.d.ts +4 -2
- package/dist/state/DiagramActorProvider.js +30 -37
- package/dist/state/assign.js +2 -2
- package/dist/state/diagram-machine.d.ts +1731 -1040
- package/dist/state/diagram-machine.js +126 -60
- package/dist/state/syncManualLayoutActor.d.ts +1 -1
- package/dist/styles.css.js +1 -1
- package/package.json +11 -11
- package/src/LikeC4Diagram.props.ts +7 -3
- package/src/LikeC4Diagram.tsx +2 -8
- package/src/StaticLikeC4Diagram.tsx +1 -1
- package/src/base/BaseXYFlow.tsx +2 -1
- package/src/base/primitives/compound/CompoundNodeContainer.tsx +1 -3
- package/src/base/primitives/edge/EdgeLabel.css.ts +1 -4
- package/src/base/primitives/edge/EdgeLabelContainer.tsx +1 -1
- package/src/base/primitives/edge/EdgePath.tsx +0 -3
- package/src/base/primitives/edge/edge.css.ts +2 -0
- package/src/base/primitives/element/ElementActionButtons.tsx +6 -7
- package/src/base/primitives/element/ElementNodeContainer.css.ts +5 -4
- package/src/base/primitives/element/ElementShape.css.ts +1 -12
- package/src/base/primitives/element/ElementTitle.css.ts +3 -2
- package/src/base/types.ts +2 -7
- package/src/base/updateEdges.ts +8 -8
- package/src/base/updateNodes.ts +11 -11
- package/src/bundle/LikeC4View.props.ts +7 -7
- package/src/bundle/LikeC4View.tsx +2 -2
- package/src/components/Link.tsx +3 -1
- package/src/hooks/useCurrentViewId.ts +4 -2
- package/src/hooks/useDiagram.ts +18 -9
- package/src/likec4diagram/DiagramXYFlow.tsx +11 -9
- package/src/likec4diagram/custom/edges/RelationshipEdge.tsx +39 -48
- package/src/likec4diagram/custom/nodes/toolbar/Toolbar.tsx +2 -2
- package/src/likec4diagram/custom/nodes/toolbar/styles.css.ts +5 -0
- package/src/likec4diagram/ui/controls/LayoutDriftNotification.tsx +8 -4
- package/src/likec4diagram/ui/notation/NotationPanel.css.ts +2 -2
- package/src/likec4diagram/ui/notation/NotationPanel.tsx +8 -6
- package/src/likec4diagram/useLayoutConstraints.ts +84 -89
- package/src/overlays/Overlays.tsx +5 -3
- package/src/overlays/overlay/Overlay.css.ts +83 -15
- package/src/overlays/overlay/Overlay.tsx +52 -41
- package/src/overlays/overlaysActor.ts +1 -1
- package/src/overlays/relationship-details/RelationshipDetails.tsx +131 -68
- package/src/overlays/relationship-details/SelectEdge.tsx +19 -12
- package/src/overlays/relationship-details/_types.ts +3 -1
- package/src/overlays/relationship-details/actor.ts +217 -68
- package/src/overlays/relationship-details/compute.ts +186 -0
- package/src/overlays/relationship-details/custom/ElementActions.tsx +27 -18
- package/src/overlays/relationship-details/custom/edgeTypes.tsx +1 -1
- package/src/overlays/relationship-details/hooks.ts +10 -8
- package/src/overlays/relationship-details/layout-to-xyflow.ts +195 -0
- package/src/overlays/relationship-details/layout.ts +470 -0
- package/src/overlays/relationships-browser/RelationshipsBrowser.tsx +2 -2
- package/src/overlays/relationships-browser/SelectElement.tsx +5 -5
- package/src/overlays/relationships-browser/_types.ts +2 -0
- package/src/overlays/relationships-browser/actor.ts +39 -14
- package/src/overlays/relationships-browser/custom/ElementActions.tsx +5 -3
- package/src/overlays/relationships-browser/custom/edgeTypes.tsx +1 -1
- package/src/overlays/relationships-browser/hooks.ts +2 -2
- package/src/overlays/relationships-browser/{-useRelationshipsView.ts → layout.ts} +23 -13
- package/src/overlays/relationships-browser/useViewToNodesEdges.ts +3 -1
- package/src/state/DiagramActorProvider.tsx +40 -50
- package/src/state/assign.ts +2 -2
- package/src/state/diagram-machine.ts +151 -75
- package/dist/overlays/relationship-details/useLayoutedDetails.d.ts +0 -19
- package/dist/overlays/relationship-details/useLayoutedDetails.js +0 -267
- package/src/overlays/relationship-details/useLayoutedDetails.ts +0 -376
|
@@ -132,7 +132,7 @@ export const RelationshipEdge = customEdge<Types.RelationshipEdgeData>((props) =
|
|
|
132
132
|
return
|
|
133
133
|
}
|
|
134
134
|
// This update stays only in internal xystate, not in diagram xstate
|
|
135
|
-
|
|
135
|
+
diagram.updateEdgeData(id as EdgeId, {
|
|
136
136
|
labelXY: {
|
|
137
137
|
x: labelPos.x,
|
|
138
138
|
y: labelPos.y,
|
|
@@ -169,15 +169,13 @@ export const RelationshipEdge = customEdge<Types.RelationshipEdgeData>((props) =
|
|
|
169
169
|
hasMoved = true
|
|
170
170
|
pointer = clientPoint
|
|
171
171
|
const { x, y } = xyflow.screenToFlowPosition(pointer, { snapToGrid: false })
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
controlPoints: cp,
|
|
180
|
-
}
|
|
172
|
+
const cp = controlPoints.slice()
|
|
173
|
+
cp[index] = {
|
|
174
|
+
x: Math.round(x),
|
|
175
|
+
y: Math.round(y),
|
|
176
|
+
}
|
|
177
|
+
diagram.updateEdgeData(id as EdgeId, {
|
|
178
|
+
controlPoints: cp,
|
|
181
179
|
})
|
|
182
180
|
}
|
|
183
181
|
e.stopPropagation()
|
|
@@ -216,14 +214,9 @@ export const RelationshipEdge = customEdge<Types.RelationshipEdgeData>((props) =
|
|
|
216
214
|
e.stopPropagation()
|
|
217
215
|
// Defer the update to avoid conflict with the pointerup event
|
|
218
216
|
setTimeout(() => {
|
|
219
|
-
|
|
217
|
+
diagram.updateEdgeData(id as EdgeId, { controlPoints: newControlPoints })
|
|
220
218
|
diagram.scheduleSaveManualLayout()
|
|
221
219
|
}, 10)
|
|
222
|
-
|
|
223
|
-
domNode.removeEventListener('pointerup', onPointerUp, {
|
|
224
|
-
capture: true,
|
|
225
|
-
})
|
|
226
|
-
e.stopPropagation()
|
|
227
220
|
}
|
|
228
221
|
|
|
229
222
|
domNode.addEventListener('pointerup', onPointerUp, {
|
|
@@ -259,42 +252,40 @@ export const RelationshipEdge = customEdge<Types.RelationshipEdgeData>((props) =
|
|
|
259
252
|
if (e.button !== 2) {
|
|
260
253
|
return
|
|
261
254
|
}
|
|
255
|
+
const points: VectorImpl[] = [
|
|
256
|
+
new VectorImpl(sourceX, sourceY),
|
|
257
|
+
...controlPoints.map(vector) || [],
|
|
258
|
+
new VectorImpl(targetX, targetY),
|
|
259
|
+
]
|
|
262
260
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
// Calculate distance by approximating edge segment with a staight line
|
|
284
|
-
const distanceToEdge = Math.abs(fromCurrentToNext.cross(fromCurrentToNew).abs() / fromCurrentToNext.abs())
|
|
285
|
-
|
|
286
|
-
if (distanceToEdge < minDistance) {
|
|
287
|
-
minDistance = distanceToEdge
|
|
288
|
-
insertionIndex = i
|
|
289
|
-
}
|
|
261
|
+
let pointer = { x: e.clientX, y: e.clientY }
|
|
262
|
+
const newPoint = vector(xyflow.screenToFlowPosition(pointer, { snapToGrid: false }))
|
|
263
|
+
|
|
264
|
+
let insertionIndex = 0
|
|
265
|
+
let minDistance = Infinity
|
|
266
|
+
for (let i = 0; i < points.length - 1; i++) {
|
|
267
|
+
const a = points[i]!,
|
|
268
|
+
b = points[i + 1]!,
|
|
269
|
+
fromCurrentToNext = b.sub(a),
|
|
270
|
+
fromCurrentToNew = newPoint.sub(a),
|
|
271
|
+
fromNextToNew = newPoint.sub(b)
|
|
272
|
+
|
|
273
|
+
// Is pointer above the current segment?
|
|
274
|
+
if (fromCurrentToNext.dot(fromCurrentToNew) * fromCurrentToNext.dot(fromNextToNew) < 0) {
|
|
275
|
+
// Calculate distance by approximating edge segment with a staight line
|
|
276
|
+
const distanceToEdge = Math.abs(fromCurrentToNext.cross(fromCurrentToNew).abs() / fromCurrentToNext.abs())
|
|
277
|
+
|
|
278
|
+
if (distanceToEdge < minDistance) {
|
|
279
|
+
minDistance = distanceToEdge
|
|
280
|
+
insertionIndex = i
|
|
290
281
|
}
|
|
291
282
|
}
|
|
283
|
+
}
|
|
292
284
|
|
|
293
|
-
|
|
294
|
-
|
|
285
|
+
const newControlPoints = controlPoints.slice() || []
|
|
286
|
+
newControlPoints.splice(insertionIndex, 0, newPoint)
|
|
295
287
|
|
|
296
|
-
|
|
297
|
-
})
|
|
288
|
+
diagram.updateEdgeData(id as EdgeId, { controlPoints: newControlPoints })
|
|
298
289
|
|
|
299
290
|
diagram.scheduleSaveManualLayout()
|
|
300
291
|
|
|
@@ -357,7 +348,7 @@ export const RelationshipEdge = customEdge<Types.RelationshipEdgeData>((props) =
|
|
|
357
348
|
</EdgeLabelContainer>
|
|
358
349
|
</EdgeContainer>
|
|
359
350
|
{/* Render control points above edge label */}
|
|
360
|
-
{enableEdgeEditing && controlPoints.length > 0 && (
|
|
351
|
+
{enableEdgeEditing && controlPoints.length > 0 && (selected || hovered || isControlPointDragging) && (
|
|
361
352
|
<EdgeLabelRenderer>
|
|
362
353
|
<EdgeContainer
|
|
363
354
|
component="svg"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Box, Group, Paper, Stack, Text } from '@mantine/core'
|
|
1
|
+
import { Box, Group, Paper, Stack, Text, Tooltip } from '@mantine/core'
|
|
2
2
|
import { useDebouncedValue } from '@mantine/hooks'
|
|
3
3
|
import { type NodeToolbarProps, NodeToolbar } from '@xyflow/react'
|
|
4
4
|
import clsx from 'clsx'
|
|
@@ -69,7 +69,7 @@ export function Toolbar({ title, children, nodeProps, ...props }: ToolbarProps)
|
|
|
69
69
|
withBorder>
|
|
70
70
|
<Stack gap={'6px'}>
|
|
71
71
|
<Box px={'4px'}>
|
|
72
|
-
<Text
|
|
72
|
+
<Text className={styles.toolbarTitle}>{title}</Text>
|
|
73
73
|
</Box>
|
|
74
74
|
<Group gap={4}>
|
|
75
75
|
{children}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { css } from '@likec4/styles/css'
|
|
2
2
|
|
|
3
3
|
export const toolbarTitle = css({
|
|
4
|
+
color: 'mantine.colors.dimmed',
|
|
4
5
|
fontSize: '10px',
|
|
5
6
|
fontWeight: 600,
|
|
7
|
+
maxWidth: '220px',
|
|
6
8
|
cursor: 'default',
|
|
7
9
|
userSelect: 'all',
|
|
10
|
+
textOverflow: 'ellipsis',
|
|
11
|
+
overflow: 'hidden',
|
|
12
|
+
whiteSpace: 'nowrap',
|
|
8
13
|
})
|
|
@@ -5,14 +5,13 @@ import {
|
|
|
5
5
|
HoverCardDropdown,
|
|
6
6
|
HoverCardTarget,
|
|
7
7
|
Notification,
|
|
8
|
-
Overlay,
|
|
9
8
|
Text,
|
|
10
9
|
} from '@mantine/core'
|
|
11
10
|
import {
|
|
12
11
|
IconAlertTriangle,
|
|
13
12
|
} from '@tabler/icons-react'
|
|
14
13
|
|
|
15
|
-
|
|
14
|
+
import { css } from '@likec4/styles/css'
|
|
16
15
|
import { ActionIcon } from './_shared'
|
|
17
16
|
|
|
18
17
|
export const LayoutDriftNotification = (props: PopoverProps) => (
|
|
@@ -22,8 +21,13 @@ export const LayoutDriftNotification = (props: PopoverProps) => (
|
|
|
22
21
|
closeDelay={100}
|
|
23
22
|
{...props}>
|
|
24
23
|
<HoverCardTarget>
|
|
25
|
-
<ActionIcon
|
|
26
|
-
|
|
24
|
+
<ActionIcon
|
|
25
|
+
color={'orange'}
|
|
26
|
+
c="orange"
|
|
27
|
+
className={css({
|
|
28
|
+
bg: 'mantine.colors.orange.light',
|
|
29
|
+
})}
|
|
30
|
+
>
|
|
27
31
|
<IconAlertTriangle />
|
|
28
32
|
</ActionIcon>
|
|
29
33
|
</HoverCardTarget>
|
|
@@ -98,6 +98,6 @@ export const shapeBadge = css({
|
|
|
98
98
|
['--badge-padding-x']: '3px',
|
|
99
99
|
['--badge-height']: '13.5px',
|
|
100
100
|
['--badge-lh']: '1',
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
['--badge-bg']: '{colors.likec4.palette.fill}',
|
|
102
|
+
['--badge-color']: '{colors.likec4.palette.hiContrast}',
|
|
103
103
|
})
|
|
@@ -54,13 +54,14 @@ const ElementNotation = ({ value }: { value: ElementNotationData }) => {
|
|
|
54
54
|
likec4Palette: color,
|
|
55
55
|
}),
|
|
56
56
|
)}
|
|
57
|
+
data-likec4-color={color}
|
|
57
58
|
onMouseEnter={() => {
|
|
58
59
|
setOnlyKind(null)
|
|
59
|
-
|
|
60
|
+
diagram.highlightNotation(value)
|
|
60
61
|
}}
|
|
61
62
|
onMouseLeave={() => {
|
|
62
63
|
setOnlyKind(null)
|
|
63
|
-
|
|
64
|
+
diagram.unhighlightNotation()
|
|
64
65
|
}}
|
|
65
66
|
>
|
|
66
67
|
<Group
|
|
@@ -88,17 +89,18 @@ const ElementNotation = ({ value }: { value: ElementNotationData }) => {
|
|
|
88
89
|
{value.kinds.map((kind) => (
|
|
89
90
|
<Badge
|
|
90
91
|
key={kind}
|
|
91
|
-
className={
|
|
92
|
+
className={cx(
|
|
93
|
+
styles.shapeBadge,
|
|
94
|
+
)}
|
|
92
95
|
onMouseEnter={() => {
|
|
93
96
|
setOnlyKind(kind)
|
|
94
|
-
|
|
97
|
+
diagram.highlightNotation(value, kind)
|
|
95
98
|
}}
|
|
96
99
|
onMouseLeave={() => {
|
|
97
100
|
setOnlyKind(null)
|
|
98
|
-
|
|
101
|
+
diagram.highlightNotation(value)
|
|
99
102
|
}}
|
|
100
103
|
opacity={isNonNullish(onlyKind) && onlyKind !== kind ? 0.25 : 1}
|
|
101
|
-
color={isNonNullish(onlyKind) && onlyKind !== kind ? 'gray' : token('colors.likec4.palette.fill')}
|
|
102
104
|
>
|
|
103
105
|
{kind}
|
|
104
106
|
</Badge>
|
|
@@ -58,11 +58,11 @@ abstract class Rect {
|
|
|
58
58
|
y: positionAbsolute.y - parentPosition.y,
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
|
-
protected abstract parent:
|
|
61
|
+
protected abstract parent: CompoundRect | null
|
|
62
62
|
|
|
63
63
|
constructor(
|
|
64
64
|
xynode: InternalNode,
|
|
65
|
-
parent:
|
|
65
|
+
parent: CompoundRect | null = null,
|
|
66
66
|
) {
|
|
67
67
|
this.id = xynode.id
|
|
68
68
|
|
|
@@ -82,12 +82,12 @@ abstract class Rect {
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
class
|
|
85
|
+
class CompoundRect extends Rect {
|
|
86
86
|
public readonly children = [] as Rect[]
|
|
87
87
|
|
|
88
88
|
constructor(
|
|
89
89
|
xynode: InternalNode,
|
|
90
|
-
public readonly parent:
|
|
90
|
+
public readonly parent: CompoundRect | null = null,
|
|
91
91
|
) {
|
|
92
92
|
super(xynode, parent)
|
|
93
93
|
}
|
|
@@ -96,22 +96,18 @@ class Compound extends Rect {
|
|
|
96
96
|
class Leaf extends Rect {
|
|
97
97
|
constructor(
|
|
98
98
|
xynode: InternalNode,
|
|
99
|
-
public readonly parent:
|
|
99
|
+
public readonly parent: CompoundRect | null = null,
|
|
100
100
|
) {
|
|
101
101
|
super(xynode, parent)
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
type NodePositionUpdater = (
|
|
106
|
-
nodes: Array<{ rect: Rect | Compound; node: InternalNode }>,
|
|
107
|
-
) => void
|
|
108
|
-
|
|
109
105
|
export function createLayoutConstraints(
|
|
110
106
|
xyflowApi: XYStoreApi,
|
|
111
107
|
editingNodeIds: NonEmptyArray<string>,
|
|
112
108
|
) {
|
|
113
109
|
const { parentLookup, nodeLookup } = xyflowApi.getState()
|
|
114
|
-
const rects = new Map<string, Leaf |
|
|
110
|
+
const rects = new Map<string, Leaf | CompoundRect>()
|
|
115
111
|
|
|
116
112
|
const ancestorsOf = (nodeId: string) => {
|
|
117
113
|
const ancestors = [] as string[]
|
|
@@ -129,36 +125,31 @@ export function createLayoutConstraints(
|
|
|
129
125
|
editingNodeIds.flatMap(ancestorsOf),
|
|
130
126
|
)
|
|
131
127
|
|
|
132
|
-
const traverse
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (isNullish(xynode.parentId)) {
|
|
136
|
-
traverse.push({
|
|
137
|
-
xynode,
|
|
138
|
-
parent: null,
|
|
139
|
-
})
|
|
140
|
-
}
|
|
141
|
-
}
|
|
128
|
+
const traverse: Array<{ xynode: InternalNode; parent: CompoundRect | null }> = [...nodeLookup.values()].flatMap(x =>
|
|
129
|
+
!x.parentId ? { xynode: x, parent: null } : []
|
|
130
|
+
)
|
|
142
131
|
|
|
143
132
|
while (traverse.length > 0) {
|
|
144
133
|
const { xynode, parent } = traverse.shift()!
|
|
145
134
|
const isEditing = editingNodeIds.includes(xynode.id)
|
|
146
135
|
|
|
147
136
|
// Traverse children if the node is a compound, not dragging, and is an ancestor of the dragging node
|
|
148
|
-
const shouldTraverse = !isEditing
|
|
149
|
-
|
|
137
|
+
const shouldTraverse = !isEditing
|
|
138
|
+
&& xynode.type !== 'element'
|
|
139
|
+
&& xynode.type !== 'deployment'
|
|
150
140
|
&& ancestorsOfDraggingNodes.has(xynode.id)
|
|
151
141
|
|
|
152
|
-
const rect = shouldTraverse ? new Compound(xynode, parent) : new Leaf(xynode, parent)
|
|
153
|
-
rects.set(xynode.id, rect)
|
|
154
|
-
|
|
155
142
|
if (shouldTraverse) {
|
|
143
|
+
const rect = new CompoundRect(xynode, parent)
|
|
144
|
+
rects.set(xynode.id, rect)
|
|
156
145
|
parentLookup.get(xynode.id)?.forEach(child => {
|
|
157
146
|
traverse.push({
|
|
158
147
|
xynode: child,
|
|
159
|
-
parent: rect as
|
|
148
|
+
parent: rect as CompoundRect,
|
|
160
149
|
})
|
|
161
150
|
})
|
|
151
|
+
} else {
|
|
152
|
+
rects.set(xynode.id, new Leaf(xynode, parent))
|
|
162
153
|
}
|
|
163
154
|
}
|
|
164
155
|
|
|
@@ -166,28 +157,32 @@ export function createLayoutConstraints(
|
|
|
166
157
|
applyConstraints(rectsToUpdate)
|
|
167
158
|
|
|
168
159
|
function applyConstraints(targets: Rect[]) {
|
|
169
|
-
targets
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const childrenBB = r.children.reduce((acc, r) => ({
|
|
175
|
-
minX: Math.min(acc.minX, r.minX),
|
|
176
|
-
minY: Math.min(acc.minY, r.minY),
|
|
177
|
-
maxX: Math.max(acc.maxX, r.maxX),
|
|
178
|
-
maxY: Math.max(acc.maxY, r.maxY),
|
|
179
|
-
}), {
|
|
180
|
-
minX: Infinity,
|
|
181
|
-
minY: Infinity,
|
|
182
|
-
maxX: -Infinity,
|
|
183
|
-
maxY: -Infinity,
|
|
184
|
-
})
|
|
160
|
+
for (const r of targets) {
|
|
161
|
+
if (!(r instanceof CompoundRect)) {
|
|
162
|
+
continue
|
|
163
|
+
}
|
|
185
164
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
165
|
+
applyConstraints(r.children)
|
|
166
|
+
|
|
167
|
+
const childrenBB = {
|
|
168
|
+
minX: Infinity,
|
|
169
|
+
minY: Infinity,
|
|
170
|
+
maxX: -Infinity,
|
|
171
|
+
maxY: -Infinity,
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
for (const child of r.children) {
|
|
175
|
+
childrenBB.minX = Math.min(childrenBB.minX, child.minX)
|
|
176
|
+
childrenBB.minY = Math.min(childrenBB.minY, child.minY)
|
|
177
|
+
childrenBB.maxX = Math.max(childrenBB.maxX, child.maxX)
|
|
178
|
+
childrenBB.maxY = Math.max(childrenBB.maxY, child.maxY)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
r.minX = childrenBB.minX - Rect.LeftPadding
|
|
182
|
+
r.minY = childrenBB.minY - Rect.TopPadding
|
|
183
|
+
r.maxX = childrenBB.maxX + Rect.RightPadding
|
|
184
|
+
r.maxY = childrenBB.maxY + Rect.BottomPadding
|
|
185
|
+
}
|
|
191
186
|
}
|
|
192
187
|
|
|
193
188
|
function updateXYFlowNodes() {
|
|
@@ -201,7 +196,7 @@ export function createLayoutConstraints(
|
|
|
201
196
|
position: r.position,
|
|
202
197
|
positionAbsolute: r.positionAbsolute,
|
|
203
198
|
})
|
|
204
|
-
if (r instanceof
|
|
199
|
+
if (r instanceof CompoundRect) {
|
|
205
200
|
acc.push({
|
|
206
201
|
id: r.id,
|
|
207
202
|
type: 'dimensions',
|
|
@@ -216,29 +211,24 @@ export function createLayoutConstraints(
|
|
|
216
211
|
|
|
217
212
|
let animationFrameId: number | null = null
|
|
218
213
|
|
|
219
|
-
function onMove(
|
|
220
|
-
updater: NodePositionUpdater,
|
|
221
|
-
) {
|
|
214
|
+
function onMove() {
|
|
222
215
|
if (rectsToUpdate.length === 0) {
|
|
223
216
|
return
|
|
224
217
|
}
|
|
225
218
|
animationFrameId ??= requestAnimationFrame(() => {
|
|
226
219
|
animationFrameId = null
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
node: nonNullable(nodeLookup.get(id)),
|
|
234
|
-
})),
|
|
235
|
-
),
|
|
236
|
-
)
|
|
220
|
+
const { nodeLookup } = xyflowApi.getState()
|
|
221
|
+
for (const id of editingNodeIds) {
|
|
222
|
+
const rect = nonNullable(rects.get(id))
|
|
223
|
+
const node = nonNullable(nodeLookup.get(id))
|
|
224
|
+
rect.positionAbsolute = node.internals.positionAbsolute
|
|
225
|
+
}
|
|
237
226
|
updateXYFlowNodes()
|
|
238
227
|
})
|
|
239
228
|
}
|
|
240
229
|
|
|
241
230
|
return {
|
|
231
|
+
rects: rects as ReadonlyMap<string, Leaf | CompoundRect>,
|
|
242
232
|
updateXYFlowNodes,
|
|
243
233
|
onMove,
|
|
244
234
|
}
|
|
@@ -256,32 +246,37 @@ export function useLayoutConstraints(): LayoutConstraints {
|
|
|
256
246
|
const xystore = useXYStoreApi()
|
|
257
247
|
const diagram = useDiagram()
|
|
258
248
|
const solverRef = useRef<ReturnType<typeof createLayoutConstraints>>(undefined)
|
|
259
|
-
return useMemo((): LayoutConstraints =>
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
249
|
+
return useMemo((): LayoutConstraints => {
|
|
250
|
+
let wasPending = false
|
|
251
|
+
const initial = { x: 0, y: 0 }
|
|
252
|
+
let moved = false
|
|
253
|
+
return ({
|
|
254
|
+
onNodeDragStart: (_event, xynode) => {
|
|
255
|
+
wasPending = diagram.cancelSaveManualLayout()
|
|
256
|
+
const { nodeLookup } = xystore.getState()
|
|
257
|
+
const draggingNodes = pipe(
|
|
258
|
+
Array.from(nodeLookup.values()),
|
|
259
|
+
filter(n => n.draggable !== false && (n.dragging === true || n.id === xynode.id || n.selected === true)),
|
|
260
|
+
)
|
|
261
|
+
if (hasAtLeast(draggingNodes, 1)) {
|
|
262
|
+
solverRef.current = createLayoutConstraints(xystore, map(draggingNodes, x => x.id))
|
|
263
|
+
}
|
|
264
|
+
initial.x = _event.clientX
|
|
265
|
+
initial.y = _event.clientY
|
|
266
|
+
moved = false
|
|
267
|
+
},
|
|
268
|
+
onNodeDrag: (_event) => {
|
|
269
|
+
moved = Math.abs(_event.clientX - initial.x) > 4 || Math.abs(_event.clientY - initial.y) > 4
|
|
270
|
+
solverRef.current?.onMove()
|
|
271
|
+
},
|
|
272
|
+
onNodeDragStop: (_event) => {
|
|
273
|
+
moved = Math.abs(_event.clientX - initial.x) > 4 || Math.abs(_event.clientY - initial.y) > 4
|
|
274
|
+
console.log('moved', moved)
|
|
275
|
+
if (wasPending || moved) {
|
|
276
|
+
diagram.scheduleSaveManualLayout()
|
|
277
|
+
}
|
|
278
|
+
solverRef.current = undefined
|
|
279
|
+
},
|
|
280
|
+
})
|
|
281
|
+
}, [xystore, diagram])
|
|
287
282
|
}
|
|
@@ -2,7 +2,7 @@ import { nonexhaustive } from '@likec4/core'
|
|
|
2
2
|
import { Box, Button, Code, Group, Notification, ScrollAreaAutosize } from '@mantine/core'
|
|
3
3
|
import { IconX } from '@tabler/icons-react'
|
|
4
4
|
import { useSelector } from '@xstate/react'
|
|
5
|
-
import { AnimatePresence, LayoutGroup, useReducedMotion } from 'framer-motion'
|
|
5
|
+
import { AnimatePresence, LayoutGroup, useReducedMotion, useReducedMotionConfig } from 'framer-motion'
|
|
6
6
|
import { animate } from 'framer-motion/dom'
|
|
7
7
|
import { memo, useEffect, useMemo } from 'react'
|
|
8
8
|
import { type FallbackProps, ErrorBoundary } from 'react-error-boundary'
|
|
@@ -83,7 +83,7 @@ export const Overlays = memo(({ overlaysActorRef }: { overlaysActorRef: Overlays
|
|
|
83
83
|
xyflowDomNode,
|
|
84
84
|
])
|
|
85
85
|
const overlays = useSelector(overlaysActorRef, selectOverlays, compareSelectOverlays)
|
|
86
|
-
const isMotionReduced =
|
|
86
|
+
const isMotionReduced = useReducedMotionConfig() ?? false
|
|
87
87
|
|
|
88
88
|
const isActiveOverlay = overlays.length > 0
|
|
89
89
|
|
|
@@ -102,12 +102,13 @@ export const Overlays = memo(({ overlaysActorRef }: { overlaysActorRef: Overlays
|
|
|
102
102
|
overlaysActorRef.send({ type: 'close', actorId: actorRef.id })
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
const overlaysReact = overlays.map((overlay) => {
|
|
105
|
+
const overlaysReact = overlays.map((overlay, index) => {
|
|
106
106
|
switch (overlay.type) {
|
|
107
107
|
case 'relationshipsBrowser':
|
|
108
108
|
return (
|
|
109
109
|
<Overlay
|
|
110
110
|
key={overlay.actorRef.sessionId}
|
|
111
|
+
overlayLevel={Math.min(index, 3) as 0 | 1 | 2 | 3}
|
|
111
112
|
onClose={() => close(overlay.actorRef)}>
|
|
112
113
|
<RelationshipsBrowser actorRef={overlay.actorRef} />
|
|
113
114
|
</Overlay>
|
|
@@ -115,6 +116,7 @@ export const Overlays = memo(({ overlaysActorRef }: { overlaysActorRef: Overlays
|
|
|
115
116
|
case 'relationshipDetails':
|
|
116
117
|
return (
|
|
117
118
|
<Overlay
|
|
119
|
+
overlayLevel={Math.min(index, 3) as 0 | 1 | 2 | 3}
|
|
118
120
|
key={overlay.actorRef.sessionId}
|
|
119
121
|
onClose={() => close(overlay.actorRef)}>
|
|
120
122
|
<RelationshipDetails actorRef={overlay.actorRef} />
|