chart = {
const margin = {top: 10, right: 10, bottom: 10, left: 10};
const svg_width = width + margin.left + margin.right;;
const svg_height = height + margin.top + margin.bottom;
const svg = d3.create("svg")
.attr("width", svg_width)
.attr("height", svg_height)
.attr("viewBox", [0, 0, svg_width, svg_height])
.attr("style", "max-width: 100%; height: auto; font: 10px sans-serif;");
const chart = svg.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top})`);
const x = d3.scaleLinear().rangeRound([0, width]);
const y = d3.scaleLinear().rangeRound([0, height]);
const color = d3.scaleSequential([8, 0], d3[schemeColor]);
const format = d3.format(",d");
function tile(node, x0, y0, x1, y1) {
d3[treemapType](node, 0, 0, width, height);
for (const child of node.children) {
child.x0 = x0 + child.x0 / width * (x1 - x0);
child.x1 = x0 + child.x1 / width * (x1 - x0);
child.y0 = y0 + child.y0 / height * (y1 - y0);
child.y1 = y0 + child.y1 / height * (y1 - y0);
}
}
const treemap = data => d3.treemap()
.tile(tile)
.size([width, height])
.paddingOuter(3)
.paddingTop(21)
(d3.hierarchy(data)
.sum(d => d[valueColumn])
.sort((a, b) => b[valueColumn] - a[valueColumn]));
const treeroot = treemap(data);
x.domain([treeroot.x0, treeroot.x1]);
y.domain([treeroot.y0, treeroot.y1]);
let chartGroup = chart.append("g").call(render, treeroot); // Need to use `let` here!!!
function render(group, root) {
const node = group
.selectAll("g")
.data(d3.group(root, d => d.height))
.join("g")
.selectAll("g")
.data(d => d[1])
.join("g");
node.append("rect")
.attr("fill", d => color(d.height));
node.append("text")
.selectAll("tspan")
.data(d => d.data[nameColumn].split(/(?=[A-Z][^A-Z])/g).concat(format(d[valueColumn])))
.join("tspan")
.text(d => d);
node.filter(d => d.children).selectAll("tspan")
.attr("dx", 3)
.attr("y", 13)
.style("font-size", (d, i, nodes) => `${(i === nodes.length-1) ? 1 : 1.3}em`)
.style("opacity", (d, i, nodes) => `${(i === nodes.length-1) ? 0.7 : 1}`);
node.filter(d => !d.children).selectAll("tspan")
.attr("x", 3)
.attr("y", (d, i, nodes) => `${(i === nodes.length-1) * 0.5 + 0.9 * i + 1.1}em`)
.style("font-size", (d, i, nodes) => `${(i === nodes.length-1) ? 1 : 1.1}em`)
.style("opacity", (d, i, nodes) => `${(i === nodes.length-1) ? 0.7 : 1}`);
node.filter(d => d === root ? d.parent : d.children)
.attr("cursor", "pointer")
.on("click", (event, d) => d === root ? zoomOut(d) : zoomIn(d));
group.call(position, root);
}
function position(group, root) {
group.selectAll("g")
.selectAll("g")
.attr("transform", d => `translate(${x(d.x0)}, ${y(d.y0)})`)
.select("rect")
.attr("width", d => x(d.x1) - x(d.x0))
.attr("height", d => y(d.y1) - y(d.y0));
}
function zoomIn(d) {
const group0 = chartGroup.attr("pointer-events", "none");
const group1 = chartGroup = chart.append("g").call(render, d);
x.domain([d.x0, d.x1]);
y.domain([d.y0, d.y1]);
chart.transition()
.duration(750)
.call(t => group0.transition(t)
.attrTween("opacity", () => d3.interpolate(1, 0))
.call(position, d.parent)
.remove()
)
.call(t => group1.transition(t)
.attrTween("opacity", () => d3.interpolate(0, 1))
.call(position, d)
);
}
function zoomOut(d) {
const group0 = chartGroup.attr("pointer-events", "none");
const group1 = chartGroup = chart.append("g").call(render, d.parent);
x.domain([d.parent.x0, d.parent.x1]);
y.domain([d.parent.y0, d.parent.y1]);
chart.transition()
.duration(750)
.call(t => group0.transition(t)
.attrTween("opacity", () => d3.interpolate(1, 0))
.call(position, d)
.remove()
)
.call(t => group1.transition(t)
.attrTween("opacity", () => d3.interpolate(0, 1))
.call(position, d.parent)
);
}
return svg.node();
}