@likec4/diagram 1.27.0 → 1.27.1
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 +327 -281
- package/dist/LikeC4Diagram.js +2 -8
- package/dist/base/primitives/compound/CompoundNodeContainer.js +1 -1
- 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/ElementNodeContainer.css.js +1 -0
- 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/components/Link.js +1 -1
- package/dist/hooks/useDiagram.d.ts +320 -490
- 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/useLayoutConstraints.d.ts +11 -10
- package/dist/likec4diagram/useLayoutConstraints.js +64 -61
- package/dist/overlays/Overlays.js +2 -2
- package/dist/overlays/overlay/Overlay.css.js +31 -12
- package/dist/overlays/overlay/Overlay.js +30 -27
- package/dist/overlays/relationship-details/useLayoutedDetails.js +1 -1
- package/dist/overlays/relationships-browser/SelectElement.js +4 -4
- 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 +450 -608
- package/dist/state/diagram-machine.js +87 -57
- package/dist/styles.css.js +1 -1
- package/package.json +5 -5
- package/src/LikeC4Diagram.tsx +1 -7
- package/src/base/primitives/compound/CompoundNodeContainer.tsx +1 -1
- 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/ElementNodeContainer.css.ts +1 -0
- 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/components/Link.tsx +3 -1
- 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/useLayoutConstraints.ts +84 -89
- package/src/overlays/Overlays.tsx +2 -2
- package/src/overlays/overlay/Overlay.css.ts +30 -12
- package/src/overlays/overlay/Overlay.tsx +35 -29
- package/src/overlays/relationship-details/useLayoutedDetails.ts +1 -1
- package/src/overlays/relationships-browser/SelectElement.tsx +5 -5
- package/src/state/DiagramActorProvider.tsx +40 -50
- package/src/state/assign.ts +2 -2
- package/src/state/diagram-machine.ts +100 -69
|
@@ -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
|
|
|
@@ -2,14 +2,12 @@ import { css } from '@likec4/styles/css'
|
|
|
2
2
|
|
|
3
3
|
export const backdropBlur = '--_blur'
|
|
4
4
|
export const backdropOpacity = '--_opacity'
|
|
5
|
-
const backdropColor = '--backdrop-color'
|
|
6
5
|
|
|
7
|
-
const borderRadius = '
|
|
6
|
+
const borderRadius = '--_border-radius'
|
|
8
7
|
export const dialog = css({
|
|
9
8
|
boxSizing: 'border-box',
|
|
10
9
|
margin: 0,
|
|
11
10
|
position: 'fixed',
|
|
12
|
-
inset: '3rem',
|
|
13
11
|
width: 'auto',
|
|
14
12
|
height: 'auto',
|
|
15
13
|
maxWidth: '100vw',
|
|
@@ -18,8 +16,7 @@ export const dialog = css({
|
|
|
18
16
|
shadow: 'xl',
|
|
19
17
|
border: 'transparent',
|
|
20
18
|
outline: 'none',
|
|
21
|
-
borderRadius: borderRadius
|
|
22
|
-
padding: '6px',
|
|
19
|
+
borderRadius: `var(${borderRadius})`,
|
|
23
20
|
[backdropBlur]: '0px',
|
|
24
21
|
[backdropOpacity]: '0%',
|
|
25
22
|
_backdrop: {
|
|
@@ -30,19 +27,38 @@ export const dialog = css({
|
|
|
30
27
|
_light: `[rgb(15 15 15/ var(${backdropOpacity}))]`,
|
|
31
28
|
},
|
|
32
29
|
},
|
|
33
|
-
mdDown: {
|
|
34
|
-
borderRadius: 'sm',
|
|
35
|
-
inset: '2rem',
|
|
36
|
-
width: 'calc(100vw - 4rem)',
|
|
37
|
-
height: 'calc(100vh - 4rem)',
|
|
38
|
-
},
|
|
39
30
|
smDown: {
|
|
31
|
+
[borderRadius]: '0px',
|
|
40
32
|
border: 'none',
|
|
41
33
|
inset: 0,
|
|
42
34
|
padding: 0,
|
|
43
35
|
width: '100vw',
|
|
44
36
|
height: '100vh',
|
|
45
37
|
},
|
|
38
|
+
sm: {
|
|
39
|
+
[borderRadius]: '6px',
|
|
40
|
+
padding: '6px',
|
|
41
|
+
inset: '1rem',
|
|
42
|
+
width: 'calc(100vw - 2rem)',
|
|
43
|
+
height: 'calc(100vh - 2rem)',
|
|
44
|
+
},
|
|
45
|
+
md: {
|
|
46
|
+
inset: '2rem',
|
|
47
|
+
width: 'calc(100vw - 4rem)',
|
|
48
|
+
height: 'calc(100vh - 4rem)',
|
|
49
|
+
},
|
|
50
|
+
lg: {
|
|
51
|
+
inset: '4rem',
|
|
52
|
+
width: 'calc(100vw - 8rem)',
|
|
53
|
+
height: 'calc(100vh - 8rem)',
|
|
54
|
+
},
|
|
55
|
+
xl: {
|
|
56
|
+
[borderRadius]: '8px',
|
|
57
|
+
padding: '8px',
|
|
58
|
+
inset: '5rem',
|
|
59
|
+
width: 'calc(100vw - 10rem)',
|
|
60
|
+
height: 'calc(100vh - 10rem)',
|
|
61
|
+
},
|
|
46
62
|
})
|
|
47
63
|
|
|
48
64
|
export const body = css({
|
|
@@ -50,9 +66,11 @@ export const body = css({
|
|
|
50
66
|
containerName: 'overlay-dialog',
|
|
51
67
|
containerType: 'size',
|
|
52
68
|
border: `0 solid transparent`,
|
|
53
|
-
borderRadius: `calc(${borderRadius} - 2px)`,
|
|
54
69
|
background: 'mantine.colors.body',
|
|
55
70
|
overflow: 'hidden',
|
|
56
71
|
width: '100%',
|
|
57
72
|
height: '100%',
|
|
73
|
+
sm: {
|
|
74
|
+
borderRadius: `calc(var(${borderRadius}) - 2px)`,
|
|
75
|
+
},
|
|
58
76
|
})
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
} from '@mantine/core'
|
|
6
6
|
import { useMergedRef } from '@mantine/hooks'
|
|
7
7
|
import { useDebouncedCallback, useSyncedRef, useTimeoutEffect } from '@react-hookz/web'
|
|
8
|
-
import { type HTMLMotionProps, m } from 'framer-motion'
|
|
8
|
+
import { type HTMLMotionProps, m, useReducedMotionConfig } from 'framer-motion'
|
|
9
9
|
import { type PropsWithChildren, forwardRef, useLayoutEffect, useRef, useState } from 'react'
|
|
10
10
|
import { stopPropagation } from '../../utils'
|
|
11
11
|
import * as styles from './Overlay.css'
|
|
@@ -27,6 +27,8 @@ export const Overlay = forwardRef<HTMLDialogElement, OverlayProps>(
|
|
|
27
27
|
const dialogRef = useRef<HTMLDialogElement>(null)
|
|
28
28
|
const isClosingRef = useRef(false)
|
|
29
29
|
|
|
30
|
+
const motionNotReduced = useReducedMotionConfig() !== true
|
|
31
|
+
|
|
30
32
|
const onCloseRef = useSyncedRef(onClose)
|
|
31
33
|
const close = useDebouncedCallback(
|
|
32
34
|
() => {
|
|
@@ -64,34 +66,38 @@ export const Overlay = forwardRef<HTMLDialogElement, OverlayProps>(
|
|
|
64
66
|
<m.dialog
|
|
65
67
|
ref={useMergedRef(dialogRef, ref)}
|
|
66
68
|
className={cx(RemoveScroll.classNames.fullWidth, styles.dialog, classes?.dialog, className)}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
69
|
+
layout
|
|
70
|
+
{...motionNotReduced
|
|
71
|
+
? ({
|
|
72
|
+
initial: {
|
|
73
|
+
[styles.backdropBlur]: '0px',
|
|
74
|
+
[styles.backdropOpacity]: '0%',
|
|
75
|
+
scale: 1.075,
|
|
76
|
+
opacity: 0,
|
|
77
|
+
},
|
|
78
|
+
animate: {
|
|
79
|
+
[styles.backdropBlur]: '8px',
|
|
80
|
+
[styles.backdropOpacity]: '60%',
|
|
81
|
+
scale: 1,
|
|
82
|
+
opacity: 1,
|
|
83
|
+
transition: {
|
|
84
|
+
delay: 0.075,
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
exit: {
|
|
88
|
+
scale: 1.3,
|
|
89
|
+
opacity: 0,
|
|
90
|
+
translateY: -10,
|
|
91
|
+
[styles.backdropBlur]: '0px',
|
|
92
|
+
[styles.backdropOpacity]: '0%',
|
|
93
|
+
},
|
|
94
|
+
})
|
|
95
|
+
: {
|
|
96
|
+
initial: {
|
|
97
|
+
[styles.backdropBlur]: '8px',
|
|
98
|
+
[styles.backdropOpacity]: '60%',
|
|
99
|
+
},
|
|
100
|
+
}}
|
|
95
101
|
onClick={e => {
|
|
96
102
|
e.stopPropagation()
|
|
97
103
|
if ((e.target as any)?.nodeName?.toUpperCase() === 'DIALOG') {
|
|
@@ -102,7 +102,7 @@ function nodeData(
|
|
|
102
102
|
// Ansector separetely, because we want to inherit
|
|
103
103
|
// color from it if there is no diagram node
|
|
104
104
|
const ancestor = diagramNode ?? pipe(
|
|
105
|
-
element.ancestors()
|
|
105
|
+
[...element.ancestors()],
|
|
106
106
|
map(ancestor => ctx.diagramNodes.get(ancestor.id)),
|
|
107
107
|
filter(isTruthy),
|
|
108
108
|
first(),
|
|
@@ -61,9 +61,9 @@ export const SelectElement = memo(() => {
|
|
|
61
61
|
}, [subjectId])
|
|
62
62
|
|
|
63
63
|
return (
|
|
64
|
-
<Group ref={root} gap={'xs'}
|
|
64
|
+
<Group ref={root} gap={'xs'} pos={'relative'}>
|
|
65
65
|
{enableSelectSubject && (
|
|
66
|
-
|
|
66
|
+
<Group gap={4} wrap="nowrap">
|
|
67
67
|
<Box
|
|
68
68
|
fz={'xs'}
|
|
69
69
|
fw={'500'}
|
|
@@ -159,10 +159,10 @@ export const SelectElement = memo(() => {
|
|
|
159
159
|
</PopoverDropdown>
|
|
160
160
|
</Popover>
|
|
161
161
|
</Box>
|
|
162
|
-
|
|
162
|
+
</Group>
|
|
163
163
|
)}
|
|
164
164
|
{enableChangeScope && (
|
|
165
|
-
|
|
165
|
+
<Group gap={4} wrap="nowrap">
|
|
166
166
|
{/* Show if only "select" is enabled */}
|
|
167
167
|
{enableSelectSubject && (
|
|
168
168
|
<Box
|
|
@@ -220,7 +220,7 @@ export const SelectElement = memo(() => {
|
|
|
220
220
|
/>
|
|
221
221
|
</Tooltip>
|
|
222
222
|
</div>
|
|
223
|
-
|
|
223
|
+
</Group>
|
|
224
224
|
)}
|
|
225
225
|
</Group>
|
|
226
226
|
)
|
|
@@ -1,24 +1,37 @@
|
|
|
1
|
-
import type { ViewId } from '@likec4/core'
|
|
1
|
+
import type { ViewId, WhereOperator } from '@likec4/core'
|
|
2
|
+
import { useDeepCompareEffect } from '@react-hookz/web'
|
|
2
3
|
import { useActorRef, useSelector } from '@xstate/react'
|
|
3
4
|
import { useStoreApi } from '@xyflow/react'
|
|
4
5
|
import { shallowEqual } from 'fast-equals'
|
|
5
|
-
import { type PropsWithChildren, useEffect
|
|
6
|
+
import { type PropsWithChildren, useEffect } from 'react'
|
|
6
7
|
import { useDiagramEventHandlersRef } from '../context/DiagramEventHandlers'
|
|
7
8
|
import { DiagramFeatures, useEnabledFeatures } from '../context/DiagramFeatures'
|
|
8
9
|
import { DiagramActorSafeContext } from '../hooks/safeContext'
|
|
9
10
|
import { useUpdateEffect } from '../hooks/useUpdateEffect'
|
|
10
11
|
import type { Types } from '../likec4diagram/types'
|
|
12
|
+
import { useViewToNodesEdges } from '../likec4diagram/useViewToNodesEdges'
|
|
11
13
|
import { type Input, diagramMachine } from './diagram-machine'
|
|
12
14
|
import { syncManualLayoutActorLogic } from './syncManualLayoutActor'
|
|
13
|
-
import type {
|
|
15
|
+
import type { DiagramActorSnapshot } from './types'
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
const selectToggledFeatures = (state: DiagramActorSnapshot) => state.context.toggledFeatures
|
|
18
|
+
type ActorContextInput = Omit<Input, 'xystore' | 'xynodes' | 'xyedges'>
|
|
19
|
+
export function DiagramActorProvider({
|
|
20
|
+
input: {
|
|
21
|
+
view,
|
|
22
|
+
...inputs
|
|
23
|
+
},
|
|
24
|
+
where,
|
|
25
|
+
children,
|
|
26
|
+
}: PropsWithChildren<{
|
|
27
|
+
input: ActorContextInput
|
|
28
|
+
where?: WhereOperator<string, string> | undefined
|
|
29
|
+
}>) {
|
|
17
30
|
const handlersRef = useDiagramEventHandlersRef()
|
|
18
31
|
const xystore = useStoreApi<Types.Node, Types.Edge>()
|
|
19
32
|
|
|
20
|
-
const
|
|
21
|
-
|
|
33
|
+
const actorRef = useActorRef(
|
|
34
|
+
diagramMachine.provide({
|
|
22
35
|
actions: {
|
|
23
36
|
'trigger:NavigateTo': ((_, { viewId }) => {
|
|
24
37
|
handlersRef.current.onNavigateTo?.(viewId as ViewId)
|
|
@@ -41,68 +54,45 @@ export function DiagramActorProvider({ input, children }: PropsWithChildren<{ in
|
|
|
41
54
|
},
|
|
42
55
|
}),
|
|
43
56
|
},
|
|
44
|
-
})
|
|
45
|
-
}, [handlersRef])
|
|
46
|
-
|
|
47
|
-
const actorRef = useActorRef(
|
|
48
|
-
logic,
|
|
57
|
+
}),
|
|
49
58
|
{
|
|
50
|
-
id: `diagram:${
|
|
59
|
+
id: `diagram:${view.id}`,
|
|
51
60
|
systemId: 'diagram',
|
|
52
61
|
// ...inspector,
|
|
53
62
|
input: {
|
|
54
63
|
xystore,
|
|
55
|
-
|
|
64
|
+
view,
|
|
65
|
+
...inputs,
|
|
56
66
|
},
|
|
57
67
|
},
|
|
58
68
|
)
|
|
59
69
|
|
|
60
|
-
return (
|
|
61
|
-
<DiagramActorSafeContext value={actorRef}>
|
|
62
|
-
<SyncStore input={input} actorRef={actorRef} />
|
|
63
|
-
<DiagramActorToggledFeatures actorRef={actorRef}>
|
|
64
|
-
{children}
|
|
65
|
-
</DiagramActorToggledFeatures>
|
|
66
|
-
</DiagramActorSafeContext>
|
|
67
|
-
)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const SyncStore = (
|
|
71
|
-
{ input: { view, xyedges, xynodes, ...inputs }, actorRef }: { input: ActorContextInput; actorRef: DiagramActorRef },
|
|
72
|
-
) => {
|
|
73
70
|
const features = useEnabledFeatures()
|
|
74
71
|
useUpdateEffect(() => {
|
|
75
72
|
actorRef.send({ type: 'update.inputs', inputs })
|
|
76
|
-
}, [
|
|
73
|
+
}, [inputs])
|
|
77
74
|
|
|
78
75
|
useEffect(() => {
|
|
79
76
|
actorRef.send({ type: 'update.features', features })
|
|
80
|
-
}, [
|
|
77
|
+
}, [features])
|
|
81
78
|
|
|
82
|
-
const
|
|
79
|
+
const { xyedges, xynodes } = useViewToNodesEdges({
|
|
80
|
+
view,
|
|
81
|
+
where,
|
|
82
|
+
nodesSelectable: inputs.nodesSelectable,
|
|
83
|
+
})
|
|
83
84
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
actorRef.send({ type: 'update.view', view, xyedges, xynodes })
|
|
88
|
-
})
|
|
89
|
-
return () => {
|
|
90
|
-
if (frameReq.current != null) {
|
|
91
|
-
cancelAnimationFrame(frameReq.current)
|
|
92
|
-
}
|
|
93
|
-
frameReq.current = null
|
|
94
|
-
}
|
|
95
|
-
}, [actorRef, view, xyedges, xynodes])
|
|
85
|
+
useDeepCompareEffect(() => {
|
|
86
|
+
actorRef.send({ type: 'update.view', view, xyedges, xynodes })
|
|
87
|
+
}, [view, xyedges, xynodes])
|
|
96
88
|
|
|
97
|
-
return null
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const selectToggledFeatures = (state: DiagramActorSnapshot) => state.context.toggledFeatures
|
|
101
|
-
function DiagramActorToggledFeatures({ children, actorRef }: PropsWithChildren<{ actorRef: DiagramActorRef }>) {
|
|
102
89
|
const toggledFeatures = useSelector(actorRef, selectToggledFeatures, shallowEqual)
|
|
90
|
+
|
|
103
91
|
return (
|
|
104
|
-
<
|
|
105
|
-
{
|
|
106
|
-
|
|
92
|
+
<DiagramActorSafeContext value={actorRef}>
|
|
93
|
+
<DiagramFeatures overrides={toggledFeatures}>
|
|
94
|
+
{children}
|
|
95
|
+
</DiagramFeatures>
|
|
96
|
+
</DiagramActorSafeContext>
|
|
107
97
|
)
|
|
108
98
|
}
|
package/src/state/assign.ts
CHANGED
|
@@ -391,8 +391,8 @@ export function updateActiveWalkthrough({ context }: ActionArg): Partial<Diagram
|
|
|
391
391
|
})
|
|
392
392
|
}),
|
|
393
393
|
xynodes: context.xynodes.map(node => {
|
|
394
|
-
const
|
|
395
|
-
return Base.setDimmed(node,
|
|
394
|
+
const dimmed = step.source !== node.id && step.target !== node.id
|
|
395
|
+
return Base.setDimmed(node, dimmed)
|
|
396
396
|
}),
|
|
397
397
|
}
|
|
398
398
|
}
|