Newer
Older
import React, { useEffect, useRef } from "react";
import * as d3 from "d3";
export default function DonutChart({ data }) {
const ref = useRef();
const svgWidth = 1100;
const svgHeight = 1000;
const width = 400;
const height = 400;
const radius = Math.min(width, height) / 2;
const innerRadius = radius * 0.6;
const outerRadius = radius * 0.9;
const hoverOuterRadius = outerRadius + 20;
const minPercentage = 1; // le seuil pour regrouper les petites divisions
useEffect(() => {
if (!data || data.length === 0) {
return;
}
const totalOccurrences = data.reduce((acc, item) => acc + item.occurrences, 0);
let otherData = { region: "Autres", occurrences: 0, details: [] };
let newData = data.filter(d => {
let percentage = (d.occurrences / totalOccurrences) * 100;
if (percentage < minPercentage) {
otherData.occurrences += d.occurrences;
otherData.details.push(`${d.region} (${percentage.toFixed(2)}%)`);
return false;
}
return true;
});
if (otherData.occurrences > 0) {
newData.push(otherData);
}
const pieData = d3.pie().value(d => d.occurrences)(newData);
const arc = d3.arc().innerRadius(innerRadius).outerRadius(outerRadius);
const hoverArc = d3.arc().innerRadius(innerRadius).outerRadius(hoverOuterRadius);
const labelArc = d3.arc().innerRadius(outerRadius + 30).outerRadius(outerRadius + 30);
const svg = d3.select(ref.current)
.attr("width", svgWidth)
.attr("height", svgHeight)
.append("g")
.attr("transform", `translate(${svgWidth / 2}, ${svgHeight / 2})`);
svg.selectAll("path")
.data(pieData)
.enter()
.append("path")
.attr("d", arc)
.attr("fill", (d, i) => d3.schemeCategory10[i % 10])
.on("mouseover", function(event, d) {
d3.select(this).transition().attr("d", hoverArc);
let text = (d.data.region === "Autres") ?
otherData.details.join(", ") :
`${d.data.region}: ${((d.data.occurrences / totalOccurrences) * 100).toFixed(2)}%`;
const centroid = hoverArc.centroid(d);
svg.append("text")
.attr("class", "tooltip")
.attr("x", centroid[0])
.attr("y", centroid[1])
.attr("text-anchor", "middle")
.text(text);
})
.on("mouseout", function() {
d3.select(this).transition().attr("d", arc);
svg.selectAll(".tooltip").remove();
});
// l'ajout des lignes et textes pour chaque division sauf pour autres
svg.selectAll(".label-line")
.data(pieData.filter(d => d.data.region !== "Autres"))
.enter()
.append("line")
.attr("x1", d => labelArc.centroid(d)[0])
.attr("y1", d => labelArc.centroid(d)[1])
.attr("x2", d => labelArc.centroid(d)[0] * 2)
.attr("y2", d => labelArc.centroid(d)[1] * 2)
.attr("stroke", "gray");
svg.selectAll(".label-text")
.data(pieData.filter(d => d.data.region !== "Autres"))
.enter()
.append("text")
.attr("transform", d => {
const [x, y] = labelArc.centroid(d);
return `translate(${x * 2}, ${y * 2})`;
})
.attr("text-anchor", "middle")
.text(d => `${d.data.region}: ${((d.data.occurrences / totalOccurrences) * 100).toFixed(2)}%`);
return <svg ref={ref}></svg>;