@@ -13,10 +13,11 @@ import { useShallow } from "zustand/shallow"
1313
1414export const useNodeDragStop = ( ) => {
1515 const { screenToFlowPosition, getIntersectingNodes } = useReactFlow ( )
16- const { nodes, setNodes } = useDiagramStore (
16+ const { nodes, setNodes, endTransientNodeBroadcast } = useDiagramStore (
1717 useShallow ( ( state ) => ( {
1818 nodes : state . nodes ,
1919 setNodes : state . setNodes ,
20+ endTransientNodeBroadcast : state . endTransientNodeBroadcast ,
2021 } ) )
2122 )
2223
@@ -31,97 +32,117 @@ export const useNodeDragStop = () => {
3132 // Clear alignment guides when drag stops
3233 clearGuides ( )
3334
34- const draggedLastPoint = screenToFlowPosition ( {
35- x :
36- "changedTouches" in event
37- ? // event is handled as Mouse event in the library but also it is touch event for mobile users
38- // eslint-disable-next-line @typescript-eslint/no-explicit-any
39- ( event as any ) . changedTouches [ 0 ] . clientX
40- : event . clientX ,
41- y :
42- "changedTouches" in event
43- ? // event is handled as Mouse event in the library but also it is touch event for mobile users
44- // eslint-disable-next-line @typescript-eslint/no-explicit-any
45- ( event as any ) . changedTouches [ 0 ] . clientY
46- : event . clientY ,
47- } )
35+ // Tear down the peers' live-drag overlay once this handler's settle
36+ // `setNodes` (below, every branch) has committed the final position to
37+ // the document — `finally` guarantees the order, so peers apply the
38+ // durable position before the overlay is removed (no snap-back). The
39+ // dragging:false `onNodesChange` frame usually carries the same position
40+ // as the last drag frame and short-circuits, so this is the drag path's
41+ // clear; resize (no drag-stop) clears from `onNodesChange` instead.
42+ try {
43+ const draggedLastPoint = screenToFlowPosition ( {
44+ x :
45+ "changedTouches" in event
46+ ? // event is handled as Mouse event in the library but also it is touch event for mobile users
47+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
48+ ( event as any ) . changedTouches [ 0 ] . clientX
49+ : event . clientX ,
50+ y :
51+ "changedTouches" in event
52+ ? // event is handled as Mouse event in the library but also it is touch event for mobile users
53+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
54+ ( event as any ) . changedTouches [ 0 ] . clientY
55+ : event . clientY ,
56+ } )
4857
49- const intersectionsWithDroppedLocation = getIntersectingNodes ( {
50- x : draggedLastPoint . x ,
51- y : draggedLastPoint . y ,
52- width : CANVAS . MOUSE_UP_OFFSET_PX ,
53- height : CANVAS . MOUSE_UP_OFFSET_PX ,
54- } ) . filter ( ( n ) => {
55- return (
56- isParentNodeType ( n . type ) &&
57- n . id !== draggedNode . id &&
58- n . type &&
59- draggedNode . type &&
60- canDropIntoParent ( draggedNode . type , n . type )
61- )
62- } )
58+ const intersectionsWithDroppedLocation = getIntersectingNodes ( {
59+ x : draggedLastPoint . x ,
60+ y : draggedLastPoint . y ,
61+ width : CANVAS . MOUSE_UP_OFFSET_PX ,
62+ height : CANVAS . MOUSE_UP_OFFSET_PX ,
63+ } ) . filter ( ( n ) => {
64+ return (
65+ isParentNodeType ( n . type ) &&
66+ n . id !== draggedNode . id &&
67+ n . type &&
68+ draggedNode . type &&
69+ canDropIntoParent ( draggedNode . type , n . type )
70+ )
71+ } )
6372
64- const parentNode = intersectionsWithDroppedLocation . length
65- ? intersectionsWithDroppedLocation [
66- intersectionsWithDroppedLocation . length - 1
67- ]
68- : null
73+ const parentNode = intersectionsWithDroppedLocation . length
74+ ? intersectionsWithDroppedLocation [
75+ intersectionsWithDroppedLocation . length - 1
76+ ]
77+ : null
6978
70- if ( ! parentNode ) {
71- const updatedNode = nodes . map ( ( n ) =>
72- n . id === draggedNode . id
73- ? {
74- ...draggedNode ,
75- position : getPositionOnCanvas ( draggedNode , nodes ) ,
76- parentId : undefined ,
77- }
78- : n
79- )
80- setNodes ( updatedNode )
81- return
82- }
79+ if ( ! parentNode ) {
80+ const updatedNode = nodes . map ( ( n ) =>
81+ n . id === draggedNode . id
82+ ? {
83+ ...draggedNode ,
84+ position : getPositionOnCanvas ( draggedNode , nodes ) ,
85+ parentId : undefined ,
86+ }
87+ : n
88+ )
89+ setNodes ( updatedNode )
90+ return
91+ }
8392
84- const isThisNewParent =
85- parentNode && parentNode ?. id !== draggedNode . parentId
93+ const isThisNewParent =
94+ parentNode && parentNode ?. id !== draggedNode . parentId
8695
87- if ( isThisNewParent ) {
88- const updatedNode : Node = {
89- ...structuredClone ( draggedNode ) ,
90- position : getPositionOnCanvas ( draggedNode , nodes ) ,
91- parentId : undefined ,
92- }
93- const parentsFlowPosition = getPositionOnCanvas ( parentNode , nodes )
96+ if ( isThisNewParent ) {
97+ const updatedNode : Node = {
98+ ...structuredClone ( draggedNode ) ,
99+ position : getPositionOnCanvas ( draggedNode , nodes ) ,
100+ parentId : undefined ,
101+ }
102+ const parentsFlowPosition = getPositionOnCanvas ( parentNode , nodes )
94103
95- updatedNode . position . x -= parentsFlowPosition . x
96- updatedNode . position . y -= parentsFlowPosition . y
97- updatedNode . parentId = parentNode . id
104+ updatedNode . position . x -= parentsFlowPosition . x
105+ updatedNode . position . y -= parentsFlowPosition . y
106+ updatedNode . parentId = parentNode . id
98107
99- const updatedNodes = structuredClone ( nodes )
100- const updatedNodesList = sortNodesTopologically (
101- resizeAllParents (
102- updatedNode ,
103- updatedNodes . map ( ( n ) => ( n . id === updatedNode . id ? updatedNode : n ) )
108+ const updatedNodes = structuredClone ( nodes )
109+ const updatedNodesList = sortNodesTopologically (
110+ resizeAllParents (
111+ updatedNode ,
112+ updatedNodes . map ( ( n ) =>
113+ n . id === updatedNode . id ? updatedNode : n
114+ )
115+ )
104116 )
105- )
106117
107- setNodes ( updatedNodesList )
108- return
109- }
118+ setNodes ( updatedNodesList )
119+ return
120+ }
110121
111- if ( draggedNode . parentId ) {
112- const updatedNodes = structuredClone ( nodes )
113- const updatedNodesList = sortNodesTopologically (
114- resizeAllParents (
115- draggedNode ,
116- updatedNodes . map ( ( n ) =>
117- n . id === draggedNode . id ? { ...draggedNode } : n
122+ if ( draggedNode . parentId ) {
123+ const updatedNodes = structuredClone ( nodes )
124+ const updatedNodesList = sortNodesTopologically (
125+ resizeAllParents (
126+ draggedNode ,
127+ updatedNodes . map ( ( n ) =>
128+ n . id === draggedNode . id ? { ...draggedNode } : n
129+ )
118130 )
119131 )
120- )
121- setNodes ( updatedNodesList )
132+ setNodes ( updatedNodesList )
133+ }
134+ } finally {
135+ endTransientNodeBroadcast ( )
122136 }
123137 } ,
124- [ screenToFlowPosition , nodes , getIntersectingNodes , setNodes , clearGuides ]
138+ [
139+ screenToFlowPosition ,
140+ nodes ,
141+ getIntersectingNodes ,
142+ setNodes ,
143+ clearGuides ,
144+ endTransientNodeBroadcast ,
145+ ]
125146 )
126147
127148 return onNodeDragStop
0 commit comments