|
|
|
@ -1,15 +1,28 @@ |
|
|
|
|
import { Group } from "@visx/group"; |
|
|
|
|
import Pie, { ProvidedProps } from "@visx/shape/lib/shapes/Pie"; |
|
|
|
|
import { Text } from "@visx/text"; |
|
|
|
|
import React from "react"; |
|
|
|
|
|
|
|
|
|
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. */ |
|
|
|
|
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 text inside the pie, in pixels*/ |
|
|
|
|
pieTextSize?: number; |
|
|
|
|
/** Font size of labels outside the pie, in pixels */ |
|
|
|
|
labelTextSize?: number; |
|
|
|
|
/** X-axis offset of the pie text, in pixels */ |
|
|
|
|
pieTextXOffset?: number; |
|
|
|
|
/** Y-axis offset of the pie text, in pixels */ |
|
|
|
|
pieTextYOffset?: number; |
|
|
|
|
className?: string; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -19,18 +32,23 @@ interface PieChartData { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
export function PieChart({ |
|
|
|
|
data, |
|
|
|
|
width, |
|
|
|
|
labelWidth, |
|
|
|
|
padRadius = width * 0.35, |
|
|
|
|
innerRadius = width * 0.015, |
|
|
|
|
...props |
|
|
|
|
pieTextSize = 40, |
|
|
|
|
labelTextSize = 40, |
|
|
|
|
pieTextXOffset = 0, |
|
|
|
|
pieTextYOffset = 10, |
|
|
|
|
className, |
|
|
|
|
}: PieChartProps) { |
|
|
|
|
const pieWidth = width * 0.5 - labelWidth; |
|
|
|
|
return ( |
|
|
|
|
<svg className={props.className} width={width} height={width}> |
|
|
|
|
<svg className={className} width={width} height={width}> |
|
|
|
|
<Group top={width * 0.5} left={width * 0.5}> |
|
|
|
|
<Pie |
|
|
|
|
data={props.data} |
|
|
|
|
data={data} |
|
|
|
|
pieValue={(d: PieChartData) => d.value} |
|
|
|
|
cornerRadius={10} |
|
|
|
|
padAngle={0.075} |
|
|
|
@ -38,47 +56,78 @@ export function PieChart({ |
|
|
|
|
innerRadius={innerRadius} |
|
|
|
|
outerRadius={pieWidth} |
|
|
|
|
> |
|
|
|
|
{(pie) => <PieSlices {...pie} isLabel={false} />} |
|
|
|
|
{(pie) => ( |
|
|
|
|
<PieSlice |
|
|
|
|
{...pie} |
|
|
|
|
isLabel={false} |
|
|
|
|
pieTextSize={pieTextSize} |
|
|
|
|
labelTextSize={labelTextSize} |
|
|
|
|
pieTextXOffset={pieTextXOffset} |
|
|
|
|
pieTextYOffset={pieTextYOffset} |
|
|
|
|
/> |
|
|
|
|
)} |
|
|
|
|
</Pie> |
|
|
|
|
<Pie |
|
|
|
|
data={props.data} |
|
|
|
|
data={data} |
|
|
|
|
pieValue={(d: PieChartData) => d.value} |
|
|
|
|
innerRadius={pieWidth} |
|
|
|
|
outerRadius={width * 0.5} |
|
|
|
|
> |
|
|
|
|
{(pie) => <PieSlices {...pie} isLabel={true} />} |
|
|
|
|
{(pie) => ( |
|
|
|
|
<PieSlice |
|
|
|
|
{...pie} |
|
|
|
|
isLabel={true} |
|
|
|
|
pieTextSize={pieTextSize} |
|
|
|
|
labelTextSize={labelTextSize} |
|
|
|
|
pieTextXOffset={pieTextXOffset} |
|
|
|
|
pieTextYOffset={pieTextYOffset} |
|
|
|
|
/> |
|
|
|
|
)} |
|
|
|
|
</Pie> |
|
|
|
|
</Group> |
|
|
|
|
</svg> |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type PieSlicesProps<PieChartData> = ProvidedProps<PieChartData> & { |
|
|
|
|
type PieSliceProps<PieChartData> = ProvidedProps<PieChartData> & { |
|
|
|
|
isLabel: boolean; |
|
|
|
|
pieTextSize: number; |
|
|
|
|
labelTextSize: number; |
|
|
|
|
pieTextXOffset: number; |
|
|
|
|
pieTextYOffset: number; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
export function PieSlices({ isLabel, ...props }: PieSlicesProps<PieChartData>) { |
|
|
|
|
export function PieSlice({ |
|
|
|
|
path, |
|
|
|
|
arcs, |
|
|
|
|
isLabel, |
|
|
|
|
pieTextSize, |
|
|
|
|
labelTextSize, |
|
|
|
|
pieTextXOffset, |
|
|
|
|
pieTextYOffset, |
|
|
|
|
}: PieSliceProps<PieChartData>) { |
|
|
|
|
return ( |
|
|
|
|
<> |
|
|
|
|
{props.arcs.map((arc) => { |
|
|
|
|
const [centroidX, centroidY] = props.path.centroid(arc); |
|
|
|
|
const pathArc = props.path(arc) as string; |
|
|
|
|
{arcs.map((arc) => { |
|
|
|
|
const [centroidX, centroidY] = path.centroid(arc); |
|
|
|
|
const pathArc = path(arc) as string; |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<g className={styles.group} key={`arc-${arc.data.category}`}> |
|
|
|
|
<Group className={styles.group} key={`arc-${arc.data.category}`}> |
|
|
|
|
<path |
|
|
|
|
className={isLabel ? styles.labelPath : styles.piePath} |
|
|
|
|
d={pathArc} |
|
|
|
|
/> |
|
|
|
|
<text |
|
|
|
|
<Text |
|
|
|
|
className={isLabel ? styles.labelText : styles.pieText} |
|
|
|
|
x={isLabel ? centroidX : centroidX} |
|
|
|
|
y={isLabel ? centroidY : centroidY + 10} |
|
|
|
|
x={isLabel ? centroidX : centroidX + pieTextXOffset} |
|
|
|
|
y={isLabel ? centroidY : centroidY + pieTextYOffset} |
|
|
|
|
textAnchor="middle" |
|
|
|
|
fontSize={isLabel ? labelTextSize : pieTextSize} |
|
|
|
|
> |
|
|
|
|
{isLabel ? `${arc.data.category}` : `${arc.data.value}%`} |
|
|
|
|
</text> |
|
|
|
|
</g> |
|
|
|
|
</Text> |
|
|
|
|
</Group> |
|
|
|
|
); |
|
|
|
|
})} |
|
|
|
|
</> |
|
|
|
|