react-d3-graph icon indicating copy to clipboard operation
react-d3-graph copied to clipboard

Add x,y to onClickGraph callback

Open Taresin opened this issue 4 years ago • 4 comments

Is your feature request related to a problem? Please describe. No.

Describe the solution you'd like I would like x and y coordinates to be passed in to the callback

Describe your use case I would like to create a new node at a specific user-defined point on the canvas. It is much easier to do this graphically by clicking instead of adding a node, adding it randomly on the page and dragging it to my desired position.

Describe alternatives you've considered I've been adding nodes with the interaction as above.

Additional context I'm not super familiar with the codebase but I've checked the SyntheticBaseEvent that is returned by the onClickGraph function and couldn't find numbers that resembled the coords that I need.

Taresin avatar Dec 07 '20 11:12 Taresin

Could I double-check with you if this is the functionality you're looking for?

In order to provide x,y coordinates when creating a node, check our example in the project sandbox where we attribute random coordinates to newly added nodes.

danielcaldas avatar Dec 17 '20 13:12 danielcaldas

Hi @danielcaldas, Thanks for looking at my feature request.

It's not exactly what I'm looking for. I was hoping the user can click on a blank space on the graph to create a node at that coordinate.

I'm able to create the nodes at a given x,y coordinate but I'm unable to get the coordinates when the user clicks on the graph. The callback that I found was onClickGraph but I couldn't find a way to get the coordinates from it.

Taresin avatar Dec 18 '20 00:12 Taresin

Hmmm... @Taresin still looks like something you should be able to achieve on your own without react-d3-graph. Look at my example, maybe it clears the ideas for you.

click-create

Quick & dirty implementation

⚠️ Please don't copy&paste this as it is, just a demonstration.

import React, { useState } from "react";
import "./styles.css";
import { Graph } from "react-d3-graph";

export default function App() {
  // graph payload (with minimalist structure)
  const [data, setData] = useState({
    nodes: [{ id: "Harry" }, { id: "Sally" }, { id: "Alice" }],
    links: [
      { source: "Harry", target: "Sally" },
      { source: "Harry", target: "Alice" }
    ]
  });

  // the graph configuration, just override the ones you need
  const myConfig = {
    nodeHighlightBehavior: true,
    freezeAllDragEvents: true,
    node: {
      color: "lightgreen",
      size: 120,
      highlightStrokeColor: "blue"
    },
    link: {
      highlightColor: "lightblue"
    }
  };

  const onClickNode = function (nodeId) {
    window.alert(`Clicked node ${nodeId}`);
  };

  document.addEventListener("click", (event) => {
    console.log(event);
    const { clientX, clientY } = event;

    const newNode = {
      id: `${Date.now()}`,
      x: clientX,
      y: clientY
    };
    setData({
      ...data,
      nodes: [...data.nodes, newNode]
    });
  });

  return (
    <Graph
      id="graph-id" // id is mandatory
      data={data}
      config={myConfig}
      onClickNode={onClickNode}
    />
  );
}

danielcaldas avatar Dec 18 '20 04:12 danielcaldas

But when the canvas is moved or scaled the nodes will not appear in the exact canvas position, we need to shift and rescale according to the translation. The way I found to access the translation values from the event is quite messy so maybe a feature should be added. Nevertheless, this is what I think you are looking for:


const onClickGraph = function(event) {
    var screen_x = event.nativeEvent.layerX;
    var screen_y = event.nativeEvent.layerY;
    var trans_x = 0
    var trans_y = 0
    var trans_scale = 1
    if (event.target.childNodes[1].transform.baseVal.length > 0) {
      trans_x = event.target.childNodes[1].transform.baseVal[0].matrix.e;
      trans_y = event.target.childNodes[1].transform.baseVal[0].matrix.f;
      trans_scale = event.target.childNodes[1].transform.baseVal[1].matrix.a;
    }
    var canvas_x = (screen_x - trans_x)/trans_scale;
    var canvas_y = (screen_y - trans_y)/trans_scale;
    console.log(canvas_x)
    console.log(canvas_y)

    const newNode = {
      id: `${Date.now()}`,
      x: canvas_x,
      y: canvas_y
    };

    setGraphData((state, props) => {
      var newNodes = state.nodes;
      newNodes.push(newNode);
      return {
        nodes: newNodes,
        links: state.links
      }
    });
  };

henri-edinb avatar Jan 16 '21 22:01 henri-edinb