1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
import * as d3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm";
const COLORS = ["#0062ff", "#da1e28", "#fdd13a", "#24ab48", "#9966ff", "#606e85"];
const SIZE = 420;
const RADIUS = SIZE / 2 - 30;
const INNER_RADIUS = RADIUS * 0.45;
// data: [{label, value, alpha3}] (alpha3 is null for "Rest of World")
// onSliceClick: (alpha3) => void — called only for named-country slices
export function renderPieChart(container, data, onSliceClick) {
const el = typeof container === "string"
? document.querySelector(container)
: container;
el.innerHTML = "";
const svg = d3.select(el).append("svg")
.attr("viewBox", `0 0 ${SIZE} ${SIZE}`)
.attr("width", "100%");
const g = svg.append("g")
.attr("transform", `translate(${SIZE / 2},${SIZE / 2 - 10})`);
const pie = d3.pie().value(d => d.value).sort(null);
const arc = d3.arc().outerRadius(RADIUS).innerRadius(INNER_RADIUS);
const labelArc = d3.arc()
.outerRadius(RADIUS * 0.78)
.innerRadius(RADIUS * 0.78);
const arcs = g.selectAll(".arc")
.data(pie(data))
.join("g")
.attr("class", "arc");
arcs.append("path")
.attr("d", arc)
.attr("fill", (d, i) => COLORS[i % COLORS.length])
.attr("stroke", "#161616")
.attr("stroke-width", 2)
.style("cursor", d => d.data.alpha3 ? "pointer" : "default")
.on("click", (event, d) => {
if (d.data.alpha3 && onSliceClick) onSliceClick(d.data.alpha3);
});
arcs.append("text")
.attr("transform", d => `translate(${labelArc.centroid(d)})`)
.attr("text-anchor", "middle")
.attr("dy", "0.35em")
.style("fill", "#fff")
.style("font-size", "11px")
.style("pointer-events", "none")
.text(d => {
const angle = d.endAngle - d.startAngle;
return angle > 0.3 ? d.data.label : "";
});
// Legend
const legend = svg.append("g")
.attr("transform", `translate(10, ${SIZE - 20 - data.length * 18})`);
data.forEach((d, i) => {
const row = legend.append("g").attr("transform", `translate(0, ${i * 18})`);
row.append("rect").attr("width", 12).attr("height", 12).attr("fill", COLORS[i % COLORS.length]);
row.append("text")
.attr("x", 16)
.attr("y", 10)
.style("fill", "#ccc")
.style("font-size", "11px")
.text(d.label);
});
}
|