import { Group } from "@visx/group"; import Pie, { ProvidedProps } from "@visx/shape/lib/shapes/Pie"; import { Text } from "@visx/text"; import { withTooltip } from "@visx/tooltip"; import React from "react"; import { getTooltipPosition, TooltipWrapper } from "./TooltipWrapper"; import styles from "./PieChart.module.css"; interface PieChartProps { data: PieChartData[]; /** Width of the entire graph, including labels, in pixels. */ width: number; /** Width of the outer ring of labels, in pixels. Label text may be cut off if specified value is too small. */ labelWidth: number; /** Distance between pie slices, in pixels. */ padRadius?: number; /** Distance of gap in center of pie graph, in pixels. */ innerRadius?: number; /** Font size of labels outside the pie, in pixels. */ labelTextSize?: number; /** X-axis offset of the label text, in pixels. */ labelTextXOffset?: number; /** Y-axis offset of the label text, in pixels. */ labelTextYOffset?: number; /** The radial offset of the label text, in pixels. */ labelTextRadialOffset?: number; /** If set, the minimum width of this graph */ minWidth?: number; /** Accessor function to get value to display as label text from datum. */ getLabelDisplayValueFromDatum?: (datum: PieChartData) => string; className?: string; } interface PieChartData { category: string; value: number; } export const PieChart = withTooltip( ({ data, width, labelWidth, padRadius = width * 0.25, innerRadius = width * 0, labelTextSize = 40, labelTextXOffset = 0, labelTextYOffset = 0, labelTextRadialOffset = -20, minWidth = 500, getLabelDisplayValueFromDatum = (datum: PieChartData) => `${datum.category}`, className, tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip, }) => { if (minWidth) { width = width < minWidth ? minWidth : width; } const pieWidth = width * 0.5 - labelWidth; const cornerRadius = 0; const padAngle = 0; return (
d.value} cornerRadius={cornerRadius} padAngle={padAngle} padRadius={padRadius} innerRadius={innerRadius} outerRadius={pieWidth} > {({ arcs, path }) => { return arcs.map((arc) => { const pathArc = path(arc) as string; return ( { const tooltipPos = getTooltipPosition(e); showTooltip({ tooltipData: `${arc.data.category}: ${arc.data.value}%`, tooltipLeft: tooltipPos.x, tooltipTop: tooltipPos.y, }); }} onMouseOut={hideTooltip} className={styles.piePath} d={pathArc} /> ); }); }} d.value} innerRadius={pieWidth} outerRadius={width * 0.5} > {(pie) => ( )} {tooltipOpen && ( )}
); } ); type PieSliceLabelProps = ProvidedProps & { labelTextSize: number; labelTextXOffset: number; labelTextYOffset: number; labelTextRadialOffset: number; getLabelDisplayValueFromDatum: (datum: PieChartData) => string; }; export function PieSliceLabel({ path, arcs, labelTextSize, labelTextXOffset, labelTextYOffset, labelTextRadialOffset, getLabelDisplayValueFromDatum, }: PieSliceLabelProps) { return ( <> {arcs.map((arc) => { const [centroidX, centroidY] = path.centroid(arc); const pathArc = path(arc) as string; return ( 100 ? "start" : centroidX < -100 ? "end" : "middle" } fontSize={labelTextSize} > {`${getLabelDisplayValueFromDatum(arc.data)}`} ); })} ); }