react-digraph icon indicating copy to clipboard operation
react-digraph copied to clipboard

Edge centered when using renderNode

Open hectorpiteau opened this issue 5 years ago • 5 comments

Hi, I'm trying to use the renderNode function to change the color of the node, as well as the border thickness and color.

Here is my code :

const onRenderNode = (nodeRef: any,data: any,index: number,selected: boolean,hovered: boolean) => {
        return (
            <g>
                <symbol viewBox="0 0 150 150" id={`myNode-${data.id}`} width="150" height="150">
                    <rect  id={`${data.type}`}  x="0" y="0" rx="4" ry="4" height="50" width="100" fill={(data.hexColor)?data.hexColor:"#FF00FF"} stroke="#FFFF00" strokeWidth={(hovered)?"4":"0%"} onContextMenu={(e)=>contextMenuHandler(e,data)}/>
                </symbol>
                <use xlinkHref={`#myNode-${data.id}`} x="-50" y="-25"></use>
            </g>
            );
    }

I added an id property to data to be able to link this to my model

The probleme is the the arrow is pointing in the middle of the node, like this :

image

And I don't know what i'm missing here, can you help me ?

Have a nice day !

hectorpiteau avatar Apr 16 '20 09:04 hectorpiteau

Your symbol with the viewbox and width/height properties is inside the element. They have to be the outer-most properties or else the code can't determine how big the box is. The code uses this information, and the shape within (rect, circle, path), to adjust the edge rendering so that it closely follows the outside path of the SVG.

ajbogh avatar Apr 17 '20 17:04 ajbogh

Your symbol with the viewbox and width/height properties is inside the element. They have to be the outer-most properties or else the code can't determine how big the box is. The code uses this information, and the shape within (rect, circle, path), to adjust the edge rendering so that it closely follows the outside path of the SVG.

Does it means that the viewbox, width and height have to be on the group, or that the symbol have to be declare before the use calls it ? I've tested to put the viewbox, width and height on the group, and on the use, but nothing seems to work.

hectorpiteau avatar Apr 18 '20 10:04 hectorpiteau

If you look at the example graph config you can find several different shapes, including a complex shape that might give you a better hint into the edge detection.

https://github.com/uber/react-digraph/blob/master/src/examples/graph-config.js

The one I mentioned is the ComplexCircleShape:

const ComplexCircleShape = (
  <symbol viewBox="0 0 100 100" id="complexCircle" width="100" height="100">
    <circle cx="50" cy="50" r="50" fill="transparent" stroke="transparent" />
    <circle cx="50" cy="50" r="34" />
    <path
      d="M50,0a50,50,0,1,0,50,50A50,50,0,0,0,50,0Zm0,90A40,40,0,1,1,90,50,40,40,0,0,1,50,90Z"
      data-intersect-ignore="true"
    />
  </symbol>
);

The edge detection finds the first usable shape (circle, rect, or path), and places the arrow around that shape. The example above one uses a transparent circle around the outside of the shape so that the edge detection can follow the largest circle.

Every example is wrapped in a <symbol> element. This isn't necessarily required, you could use <svg> or <g> I suppose, but <symbol> is more syntactically correct, so please consider using it instead of a <g>.

Regarding changing the color and stroke thickness, this can certainly be done within the element properties or CSS. A <use> is typically used for icons and other complex shapes that you don't want filling up the DOM. Consider a highly complex icon that needs to be rendered thousands of times in the graph, if you didn't use a <use> element then you would be duplicating that SVG in the DOM. With React, it can be handy for switching between multi-colored icons and greyscale icons.

ajbogh avatar Apr 19 '20 00:04 ajbogh

I'm having the same issue as OP. I've got the following renderNode:

const renderNode = (nodeRef, node: MapNode, id, selected, hovered) => {
    const children = node.type.sort().reverse().map(t => (<use
      id={t}
      key={t}
      x={-100 / 2}
      y={-100 / 2}
      width={100}
      height={100}
      xlinkHref={'#' + t}
    />));

    return (
      <g className="node"
              x={-100 / 2}
              y={-100 / 2}
              width={100}
              height={100}>
        {children}
      </g>
    );
  };

where the linked shapes look like this:

<symbol viewBox="0 0 100 100" id="1_slow" width="100" height="100" fill={SLOW_COLOR}>
      <circle r={10} cx={50} cy={50}/>
</symbol>

But I have my edges overlapping the nodes and going all the way to the center:

image

The two examples I can find of using this both don't use the renderNode function, and just rely on the types, I think this problem is specific to using the renderNode, are there examples of that somewhere?

mbonig avatar Nov 13 '20 02:11 mbonig

I'm not sure what the problem is for mbonig's case. But as a general comment for others, it seems crucial to give the className of "node" to the returned use tag in the renderNode function, for the edge to follow the svg contour.

Aviemusca avatar Nov 24 '20 11:11 Aviemusca