D3 adding list of text to a node in forecDirected layout

131 views Asked by At

I have a lot of success creating forceDirect layouts with a single line of text as a label for each node. Now I have a project where I need to add a list (multiple text) to each node.

I have been able to add multiple text to using the same model that works for adding a single label and a single to each node.

The label, rect, and multiline text are each added to separate but only the list of text is not "pinned" to the node.

            var node = svg.append("g")
            .attr("transform", "translate("+[margin.left, margin.top]+")")
            .attr("class", "nodes")
            .selectAll("rect")
            .data(data.nodes)
            .enter()
            .append("rect")
            .style("width",function(d){
                return setRectWidth(d)
            })
            .style("height",function(d){
                return 30
            })
            .attr("fill",function(d){
                return setBackground(d)
            })
            //.attr("x",0)
            .attr("rx",4)
            .attr("ry",4)
            .call(d3.drag()
                .on("start", dragstarted)
                .on("drag", dragged)
                .on("end", dragended))


        var text = svg.append("g")
            .attr("class", "text")
            .selectAll("txt")
            .data(data.nodes)
            .enter()
            .append("text")
            .attr("x", 0)
            .text(function(d){
                return d.label
            })
            .style("text-anchor","middle")
            .call(d3.drag()
            .on("start", dragstarted)
            .on("drag", dragged)
            .on("end", dragended));



        var subtext = svg.append("g")
            .attr("class", "subtext")
            .selectAll("subtext")
            .data(data.node_items)
            .enter()
            .append("text")
            .attr("x", 0)
            .text(function(d){
                return d.label
            })
            .style("text-anchor","middle")
            .call(d3.drag()
            .on("start", dragstarted)
            .on("drag", dragged)
            .on("end", dragended));

Here is the raw result

enter image description here

Note Subtext has no xy assigned

Here is what I am going for:

enter image description here

The tick function looks ok to me so I don't know what I am missing.

 var ticked = function() {

 node
 .attr("x", function(d) { return  d.x + setRectWidth(d)*-.5; })
 .attr("y", function(d) { return d.y + setRectHeight(d)*-.5 });

 link
 .attr("d", function(d) {
 var a = []
 a.push({x: d.source.x,y:d.source.y});
 a.push({x: d.target.x,y:d.target.y});
 return line(a)
 })

 text
 .attr("x", function(d) { return  d.x  })
 .attr("y", function(d) { return d.y + setTextY(d)  });

 subtext
 .attr("x", function(d) { return  d.x  })
 .attr("y", function(d) { return d.y  });

}
1

There are 1 answers

0
alQemist On

The solution I came up with was to add list times to each node as node items, then use the ticked function to iterate the list and re-assign the x, y positions based on the items index.

var ticked = function(){.....

nodeitems.attr("x", function (d) {
                let new_x = d.fixed_x ? d.fixed_x : d.x;
                return new_x - setRectWidth(d) * .4;
            })
            .attr("y", function (d, i) {
                let new_y = d.fixed_y ? d.fixed_y : d.y;
                return new_y + (i * row_height) + setTextY(d) + row_height + 5
            });

You can see the working solution here - https://alqemist.github.io/EAGIR/erd/

Source code is here - look at erd folder https://github.com/alQemist/EAGIR