chart = () => {
const wrapper = d3.create("div");
wrapper.append("style").html(css);
const chart = wrapper.selectAll(".chart")
.data(data)
.join("div")
.attr("class", "chart");
const title = chart.append("div")
.attr("class", "title")
.text(d => `${d.name}, ${d.country}`);
const subtitle = chart.append("div")
.attr("class", "subtitle")
.text(d => `${+(d.pop / 1e6).toFixed(1)} million people`)
const svg = chart.append("svg");
const g = svg.append("g");
const xaxis = g.append("g")
.attr("class", "axis x-axis");
const yaxis = g.append("g")
.attr("class", "axis y-axis");
const averageLine = g.append("line").attr("class", "average-line");
const circle = g.selectAll(".circle")
.data(d => d.data)
.join("circle")
.attr("class", "circle")
.attr("fill", d => color(d.danger))
.attr("stroke", d => d3.color(color(d.danger)).darker(0.3));
const averageText = g.append("g")
.attr("class", "average-text");
const averageLabel = averageText.append("text").attr("class", "average-label").text("Average");
const averageValue = averageText.append("text").attr("class", "average-value").text(d => `${Math.round(d.average)} days`);
return Object.assign(wrapper.node(), {
resize(ww) {
// Resize: Dimensions
const cols = ww <= 900 ? 2 : 3
const rows = data.length / cols;
const padInner = 24;
const padTotal = padInner * (cols - 1);
const pad = padTotal / cols;
const r = ww <= 400 ? 3 : 4;
const margin = { left: 29, right: 17, top: 5, bottom: 24};
const chartMarginBottom = 24;
const basewidth = ww / cols - pad;
const width = basewidth - margin.left - margin.right;
const height = Math.max(200, basewidth * 9 / 16) - margin.top - margin.bottom;
const topOffset = 96;
// Resize: Scales
x.range([0, width]);
y.range([height, 0]);
// Resize: Axes
xaxis
.attr("transform", `translate(0, ${height})`)
.call(xaxisGenerator);
yaxisGenerator.tickSize(width + 10)
yaxis
.attr("transform", `translate(${width})`)
.call(yaxisGenerator);
const ytick = yaxis.select(".tick:last-of-type");
ytick.select("line").attr("x2", -yaxisGenerator.tickSize() + topOffset)
ytick.select("text").attr("x", -yaxisGenerator.tickSize() - yaxisGenerator.tickPadding() + topOffset)
// Resize: Scaffold
chart
.style("margin-bottom", (_, i) => 1 + Math.floor(i / cols) === rows ? "0px" : `${chartMarginBottom}px`)
.style("margin-left", (_, i) => i % cols === 0 ? "0px" : `${padInner / 2}px`)
.style("margin-right", (_, i) => i % cols === (cols - 1) ? "0px" : `${padInner / 2}px`)
.style("width", `calc(${100 / cols}% - ${pad}px)`)
svg
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
g
.attr("transform", `translate(${[margin.left, margin.top]})`);
// Resize: Marks
averageText
.attr("transform", d => `translate(${[-margin.left, y(d.average)]})`);
averageLine
.attr("transform", d => `translate(${[-margin.left, y(d.average)]})`)
.attr("x2", margin.left + x(2024));
circle
.attr("cx", d => x(d.year))
.attr("cy", d => y(d.danger))
.attr("r", r);
}
})
}