xyflow / xyflow

React Flow | Svelte Flow - Powerful open source libraries for building node-based UIs with React (https://reactflow.dev) or Svelte (https://svelteflow.dev). Ready out-of-the-box and infinitely customizable.
https://xyflow.com
MIT License
21.54k stars 1.43k forks source link

Detaching a node from its parent on drag start produces unexpected positions. (v11 only) #4241

Closed collinsleewyatt closed 1 week ago

collinsleewyatt commented 2 weeks ago

Describe the Bug

I have an app where I want the node I'm dragging to be positioned relative to (0,0). This is because my app has a "drag preview" feature where it will move nodes and often times, the preview will move the parent and therefore the dragging node. (Keeping track of where the parent moves is often inconvienient)

As such, I delete parentId in onNodeDragStart, however this leads to inconsistent movement if the parent moves during the drag.

Your Example Website or App

https://codesandbox.io/p/sandbox/reactflow-dragparent-bug-mlsxzx

Steps to Reproduce the Bug or Issue

In this codesandbox, there is a parent node and a child node. The parent node moves every 2 seconds, and the child node moves with it. Once you drag the child node, its parentId is deleted and the nodes are updated. Ideally, it should no longer have its positioned influenced by its parent.

  1. Drag the child node. This will delete the parentId from the child.
  2. Notice that while dragging, when the parent moves, the former child now moves opposite the parent, even though it is no longer a child of it.
  3. Also notice that if you keep the drag held down but don't move the node, its position won't move opposite the parent until you move it. This likely means that the drag/drop code has a reference to the parent that doesn't get updated.

Expected behavior

I expect that once I delete parentId from the child and update the nodes, its position will no longer be influenced by its parent in any way.

Screenshots or Videos

https://github.com/xyflow/xyflow/assets/54917619/935c954e-4adf-462b-b89b-c3bc7127fa4f

Platform

Additional context

I've found node.parentId to be difficult because it's easiest for my nodes to be positioned relative to 0,0 for auto-layouting purposes. But occasionally, I want multiple nodes to be dragged along with another node automatically. Having a way to specify that a node should be dragged with another node, but still positioned relative to (0,0), would be helpful to my project and likely others.

This could be accomplished through an attribute like node.dragsWithId or node.disableRelativePositioning, though an attribute like this may present ergononic concerns. (React Flow v12 may already have features like this, I'm not too sure.)

I cannot reproduce this issue on v12.0.0-next.17, it appears to be working as expected there:

https://codesandbox.io/p/sandbox/reactflow-dragparent-bug-forked-4qnl5z

moklick commented 1 week ago

Thanks for the report! We already looked into this and it's quite complicated to support relative and absolute positioning.

Regarding your issue: In v12 you should not update nodes by mutating them but always create a new node object. Instead of

// 👇 this is bad, you should not do this
const child = newNodes.find((node) => node.id == "c");
delete child.parentId;
child.data.label = "no longer a child";
child.position.x = child.measured.x;
child.position.y = child.measured.y;
setNodes(newNodes);

you should do:

setNodes(nds => nds.map((node) => {
  if (node.id == "c") {
    return {
      ...node,
      parentId: undefined,
      position: child.measured,
      label: "no longer a child"
    }
  }

  return node;
}));