@likec4/diagram 1.9.0 → 1.12.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/package.json +20 -43
- package/src/LikeC4CustomColors.tsx +5 -2
- package/src/LikeC4Diagram.css.ts +1 -1
- package/src/LikeC4Diagram.props.ts +40 -94
- package/src/LikeC4Diagram.tsx +14 -4
- package/src/LikeC4ModelProvider.tsx +21 -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 +9 -0
- package/src/likec4model/LikeC4ModelContext.ts +4 -0
- package/src/likec4model/index.ts +1 -0
- package/src/likec4model/useLikeC4Model.ts +58 -0
- package/src/state/diagram-to-xyflow.ts +1 -2
- 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 +25 -3
- 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 +5 -2
- package/src/ui/notation/NotationPanel.css.ts +2 -3
- package/src/ui/notation/NotationPanel.tsx +10 -11
- package/src/ui/options/NodeOptions.tsx +4 -1
- package/src/ui/top-left/ChangeAutoLayoutButton.tsx +122 -71
- 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 +48 -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 +13 -17
- package/src/xyflow/XYFlowEvents.tsx +58 -58
- package/src/xyflow/XYFlowInner.tsx +4 -8
- 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 +25 -8
- package/src/xyflow/nodes/element/ElementLink.css.ts +1 -1
- package/src/xyflow/nodes/element/ElementLink.tsx +4 -8
- package/src/xyflow/nodes/element/ElementNode.tsx +47 -8
- package/src/xyflow/nodes/element/element.css.ts +1 -2
- package/src/xyflow/nodes/shared/Toolbar.tsx +426 -0
- package/src/xyflow/types.ts +1 -24
- package/src/xyflow/useLayoutConstraints.ts +10 -6
- package/dist/LikeC4CustomColors.d.ts +0 -3
- package/dist/LikeC4CustomColors.props.d.ts +0 -5
- package/dist/LikeC4Diagram.css.d.ts +0 -4
- package/dist/LikeC4Diagram.d.ts +0 -7
- package/dist/LikeC4Diagram.props.d.ts +0 -154
- 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 -3684
- package/dist/mantine/EnsureMantine.d.ts +0 -8
- package/dist/mantine.css.d.ts +0 -3
- package/dist/state/DiagramContext.d.ts +0 -1192
- 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 -12
- 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
|
@@ -1,27 +1,28 @@
|
|
|
1
1
|
import {
|
|
2
2
|
invariant,
|
|
3
|
+
isStepEdgeId,
|
|
3
4
|
type NonEmptyArray,
|
|
4
5
|
nonexhaustive,
|
|
5
6
|
nonNullable,
|
|
6
7
|
type Point,
|
|
7
8
|
type RelationshipArrowType
|
|
8
9
|
} from '@likec4/core'
|
|
9
|
-
import { Box } from '@mantine/core'
|
|
10
10
|
import { useDebouncedEffect } from '@react-hookz/web'
|
|
11
11
|
import { assignInlineVars } from '@vanilla-extract/dynamic'
|
|
12
12
|
import type { EdgeProps, XYPosition } from '@xyflow/react'
|
|
13
|
-
import { EdgeLabelRenderer } from '@xyflow/react'
|
|
14
13
|
import clsx from 'clsx'
|
|
15
14
|
import { curveCatmullRomOpen, line as d3line } from 'd3-shape'
|
|
16
|
-
import { deepEqual
|
|
15
|
+
import { deepEqual as eq } from 'fast-equals'
|
|
17
16
|
import { memo, type PointerEvent as ReactPointerEvent, useEffect, useRef, useState } from 'react'
|
|
18
17
|
import { first, hasAtLeast, isArray, isTruthy, last } from 'remeda'
|
|
19
18
|
import { useDiagramState, useDiagramStoreApi } from '../../hooks/useDiagramState'
|
|
20
19
|
import { useXYStoreApi } from '../../hooks/useXYFlow'
|
|
20
|
+
import { vector, VectorImpl } from '../../utils/vector'
|
|
21
21
|
import { ZIndexes } from '../const'
|
|
22
22
|
import { EdgeMarkers, type EdgeMarkerType } from '../EdgeMarkers'
|
|
23
|
-
import { type
|
|
23
|
+
import { type XYFlowEdge } from '../types'
|
|
24
24
|
import { bezierControlPoints } from '../utils'
|
|
25
|
+
import { EdgeLabel } from './EdgeLabel'
|
|
25
26
|
import * as edgesCss from './edges.css'
|
|
26
27
|
import { getNodeIntersectionFromCenterToPoint } from './utils'
|
|
27
28
|
// import { getEdgeParams } from './utils'
|
|
@@ -103,9 +104,8 @@ const isSame = (a: number, b: number) => {
|
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
const isSamePoint = (a: XYPosition | Point, b: XYPosition | Point) => {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
return isSame(ax, bx) && isSame(ay, by)
|
|
107
|
+
return isSame(isArray(a) ? a[0] : a.x, isArray(b) ? b[0] : b.x)
|
|
108
|
+
&& isSame(isArray(a) ? a[1] : a.y, isArray(b) ? b[1] : b.y)
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
const sameControlPoints = (a: XYPosition[] | null, b: XYPosition[] | null) => {
|
|
@@ -123,7 +123,6 @@ const isEqualProps = (prev: EdgeProps<XYFlowEdge>, next: EdgeProps<XYFlowEdge>)
|
|
|
123
123
|
&& isSame(prev.sourceY, next.sourceY)
|
|
124
124
|
&& isSame(prev.targetX, next.targetX)
|
|
125
125
|
&& isSame(prev.targetY, next.targetY)
|
|
126
|
-
&& eq(prev.data.stepNum, next.data.stepNum)
|
|
127
126
|
&& eq(prev.data.label, next.data.label)
|
|
128
127
|
&& sameControlPoints(prev.data.controlPoints, next.data.controlPoints)
|
|
129
128
|
&& eq(prev.data.edge, next.data.edge)
|
|
@@ -134,7 +133,7 @@ const curve = d3line<XYPosition>()
|
|
|
134
133
|
.x(d => d.x)
|
|
135
134
|
.y(d => d.y)
|
|
136
135
|
|
|
137
|
-
export const RelationshipEdge =
|
|
136
|
+
export const RelationshipEdge = memo<EdgeProps<XYFlowEdge>>(function RelationshipEdgeR({
|
|
138
137
|
id,
|
|
139
138
|
data,
|
|
140
139
|
selected,
|
|
@@ -150,15 +149,25 @@ export const RelationshipEdge = /* @__PURE__ */ memo<EdgeProps<XYFlowEdge>>(func
|
|
|
150
149
|
const [isControlPointDragging, setIsControlPointDragging] = useState(false)
|
|
151
150
|
const diagramStore = useDiagramStoreApi()
|
|
152
151
|
const xyflowStore = useXYStoreApi()
|
|
153
|
-
const {
|
|
152
|
+
const {
|
|
153
|
+
connectedToFocusedNode,
|
|
154
|
+
isActiveWalkthroughStep,
|
|
155
|
+
isEdgePathEditable,
|
|
156
|
+
isHovered,
|
|
157
|
+
isDimmed,
|
|
158
|
+
isActiveAsParallel
|
|
159
|
+
} = useDiagramState(s => ({
|
|
154
160
|
isEdgePathEditable: s.readonly !== true && s.experimentalEdgeEditing === true && s.focusedNodeId === null
|
|
155
|
-
&& s.
|
|
156
|
-
|
|
157
|
-
|
|
161
|
+
&& s.activeWalkthrough === null,
|
|
162
|
+
connectedToFocusedNode: s.focusedNodeId === source || s.focusedNodeId === target,
|
|
163
|
+
isActiveWalkthroughStep: s.activeWalkthrough?.stepId === data.edge.id,
|
|
164
|
+
// If activeWalkthrough and this edge is part of the parallel group
|
|
165
|
+
isActiveAsParallel: !!s.activeWalkthrough?.parallelPrefix && id.startsWith(s.activeWalkthrough.parallelPrefix),
|
|
158
166
|
isHovered: s.hoveredEdgeId === id,
|
|
159
|
-
isDimmed: s.dimmed.has(id)
|
|
160
|
-
hasOpenSourceRelation: !!s.onOpenSourceRelation
|
|
167
|
+
isDimmed: s.dimmed.has(id)
|
|
161
168
|
}))
|
|
169
|
+
|
|
170
|
+
const isActive = connectedToFocusedNode || isActiveWalkthroughStep
|
|
162
171
|
const { nodeLookup, edgeLookup } = xyflowStore.getState()
|
|
163
172
|
const sourceNode = nonNullable(nodeLookup.get(source)!, `source node ${source} not found`)
|
|
164
173
|
const targetNode = nonNullable(nodeLookup.get(target)!, `target node ${target} not found`)
|
|
@@ -180,7 +189,7 @@ export const RelationshipEdge = /* @__PURE__ */ memo<EdgeProps<XYFlowEdge>>(func
|
|
|
180
189
|
|
|
181
190
|
let controlPoints = data.controlPoints ?? bezierControlPoints(data.edge)
|
|
182
191
|
|
|
183
|
-
const isStepEdge = data.
|
|
192
|
+
const isStepEdge = isStepEdgeId(data.edge.id)
|
|
184
193
|
const isDotted = line === 'dotted'
|
|
185
194
|
const isDashed = isDotted || line === 'dashed'
|
|
186
195
|
|
|
@@ -194,7 +203,7 @@ export const RelationshipEdge = /* @__PURE__ */ memo<EdgeProps<XYFlowEdge>>(func
|
|
|
194
203
|
let labelX = labelBBox?.x ?? 0,
|
|
195
204
|
labelY = labelBBox?.y ?? 0
|
|
196
205
|
|
|
197
|
-
const [labelPos,
|
|
206
|
+
const [labelPos, setLabelPos] = useState<XYPosition>({
|
|
198
207
|
x: label?.bbox.x ?? labelX,
|
|
199
208
|
y: label?.bbox.y ?? labelY
|
|
200
209
|
})
|
|
@@ -235,7 +244,7 @@ export const RelationshipEdge = /* @__PURE__ */ memo<EdgeProps<XYFlowEdge>>(func
|
|
|
235
244
|
x: Math.round(dompoint.x),
|
|
236
245
|
y: Math.round(dompoint.y)
|
|
237
246
|
}
|
|
238
|
-
|
|
247
|
+
setLabelPos(current => isSamePoint(current, point) ? current : point)
|
|
239
248
|
}, [edgePath])
|
|
240
249
|
|
|
241
250
|
useDebouncedEffect(
|
|
@@ -263,35 +272,26 @@ export const RelationshipEdge = /* @__PURE__ */ memo<EdgeProps<XYFlowEdge>>(func
|
|
|
263
272
|
labelY = labelPos.y
|
|
264
273
|
}
|
|
265
274
|
|
|
266
|
-
const
|
|
267
|
-
const {
|
|
268
|
-
if (!domNode || e.pointerType !== 'mouse') {
|
|
269
|
-
return
|
|
270
|
-
}
|
|
275
|
+
const onLmbControlPointerDown = (index: number, e: ReactPointerEvent<SVGCircleElement>, domNode: HTMLDivElement) => {
|
|
276
|
+
const { addSelectedEdges } = xyflowStore.getState()
|
|
271
277
|
const { xyflow } = diagramStore.getState()
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
e.stopPropagation()
|
|
276
|
-
// Defer the update to avoid conflict with the pointerup event
|
|
277
|
-
setTimeout(() => {
|
|
278
|
-
xyflow.updateEdgeData(id, { controlPoints: newControlPoints })
|
|
279
|
-
}, 10)
|
|
280
|
-
return
|
|
281
|
-
}
|
|
282
|
-
if (e.button !== 0) {
|
|
283
|
-
return
|
|
284
|
-
}
|
|
278
|
+
|
|
279
|
+
addSelectedEdges([id])
|
|
280
|
+
|
|
285
281
|
const wasCanceled = diagramStore.getState().cancelSaveManualLayout()
|
|
286
282
|
e.stopPropagation()
|
|
287
283
|
let hasMoved = false
|
|
288
284
|
let pointer = { x: e.clientX, y: e.clientY }
|
|
289
285
|
|
|
290
286
|
const onPointerMove = (e: PointerEvent) => {
|
|
291
|
-
|
|
287
|
+
const clientPoint = {
|
|
288
|
+
x: e.clientX,
|
|
289
|
+
y: e.clientY
|
|
290
|
+
}
|
|
291
|
+
if (!isSamePoint(pointer, clientPoint)) {
|
|
292
292
|
setIsControlPointDragging(true)
|
|
293
293
|
hasMoved = true
|
|
294
|
-
pointer =
|
|
294
|
+
pointer = clientPoint
|
|
295
295
|
const { x, y } = xyflow.screenToFlowPosition(pointer, { snapToGrid: false })
|
|
296
296
|
xyflow.updateEdgeData(id, xyedge => {
|
|
297
297
|
const cp = (xyedge.data.controlPoints ?? controlPoints).slice()
|
|
@@ -329,6 +329,105 @@ export const RelationshipEdge = /* @__PURE__ */ memo<EdgeProps<XYFlowEdge>>(func
|
|
|
329
329
|
})
|
|
330
330
|
}
|
|
331
331
|
|
|
332
|
+
const onRmbControlPointerDown = (index: number, e: ReactPointerEvent<SVGCircleElement>, domNode: HTMLDivElement) => {
|
|
333
|
+
const { xyflow } = diagramStore.getState()
|
|
334
|
+
|
|
335
|
+
if(controlPoints.length <= 1) {
|
|
336
|
+
return
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const onPointerUp = (e: PointerEvent) => {
|
|
340
|
+
const newControlPoints = controlPoints.slice()
|
|
341
|
+
newControlPoints.splice(index, 1)
|
|
342
|
+
e.stopPropagation()
|
|
343
|
+
// Defer the update to avoid conflict with the pointerup event
|
|
344
|
+
setTimeout(() => {
|
|
345
|
+
xyflow.updateEdgeData(id, { controlPoints: newControlPoints })
|
|
346
|
+
diagramStore.getState().scheduleSaveManualLayout()
|
|
347
|
+
}, 10)
|
|
348
|
+
|
|
349
|
+
domNode.removeEventListener('pointerup', onPointerUp, {
|
|
350
|
+
capture: true
|
|
351
|
+
})
|
|
352
|
+
e.stopPropagation()
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
domNode.addEventListener('pointerup', onPointerUp, {
|
|
356
|
+
once: true,
|
|
357
|
+
capture: true
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
e.stopPropagation()
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const onControlPointerDown = (index: number, e: ReactPointerEvent<SVGCircleElement>) => {
|
|
364
|
+
const { domNode } = xyflowStore.getState()
|
|
365
|
+
if (!domNode || e.pointerType !== 'mouse') {
|
|
366
|
+
return
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
switch (e.button) {
|
|
370
|
+
case 0:
|
|
371
|
+
onLmbControlPointerDown(index, e, domNode)
|
|
372
|
+
break
|
|
373
|
+
case 2:
|
|
374
|
+
onRmbControlPointerDown(index, e, domNode)
|
|
375
|
+
break
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const onEdgePointerDown = (e: ReactPointerEvent<SVGElement>) => {
|
|
380
|
+
const { domNode } = xyflowStore.getState()
|
|
381
|
+
const { xyflow, scheduleSaveManualLayout } = diagramStore.getState()
|
|
382
|
+
|
|
383
|
+
if (!domNode || e.pointerType !== 'mouse') {
|
|
384
|
+
return
|
|
385
|
+
}
|
|
386
|
+
if (e.button !== 2) {
|
|
387
|
+
return
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
xyflow.updateEdgeData(id, edge => {
|
|
391
|
+
const points: VectorImpl[] = [
|
|
392
|
+
new VectorImpl(sourceX, sourceY),
|
|
393
|
+
...controlPoints.map(vector) || [],
|
|
394
|
+
new VectorImpl(targetX, targetY)
|
|
395
|
+
]
|
|
396
|
+
|
|
397
|
+
let pointer = { x: e.clientX, y: e.clientY }
|
|
398
|
+
const newPoint = vector(xyflow.screenToFlowPosition(pointer, { snapToGrid: false }))
|
|
399
|
+
|
|
400
|
+
let insertionIndex = 0
|
|
401
|
+
let minDistance = Infinity
|
|
402
|
+
for (let i = 0; i < points.length - 1; i++) {
|
|
403
|
+
const a = points[i]!, b = points[i + 1]!
|
|
404
|
+
const fromCurrentToNext = b.sub(a)
|
|
405
|
+
const fromCurrentToNew = newPoint.sub(a)
|
|
406
|
+
const fromNextToNew = newPoint.sub(b)
|
|
407
|
+
|
|
408
|
+
// Is pointer above the current segment?
|
|
409
|
+
if (fromCurrentToNext.dot(fromCurrentToNew) * fromCurrentToNext.dot(fromNextToNew) < 0) {
|
|
410
|
+
// Calculate distance by approximating edge segment with a staight line
|
|
411
|
+
const distanceToEdge = Math.abs(fromCurrentToNext.cross(fromCurrentToNew).abs() / fromCurrentToNext.abs())
|
|
412
|
+
|
|
413
|
+
if (distanceToEdge < minDistance) {
|
|
414
|
+
minDistance = distanceToEdge
|
|
415
|
+
insertionIndex = i
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const newControlPoints = edge.data.controlPoints?.slice() || []
|
|
421
|
+
newControlPoints.splice(insertionIndex, 0, newPoint)
|
|
422
|
+
|
|
423
|
+
return { controlPoints: newControlPoints }
|
|
424
|
+
})
|
|
425
|
+
|
|
426
|
+
scheduleSaveManualLayout()
|
|
427
|
+
|
|
428
|
+
e.stopPropagation()
|
|
429
|
+
}
|
|
430
|
+
|
|
332
431
|
let markerStartName = toMarker(diagramEdge.tail)
|
|
333
432
|
let markerEndName = toMarker(diagramEdge.head ?? 'normal')
|
|
334
433
|
if (diagramEdge.dir === 'back') {
|
|
@@ -338,10 +437,11 @@ export const RelationshipEdge = /* @__PURE__ */ memo<EdgeProps<XYFlowEdge>>(func
|
|
|
338
437
|
const MarkerStart = markerStartName ? EdgeMarkers[markerStartName] : null
|
|
339
438
|
const MarkerEnd = markerEndName ? EdgeMarkers[markerEndName] : null
|
|
340
439
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
440
|
+
let labelZIndex = 1 + (isHovered ? ZIndexes.Element : (edgeLookup.get(id)!.zIndex ?? ZIndexes.Edge))
|
|
441
|
+
if (isEdgePathEditable && selected) {
|
|
442
|
+
// Move label below ControlPoints, otherwise they don't capture events
|
|
443
|
+
labelZIndex = (edgeLookup.get(id)!.zIndex ?? ZIndexes.Edge) - 1
|
|
444
|
+
}
|
|
345
445
|
|
|
346
446
|
return (
|
|
347
447
|
<g
|
|
@@ -353,6 +453,7 @@ export const RelationshipEdge = /* @__PURE__ */ memo<EdgeProps<XYFlowEdge>>(func
|
|
|
353
453
|
data-likec4-color={color}
|
|
354
454
|
data-edge-dir={diagramEdge.dir}
|
|
355
455
|
data-edge-active={isActive}
|
|
456
|
+
data-edge-animated={isActive || isActiveAsParallel}
|
|
356
457
|
data-edge-hovered={isHovered}>
|
|
357
458
|
<path
|
|
358
459
|
className={clsx('react-flow__edge-interaction')}
|
|
@@ -361,7 +462,7 @@ export const RelationshipEdge = /* @__PURE__ */ memo<EdgeProps<XYFlowEdge>>(func
|
|
|
361
462
|
stroke={'transparent'}
|
|
362
463
|
strokeWidth={interactionWidth ?? 10}
|
|
363
464
|
/>
|
|
364
|
-
<g className={edgesCss.markerContext}>
|
|
465
|
+
<g className={edgesCss.markerContext} onPointerDown={e => onEdgePointerDown(e)}>
|
|
365
466
|
<defs>
|
|
366
467
|
{MarkerStart && <MarkerStart id={'start' + id} />}
|
|
367
468
|
{MarkerEnd && <MarkerEnd id={'end' + id} />}
|
|
@@ -404,102 +505,34 @@ export const RelationshipEdge = /* @__PURE__ */ memo<EdgeProps<XYFlowEdge>>(func
|
|
|
404
505
|
)}
|
|
405
506
|
{(data.label || isStepEdge) && (
|
|
406
507
|
<EdgeLabel
|
|
407
|
-
{
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
508
|
+
isDimmed={isDimmed}
|
|
509
|
+
edgeData={data}
|
|
510
|
+
className={clsx(
|
|
511
|
+
'nodrag nopan',
|
|
512
|
+
edgesCss.container,
|
|
513
|
+
edgesCss.edgeLabel,
|
|
514
|
+
isDimmed && edgesCss.dimmed
|
|
515
|
+
)}
|
|
516
|
+
style={{
|
|
517
|
+
...assignInlineVars({
|
|
518
|
+
[edgesCss.varLabelX]: isModified ? `calc(${labelX}px - 10%)` : `${labelX}px`,
|
|
519
|
+
[edgesCss.varLabelY]: isModified ? `${labelY - 5}px` : `${labelY}px`
|
|
520
|
+
}),
|
|
521
|
+
// ...(isEdgePathEditable && selected && {
|
|
522
|
+
// pointerEvents: 'none'
|
|
523
|
+
// }),
|
|
524
|
+
...(label && {
|
|
525
|
+
maxWidth: label.bbox.width + 18
|
|
526
|
+
}),
|
|
527
|
+
zIndex: labelZIndex
|
|
528
|
+
}}
|
|
529
|
+
mod={{
|
|
530
|
+
'data-likec4-color': color,
|
|
531
|
+
'data-edge-hovered': isHovered && !isActiveWalkthroughStep,
|
|
532
|
+
'data-edge-active': isActive
|
|
533
|
+
}}
|
|
426
534
|
/>
|
|
427
535
|
)}
|
|
428
536
|
</g>
|
|
429
537
|
)
|
|
430
538
|
}, isEqualProps)
|
|
431
|
-
|
|
432
|
-
type EdgeLabelProps = {
|
|
433
|
-
isDimmed: boolean
|
|
434
|
-
color: string
|
|
435
|
-
isModified: boolean
|
|
436
|
-
labelX: number
|
|
437
|
-
labelY: number
|
|
438
|
-
isEdgePathEditable: boolean
|
|
439
|
-
selected: boolean
|
|
440
|
-
stepNum: number | null
|
|
441
|
-
label: RelationshipData['label']
|
|
442
|
-
isHovered: boolean
|
|
443
|
-
isActive: boolean
|
|
444
|
-
zIndex: number
|
|
445
|
-
onClick: false | ((e: ReactPointerEvent<HTMLDivElement>) => void)
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
const EdgeLabel = ({
|
|
449
|
-
isDimmed,
|
|
450
|
-
color,
|
|
451
|
-
isModified,
|
|
452
|
-
labelX,
|
|
453
|
-
labelY,
|
|
454
|
-
isEdgePathEditable,
|
|
455
|
-
selected,
|
|
456
|
-
label,
|
|
457
|
-
stepNum,
|
|
458
|
-
isHovered,
|
|
459
|
-
isActive,
|
|
460
|
-
zIndex,
|
|
461
|
-
onClick
|
|
462
|
-
}: EdgeLabelProps) => {
|
|
463
|
-
return (
|
|
464
|
-
<EdgeLabelRenderer>
|
|
465
|
-
<Box
|
|
466
|
-
className={clsx([
|
|
467
|
-
'nodrag nopan',
|
|
468
|
-
edgesCss.container,
|
|
469
|
-
edgesCss.edgeLabel,
|
|
470
|
-
isDimmed && edgesCss.dimmed
|
|
471
|
-
])}
|
|
472
|
-
style={{
|
|
473
|
-
...assignInlineVars({
|
|
474
|
-
[edgesCss.varLabelX]: isModified ? `calc(${labelX}px - 10%)` : `${labelX}px`,
|
|
475
|
-
[edgesCss.varLabelY]: isModified ? `${labelY - 5}px` : `${labelY}px`
|
|
476
|
-
}),
|
|
477
|
-
...(isEdgePathEditable && selected && {
|
|
478
|
-
pointerEvents: 'none'
|
|
479
|
-
}),
|
|
480
|
-
...(label && {
|
|
481
|
-
maxWidth: label.bbox.width + 18
|
|
482
|
-
}),
|
|
483
|
-
zIndex
|
|
484
|
-
}}
|
|
485
|
-
mod={{
|
|
486
|
-
'data-likec4-color': color,
|
|
487
|
-
'data-edge-hovered': isHovered,
|
|
488
|
-
'data-edge-active': isActive
|
|
489
|
-
}}
|
|
490
|
-
onClick={onClick || undefined}
|
|
491
|
-
>
|
|
492
|
-
{stepNum !== null && (
|
|
493
|
-
<Box className={edgesCss.stepEdgeNumber}>
|
|
494
|
-
{stepNum}
|
|
495
|
-
</Box>
|
|
496
|
-
)}
|
|
497
|
-
{isTruthy(label?.text) && (
|
|
498
|
-
<Box className={edgesCss.edgeLabelText}>
|
|
499
|
-
{label.text}
|
|
500
|
-
</Box>
|
|
501
|
-
)}
|
|
502
|
-
</Box>
|
|
503
|
-
</EdgeLabelRenderer>
|
|
504
|
-
)
|
|
505
|
-
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { style } from '@vanilla-extract/css'
|
|
2
|
+
import { mantine, vars } from '../../theme-vars'
|
|
3
|
+
|
|
4
|
+
export const menuDropdown = style({
|
|
5
|
+
minWidth: 250,
|
|
6
|
+
maxWidth: 'min(90vw, 500px)'
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
export const menuItemRelationship = style({
|
|
10
|
+
// padding: mantine.spacing.sm,
|
|
11
|
+
// paddingTop: mantine.spacing.xs,
|
|
12
|
+
// borderRadius: mantine.radius.sm,
|
|
13
|
+
// paddingTop: mantine.spacing.xs,
|
|
14
|
+
// paddingBottom: mantine.spacing.xs,
|
|
15
|
+
gap: 4
|
|
16
|
+
// cursor: 'pointer',
|
|
17
|
+
// ':hover': {
|
|
18
|
+
// borderColor: mantine.colors.gray.light,
|
|
19
|
+
// background: mantine.colors.gray[2]
|
|
20
|
+
// },
|
|
21
|
+
|
|
22
|
+
// selectors: {
|
|
23
|
+
// [`:where([data-mantine-color-scheme="dark"]) &`]: {
|
|
24
|
+
// // borderColor: mantine.colors.dark[6]
|
|
25
|
+
// },
|
|
26
|
+
// [`:where([data-mantine-color-scheme="dark"]) &:hover`]: {
|
|
27
|
+
// // background: mantine.colors.dark[5]
|
|
28
|
+
// }
|
|
29
|
+
// }
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
export const endpoint = style({
|
|
33
|
+
display: 'block',
|
|
34
|
+
fontSize: 10,
|
|
35
|
+
fontWeight: 500,
|
|
36
|
+
whiteSpace: 'nowrap',
|
|
37
|
+
padding: '2px 5px',
|
|
38
|
+
borderRadius: 2,
|
|
39
|
+
background: `color-mix(in srgb , ${vars.element.fill}, transparent 45%)`,
|
|
40
|
+
lineHeight: 1.1,
|
|
41
|
+
selectors: {
|
|
42
|
+
[`:where([data-mantine-color-scheme="dark"]) &`]: {
|
|
43
|
+
color: vars.element.loContrast
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
export const title = style({
|
|
49
|
+
whiteSpaceCollapse: 'preserve-breaks',
|
|
50
|
+
fontSize: mantine.fontSizes.sm
|
|
51
|
+
})
|
|
52
|
+
//
|
|
53
|
+
// export const sourceId = style({
|
|
54
|
+
//
|
|
55
|
+
// })
|
|
56
|
+
//
|
|
57
|
+
// export const targetId = style({
|
|
58
|
+
// fontSize: 11,
|
|
59
|
+
// whiteSpace: 'nowrap'
|
|
60
|
+
// })
|