xyflow / web

📖 This monorepo contains the xyflow website and the documentation sites for React Flow and Svelte Flow.
https://xyflow.com
MIT License
57 stars 57 forks source link

D3 Force example nodes not draggable #429

Open moklick opened 4 months ago

moklick commented 4 months ago

While the simulation is running, the nodes are not draggable https://reactflow.dev/learn/layouting/layouting#d3-force

hayleigh-dot-dev commented 4 months ago

They are draggable for me (Mac, safari 17.5), although performance is notably worse than it used to be.

BensEye commented 4 months ago

They are only draggable after the simulation stops running, for me.

buchananwill commented 4 months ago

I have worked through this problem myself with V11. It was quite tricky to get all the interactivity to play ball. I'm about to update v12, so once I've checked that none of my solution breaks I will post a link.

buchananwill commented 4 months ago

I've copied over the simplest solution I found to get the node dragging to function correctly with d3 force layouting. Here's my fork of the example repo:

https://github.com/buchananwill/quizzical-lamarr-tv8srd.git

The gist of the solution is to use the callbacks for onNodeDragStart/onNodeDrag/onNodeDragStop to assign the currently dragging node to a ref:

    const draggingNodeRef = useRef(undefined);
    const [initialized, {toggle, isRunning}] = useLayoutedElements(draggingNodeRef);

    const onNodeDragStart = useCallback(
        (_event, node) => {
            draggingNodeRef.current = {...node};
        },
        []
    );

    const onNodeDragStop = useCallback(() => {
        draggingNodeRef.current = undefined;
    }, []);

    const onNodeDrag = useCallback(
        (_event, node) => {
            draggingNodeRef.current = {...node};
        },
        []
    );

This ref is passed into the useLayoutedElements hook, so the memoized function can always guarantee to see a non-stale reference. I made a few other minor changes to tidy up some janky behaviour, like copying the xyflow positions back onto the sim nodes before restarting the sim - otherwise the nodes forget where you might have dragged them to and hop back to the last position the sim remembers.

Feel free to copy my change/fork back into the example version for the web docs.

moklick commented 2 months ago

Thanks for sharing your solution @buchananwill ! We will have a look

lihebi commented 2 months ago

Some observations on the website example (https://reactflow.dev/learn/layouting/layouting#d3-force):

  1. the nodes are draggable in Safari
  2. the nodes are not draggable in Chrome/Firefox
  3. (very weird) if you open the dev tool on Chrome, the nodes are draggable

All above are after starting force simulation, i.e., auto-layout in effect.

buchananwill commented 2 months ago

I observed all of these behaviours as well, both in the example site, and my own project. The solution I posted is how I fixed it in my own project, and also applies to the example site.

On Sun, 22 Sept 2024, 06:01 Hebi Li, @.***> wrote:

Some observations on the website example ( https://reactflow.dev/learn/layouting/layouting#d3-force):

  1. the nodes are draggable in Safari
  2. the nodes are not draggable in Chrome/Firefox
  3. (very weird) if you open the dev tool on Chrome, the nodes are draggable

All above are after starting force simulation, i.e., auto-layout in effect.

— Reply to this email directly, view it on GitHub https://github.com/xyflow/web/issues/429#issuecomment-2365468410, or unsubscribe https://github.com/notifications/unsubscribe-auth/BBL2M6IAK7J4TROJ3YKTVXTZXZFMPAVCNFSM6AAAAABKUT5ZB6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDGNRVGQ3DQNBRGA . You are receiving this because you were mentioned.Message ID: @.***>

lihebi commented 2 months ago

Thanks @buchananwill , I actually did try the codesandbox link on your repo. The behavior is exactly the same as the website example (all 1,2,3 above). I'm surprised that it works on your side. I'm on MacOS 15.0, Chrome 128.0.6613.138 (Official Build).

buchananwill commented 2 months ago

Ah, you're right. I checked against my main project I copied from and I'd missed one detail: the current drag position needs to come directly from the ref, not the scoped list of nodes (which is stale).

      getNodes().forEach((node, i) => {
        const dragging = draggingNodeRef?.current?.id === node.id;

        // Setting the fx/fy properties of a node tells the simulation to "fix"
        // the node at that position and ignore any forces that would normally
        // cause it to move.
        if (dragging) {
          // These two lines were wrong.
          nodes[i].fx = draggingNodeRef?.current?.position.x; 
          nodes[i].fy = draggingNodeRef?.current?.position.y;
        } else {
          delete nodes[i].fx;
          delete nodes[i].fy;
        }
      });
buchananwill commented 2 months ago

Please try the codesandbox again and let me know if it works for you now!

lihebi commented 2 months ago

Awesome, it works! Thanks for the quick response and debugging.

buchananwill commented 2 months ago

:-) you're welcome!

On Mon, 23 Sept 2024, 19:32 Hebi Li, @.***> wrote:

Awesome, it works! Thanks for the quick response and debugging.

— Reply to this email directly, view it on GitHub https://github.com/xyflow/web/issues/429#issuecomment-2369064805, or unsubscribe https://github.com/notifications/unsubscribe-auth/BBL2M6N54CPBHEAXIAWIWSLZYBNFLAVCNFSM6AAAAABKUT5ZB6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDGNRZGA3DIOBQGU . You are receiving this because you were mentioned.Message ID: @.***>

hayleigh-dot-dev commented 1 month ago

Hey @buchananwill thank you so much for investigating! I can confirm that your repo/sandbox works outside of safari: it's so strange that it works at all in safari 🧐

We're in the process of reworking how we host our examples (#501) but I'll make sure these changes are added and we'll shout you out when things go live 💕. Until then I'll leave the issue open in case anyone else needs to find it.

buchananwill commented 1 month ago

Feel free to tag in me in anything else that is a puzzle. I've got a bit of free time at this week and have been using xyflow quite a bit lately. 🙏

On Tue, 1 Oct 2024, 09:16 Hayleigh Thompson, @.***> wrote:

Hey @buchananwill https://github.com/buchananwill thank you so much for investigating! I can confirm that your repo/sandbox works outside of safari: it's so strange that it works at all in safari 🧐

We're in the process of reworking how we host our examples (#501 https://github.com/xyflow/web/pull/501) but I'll make sure these changes are added and we'll shout you out when things go live 💕. Until then I'll leave the issue open in case anyone else needs to find it.

— Reply to this email directly, view it on GitHub https://github.com/xyflow/web/issues/429#issuecomment-2385098031, or unsubscribe https://github.com/notifications/unsubscribe-auth/BBL2M6PCLRQOWNBM5ZZXFILZZJK7NAVCNFSM6AAAAABKUT5ZB6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDGOBVGA4TQMBTGE . You are receiving this because you were mentioned.Message ID: @.***>

buchananwill commented 1 month ago

I've keep running into this issue even though I have a mostly working graph setup. Today I found that adding the useNodesData() hook into one of the node component implementations I have, breaks the dragging behaviour again. I worked around it by instead subscribing to the higher up context I am wrapping my xyflow setup in.

const { nodes } = useNodeContext<OrganizationDto>();

This doesn't trigger re-renders from selection/movement updates, because it's the external data, rather than the full UI state. So, it'll re-render if a node is added, or the "business" data on any node changes, but not the UI-level state changes.

Does this give any clues about why the dragging behaviour would break in the first place?