@likec4/diagram 1.2.2 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/LikeC4Diagram.d.ts +1 -1
- package/dist/LikeC4Diagram.props.d.ts +27 -4
- package/dist/index.mjs +1543 -1346
- package/dist/state/DiagramContext.d.ts +2353 -264
- package/dist/state/diagramStore.d.ts +2360 -268
- package/dist/state/index.d.ts +0 -1
- package/dist/state/useDiagramStore.d.ts +3 -2534
- package/dist/state/useNavigationHistory.d.ts +5 -0
- package/dist/style.css +194 -131
- package/dist/theme.css.d.ts +5 -1
- package/dist/ui/BackwardForwardPanel.css.d.ts +1 -0
- package/dist/ui/BackwardForwardPanel.d.ts +1 -0
- package/dist/ui/DiagramTitlePanel.css.d.ts +1 -1
- package/dist/xyflow/XYFlow.d.ts +2 -1
- package/dist/xyflow/XYFlowEvents.d.ts +4 -4
- package/dist/xyflow/XYFlowInner.d.ts +2 -1
- package/dist/xyflow/const.d.ts +1 -1
- package/dist/xyflow/diagram-to-xyflow.d.ts +1 -1
- package/dist/xyflow/edges/RelationshipEdge.d.ts +4 -5
- package/dist/xyflow/edges/utils.d.ts +4 -2
- package/dist/xyflow/hooks/useXYFlow.d.ts +9 -9
- package/dist/xyflow/nodes/compound/CompoundNode.css.d.ts +3 -1
- package/dist/xyflow/nodes/element/ElementNode.d.ts +1 -1
- package/dist/xyflow/nodes/element/element.css.d.ts +1 -0
- package/dist/xyflow/nodes/element/index.d.ts +1 -1
- package/dist/xyflow/types.d.ts +3 -3
- package/dist/xyflow/utils.d.ts +3 -0
- package/package.json +18 -17
- package/src/LikeC4Diagram.css.ts +15 -5
- package/src/LikeC4Diagram.props.ts +33 -5
- package/src/LikeC4Diagram.tsx +103 -32
- package/src/globals.css.ts +1 -2
- package/src/mantine/theme.ts +1 -0
- package/src/state/DiagramContext.tsx +3 -4
- package/src/state/diagramStore.ts +177 -103
- package/src/state/index.ts +0 -1
- package/src/state/useDiagramStore.ts +6 -13
- package/src/state/useNavigationHistory.ts +39 -0
- package/src/theme.css.ts +5 -1
- package/src/ui/BackwardForwardPanel.css.ts +22 -0
- package/src/ui/BackwardForwardPanel.tsx +59 -0
- package/src/ui/DiagramTitlePanel.css.ts +11 -5
- package/src/ui/DiagramTitlePanel.tsx +108 -96
- package/src/ui/DynamicViewWalkthrough.css.ts +3 -2
- package/src/ui/DynamicViewWalkthrough.tsx +36 -17
- package/src/ui/OptionsPanel.tsx +28 -19
- package/src/ui/options/NodeOptions.tsx +16 -19
- package/src/xyflow/FitviewOnDiagramChange.tsx +15 -23
- package/src/xyflow/SelectEdgesOnNodeFocus.tsx +3 -6
- package/src/xyflow/SyncWithDiagram.tsx +21 -2
- package/src/xyflow/XYFlow.tsx +20 -23
- package/src/xyflow/XYFlowEvents.tsx +26 -8
- package/src/xyflow/XYFlowInner.tsx +10 -6
- package/src/xyflow/const.ts +1 -1
- package/src/xyflow/diagram-to-xyflow.ts +4 -41
- package/src/xyflow/edges/RelationshipEdge.tsx +160 -111
- package/src/xyflow/edges/edges.css.ts +41 -26
- package/src/xyflow/edges/utils.ts +5 -2
- package/src/xyflow/hooks/useLayoutConstraints.ts +19 -4
- package/src/xyflow/nodes/compound/CompoundNode.css.ts +45 -37
- package/src/xyflow/nodes/compound/CompoundNode.tsx +36 -26
- package/src/xyflow/nodes/element/ElementNode.tsx +27 -14
- package/src/xyflow/nodes/element/element.css.ts +31 -18
- package/src/xyflow/nodes/element/index.ts +1 -1
- package/src/xyflow/nodes/shared/NavigateToBtn.tsx +7 -3
- package/src/xyflow/types.ts +5 -2
- package/src/xyflow/utils.ts +48 -0
- package/dist/ui/index.d.ts +0 -2
- package/src/ui/index.ts +0 -3
|
@@ -52,10 +52,29 @@ export function SyncWithDiagram() {
|
|
|
52
52
|
setEdges(updates.edges.map(update => {
|
|
53
53
|
const existing = edgeLookup.get(update.id)
|
|
54
54
|
if (existing) {
|
|
55
|
-
|
|
55
|
+
if (eq(existing.data.controlPoints, update.data.controlPoints) && eq(existing.data.edge, update.data.edge)) {
|
|
56
|
+
return existing
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
56
59
|
...existing,
|
|
57
|
-
...update
|
|
60
|
+
...update,
|
|
61
|
+
data: {
|
|
62
|
+
...existing.data,
|
|
63
|
+
...update.data
|
|
64
|
+
}
|
|
58
65
|
}
|
|
66
|
+
// if (update.data.controlPoints === null && existing.data.controlPoints !== null) {
|
|
67
|
+
// // we don't want accidentally
|
|
68
|
+
// // overwrite existing control points
|
|
69
|
+
// return {
|
|
70
|
+
// ...existing,
|
|
71
|
+
// ...update,
|
|
72
|
+
// data: {
|
|
73
|
+
// ...update.data,
|
|
74
|
+
// controlPoints: existing.data.controlPoints
|
|
75
|
+
// }
|
|
76
|
+
// }
|
|
77
|
+
// }
|
|
59
78
|
}
|
|
60
79
|
return update
|
|
61
80
|
}))
|
package/src/xyflow/XYFlow.tsx
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { deepEqual as eq
|
|
4
|
-
import { type CSSProperties,
|
|
5
|
-
import
|
|
6
|
-
import type { SetNonNullable, Simplify } from 'type-fest'
|
|
7
|
-
import type { LikeC4DiagramProperties } from '../LikeC4Diagram.props'
|
|
1
|
+
import { ReactFlow, useOnViewportChange } from '@xyflow/react'
|
|
2
|
+
import { DEV } from 'esm-env'
|
|
3
|
+
import { deepEqual as eq } from 'fast-equals'
|
|
4
|
+
import { type CSSProperties, type PropsWithChildren, useCallback } from 'react'
|
|
5
|
+
import useTilg from 'tilg'
|
|
8
6
|
import { type DiagramState, useDiagramState, useDiagramStoreApi } from '../state'
|
|
9
7
|
import { MinZoom } from './const'
|
|
10
8
|
import { RelationshipEdge } from './edges/RelationshipEdge'
|
|
@@ -13,7 +11,6 @@ import { useXYStoreApi } from './hooks/useXYFlow'
|
|
|
13
11
|
import { CompoundNode } from './nodes/compound'
|
|
14
12
|
import { ElementNode } from './nodes/element'
|
|
15
13
|
import { XYFlowEdge, XYFlowNode } from './types'
|
|
16
|
-
import { XYFlowBackground } from './XYFlowBackground'
|
|
17
14
|
import { useXYFlowEvents } from './XYFlowEvents'
|
|
18
15
|
|
|
19
16
|
const nodeTypes = {
|
|
@@ -25,6 +22,7 @@ const edgeTypes = {
|
|
|
25
22
|
}
|
|
26
23
|
|
|
27
24
|
type XYFlowWrapperProps = PropsWithChildren<{
|
|
25
|
+
colorMode?: 'system' | 'light' | 'dark'
|
|
28
26
|
className?: string | undefined
|
|
29
27
|
defaultNodes: XYFlowNode[]
|
|
30
28
|
defaultEdges: XYFlowEdge[]
|
|
@@ -45,7 +43,12 @@ const selector = (s: DiagramState) => ({
|
|
|
45
43
|
nodesSelectable: s.nodesSelectable || s.focusedNodeId !== null,
|
|
46
44
|
nodesDraggable: s.nodesDraggable,
|
|
47
45
|
fitView: s.fitViewEnabled,
|
|
48
|
-
|
|
46
|
+
fitViewOptions: {
|
|
47
|
+
minZoom: MinZoom,
|
|
48
|
+
maxZoom: 1,
|
|
49
|
+
padding: s.fitViewPadding,
|
|
50
|
+
includeHiddenNodes: true
|
|
51
|
+
},
|
|
49
52
|
hasOnNavigateTo: !!s.onNavigateTo,
|
|
50
53
|
hasOnNodeClick: !!s.onNodeClick,
|
|
51
54
|
hasOnNodeContextMenu: !!s.onNodeContextMenu,
|
|
@@ -57,19 +60,21 @@ const selector = (s: DiagramState) => ({
|
|
|
57
60
|
})
|
|
58
61
|
|
|
59
62
|
export function XYFlow({
|
|
63
|
+
colorMode = 'system',
|
|
60
64
|
className,
|
|
61
65
|
children,
|
|
62
66
|
defaultNodes,
|
|
63
67
|
defaultEdges,
|
|
64
68
|
style
|
|
65
69
|
}: XYFlowWrapperProps) {
|
|
70
|
+
DEV && useTilg()
|
|
66
71
|
const xyflowApi = useXYStoreApi()
|
|
67
72
|
const diagramApi = useDiagramStoreApi()
|
|
68
73
|
const {
|
|
69
74
|
nodesSelectable,
|
|
70
75
|
nodesDraggable,
|
|
71
76
|
fitView,
|
|
72
|
-
|
|
77
|
+
fitViewOptions,
|
|
73
78
|
pannable,
|
|
74
79
|
zoomable,
|
|
75
80
|
hasOnNodeClick,
|
|
@@ -78,15 +83,12 @@ export function XYFlow({
|
|
|
78
83
|
hasOnCanvasContextMenu,
|
|
79
84
|
hasOnEdgeContextMenu,
|
|
80
85
|
hasOnEdgeClick
|
|
81
|
-
} = useDiagramState(selector)
|
|
86
|
+
} = useDiagramState(selector, eq)
|
|
82
87
|
|
|
83
88
|
const layoutConstraints = useLayoutConstraints()
|
|
84
89
|
|
|
85
90
|
const handlers = useXYFlowEvents()
|
|
86
91
|
|
|
87
|
-
const { colorScheme } = useMantineColorScheme()
|
|
88
|
-
const colorMode = colorScheme !== 'auto' ? colorScheme : undefined
|
|
89
|
-
|
|
90
92
|
/**
|
|
91
93
|
* WORKAROUND - Called on viewport change
|
|
92
94
|
* Viewport transform is not rounded to integers which results in blurry nodes on some resolution
|
|
@@ -122,12 +124,7 @@ export function XYFlow({
|
|
|
122
124
|
maxZoom={zoomable ? 1.9 : 1}
|
|
123
125
|
minZoom={zoomable ? MinZoom : 1}
|
|
124
126
|
fitView={fitView}
|
|
125
|
-
fitViewOptions={
|
|
126
|
-
minZoom: MinZoom,
|
|
127
|
-
maxZoom: 1,
|
|
128
|
-
padding: fitViewPadding,
|
|
129
|
-
includeHiddenNodes: true
|
|
130
|
-
}}
|
|
127
|
+
fitViewOptions={fitViewOptions}
|
|
131
128
|
preventScrolling={zoomable || pannable}
|
|
132
129
|
defaultMarkerColor="var(--xy-edge-stroke)"
|
|
133
130
|
noDragClassName="nodrag"
|
|
@@ -159,9 +156,9 @@ export function XYFlow({
|
|
|
159
156
|
onNodeClick={handlers.onNodeClick}
|
|
160
157
|
onNodeDoubleClick={handlers.onNodeDoubleClick}
|
|
161
158
|
onEdgeClick={handlers.onEdgeClick}
|
|
162
|
-
onInit={() => {
|
|
159
|
+
onInit={useCallback(() => {
|
|
163
160
|
diagramApi.setState({ initialized: true }, false, 'initialized')
|
|
164
|
-
}}
|
|
161
|
+
}, [diagramApi])}
|
|
165
162
|
{...(hasOnNodeContextMenu && {
|
|
166
163
|
onNodeContextMenu: handlers.onNodeContextMenu
|
|
167
164
|
})}
|
|
@@ -176,4 +173,4 @@ export function XYFlow({
|
|
|
176
173
|
)
|
|
177
174
|
}
|
|
178
175
|
|
|
179
|
-
// export const XYFlow = memo(XYFlowWrapper
|
|
176
|
+
// export const XYFlow = memo(XYFlowWrapper) as typeof XYFlowWrapper
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ReactFlowProps } from '@xyflow/react'
|
|
2
2
|
import { useMemo, useRef } from 'react'
|
|
3
|
+
import { isNullish } from 'remeda'
|
|
3
4
|
import type { Simplify } from 'type-fest'
|
|
4
5
|
import { useDiagramStoreApi } from '../state'
|
|
5
6
|
import { useXYStoreApi } from './hooks'
|
|
@@ -30,6 +31,8 @@ export function useXYFlowEvents() {
|
|
|
30
31
|
const diagramApi = useDiagramStoreApi()
|
|
31
32
|
const xyflowApi = useXYStoreApi()
|
|
32
33
|
|
|
34
|
+
const lastClickTimestamp = useRef<number>()
|
|
35
|
+
|
|
33
36
|
const dblclickTimeout = useRef<number>()
|
|
34
37
|
|
|
35
38
|
const dbclickLock = () => {
|
|
@@ -42,6 +45,10 @@ export function useXYFlowEvents() {
|
|
|
42
45
|
return false
|
|
43
46
|
}
|
|
44
47
|
|
|
48
|
+
const lastClickWasRecent = (ms = 1500) => {
|
|
49
|
+
return lastClickTimestamp.current && (Date.now() - lastClickTimestamp.current) < ms
|
|
50
|
+
}
|
|
51
|
+
|
|
45
52
|
// If we are in focused mode, on edge enter we want to "highlight" the other node
|
|
46
53
|
// This ref contains the id of this node
|
|
47
54
|
const hoveredNodeFromOnEdgeEnterRef = useRef('')
|
|
@@ -68,8 +75,14 @@ export function useXYFlowEvents() {
|
|
|
68
75
|
if (dbclickLock()) {
|
|
69
76
|
return
|
|
70
77
|
}
|
|
71
|
-
const {
|
|
72
|
-
|
|
78
|
+
const {
|
|
79
|
+
focusedNodeId,
|
|
80
|
+
activeDynamicViewStep,
|
|
81
|
+
fitDiagram,
|
|
82
|
+
onCanvasClick,
|
|
83
|
+
resetLastClicked
|
|
84
|
+
} = diagramApi.getState()
|
|
85
|
+
if (focusedNodeId || activeDynamicViewStep) {
|
|
73
86
|
fitDiagram(xyflowApi)
|
|
74
87
|
if (!onCanvasClick) {
|
|
75
88
|
event.stopPropagation()
|
|
@@ -100,20 +113,28 @@ export function useXYFlowEvents() {
|
|
|
100
113
|
},
|
|
101
114
|
onNodeClick: (event, xynode) => {
|
|
102
115
|
const {
|
|
116
|
+
zoomable,
|
|
117
|
+
fitViewEnabled,
|
|
103
118
|
focusedNodeId,
|
|
104
119
|
focusOnNode,
|
|
105
120
|
onNodeClick,
|
|
121
|
+
lastClickedNodeId,
|
|
106
122
|
setLastClickedNode
|
|
107
123
|
} = diagramApi.getState()
|
|
108
124
|
setLastClickedNode(xynode.id)
|
|
125
|
+
const focusPossible = zoomable && fitViewEnabled
|
|
109
126
|
// if we focused on a node, and clicked on another node - focus on the clicked node
|
|
110
|
-
|
|
127
|
+
const shallChangeFocus = !!focusedNodeId && focusedNodeId !== xynode.id
|
|
128
|
+
// if user clicked on the same node twice in a short time, focus on it
|
|
129
|
+
const clickedRecently = isNullish(focusedNodeId) && lastClickedNodeId === xynode.id && lastClickWasRecent()
|
|
130
|
+
if (focusPossible && (shallChangeFocus || clickedRecently)) {
|
|
111
131
|
focusOnNode(xynode.id)
|
|
112
132
|
if (!onNodeClick) {
|
|
113
133
|
// user did not provide a custom handler, stop propagation
|
|
114
134
|
event.stopPropagation()
|
|
115
135
|
}
|
|
116
136
|
}
|
|
137
|
+
lastClickTimestamp.current = Date.now()
|
|
117
138
|
onNodeClick?.({
|
|
118
139
|
element: xynode.data.element,
|
|
119
140
|
xynode,
|
|
@@ -166,10 +187,7 @@ export function useXYFlowEvents() {
|
|
|
166
187
|
},
|
|
167
188
|
onNodeMouseEnter: (_event, xynode) => {
|
|
168
189
|
hoveredNodeFromOnEdgeEnterRef.current = ''
|
|
169
|
-
|
|
170
|
-
if (hoveredNodeId !== xynode.id) {
|
|
171
|
-
setHoveredNode(xynode.id)
|
|
172
|
-
}
|
|
190
|
+
diagramApi.getState().setHoveredNode(xynode.id)
|
|
173
191
|
},
|
|
174
192
|
onNodeMouseLeave: (_event, xynode) => {
|
|
175
193
|
const { hoveredNodeId, setHoveredNode } = diagramApi.getState()
|
|
@@ -191,9 +209,9 @@ export function useXYFlowEvents() {
|
|
|
191
209
|
setHoveredEdge(null)
|
|
192
210
|
}
|
|
193
211
|
if (hoveredNodeId === hoveredNodeFromOnEdgeEnterRef.current) {
|
|
194
|
-
hoveredNodeFromOnEdgeEnterRef.current = ''
|
|
195
212
|
setHoveredNode(null)
|
|
196
213
|
}
|
|
214
|
+
hoveredNodeFromOnEdgeEnterRef.current = ''
|
|
197
215
|
}
|
|
198
216
|
}) satisfies XYFlowEventHandlers, [diagramApi, xyflowApi])
|
|
199
217
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { isDynamicView } from '@likec4/core'
|
|
2
1
|
import { Controls } from '@xyflow/react'
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
2
|
+
import { shallowEqual } from 'fast-equals'
|
|
3
|
+
import { memo, type PropsWithChildren } from 'react'
|
|
4
|
+
import { isNonNull } from 'remeda'
|
|
5
5
|
import type { LikeC4DiagramProperties } from '../LikeC4Diagram.props'
|
|
6
6
|
import { useDiagramState } from '../state/useDiagramStore'
|
|
7
|
-
import {
|
|
7
|
+
import { BackwardForwardPanel } from '../ui/BackwardForwardPanel'
|
|
8
8
|
import DiagramTitlePanel from '../ui/DiagramTitlePanel'
|
|
9
|
+
import { DynamicViewWalkthrough } from '../ui/DynamicViewWalkthrough'
|
|
9
10
|
import OptionsPanel from '../ui/OptionsPanel'
|
|
10
11
|
import { XYFlowBackground } from './XYFlowBackground'
|
|
11
12
|
|
|
@@ -13,14 +14,16 @@ type XYFlowInnerProps = PropsWithChildren<{
|
|
|
13
14
|
background: NonNullable<LikeC4DiagramProperties['background']>
|
|
14
15
|
controls: boolean
|
|
15
16
|
showDiagramTitle: boolean
|
|
17
|
+
showNavigationButtons: boolean
|
|
16
18
|
enableDynamicViewWalkthrough: boolean
|
|
17
19
|
}>
|
|
18
20
|
|
|
19
|
-
export
|
|
21
|
+
export const XYFlowInner = memo(function XYFlowInnerR({
|
|
20
22
|
children,
|
|
21
23
|
background,
|
|
22
24
|
controls,
|
|
23
25
|
showDiagramTitle,
|
|
26
|
+
showNavigationButtons,
|
|
24
27
|
enableDynamicViewWalkthrough
|
|
25
28
|
}: XYFlowInnerProps) {
|
|
26
29
|
const {
|
|
@@ -44,7 +47,8 @@ export function XYFlowInner({
|
|
|
44
47
|
{readonly === false && <OptionsPanel />}
|
|
45
48
|
{isDiagramTitleVisible && <DiagramTitlePanel />}
|
|
46
49
|
{isDynamicView && enableDynamicViewWalkthrough && <DynamicViewWalkthrough />}
|
|
50
|
+
{showNavigationButtons && <BackwardForwardPanel />}
|
|
47
51
|
{children}
|
|
48
52
|
</>
|
|
49
53
|
)
|
|
50
|
-
}
|
|
54
|
+
}, shallowEqual)
|
package/src/xyflow/const.ts
CHANGED
|
@@ -1,49 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type DiagramNode,
|
|
3
|
-
type DiagramView,
|
|
4
|
-
extractStep,
|
|
5
|
-
type Fqn,
|
|
6
|
-
invariant,
|
|
7
|
-
type NonEmptyArray,
|
|
8
|
-
nonNullable,
|
|
9
|
-
type Point
|
|
10
|
-
} from '@likec4/core'
|
|
11
|
-
import { Bezier } from 'bezier-js'
|
|
1
|
+
import { type DiagramNode, type DiagramView, extractStep, type Fqn, invariant, nonNullable } from '@likec4/core'
|
|
12
2
|
import { hasAtLeast } from 'remeda'
|
|
13
3
|
import type { XYFlowData } from '../xyflow/types'
|
|
14
4
|
|
|
15
|
-
function deriveEdgePoints(bezierSpline: NonEmptyArray<Point>) {
|
|
16
|
-
let [start, ...bezierPoints] = bezierSpline
|
|
17
|
-
invariant(start, 'start should be defined')
|
|
18
|
-
const handles = [
|
|
19
|
-
// start
|
|
20
|
-
] as Point[]
|
|
21
|
-
|
|
22
|
-
while (hasAtLeast(bezierPoints, 3)) {
|
|
23
|
-
const [cp1, cp2, end, ...rest] = bezierPoints
|
|
24
|
-
const bezier = new Bezier(start[0], start[1], cp1[0], cp1[1], cp2[0], cp2[1], end[0], end[1])
|
|
25
|
-
// const { x, y } = bezier.get(0.5)
|
|
26
|
-
const inflections = bezier.inflections()
|
|
27
|
-
if (inflections.length === 0) {
|
|
28
|
-
inflections.push(0.5)
|
|
29
|
-
}
|
|
30
|
-
inflections.forEach(t => {
|
|
31
|
-
const { x, y } = bezier.get(t)
|
|
32
|
-
handles.push([x, y])
|
|
33
|
-
})
|
|
34
|
-
bezierPoints = rest
|
|
35
|
-
start = end
|
|
36
|
-
}
|
|
37
|
-
invariant(bezierPoints.length === 0, 'all points should be consumed')
|
|
38
|
-
|
|
39
|
-
return handles
|
|
40
|
-
}
|
|
41
|
-
|
|
42
5
|
// const nodeZIndex = (node: DiagramNode) => node.level + (hasAtLeast(node.children, 1) ? 2 : 1)
|
|
43
6
|
const nodeZIndex = (node: DiagramNode) => node.level + 1
|
|
44
7
|
|
|
45
8
|
export function diagramViewToXYFlowData(
|
|
46
|
-
view: Pick<DiagramView, 'nodes' | 'edges' | '__'>,
|
|
9
|
+
view: Pick<DiagramView, 'id' | 'nodes' | 'edges' | '__' | 'manualLayout'>,
|
|
47
10
|
opts: {
|
|
48
11
|
draggable: boolean
|
|
49
12
|
selectable: boolean
|
|
@@ -147,7 +110,7 @@ export function diagramViewToXYFlowData(
|
|
|
147
110
|
const target = edge.target
|
|
148
111
|
const id = ns + edge.id
|
|
149
112
|
// const points = isElkEdge(edge) ? edge.points : deriveEdgePoints(edge.points)
|
|
150
|
-
const controlPoints = deriveEdgePoints(edge.points)
|
|
113
|
+
// const controlPoints = deriveEdgePoints(edge.points)
|
|
151
114
|
// if (edge.tailArrowPoint) {
|
|
152
115
|
// controlPoints.unshift([...edge.tailArrowPoint])
|
|
153
116
|
// }
|
|
@@ -169,7 +132,7 @@ export function diagramViewToXYFlowData(
|
|
|
169
132
|
data: {
|
|
170
133
|
edge,
|
|
171
134
|
type: 'bezier',
|
|
172
|
-
controlPoints,
|
|
135
|
+
controlPoints: view.manualLayout?.edges[edge.id]?.controlPoints || null,
|
|
173
136
|
headPoint: edge.headArrowPoint ?? null,
|
|
174
137
|
tailPoint: edge.tailArrowPoint ?? null,
|
|
175
138
|
stepNum: isDynamicView ? extractStep(edge.id) : null,
|