commit
f3ad60035f
@ -0,0 +1,26 @@ |
||||
.piePath { |
||||
fill: var(--tertiary-background); |
||||
} |
||||
|
||||
.labelPath { |
||||
fill-opacity: 0; |
||||
} |
||||
|
||||
.pieText, |
||||
.labelText { |
||||
fill: var(--label); |
||||
font-weight: 800; |
||||
} |
||||
|
||||
.pieText { |
||||
display: none; |
||||
} |
||||
|
||||
.group:hover > .piePath { |
||||
fill: var(--primary-accent); |
||||
filter: drop-shadow(0px 0px calc(6rem / 16) var(--primary-accent)); |
||||
} |
||||
|
||||
.group:hover .pieText { |
||||
display: inline; |
||||
} |
@ -0,0 +1,180 @@ |
||||
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. 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 text inside the pie, in pixels. */ |
||||
pieTextSize?: number; |
||||
/** X-axis offset of the pie text, in pixels. */ |
||||
pieTextXOffset?: number; |
||||
/** Y-axis offset of the pie text, in pixels. */ |
||||
pieTextYOffset?: number; |
||||
/** Accessor function to get value to display as pie text from datum. */ |
||||
getPieDisplayValueFromDatum?: (datum: PieChartData) => string; |
||||
/** 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; |
||||
/** 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 function PieChart({ |
||||
data, |
||||
width, |
||||
labelWidth, |
||||
padRadius = width * 0.35, |
||||
innerRadius = width * 0.015, |
||||
pieTextSize = 40, |
||||
pieTextXOffset = 0, |
||||
pieTextYOffset = 10, |
||||
getPieDisplayValueFromDatum = (datum: PieChartData) => `${datum.value}%`, |
||||
labelTextSize = 40, |
||||
labelTextXOffset = 0, |
||||
labelTextYOffset = 0, |
||||
getLabelDisplayValueFromDatum = (datum: PieChartData) => `${datum.category}`, |
||||
className, |
||||
}: PieChartProps) { |
||||
const pieWidth = width * 0.5 - labelWidth; |
||||
return ( |
||||
<svg className={className} width={width} height={width}> |
||||
<Group top={width * 0.5} left={width * 0.5}> |
||||
<Pie |
||||
data={data} |
||||
pieValue={(d: PieChartData) => d.value} |
||||
cornerRadius={10} |
||||
padAngle={0.075} |
||||
padRadius={padRadius} |
||||
innerRadius={innerRadius} |
||||
outerRadius={pieWidth} |
||||
> |
||||
{(pie) => ( |
||||
<PieSlice |
||||
{...pie} |
||||
pieTextSize={pieTextSize} |
||||
pieTextXOffset={pieTextXOffset} |
||||
pieTextYOffset={pieTextYOffset} |
||||
getPieDisplayValueFromDatum={getPieDisplayValueFromDatum} |
||||
/> |
||||
)} |
||||
</Pie> |
||||
<Pie |
||||
data={data} |
||||
pieValue={(d: PieChartData) => d.value} |
||||
innerRadius={pieWidth} |
||||
outerRadius={width * 0.5} |
||||
> |
||||
{(pie) => ( |
||||
<PieSliceLabel |
||||
{...pie} |
||||
labelTextSize={labelTextSize} |
||||
labelTextXOffset={labelTextXOffset} |
||||
labelTextYOffset={labelTextYOffset} |
||||
getLabelDisplayValueFromDatum={getLabelDisplayValueFromDatum} |
||||
/> |
||||
)} |
||||
</Pie> |
||||
</Group> |
||||
</svg> |
||||
); |
||||
} |
||||
|
||||
type PieSliceProps<PieChartData> = ProvidedProps<PieChartData> & { |
||||
pieTextSize: number; |
||||
pieTextXOffset: number; |
||||
pieTextYOffset: number; |
||||
getPieDisplayValueFromDatum: (datum: PieChartData) => string; |
||||
}; |
||||
|
||||
export function PieSlice({ |
||||
path, |
||||
arcs, |
||||
pieTextSize, |
||||
pieTextXOffset, |
||||
pieTextYOffset, |
||||
getPieDisplayValueFromDatum, |
||||
}: PieSliceProps<PieChartData>) { |
||||
return ( |
||||
<> |
||||
{arcs.map((arc) => { |
||||
const [centroidX, centroidY] = path.centroid(arc); |
||||
const pathArc = path(arc) as string; |
||||
|
||||
return ( |
||||
<Group className={styles.group} key={`arc-${arc.data.category}`}> |
||||
<path className={styles.piePath} d={pathArc} /> |
||||
<Text |
||||
className={styles.pieText} |
||||
x={centroidX + pieTextXOffset} |
||||
y={centroidY + pieTextYOffset} |
||||
textAnchor="middle" |
||||
fontSize={pieTextSize} |
||||
> |
||||
{`${getPieDisplayValueFromDatum(arc.data)}`} |
||||
</Text> |
||||
</Group> |
||||
); |
||||
})} |
||||
</> |
||||
); |
||||
} |
||||
|
||||
type PieSliceLabelProps<PieChartData> = ProvidedProps<PieChartData> & { |
||||
labelTextSize: number; |
||||
labelTextXOffset: number; |
||||
labelTextYOffset: number; |
||||
getLabelDisplayValueFromDatum: (datum: PieChartData) => string; |
||||
}; |
||||
|
||||
export function PieSliceLabel({ |
||||
path, |
||||
arcs, |
||||
labelTextSize, |
||||
labelTextXOffset, |
||||
labelTextYOffset, |
||||
getLabelDisplayValueFromDatum, |
||||
}: PieSliceLabelProps<PieChartData>) { |
||||
return ( |
||||
<> |
||||
{arcs.map((arc) => { |
||||
const [centroidX, centroidY] = path.centroid(arc); |
||||
const pathArc = path(arc) as string; |
||||
|
||||
return ( |
||||
<Group className={styles.group} key={`arc-${arc.data.category}`}> |
||||
<path className={styles.labelPath} d={pathArc} /> |
||||
<Text |
||||
className={styles.labelText} |
||||
x={centroidX + labelTextXOffset} |
||||
y={centroidY + labelTextYOffset} |
||||
textAnchor="middle" |
||||
fontSize={labelTextSize} |
||||
> |
||||
{`${getLabelDisplayValueFromDatum(arc.data)}`} |
||||
</Text> |
||||
</Group> |
||||
); |
||||
})} |
||||
</> |
||||
); |
||||
} |
Loading…
Reference in new issue