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
22.07k stars 1.46k forks source link

Get relative node position on Edge Drop for new node with parentNode #4152

Open TrySpace opened 2 months ago

TrySpace commented 2 months ago

Please describe the feature that you want to propose

I'm trying to use the Add Node On Edge Drop example and have the newNode add a parentNode: connectingNodeId.current prop so it will automatically belong to its parent.

But I can't figure out how to calculate the relative position because event.clientX is the absolute position which is not the one I need when it uses the parentNode's relative position.

I would like a way to get the relative position or to subtract the parentNode's x/y coords somehow? I saw this issue https://github.com/xyflow/xyflow/issues/3393 but I don't want to use absolute positioning for the grouped/parented nodes.

See sandbox

It could be achieved if the OnConnectEnd event would have a node besides the event from which I could simply extract the position or 'absolutePosition` from. That node would be the node that I connect from.

/Update I've tried to use useRef to store the origin node and calculate the position, but I can't get it to work right

TrySpace commented 1 month ago

What's working for me right now is this:

 const onConnectStart: OnConnectStart = useCallback((evt, node) => {
    const { nodeId } = node
    const pNode = nodeId ? getNode(nodeId) : {}
    // @ts-ignore
    parentNode.current = pNode
    connectingNodeId.current = nodeId
  }, [])

  const onConnectEnd: OnConnectEnd = useCallback(
    (event) => {
      if (!connectingNodeId.current) return

      const targetIsPane = (event.target as Element).classList.contains(
        'react-flow__pane'
      )

      if (targetIsPane) {
        // Get mouse position
        // @ts-ignore
        const clientX = (event?.clientX as number) || 0
        // @ts-ignore
        const clientY = (event?.clientY as number) || 0

        // Convert mouse coordinates to relative position
        const screenPos = screenToFlowPosition({
          x: clientX,
          y: clientY,
        })

        // Get the absolute position of the parent node (if any)
        const parentX = parentNode.current?.positionAbsolute?.x || 0
        const parentY = parentNode.current?.positionAbsolute?.y || 0

        // Subtract parent coords from relative screenPos
        const posX = screenPos.x - parentX
        const posY = screenPos.y - parentY

        const newId = getId()

        const newNode = NewNode({
          id: newId,
          position: { x: posX, y: posY },
          parentId: connectingNodeId.current,
        })

        setNodes((nds) => nds.concat(newNode))

        setEdges((eds) =>
          // @ts-ignore
          eds.concat({
            id: newId,
            source: connectingNodeId.current,
            target: newId,
          })
        )
      }
    },
    [screenToFlowPosition]
  )

This won't work anymore when I upgrade to v12 though...