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)}%`); }, [data]); return ; }