CircularDiagramChart.tsx 2,85 ko
Newer Older
import React, { useMemo, useRef, useEffect, useState } from "react";
import { RegionalDistributionPoint } from "../../../models/RegionalDistribution";
import {
  WIDTH,
  HEIGHT,
  MARGIN,
  colorForRegion
} from "./circulardiagram.utils";
import * as d3 from 'd3';

export type HoverPoint = {
    regionName: string,
    collectedVolume: number;
};

type Props = {
  raw: RegionalDistributionPoint[];
};

export const CircularDiagramChart: React.FC<Props> = ({
    raw,
}) => {
    const [hover, setHover] = useState<HoverPoint | null>(null);

    const innerW = WIDTH - MARGIN.left - MARGIN.right;
    const innerH = HEIGHT - MARGIN.top - MARGIN.bottom;

    const radius = Math.min(innerW, innerH) / 2;

    const pieData = useMemo(() => {
    const pieGenerator = d3.pie<RegionalDistributionPoint>()
        .value((d) => d.collectedVolume)
        .sort(null);
        return pieGenerator(raw);
    }, [raw]);

    const arcGenerator = d3.arc<d3.PieArcDatum<RegionalDistributionPoint>>()
        .innerRadius(radius * 0.6)
        .outerRadius(radius);

    return (
      <div className="chart-wrapper">
        <svg width={innerW} height={innerH}>
            <g transform={`translate(${innerW / 2}, ${innerH / 2})`}>
                {pieData.map((slice, i) => {
                    const [x, y] = arcGenerator.centroid(slice);
                    return (
                        <path
                            key={i}
                            d={arcGenerator(slice) || ""}
                            fill={colorForRegion(slice.data.regionName)}
                            onMouseEnter={() => setHover(slice.data)}
                            onMouseLeave={() => setHover(null)}
                            stroke="#fff"
                            strokeWidth="2"
                            style={{ 
                                opacity: hover === null || hover.regionName === slice.data.regionName ? 1 : 0.6,
                                transition: 'opacity 0.2s',
                                cursor: 'pointer'
                            }}
                        />
                    );
                })}
                {hover && (
                    <g>
                        <text
                            y="-10"
                            textAnchor="middle"
                            style={{ fontSize: '14px', fontWeight: 'bold', fill: '#333' }}
                        >
                            {hover.regionName}
                        </text>
                        <text
                            y="15"
                            textAnchor="middle"
                            style={{ fontSize: '18px', fill: '#666' }}
                        >
                            {hover.collectedVolume.toLocaleString()}
                        </text>
                    </g>
                )}
            </g>
        </svg>
      </div>
    );
};