@likec4/diagram 1.0.2 → 1.1.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/dist/KeepAspectRatioContainer.d.ts +9 -0
- package/dist/LikeC4Diagram.css.d.ts +1 -0
- package/dist/LikeC4Diagram.d.ts +1 -1
- package/dist/LikeC4Diagram.props.d.ts +16 -11
- package/dist/index.mjs +639 -762
- package/dist/state/DiagramContext.d.ts +54 -18
- package/dist/state/diagramStore.d.ts +61 -21
- package/dist/state/useDiagramStore.d.ts +54 -18
- package/dist/style.css +690 -79
- package/dist/ui/DiagramTitlePanel.css.d.ts +4 -0
- package/dist/ui/DiagramTitlePanel.d.ts +2 -0
- package/dist/ui/OptionsPanel.d.ts +1 -2
- package/dist/ui/index.d.ts +0 -1
- package/dist/xyflow/SelectEdgesOnNodeFocus.d.ts +1 -0
- package/dist/xyflow/{XYFlowWrapper.d.ts → XYFlow.d.ts} +2 -2
- package/dist/xyflow/XYFlowEvents.d.ts +133 -13
- package/dist/xyflow/edges/RelationshipEdge.d.ts +2 -2
- package/dist/xyflow/edges/edges.css.d.ts +1 -0
- package/dist/xyflow/hooks/useXYFlow.d.ts +20 -20
- package/dist/xyflow/nodes/compound/CompoundNode.css.d.ts +7 -5
- package/dist/xyflow/types.d.ts +2 -2
- package/package.json +5 -3
- package/src/{ui/KeepAspectRatio.tsx → KeepAspectRatioContainer.tsx} +7 -6
- package/src/LikeC4Diagram.css.ts +8 -1
- package/src/LikeC4Diagram.props.ts +17 -12
- package/src/LikeC4Diagram.tsx +9 -6
- package/src/StaticLikeC4Diagram.tsx +1 -0
- package/src/globals.css.ts +48 -38
- package/src/state/diagramStore.ts +125 -68
- package/src/ui/DiagramTitlePanel.css.ts +32 -0
- package/src/ui/DiagramTitlePanel.tsx +139 -0
- package/src/ui/OptionsPanel.tsx +4 -23
- package/src/ui/index.ts +0 -1
- package/src/ui/options/NodeOptions.tsx +107 -30
- package/src/xyflow/FitviewOnDiagramChange.tsx +26 -20
- package/src/xyflow/SelectEdgesOnNodeFocus.tsx +57 -0
- package/src/xyflow/SyncWithDiagram.tsx +35 -26
- package/src/xyflow/{XYFlowWrapper.tsx → XYFlow.tsx} +24 -27
- package/src/xyflow/XYFlowEvents.tsx +175 -81
- package/src/xyflow/diagram-to-xyflow.ts +4 -4
- package/src/xyflow/edges/RelationshipEdge.tsx +43 -47
- package/src/xyflow/edges/edges.css.ts +77 -36
- package/src/xyflow/nodes/compound/CompoundNode.css.ts +94 -44
- package/src/xyflow/nodes/compound/CompoundNode.tsx +29 -31
- package/src/xyflow/nodes/element/ElementNode.tsx +2 -2
- package/src/xyflow/nodes/element/element.css.ts +1 -0
- package/src/xyflow/nodes/shared/NavigateToBtn.tsx +2 -1
- package/dist/ui/KeepAspectRatio.css.d.ts +0 -1
- package/dist/ui/KeepAspectRatio.d.ts +0 -9
- package/src/ui/KeepAspectRatio.css.ts +0 -7
|
@@ -1,20 +1,34 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
1
|
+
import type { ReactFlowProps } from '@xyflow/react'
|
|
2
|
+
import { useEffect, useMemo, useRef } from 'react'
|
|
3
|
+
import type { Simplify } from 'type-fest'
|
|
4
4
|
import { useDiagramStoreApi } from '../state'
|
|
5
5
|
import { useXYStoreApi } from './hooks'
|
|
6
|
-
import type { XYFlowEdge, XYFlowNode } from './types'
|
|
6
|
+
import type { XYFlowEdge, XYFlowNode, XYFlowState } from './types'
|
|
7
7
|
|
|
8
|
-
type XYFlowEventHandlers =
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
8
|
+
type XYFlowEventHandlers = Simplify<
|
|
9
|
+
Required<
|
|
10
|
+
Pick<
|
|
11
|
+
ReactFlowProps<XYFlowNode, XYFlowEdge>,
|
|
12
|
+
| 'onDoubleClick'
|
|
13
|
+
| 'onPaneClick'
|
|
14
|
+
| 'onNodeClick'
|
|
15
|
+
| 'onNodeDoubleClick'
|
|
16
|
+
| 'onEdgeClick'
|
|
17
|
+
| 'onMoveEnd'
|
|
18
|
+
| 'onNodeContextMenu'
|
|
19
|
+
| 'onEdgeContextMenu'
|
|
20
|
+
| 'onPaneContextMenu'
|
|
21
|
+
| 'onNodeMouseEnter'
|
|
22
|
+
| 'onNodeMouseLeave'
|
|
23
|
+
| 'onEdgeMouseEnter'
|
|
24
|
+
| 'onEdgeMouseLeave'
|
|
25
|
+
| 'onViewportChange'
|
|
26
|
+
>
|
|
27
|
+
>
|
|
28
|
+
>
|
|
29
|
+
|
|
30
|
+
const viewportDomNode = (state: XYFlowState) =>
|
|
31
|
+
state.domNode?.querySelector<HTMLDivElement>('.react-flow__viewport') ?? null
|
|
18
32
|
|
|
19
33
|
export function useXYFlowEvents() {
|
|
20
34
|
const diagramApi = useDiagramStoreApi()
|
|
@@ -22,79 +36,159 @@ export function useXYFlowEvents() {
|
|
|
22
36
|
|
|
23
37
|
const dblclickTimeout = useRef<number>()
|
|
24
38
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
const viewportRef = useRef(viewportDomNode(xyflowApi.getState()))
|
|
40
|
+
|
|
41
|
+
const dbclickGuarg = () => {
|
|
42
|
+
if (dblclickTimeout.current !== undefined) {
|
|
43
|
+
return true
|
|
44
|
+
}
|
|
45
|
+
dblclickTimeout.current = window.setTimeout(() => {
|
|
46
|
+
dblclickTimeout.current = undefined
|
|
47
|
+
}, 250)
|
|
48
|
+
return false
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const transformRef = useRef('')
|
|
52
|
+
const cbRef = useRef<number>()
|
|
53
|
+
|
|
54
|
+
useEffect(() =>
|
|
55
|
+
xyflowApi.subscribe((state, prev) => {
|
|
56
|
+
if (state.domNode !== prev.domNode || (state.domNode && !viewportRef.current)) {
|
|
57
|
+
viewportRef.current = viewportDomNode(state)
|
|
58
|
+
}
|
|
59
|
+
}), [xyflowApi])
|
|
36
60
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
61
|
+
return useMemo(() =>
|
|
62
|
+
({
|
|
63
|
+
onDoubleClick: (event) => {
|
|
64
|
+
diagramApi.getState().resetLastClicked()
|
|
65
|
+
const diagramState = diagramApi.getState()
|
|
40
66
|
if (diagramState.onCanvasDblClick) {
|
|
41
67
|
diagramState.onCanvasDblClick(event)
|
|
42
68
|
return
|
|
43
69
|
}
|
|
44
|
-
diagramState.
|
|
45
|
-
if (diagramState.fitViewEnabled && diagramState.viewportChanged) {
|
|
70
|
+
if (diagramState.fitViewEnabled && diagramState.zoomable) {
|
|
46
71
|
diagramState.fitDiagram()
|
|
72
|
+
event.stopPropagation()
|
|
73
|
+
return
|
|
47
74
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
diagramApi.getState()
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
75
|
+
},
|
|
76
|
+
onPaneClick: (event) => {
|
|
77
|
+
if (dbclickGuarg()) {
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
const { focusedNodeId, fitDiagram, onCanvasClick, resetLastClicked } = diagramApi.getState()
|
|
81
|
+
resetLastClicked()
|
|
82
|
+
if (onCanvasClick) {
|
|
83
|
+
onCanvasClick(event)
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
// reset focus if clicked on empty space
|
|
87
|
+
if (focusedNodeId) {
|
|
88
|
+
fitDiagram()
|
|
89
|
+
event.stopPropagation()
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
onNodeContextMenu: (event, xynode) => {
|
|
93
|
+
diagramApi.getState().setLastClickedNode(xynode.id)
|
|
94
|
+
diagramApi.getState().onNodeContextMenu?.({
|
|
95
|
+
element: xynode.data.element,
|
|
96
|
+
xynode,
|
|
97
|
+
event
|
|
98
|
+
})
|
|
99
|
+
},
|
|
100
|
+
onPaneContextMenu: (event) => {
|
|
101
|
+
diagramApi.getState().resetLastClicked()
|
|
102
|
+
diagramApi.getState().onCanvasContextMenu?.(event as any)
|
|
103
|
+
},
|
|
104
|
+
onEdgeContextMenu: (event, xyedge) => {
|
|
105
|
+
diagramApi.getState().setLastClickedEdge(xyedge.id)
|
|
106
|
+
diagramApi.getState().onEdgeContextMenu?.({
|
|
107
|
+
relation: xyedge.data.edge,
|
|
108
|
+
xyedge,
|
|
109
|
+
event
|
|
110
|
+
})
|
|
111
|
+
},
|
|
112
|
+
onNodeClick: (event, xynode) => {
|
|
113
|
+
diagramApi.getState().setLastClickedNode(xynode.id)
|
|
114
|
+
const { focusedNodeId, fitDiagram, onNodeClick } = diagramApi.getState()
|
|
115
|
+
if (onNodeClick) {
|
|
116
|
+
onNodeClick({
|
|
117
|
+
element: xynode.data.element,
|
|
118
|
+
xynode,
|
|
119
|
+
event
|
|
120
|
+
})
|
|
121
|
+
return
|
|
122
|
+
}
|
|
123
|
+
// if we clicked on a node that is not focused, fit the diagram
|
|
124
|
+
if (!!focusedNodeId && focusedNodeId !== xynode.id) {
|
|
125
|
+
fitDiagram(xynode)
|
|
126
|
+
event.stopPropagation()
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
onNodeDoubleClick: (event, xynode) => {
|
|
130
|
+
diagramApi.getState().setLastClickedNode(xynode.id)
|
|
131
|
+
const { zoomable, fitViewEnabled, fitDiagram, onNodeClick } = diagramApi.getState()
|
|
132
|
+
if (onNodeClick) {
|
|
133
|
+
return
|
|
134
|
+
}
|
|
135
|
+
if (zoomable && fitViewEnabled) {
|
|
136
|
+
fitDiagram(xynode)
|
|
137
|
+
event.stopPropagation()
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
onEdgeClick: (event, xyedge) => {
|
|
141
|
+
diagramApi.getState().setLastClickedEdge(xyedge.id)
|
|
142
|
+
const { focusedNodeId, fitDiagram, onEdgeClick, xyflow } = diagramApi.getState()
|
|
143
|
+
if (onEdgeClick) {
|
|
144
|
+
onEdgeClick({
|
|
145
|
+
relation: xyedge.data.edge,
|
|
146
|
+
xyedge,
|
|
147
|
+
event
|
|
148
|
+
})
|
|
149
|
+
return
|
|
150
|
+
}
|
|
151
|
+
if (focusedNodeId && (focusedNodeId === xyedge.source || focusedNodeId === xyedge.target)) {
|
|
152
|
+
const nextFocus = focusedNodeId === xyedge.source ? xyedge.target : xyedge.source
|
|
153
|
+
fitDiagram(xyflow.getNode(nextFocus))
|
|
154
|
+
event.stopPropagation()
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
onMoveEnd: (event, _viewport) => {
|
|
158
|
+
// if event is present, the move was triggered by user
|
|
159
|
+
const viewportChanged = !!event
|
|
160
|
+
if (viewportChanged !== diagramApi.getState().viewportChanged) {
|
|
161
|
+
diagramApi.setState({ viewportChanged }, false, `viewport-changed: ${viewportChanged}`)
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
onNodeMouseEnter: (event, xynode) => {
|
|
165
|
+
diagramApi.getState().setHoveredNode(xynode.id)
|
|
166
|
+
},
|
|
167
|
+
onNodeMouseLeave: (event, xynode) => {
|
|
168
|
+
diagramApi.getState().setHoveredNode(null)
|
|
169
|
+
},
|
|
170
|
+
onEdgeMouseEnter: (event, xyedge) => {
|
|
171
|
+
diagramApi.getState().setHoveredEdge(xyedge.id)
|
|
172
|
+
},
|
|
173
|
+
onEdgeMouseLeave: (event, xyedge) => {
|
|
174
|
+
diagramApi.getState().setHoveredEdge(null)
|
|
175
|
+
},
|
|
176
|
+
/**
|
|
177
|
+
* WOKAROUND:
|
|
178
|
+
* Viewport transform is not rounded to integers which results in blurry nodes on some resolution
|
|
179
|
+
* https://github.com/xyflow/xyflow/issues/3282
|
|
180
|
+
*/
|
|
181
|
+
onViewportChange: ({ x, y, zoom }) => {
|
|
182
|
+
const scale = zoom != 1 ? zoom.toFixed(6) : '1'
|
|
183
|
+
transformRef.current = `translate(${Math.round(x)}px, ${Math.round(y)}px) scale(${scale})`
|
|
184
|
+
cbRef.current ??= requestIdleCallback(() => {
|
|
185
|
+
cbRef.current = undefined
|
|
186
|
+
if (viewportRef.current && viewportRef.current.style.transform !== transformRef.current) {
|
|
187
|
+
viewportRef.current.style.transform = transformRef.current
|
|
188
|
+
}
|
|
189
|
+
}, {
|
|
190
|
+
timeout: 1000
|
|
191
|
+
})
|
|
97
192
|
}
|
|
98
|
-
}
|
|
99
|
-
}), [diagramApi, xyflowApi])
|
|
193
|
+
}) satisfies XYFlowEventHandlers, [diagramApi, xyflowApi])
|
|
100
194
|
}
|
|
@@ -118,10 +118,10 @@ export function diagramViewToXYFlowData(
|
|
|
118
118
|
initialHeight: node.height, */
|
|
119
119
|
width: node.width,
|
|
120
120
|
height: node.height,
|
|
121
|
-
measured: {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
},
|
|
121
|
+
// measured: {
|
|
122
|
+
// width: node.width,
|
|
123
|
+
// height: node.height
|
|
124
|
+
// },
|
|
125
125
|
...(parent && {
|
|
126
126
|
parentId: ns + parent.id
|
|
127
127
|
})
|
|
@@ -3,7 +3,9 @@ import { Box } from '@mantine/core'
|
|
|
3
3
|
import { assignInlineVars } from '@vanilla-extract/dynamic'
|
|
4
4
|
import type { EdgeProps, XYPosition } from '@xyflow/react'
|
|
5
5
|
import { EdgeLabelRenderer, getBezierPath } from '@xyflow/react'
|
|
6
|
+
import { getNodePositionWithOrigin } from '@xyflow/system'
|
|
6
7
|
import clsx from 'clsx'
|
|
8
|
+
import { curveCatmullRomOpen, line as d3line } from 'd3-shape'
|
|
7
9
|
import { deepEqual as eq } from 'fast-equals'
|
|
8
10
|
import { type CSSProperties, memo } from 'react'
|
|
9
11
|
import { hasAtLeast } from 'remeda'
|
|
@@ -12,16 +14,7 @@ import { ZIndexes } from '../const'
|
|
|
12
14
|
import { useXYStoreApi } from '../hooks'
|
|
13
15
|
import { type XYFlowEdge } from '../types'
|
|
14
16
|
import { toDomPrecision } from '../utils'
|
|
15
|
-
import
|
|
16
|
-
container,
|
|
17
|
-
cssEdgePath,
|
|
18
|
-
edgeLabel,
|
|
19
|
-
edgeLabelBody,
|
|
20
|
-
edgePathBg,
|
|
21
|
-
fillStrokeCtx,
|
|
22
|
-
varLabelX,
|
|
23
|
-
varLabelY
|
|
24
|
-
} from './edges.css'
|
|
17
|
+
import * as edgesCss from './edges.css'
|
|
25
18
|
import { getEdgeParams } from './utils'
|
|
26
19
|
// import { getEdgeParams } from './utils'
|
|
27
20
|
|
|
@@ -83,6 +76,7 @@ const isEqualProps = (prev: EdgeProps<XYFlowEdge>, next: EdgeProps<XYFlowEdge>)
|
|
|
83
76
|
prev.id === next.id
|
|
84
77
|
&& prev.source === next.source
|
|
85
78
|
&& prev.target === next.target
|
|
79
|
+
&& eq(prev.selected ?? false, next.selected ?? false)
|
|
86
80
|
&& isSamePoint(prev.sourceX, next.sourceX)
|
|
87
81
|
&& isSamePoint(prev.sourceY, next.sourceY)
|
|
88
82
|
&& isSamePoint(prev.targetX, next.targetX)
|
|
@@ -90,11 +84,12 @@ const isEqualProps = (prev: EdgeProps<XYFlowEdge>, next: EdgeProps<XYFlowEdge>)
|
|
|
90
84
|
&& eq(prev.data, next.data)
|
|
91
85
|
)
|
|
92
86
|
|
|
87
|
+
const curve = d3line<Point>().curve(curveCatmullRomOpen.alpha(1))
|
|
88
|
+
|
|
93
89
|
export const RelationshipEdge = /* @__PURE__ */ memo<EdgeProps<XYFlowEdge>>(function RelationshipEdgeR({
|
|
94
90
|
id,
|
|
95
91
|
data,
|
|
96
92
|
selected: _selected,
|
|
97
|
-
markerEnd: _markerEnd,
|
|
98
93
|
style,
|
|
99
94
|
source,
|
|
100
95
|
target,
|
|
@@ -107,16 +102,13 @@ export const RelationshipEdge = /* @__PURE__ */ memo<EdgeProps<XYFlowEdge>>(func
|
|
|
107
102
|
const isNotModified = isSamePosition(sourceNode.internals.positionAbsolute, sourceNode.data.element.position)
|
|
108
103
|
&& isSamePosition(targetNode.internals.positionAbsolute, targetNode.data.element.position)
|
|
109
104
|
|
|
110
|
-
|
|
111
|
-
const {
|
|
112
|
-
edge
|
|
113
|
-
} = data
|
|
105
|
+
const { edge: diagramEdge, controlPoints } = data
|
|
114
106
|
// const edgePath = bezierPath(edge.points)
|
|
115
107
|
|
|
116
|
-
const color =
|
|
108
|
+
const color = diagramEdge.color ?? 'gray'
|
|
117
109
|
const isHovered = useDiagramState(s => s.hoveredEdgeId === id)
|
|
118
110
|
|
|
119
|
-
const line =
|
|
111
|
+
const line = diagramEdge.line ?? 'dashed'
|
|
120
112
|
const isDotted = line === 'dotted'
|
|
121
113
|
const isDashed = isDotted || line === 'dashed'
|
|
122
114
|
|
|
@@ -132,7 +124,7 @@ export const RelationshipEdge = /* @__PURE__ */ memo<EdgeProps<XYFlowEdge>>(func
|
|
|
132
124
|
let edgePath: string, labelX: number, labelY: number
|
|
133
125
|
|
|
134
126
|
if (isNotModified) {
|
|
135
|
-
edgePath = bezierPath(
|
|
127
|
+
edgePath = bezierPath(diagramEdge.points)
|
|
136
128
|
labelX = data.label?.bbox.x ?? 0
|
|
137
129
|
labelY = data.label?.bbox.y ?? 0
|
|
138
130
|
} else {
|
|
@@ -149,17 +141,22 @@ export const RelationshipEdge = /* @__PURE__ */ memo<EdgeProps<XYFlowEdge>>(func
|
|
|
149
141
|
edgePath = _edgePath
|
|
150
142
|
labelX = _labelX
|
|
151
143
|
labelY = _labelY
|
|
144
|
+
|
|
145
|
+
const sourceCenterPos = getNodePositionWithOrigin(sourceNode, [-0.5, -0.5])
|
|
146
|
+
const targetCenterPos = getNodePositionWithOrigin(targetNode, [-0.5, -0.5])
|
|
147
|
+
|
|
148
|
+
edgePath = curve([
|
|
149
|
+
[sourceCenterPos.positionAbsolute.x, sourceCenterPos.positionAbsolute.y],
|
|
150
|
+
[sx, sy],
|
|
151
|
+
...controlPoints,
|
|
152
|
+
[tx, ty],
|
|
153
|
+
[targetCenterPos.positionAbsolute.x, targetCenterPos.positionAbsolute.y]
|
|
154
|
+
])!
|
|
152
155
|
}
|
|
153
|
-
// const deferredEdgePath = edgePath
|
|
154
|
-
// useTilg()`
|
|
155
|
-
// ${id}
|
|
156
|
-
// path=${edgePath}
|
|
157
|
-
// isModified=${isModified}
|
|
158
|
-
// `
|
|
159
156
|
|
|
160
157
|
return (
|
|
161
|
-
<g className={clsx(container)} data-likec4-color={color} data-edge-hovered={isHovered}>
|
|
162
|
-
<g className={clsx(fillStrokeCtx)}>
|
|
158
|
+
<g className={clsx(edgesCss.container)} data-likec4-color={color} data-edge-hovered={isHovered}>
|
|
159
|
+
<g className={clsx(edgesCss.fillStrokeCtx)}>
|
|
163
160
|
<defs>
|
|
164
161
|
<marker
|
|
165
162
|
id={`arrow-${id}`}
|
|
@@ -179,30 +176,29 @@ export const RelationshipEdge = /* @__PURE__ */ memo<EdgeProps<XYFlowEdge>>(func
|
|
|
179
176
|
edgePath={edgePath}
|
|
180
177
|
interactionWidth={interactionWidth ?? 10}
|
|
181
178
|
strokeDasharray={strokeDasharray}
|
|
182
|
-
markerStart={
|
|
183
|
-
markerEnd={
|
|
179
|
+
markerStart={diagramEdge.tailArrow ? marker : undefined}
|
|
180
|
+
markerEnd={diagramEdge.headArrow ? marker : undefined}
|
|
184
181
|
style={style} />
|
|
185
182
|
{
|
|
186
|
-
/*
|
|
187
|
-
{controlPoints.map((p, i) => (
|
|
183
|
+
/* controlPoints.map((p, i) => (
|
|
188
184
|
<circle
|
|
189
|
-
className={
|
|
185
|
+
className={edgesCss.controlPoint}
|
|
190
186
|
key={i}
|
|
191
187
|
cx={p[0]}
|
|
192
188
|
cy={p[1]}
|
|
193
189
|
r={5}
|
|
194
190
|
/>
|
|
195
|
-
))
|
|
191
|
+
)) */
|
|
196
192
|
}
|
|
197
193
|
{data.label && (
|
|
198
194
|
<EdgeLabelRenderer>
|
|
199
195
|
<Box
|
|
200
|
-
className={clsx(container, edgeLabel)}
|
|
196
|
+
className={clsx(edgesCss.container, edgesCss.edgeLabel)}
|
|
201
197
|
data-likec4-color={color}
|
|
202
198
|
style={{
|
|
203
199
|
...assignInlineVars({
|
|
204
|
-
[varLabelX]: toDomPrecision(labelX) + 'px',
|
|
205
|
-
[varLabelY]: toDomPrecision(labelY) + 'px'
|
|
200
|
+
[edgesCss.varLabelX]: toDomPrecision(labelX) + 'px',
|
|
201
|
+
[edgesCss.varLabelY]: toDomPrecision(labelY) + 'px'
|
|
206
202
|
}),
|
|
207
203
|
maxWidth: data.label.bbox.width + 10,
|
|
208
204
|
zIndex: edgeLookup.get(id)?.zIndex ?? ZIndexes.Edge
|
|
@@ -211,7 +207,7 @@ export const RelationshipEdge = /* @__PURE__ */ memo<EdgeProps<XYFlowEdge>>(func
|
|
|
211
207
|
'data-edge-hovered': isHovered
|
|
212
208
|
}}
|
|
213
209
|
>
|
|
214
|
-
<Box className={edgeLabelBody}>
|
|
210
|
+
<Box className={edgesCss.edgeLabelBody}>
|
|
215
211
|
{data.label.text}
|
|
216
212
|
</Box>
|
|
217
213
|
{
|
|
@@ -246,30 +242,30 @@ export const RelationshipEdge = /* @__PURE__ */ memo<EdgeProps<XYFlowEdge>>(func
|
|
|
246
242
|
)
|
|
247
243
|
}, isEqualProps)
|
|
248
244
|
|
|
249
|
-
const RelationshipPath =
|
|
250
|
-
edgePath: string
|
|
251
|
-
interactionWidth: number
|
|
252
|
-
strokeDasharray: string | undefined
|
|
253
|
-
markerStart: string | undefined
|
|
254
|
-
markerEnd: string | undefined
|
|
255
|
-
style: CSSProperties | undefined
|
|
256
|
-
}>(({
|
|
245
|
+
const RelationshipPath = ({
|
|
257
246
|
edgePath,
|
|
258
247
|
interactionWidth,
|
|
259
248
|
strokeDasharray,
|
|
260
249
|
markerStart,
|
|
261
250
|
markerEnd,
|
|
262
251
|
style
|
|
252
|
+
}: {
|
|
253
|
+
edgePath: string
|
|
254
|
+
interactionWidth: number
|
|
255
|
+
strokeDasharray: string | undefined
|
|
256
|
+
markerStart: string | undefined
|
|
257
|
+
markerEnd: string | undefined
|
|
258
|
+
style: CSSProperties | undefined
|
|
263
259
|
}) => (
|
|
264
260
|
<>
|
|
265
261
|
<path
|
|
266
|
-
className={clsx('react-flow__edge-path', edgePathBg)}
|
|
262
|
+
className={clsx('react-flow__edge-path', edgesCss.edgePathBg)}
|
|
267
263
|
d={edgePath}
|
|
268
264
|
style={style}
|
|
269
265
|
strokeLinecap={'round'}
|
|
270
266
|
/>
|
|
271
267
|
<path
|
|
272
|
-
className={clsx('react-flow__edge-path', cssEdgePath)}
|
|
268
|
+
className={clsx('react-flow__edge-path', edgesCss.cssEdgePath)}
|
|
273
269
|
d={edgePath}
|
|
274
270
|
style={style}
|
|
275
271
|
strokeLinecap={'round'}
|
|
@@ -285,4 +281,4 @@ const RelationshipPath = memo<{
|
|
|
285
281
|
strokeWidth={interactionWidth}
|
|
286
282
|
/>
|
|
287
283
|
</>
|
|
288
|
-
)
|
|
284
|
+
)
|
|
@@ -1,50 +1,56 @@
|
|
|
1
1
|
import { rem } from '@mantine/core'
|
|
2
|
-
import { createVar, globalStyle, style } from '@vanilla-extract/css'
|
|
3
|
-
import { mantine } from '../../mantine.css'
|
|
2
|
+
import { createVar, generateIdentifier, globalKeyframes, globalStyle, style } from '@vanilla-extract/css'
|
|
4
3
|
import { vars, xyvars } from '../../theme.css'
|
|
5
4
|
|
|
5
|
+
const mixColor = createVar('mix-color')
|
|
6
|
+
|
|
6
7
|
export const container = style({
|
|
7
8
|
vars: {
|
|
9
|
+
[mixColor]: `black`,
|
|
8
10
|
[xyvars.edge.stroke]: vars.relation.lineColor,
|
|
9
|
-
[xyvars.edge.strokeSelected]: `color-mix(in srgb, ${vars.relation.lineColor},
|
|
11
|
+
[xyvars.edge.strokeSelected]: `color-mix(in srgb, ${vars.relation.lineColor}, ${mixColor} 35%)`,
|
|
10
12
|
[xyvars.edge.labelColor]: `color-mix(in srgb, ${vars.relation.labelColor}, rgba(255 255 255 / 0.85) 20%)`,
|
|
11
13
|
[xyvars.edge.labelBgColor]: `color-mix(in srgb, ${vars.relation.labelBgColor}, transparent 35%)`,
|
|
12
|
-
[xyvars.edge.strokeWidth]: '2.
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
14
|
+
[xyvars.edge.strokeWidth]: '2.2px'
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
globalStyle(`:where([data-mantine-color-scheme="dark"]) ${container}`, {
|
|
19
|
+
vars: {
|
|
20
|
+
[mixColor]: `white`,
|
|
21
|
+
[xyvars.edge.labelColor]: vars.relation.labelColor,
|
|
22
|
+
[xyvars.edge.labelBgColor]: `color-mix(in srgb, ${vars.relation.labelBgColor}, transparent 55%)`
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
const isSelected = '.react-flow__edge.selected'
|
|
27
|
+
|
|
28
|
+
globalStyle(`:where(${isSelected}) ${container}`, {
|
|
29
|
+
vars: {
|
|
30
|
+
[xyvars.edge.stroke]: xyvars.edge.strokeSelected,
|
|
31
|
+
[xyvars.edge.strokeWidth]: '3px'
|
|
27
32
|
}
|
|
28
33
|
})
|
|
29
34
|
|
|
30
35
|
globalStyle(`${container}[data-edge-hovered='true']`, {
|
|
31
36
|
vars: {
|
|
32
|
-
[xyvars.edge.stroke]: `color-mix(in srgb, ${vars.relation.lineColor},
|
|
37
|
+
// [xyvars.edge.stroke]: `color-mix(in srgb, ${vars.relation.lineColor}, ${mixColor} 35%)`,
|
|
38
|
+
[xyvars.edge.stroke]: xyvars.edge.strokeSelected,
|
|
33
39
|
[xyvars.edge.strokeWidth]: '3px'
|
|
34
40
|
}
|
|
35
41
|
})
|
|
42
|
+
|
|
43
|
+
globalStyle(`:where(${isSelected}) ${container}[data-edge-hovered='true']`, {
|
|
44
|
+
vars: {
|
|
45
|
+
[xyvars.edge.strokeWidth]: '3.6px'
|
|
46
|
+
}
|
|
47
|
+
})
|
|
36
48
|
// globalStyle(`${container}[data-edge-hovered='true']`, {
|
|
37
49
|
// vars: {
|
|
38
50
|
// [xyvars.edge.stroke]: `color-mix(in srgb, ${vars.relation.lineColor}, white 35%)`
|
|
39
51
|
// },
|
|
40
52
|
// })
|
|
41
53
|
|
|
42
|
-
globalStyle(`.react-flow__edge.selected ${container}`, {
|
|
43
|
-
vars: {
|
|
44
|
-
[xyvars.edge.strokeWidth]: '2.5'
|
|
45
|
-
}
|
|
46
|
-
})
|
|
47
|
-
|
|
48
54
|
globalStyle(`.react-flow__edges > svg`, {
|
|
49
55
|
mixBlendMode: 'screen'
|
|
50
56
|
})
|
|
@@ -53,25 +59,60 @@ globalStyle(`:where([data-mantine-color-scheme="dark"]) .react-flow__edges > svg
|
|
|
53
59
|
})
|
|
54
60
|
|
|
55
61
|
export const edgePathBg = style({
|
|
56
|
-
strokeWidth:
|
|
57
|
-
strokeOpacity: 0.
|
|
62
|
+
strokeWidth: xyvars.edge.strokeWidth,
|
|
63
|
+
strokeOpacity: 0.05,
|
|
64
|
+
// transition: 'stroke-width 175ms ease-in-out',
|
|
65
|
+
// transition: 'stroke-width 175ms ease-in-out, stroke-opacity 150ms ease-out',
|
|
66
|
+
transitionProperty: 'stroke-width, stroke-opacity',
|
|
67
|
+
transitionDuration: '155ms',
|
|
68
|
+
transitionTimingFunction: 'ease-out',
|
|
69
|
+
selectors: {
|
|
70
|
+
[`:where(${isSelected}, [data-edge-hovered='true']) &`]: {
|
|
71
|
+
strokeWidth: `calc(${xyvars.edge.strokeWidth} + 8px)`,
|
|
72
|
+
strokeOpacity: 0.11
|
|
73
|
+
}
|
|
74
|
+
}
|
|
58
75
|
})
|
|
59
76
|
|
|
60
77
|
export const fillStrokeCtx = style({
|
|
61
78
|
fill: xyvars.edge.stroke,
|
|
62
|
-
stroke: xyvars.edge.stroke
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
79
|
+
stroke: xyvars.edge.stroke
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
export const controlPoint = style({
|
|
83
|
+
fill: xyvars.edge.stroke,
|
|
84
|
+
stroke: xyvars.edge.stroke
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
globalStyle(`:where(${isSelected}) ${fillStrokeCtx}`, {
|
|
88
|
+
vars: {
|
|
89
|
+
[xyvars.edge.stroke]: xyvars.edge.strokeSelected
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
const strokeKeyframes = generateIdentifier('strokeKeyframes')
|
|
94
|
+
globalKeyframes(strokeKeyframes, {
|
|
95
|
+
'from': {
|
|
96
|
+
strokeDashoffset: 18 * 2
|
|
97
|
+
},
|
|
98
|
+
'to': {
|
|
99
|
+
strokeDashoffset: 1
|
|
69
100
|
}
|
|
70
101
|
})
|
|
71
102
|
|
|
72
103
|
export const cssEdgePath = style({
|
|
104
|
+
animationDuration: '800ms',
|
|
105
|
+
animationIterationCount: 'infinite',
|
|
106
|
+
animationTimingFunction: 'linear',
|
|
73
107
|
selectors: {
|
|
74
|
-
[
|
|
108
|
+
[`:where([data-edge-hovered='true']) &`]: {
|
|
109
|
+
animationName: strokeKeyframes,
|
|
110
|
+
animationDelay: '500ms',
|
|
111
|
+
transition: 'all 130ms ease-out'
|
|
112
|
+
},
|
|
113
|
+
[`:where(${isSelected}) &`]: {
|
|
114
|
+
animationName: strokeKeyframes,
|
|
115
|
+
animationDelay: '0ms',
|
|
75
116
|
transition: 'all 130ms ease-out'
|
|
76
117
|
}
|
|
77
118
|
}
|
|
@@ -97,7 +138,7 @@ export const edgeLabel = style({
|
|
|
97
138
|
backgroundColor: xyvars.edge.labelBgColor,
|
|
98
139
|
borderRadius: '3px',
|
|
99
140
|
transform: varTranslate,
|
|
100
|
-
|
|
141
|
+
backfaceVisibility: 'hidden',
|
|
101
142
|
vars: {
|
|
102
143
|
// [varTranslate]: `translate(calc(${varLabelX} - 50%), calc(${varLabelY} - 50%))`,
|
|
103
144
|
[varTranslate]: `translate(${varLabelX}, ${varLabelY})`,
|