cs-2022-class-profile/components/PieChart.tsx

181 lines
5.2 KiB
TypeScript
Raw Normal View History

import { Group } from "@visx/group";
import Pie, { ProvidedProps } from "@visx/shape/lib/shapes/Pie";
2022-08-09 18:00:22 -04:00
import { Text } from "@visx/text";
2022-06-18 22:50:08 -04:00
import React from "react";
import styles from "./PieChart.module.css";
interface PieChartProps {
data: PieChartData[];
2022-08-09 18:00:22 -04:00
/** Width of the entire graph, including labels, in pixels. */
width: number;
2022-09-07 20:49:11 -04:00
/** Width of the outer ring of labels, in pixels. Label text may be cut off if specified value is too small. */
2022-06-18 22:55:05 -04:00
labelWidth: number;
2022-09-07 20:49:11 -04:00
/** Distance between pie slices, in pixels. */
2022-07-06 19:44:48 -04:00
padRadius?: number;
2022-09-07 20:49:11 -04:00
/** Distance of gap in center of pie graph, in pixels. */
2022-07-06 19:44:48 -04:00
innerRadius?: number;
2022-09-07 20:49:11 -04:00
/** Font size of text inside the pie, in pixels. */
2022-08-09 18:00:22 -04:00
pieTextSize?: number;
2022-09-07 20:49:11 -04:00
/** X-axis offset of the pie text, in pixels. */
2022-08-09 18:00:22 -04:00
pieTextXOffset?: number;
2022-09-07 20:49:11 -04:00
/** Y-axis offset of the pie text, in pixels. */
2022-08-09 18:00:22 -04:00
pieTextYOffset?: number;
2022-09-07 20:49:11 -04:00
/** Accessor function to get value to display as pie text from datum. */
getPieDisplayValueFromDatum?: (datum: PieChartData) => string;
2022-09-07 20:49:11 -04:00
/** Font size of labels outside the pie, in pixels. */
labelTextSize?: number;
2022-09-07 20:49:11 -04:00
/** X-axis offset of the label text, in pixels. */
labelTextXOffset?: number;
2022-09-07 20:49:11 -04:00
/** Y-axis offset of the label text, in pixels. */
labelTextYOffset?: number;
2022-09-07 20:49:11 -04:00
/** Accessor function to get value to display as label text from datum. */
getLabelDisplayValueFromDatum?: (datum: PieChartData) => string;
className?: string;
}
interface PieChartData {
category: string;
value: number;
}
2022-07-06 19:44:48 -04:00
export function PieChart({
2022-08-09 18:00:22 -04:00
data,
2022-07-06 19:44:48 -04:00
width,
labelWidth,
padRadius = width * 0.35,
innerRadius = width * 0.015,
2022-08-09 18:00:22 -04:00
pieTextSize = 40,
pieTextXOffset = 0,
pieTextYOffset = 10,
getPieDisplayValueFromDatum = (datum: PieChartData) => `${datum.value}%`,
labelTextSize = 40,
labelTextXOffset = 0,
labelTextYOffset = 0,
getLabelDisplayValueFromDatum = (datum: PieChartData) => `${datum.category}`,
2022-08-09 18:00:22 -04:00
className,
2022-07-06 19:44:48 -04:00
}: PieChartProps) {
2022-06-18 22:55:05 -04:00
const pieWidth = width * 0.5 - labelWidth;
return (
2022-08-09 18:00:22 -04:00
<svg className={className} width={width} height={width}>
2022-06-18 22:35:39 -04:00
<Group top={width * 0.5} left={width * 0.5}>
<Pie
2022-08-09 18:00:22 -04:00
data={data}
pieValue={(d: PieChartData) => d.value}
2022-06-18 22:35:39 -04:00
cornerRadius={10}
padAngle={0.075}
2022-07-06 19:44:48 -04:00
padRadius={padRadius}
innerRadius={innerRadius}
2022-06-18 22:35:39 -04:00
outerRadius={pieWidth}
>
2022-08-09 18:00:22 -04:00
{(pie) => (
<PieSlice
{...pie}
pieTextSize={pieTextSize}
pieTextXOffset={pieTextXOffset}
pieTextYOffset={pieTextYOffset}
getPieDisplayValueFromDatum={getPieDisplayValueFromDatum}
2022-08-09 18:00:22 -04:00
/>
)}
2022-06-18 22:35:39 -04:00
</Pie>
<Pie
2022-08-09 18:00:22 -04:00
data={data}
2022-06-18 22:35:39 -04:00
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>
);
}
2022-08-09 18:00:22 -04:00
type PieSliceProps<PieChartData> = ProvidedProps<PieChartData> & {
pieTextSize: number;
pieTextXOffset: number;
pieTextYOffset: number;
getPieDisplayValueFromDatum: (datum: PieChartData) => string;
2022-06-18 22:35:39 -04:00
};
2022-08-09 18:00:22 -04:00
export function PieSlice({
path,
arcs,
pieTextSize,
pieTextXOffset,
pieTextYOffset,
getPieDisplayValueFromDatum,
2022-08-09 18:00:22 -04:00
}: PieSliceProps<PieChartData>) {
2022-06-18 22:35:39 -04:00
return (
<>
2022-08-09 18:00:22 -04:00
{arcs.map((arc) => {
const [centroidX, centroidY] = path.centroid(arc);
const pathArc = path(arc) as string;
2022-06-18 22:35:39 -04:00
return (
2022-08-09 18:00:22 -04:00
<Group className={styles.group} key={`arc-${arc.data.category}`}>
2022-08-31 19:50:22 -04:00
<path className={styles.piePath} d={pathArc} />
<Text
className={styles.pieText}
x={centroidX + pieTextXOffset}
y={centroidY + pieTextYOffset}
textAnchor="middle"
fontSize={pieTextSize}
>
{`${getPieDisplayValueFromDatum(arc.data)}`}
2022-08-31 19:50:22 -04:00
</Text>
</Group>
);
})}
</>
);
}
type PieSliceLabelProps<PieChartData> = ProvidedProps<PieChartData> & {
labelTextSize: number;
labelTextXOffset: number;
labelTextYOffset: number;
getLabelDisplayValueFromDatum: (datum: PieChartData) => string;
2022-08-31 19:50:22 -04:00
};
export function PieSliceLabel({
path,
arcs,
labelTextSize,
labelTextXOffset,
labelTextYOffset,
getLabelDisplayValueFromDatum,
2022-08-31 19:50:22 -04:00
}: 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} />
2022-08-09 18:00:22 -04:00
<Text
2022-08-31 19:50:22 -04:00
className={styles.labelText}
x={centroidX + labelTextXOffset}
y={centroidY + labelTextYOffset}
2022-06-18 22:35:39 -04:00
textAnchor="middle"
2022-08-31 19:50:22 -04:00
fontSize={labelTextSize}
2022-06-18 22:35:39 -04:00
>
{`${getLabelDisplayValueFromDatum(arc.data)}`}
2022-08-09 18:00:22 -04:00
</Text>
</Group>
2022-06-18 22:35:39 -04:00
);
})}
</>
);
}