donut.jsx 3,23 ko
Newer Older
import React, { useEffect, useRef } from "react";
import * as d3 from "d3";

export default function DonutChart({ data }) {
    const ref = useRef();
    const svgWidth = 600;
    const svgHeight = 600;
    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;
    useEffect(() => {
        if (!data || data.length === 0) {
            return;
        }
        const totalOccurrences = data.reduce((acc, item) => acc + item.occurrences, 0);
        const minPercentage = 1;
        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 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);
                if (d.data.region === "Autres") {
                    svg.append("text")
                        .attr("class", "details-text")
                        .attr("x", 0)
                        .attr("y", 0)
                        .attr("text-anchor", "middle")
                        .selectAll("tspan")
                        .data(otherData.details)
                        .enter()
                        .append("tspan")
                        .attr("x", 0)
                        .attr("dy", "1.2em")
                        .text(detail => detail);
                } else {
                    const percentage = ((d.data.occurrences / totalOccurrences) * 100).toFixed(2);
                    const text = `${d.data.region}: ${percentage}%`;
                    svg.append("text")
                        .attr("class", "tooltip")
                        .attr("x", 0)
                        .attr("y", 20)
                        .attr("text-anchor", "middle")
                        .text(text);
                }
            })
            .on("mouseout", function() {
                d3.select(this).transition().attr("d", arc);
                svg.selectAll(".details-text").remove();
                svg.selectAll(".tooltip").remove();
            });
    return <svg ref={ref}></svg>;