Skip to main content

How to set the new Node position always be Under the Previous node even if i drag the Node around the diagram

When I click add Node or add Crossroad it adds 1 node and 2 nodes consecutively. What I want to achieve is that every time I add nodes I want their positions to be rendered on the previous node position not always render on the same place. I tried some stuff but I can't achieve that, I need someone who has done this before with react flow. Look at addNode and addCrossroad functions. This is what I want to achieve:

enter image description here

import { useState, useCallback, useRef } from "react";
import ReactFlow, {
  applyEdgeChanges,
  applyNodeChanges,
  addEdge,
  Background,
} from "react-flow-renderer";
import { faker } from "@faker-js/faker";

const initialEdges = [];
const initialNodes = [];

function Flow() {
  const [nodes, setNodes] = useState(initialNodes);
  const [edges, setEdges] = useState(initialEdges);
  const [nodeId, setNodeId] = useState(undefined);
  const [ nodePos , setNodePos ] = useState({});
  const yPos = useRef(0);
  const [openModal, setOpenModal] = useState(false);
  const [openModalForNodes, SetopenModalForNodes] = useState(false);
  const [prevNode, setPrevNode] = useState(false);

  // Controlled component react node functions
  const onNodesChange = useCallback(
    (changes) => {
      // setNodePos({ x: changes[0]?.position?.x, y:changes[0]?.position?.y});
      // setNodePos(changes[0]?.position);
      setNodes((nds) => applyNodeChanges(changes, nds))
    },
    [setNodes]
  );

  // Get node position on drag end ( when dropping node )
  const onNodeDragStop = (_, node) => {
    // console.log(node);
    setNodePos(node.position);
  }
  const onEdgesChange = useCallback(
    (changes) => setEdges((eds) => applyEdgeChanges(changes, eds)),
    [setEdges]
  );
  const onConnect = useCallback(
    (connection) => setEdges((eds) => addEdge(connection, eds)),
    [setEdges]
  );
  // Get clicked node ids
  const onNodeClick = (e) => {
    const _nodeId_ = e.target.getAttribute("data-id");
    setNodeId(_nodeId_);
  };
  
  // Generate random character ID for Node
  function getRandomUppercaseChar() {
    var r = Math.floor(Math.random() * 26);
    return String.fromCharCode(65 + r);
  }

  console.log(nodePos);

  // Add 1 node
  const addNode = useCallback((prev) => {
    yPos.current += 50;

    console.log(-Math.abs(nodePos.x),  -Math.abs(nodePos.y), nodePos.x, nodePos.y, 'node posiition here');
    const node = {
      id: `${getRandomUppercaseChar()}`,
      position: {
        x: -Math.abs(nodePos.x),
        y: -Math.abs(nodePos.y)
      } && {
        x: 100,
        y: yPos.current
      },

      data: {
        label: faker.name.fullName(),
      },
      style: {
        width: 100,
      },
    };
    setNodes((nodes) => {
      return [...nodes, node];
    });

    if (prev) {
      const _prevNode_id = prev.getAttribute("data-id");
      setEdges((edges) => {
        return [
          ...edges,
          // For connecting edges , 'source' is the current node added and 'target' is the previous node
          {
            id: `${node.id}-${_prevNode_id}`,
            source: `${node.id}`,
            target: `${_prevNode_id}`,
            type: "step",
          },
        ];
      });
    }
    setOpenModal(false);
  }, []);

  const addCrossroad = useCallback(
    (prev) => {
      console.log(prev);
      yPos.current += 50;
      const node = [
        {
          id: `${getRandomUppercaseChar()}-${getRandomUppercaseChar()}`,
          position: { x: 50, y: yPos.current },
          data: { label: faker.name.fullName() },
          style: { width: 100 },
        },
        {
          id: `${getRandomUppercaseChar()}-${getRandomUppercaseChar()}`,
          position: { x: 150, y: yPos.current },
          data: {
            label: faker.name.fullName(),
          },
          style: {
            width: 100,
          },
        },
      ];
      setNodes((nodes) => {
        return [...nodes, ...node];
      });
      if (prev) {
        setEdges((edges) => {
          return [
            ...edges,
            // For connecting edges , 'source' is the current node added and 'target' is the previous node
            {
              id: `${node[0].id}-${nodeId}`,
              source: `${node[0].id}`,
              target: `${nodeId}`,
              type: "step",
              animated: true,
            },
            {
              id: `${node[1].id}-${nodeId}`,
              source: `${node[1].id}`,
              target: `${nodeId}`,
              type: "step",
              animated: true,
            },
          ];
        });
      }
      setOpenModal(false);
    },
    [nodeId]
  );

  // Track node 
  const TrackNode = (e) => {
    if (e.target.getAttribute("data-id")) {
      setPrevNode(e.target); 
      SetopenModalForNodes((prev) => !prev);
    }
  };

  return (
    <div>
      <div className="plusIcon" onClick={() => setOpenModal((prev) => !prev)}>
        <span>+</span>
      </div>

      {openModal ? (
        <div className="actionsModal">
          <button className="tool" onClick={() => addNode(prevNode)}>
            Add Tool
          </button>
          <button className="crossroad" onClick={() => addCrossroad(prevNode)}>
            Add Crossroad
          </button>
        </div>
      ) : null}

      {openModalForNodes ? (
        <div className="actionsModal">
          <button className="tool" onClick={() => addNode(prevNode)}>
            Add Tool{" "}
          </button>
          <button className="crossroad" onClick={() => addCrossroad(prevNode)}>
            Add Crossroad
          </button>
        </div>
      ) : null}

      <div style=>
        <ReactFlow
          nodes={nodes}
          onNodesChange={onNodesChange}
          onEdgesChange={onEdgesChange}
          onNodeDragStop={onNodeDragStop}
          onNodeClick={onNodeClick}
          onConnect={onConnect}
          onClick={(e) => TrackNode(e)}
          edges={edges}
          fitView
          defaultZoom={1}
          minZoom={0.2}
          maxZoom={4}
        >
          <Background />
        </ReactFlow>
      </div>
    </div>
  );
}

export default Flow;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Via Active questions tagged javascript - Stack Overflow https://ift.tt/AjHIclf

Comments