How can I wrap text inside each node making the node automatically resize, just as in css you would do with fit-content or display flex?
Here's the snippet, available at this link: https://codepen.io/boxxello/pen/XWYvRQO
or here: css:
html, body {
margin: 0;
padding: 0;
background-color: rgb(140, 149, 226);
}
.node {
cursor: pointer;
}
.node circle {
}
.node-leaf {
}
.node text {
font: 10px sans-serif;
}
.link {
fill: none;
stroke: rgb(55, 68, 105);
stroke-width: 1px;
}
js:
var margin = {top: 20, right: 90, bottom: 30, left: 90},
width = 1300 - margin.left - margin.right,
height = 900 - margin.top - margin.bottom;
var svg = d3
.select("body")
.append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var i = 0,
duration = 750,
root;
var treemap = d3.tree().size([height, width]);
root = d3.hierarchy(treeData, function (d) {
return d.children;
});
root.x0 = height / 2;
root.y0 = 0;
root.children.forEach(collapse);
update(root);
function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}
function update(source) {
var treeData = treemap(root);
var nodes = treeData.descendants(),
links = treeData.descendants().slice(1);
nodes.forEach(function (d) {
d.y = d.depth * 180;
});
var node = svg.selectAll("g.node").data(nodes, function (d) {
return d.id || (d.id = ++i);
});
var nodeEnter = node
.enter()
.append("g")
.attr("class", "node")
.attr("transform", function (d) {
return "translate(" + source.y0 + "," + source.x0 + ")";
})
.on("click", click);
nodeEnter
.attr("class", "node")
.attr("r", 1e-6)
.style("display", function (d) {
if (d.depth === 0) { //Is top root
return 'none';
}
});
nodeEnter
.append("rect")
.attr("rx", function (d) {
if (d.parent) return d.children || d._children ? 0 : 6;
return 10;
})
.attr("ry", function (d) {
if (d.parent) return d.children || d._children ? 0 : 6;
return 10;
})
.attr("stroke-width", function (d) {
return d.parent ? 1 : 0;
})
.attr("stroke", function (d) {
return d.children || d._children
? "rgb(3, 192, 220)"
: "rgb(38, 222, 176)";
})
.attr("stroke-dasharray", function (d) {
return d.children || d._children ? "0" : "2.2";
})
.attr("stroke-opacity", function (d) {
return d.children || d._children ? "1" : "0.6";
})
.attr("x", 0)
.attr("y", -10)
.attr("width", function (d) {
return d.parent ? 40 : 20;
})
.attr("height", 20)
.classed("node-leaf", true);
nodeEnter
.append("text")
.style("fill", function (d) {
if (d.parent) {
return d.children || d._children ? "#ffffff" : "rgb(38, 222, 176)";
}
return "rgb(39, 43, 77)";
})
.attr("dy", ".35em")
.attr("x", function (d) {
return d.parent ? 20 : 10;
})
.attr("text-anchor", function (d) {
return "middle";
})
.text(function (d) {
return d.data.name;
});
var nodeUpdate = nodeEnter.merge(node);
nodeUpdate
.transition()
.duration(duration)
.attr("transform", function (d) {
return "translate(" + d.y + "," + d.x + ")";
});
var nodeExit = node
.exit()
.transition()
.duration(duration)
.attr("transform", function (d) {
return "translate(" + source.y + "," + source.x + ")";
})
.remove();
nodeExit.select("rect").style("opacity", 1e-6);
nodeExit.select("rect").attr("stroke-opacity", 1e-6);
nodeExit.select("text").style("fill-opacity", 1e-6);
var link = svg.selectAll("path.link").data(links, function (d) {
return d.id;
});
var linkEnter = link
.enter()
.insert("path", "g")
.attr("class", "link")
.attr("d", function (d) {
var o = {x: source.x0, y: source.y0};
return diagonal(o, o);
}).style("display", function (d) {
if (d.depth === 1) { //Is top link
return 'none';
}
})
var linkUpdate = linkEnter.merge(link);
linkUpdate
.transition()
.duration(duration)
.attr("d", function (d) {
return diagonal(d, d.parent);
});
var linkExit = link
.exit()
.transition()
.duration(duration)
.attr("d", function (d) {
var o = {x: source.x, y: source.y};
return diagonal(o, o);
})
.remove();
nodes.forEach(function (d) {
d.x0 = d.x;
d.y0 = d.y;
});
function diagonal(s, d) {
path = `M ${s.y} ${s.x}
C ${(s.y + d.y) / 2} ${s.x},
${(s.y + d.y) / 2} ${d.x},
${d.y} ${d.x}`;
return path;
}
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
}
The part where you could change the style property is at:
.attr("width", function (d) {
return d.parent ? 40 : 20;
})
.attr("height", 20)
.classed("node-leaf", true);
Or at least is where I've tried to deal with it. The current version I'm using of d3js is the 4.2.2, I actually used this version it fitted my needs (found the snippet online and there have been breaking changes in the 7.* which is the current version) not because I didn't want to upgrade it (if it's easier in the newer versions please let me know.
This is an expected output, as you can see the node basically fits all the text inside it (I left the parent nodes blank because they're not relevant in this case, but they would need to do the same thing, so applying a property to all the nodes would be great.