1- import { useLayoutEffect } from 'react' ;
1+ import { useLayoutEffect , useRef } from 'react' ;
22import { observer } from 'mobx-react' ;
33import {
44 Edge ,
@@ -71,6 +71,8 @@ interface DefaultEdgeProps {
7171 canDrop ?: boolean ;
7272 /** Flag if the node is the current drop target */
7373 dropTarget ?: boolean ;
74+ /** Flag indicating if an edge endpoint is currently being dragged */
75+ endpointDragging ?: boolean ;
7476 /** Flag indicating if the element is selected. Part of WithSelectionProps */
7577 selected ?: boolean ;
7678 /** Function to call when the element should become selected (or deselected). Part of WithSelectionProps */
@@ -79,6 +81,13 @@ interface DefaultEdgeProps {
7981 onContextMenu ?: ( e : React . MouseEvent ) => void ;
8082 /** Flag indicating that the context menu for the edge is currently open */
8183 contextMenuOpen ?: boolean ;
84+ /**
85+ * When true, the edge path and terminals stay visually fixed (last position) while
86+ * an associated node is being dragged. When false or omitted, the edge updates
87+ * during drag as before. Can be set per edge via this prop, element state, or
88+ * in the model as edge data: `data: { freezeEdgeDuringNodeDrag: true }`.
89+ */
90+ freezeEdgeDuringNodeDrag ?: boolean ;
8291}
8392
8493type DefaultEdgeInnerProps = Omit < DefaultEdgeProps , 'element' > & { element : Edge } ;
@@ -92,6 +101,7 @@ const DefaultEdgeInner: React.FunctionComponent<DefaultEdgeInnerProps> = observe
92101 dndDropRef,
93102 canDrop,
94103 dropTarget,
104+ endpointDragging,
95105 edgeStyle,
96106 animationDuration,
97107 onShowRemoveConnector,
@@ -111,12 +121,23 @@ const DefaultEdgeInner: React.FunctionComponent<DefaultEdgeInnerProps> = observe
111121 className,
112122 selected,
113123 onSelect,
114- onContextMenu
124+ onContextMenu,
125+ freezeEdgeDuringNodeDrag
115126 } ) => {
127+ const freezeDuringDrag =
128+ freezeEdgeDuringNodeDrag ??
129+ ( element . getData ( ) as { freezeEdgeDuringNodeDrag ?: boolean } | undefined ) ?. freezeEdgeDuringNodeDrag ??
130+ false ;
131+
116132 const [ hover , hoverRef ] = useHover ( ) ;
117133 const edgeRef = useCombineRefs ( hoverRef , dndDropRef ) ;
118134 const startPoint = element . getStartPoint ( ) ;
119135 const endPoint = element . getEndPoint ( ) ;
136+ const edgeDRef = useRef < string | null > ( null ) ;
137+ const startPointRef = useRef < Point | null > ( null ) ;
138+ const endPointRef = useRef < Point | null > ( null ) ;
139+ const targetStartPointRef = useRef < Point | null > ( null ) ;
140+ const targetEndPointRef = useRef < Point | null > ( null ) ;
120141
121142 useLayoutEffect ( ( ) => {
122143 if ( hover && ! dragging ) {
@@ -175,6 +196,23 @@ const DefaultEdgeInner: React.FunctionComponent<DefaultEdgeInnerProps> = observe
175196 const tagScale = hover && ! ( detailsLevel === ScaleDetailsLevel . high ) ? Math . max ( 1 , 1 / scale ) : 1 ;
176197 const tagPositionScale = hover && ! ( detailsLevel === ScaleDetailsLevel . high ) ? Math . min ( 1 , scale ) : 1 ;
177198
199+ const shouldFreeze = freezeDuringDrag && endpointDragging ;
200+
201+ if (
202+ ! shouldFreeze ||
203+ ! edgeDRef . current ||
204+ ! startPointRef . current ||
205+ ! endPointRef . current ||
206+ ! targetStartPointRef . current ||
207+ ! targetEndPointRef . current
208+ ) {
209+ edgeDRef . current = d ;
210+ startPointRef . current = bendpoints [ 0 ] || endPoint ;
211+ endPointRef . current = startPoint ;
212+ targetStartPointRef . current = bendpoints [ bendpoints . length - 1 ] || startPoint ;
213+ targetEndPointRef . current = endPoint ;
214+ }
215+
178216 return (
179217 < Layer id = { dragging || hover ? TOP_LAYER : undefined } >
180218 < g
@@ -190,7 +228,11 @@ const DefaultEdgeInner: React.FunctionComponent<DefaultEdgeInnerProps> = observe
190228 onMouseEnter = { onShowRemoveConnector }
191229 onMouseLeave = { onHideRemoveConnector }
192230 />
193- < path className = { linkClassName } d = { d } style = { { animationDuration : `${ edgeAnimationDuration } s` } } />
231+ < path
232+ className = { linkClassName }
233+ d = { edgeDRef . current }
234+ style = { { animationDuration : `${ edgeAnimationDuration } s` } }
235+ />
194236 { showTag && (
195237 < g transform = { `scale(${ hover ? tagScale : 1 } )` } >
196238 < DefaultConnectorTag
@@ -211,6 +253,8 @@ const DefaultEdgeInner: React.FunctionComponent<DefaultEdgeInnerProps> = observe
211253 terminalType = { startTerminalType }
212254 status = { startTerminalStatus }
213255 highlight = { dragging || hover }
256+ startPoint = { shouldFreeze ? startPointRef . current : undefined }
257+ endPoint = { shouldFreeze ? endPointRef . current : undefined }
214258 />
215259 < DefaultConnectorTerminal
216260 className = { endTerminalClass }
@@ -221,6 +265,8 @@ const DefaultEdgeInner: React.FunctionComponent<DefaultEdgeInnerProps> = observe
221265 terminalType = { endTerminalType }
222266 status = { endTerminalStatus }
223267 highlight = { dragging || hover }
268+ startPoint = { shouldFreeze ? targetStartPointRef . current : undefined }
269+ endPoint = { shouldFreeze ? targetEndPointRef . current : undefined }
224270 />
225271 { children }
226272 </ g >
0 commit comments