reactjsreact-flow

Reactflow subflow position and parentNode problems


I want to create a subflow with Reactflow by dragging and dropping the 'Group' node using onDrop. I drop another node from other nodes into the empty area of the screen using onDrop again. Then, when I drag this created node into the 'Group' node, onNodeDragStop is triggered to add the parentNode, but the node position is in a very different place. What could be the reason for this?

const onNodeDrag = (evt, node) => {
    // calculate the center point of the node from position and dimensions
    const centerX = node.position.x + node.width / 2;
    const centerY = node.position.y + node.height / 2;
    // find a node where the center point is inside
    const targetNode = nodes.find(
      (n) =>
        centerX > n.position.x &&
        centerX < n.position.x + n.width &&
        centerY > n.position.y &&
        centerY < n.position.y + n.height &&
        n.type === "group" &&
        n.id !== node.id
    );
    setTarget(targetNode);
  };

  const onNodeDragStop = (evt, node) => {
    setNodes((nodes) =>
      nodes.map((n) => {
        if (n.id === node.id && target) {
          if (node.type !== "group") {
            n.data = { ...n.data };
            n.parentNode = target?.id;
            n.extent = "parent";
          }
        } else if (n.id === target?.id) {
          n.data = { ...n.data };
        }
        return n;
      })
    );
    setTarget(null);
    dragRef.current = null;
  };

You can access the entire code block through the link.
https://codesandbox.io/p/sandbox/subflow-e-x8zmpy

I'm experiencing the following issues as well. If you add a node outside of the 'Group' first, and then add the 'Group' node afterward, when you make them 'parent', the 'Group' node stays on top, and the nodes underneath cannot be accessed.

When you place multiple 'Group' nodes side by side (4 are enough) and try to place multiple other nodes randomly, the 'targetNode' changes, causing all of them to go inside it.


Solution

  • I had the same problem and I solved it doing this (I can't remember if I got this from here or somewhere else. This is not a solution I came up with so forgive me for not sourcing the solution):

    const onNodeDragStop = useCallback(
     (evt, node) => {
      if (node.type === "group") {
        return;
      }
      nodes.forEach((nd) => {
        // Check if there's a group node in the array of nodes on the screen
        if (nd.type === "group") {
          //safety check to make sure there's a height and width
          if (nd.height && nd.width) {
            const rec = { height: nd.height, width: nd.width, ...nd.position };
    
            // Check if the dragged node is inside the group
            if (reactFlow.isNodeIntersecting(node, rec, false)) {
              //Check if dragged node isn't already a child to the group
              if (!node.parentNode) {
                node.parentNode = nd.id;
                node.extent = "parent";
                node.position = {
                  x: node.positionAbsolute.x - nd.position.x,
                  y: node.positionAbsolute.y - nd.position.y,
                };
                setNodes((nodes) =>
                  nodes.map((n) => {
                    if (n.id === node.id) {
                      n = node;
                    }
                    return n;
                  })
                );
              }
            }
          }
        }
      });
    }, [nodes]);
    

    My addition from the original is I'm checking, with useReactFlow() hook, to see if the dragged node is intersection with the 'group' node. The false flag as the last parameter is checking to make sure it's fully dropped inside the 'group'.

    I will say the problem I'm having now is programmatically detaching the node from the parent is rendering it at some weird position instead of just keeping it's current position.

    Here's a fork of your codesandbox: Update Sandbox