PieChart
Data Visualization
visx 기반 파이/도넛 차트.
Usage
import
API Reference
예제
Usage
적은 수의 범주가 전체에서 차지하는 비율을 파이 또는 도넛으로 비교할 때 사용한다.
import
import
import { PieChart } from '@mildang/design-system/Chart';예제를 복사해 쓸 때 필요한 준비
• @mildang/styled-system 은 이 저장소에서 Panda 가 생성하는 산출물이다. 저장소 안에서는 turbo run ship 이후 쓸 수 있고, 패키지 소비자는 자기 Panda 산출물이나 다른 레이아웃 수단으로 바꿔야 한다.
API Reference
PieChart Props
Prop
Type
Default
data
필수
TDatum[]
지정 안 함
valueKey
필수
string
지정 안 함
children
ReactNode
지정 안 함
className
string
지정 안 함
colors
string[]
지정 안 함
cornerRadius
number
지정 안 함
innerRadius
number | "donut"
지정 안 함
labelKey
string
지정 안 함
legendPosition
"bottom" | "left" | "right" | "top"
지정 안 함
margin
Partial<ChartMargin>
지정 안 함
padAngle
number
지정 안 함
showLabels
boolean
지정 안 함
showLegend
boolean
지정 안 함
showTooltip
boolean
지정 안 함
strokeColor
string
지정 안 함
strokeColors
string[]
지정 안 함
textColors
string[]
지정 안 함
예제
파이 vs 도넛
innerRadius="donut" 을 주면 반지름의 절반이 자동으로 비워져 가운데가 뚫린 도넛 차트가 된다.
코드
'use client';
import { PieChart } from '@mildang/design-system/Chart';
import { Text } from '@mildang/design-system/Text';
import { HStack, VStack } from '@mildang/styled-system/jsx';
import { css } from '@mildang/styled-system/css';
/**
* `innerRadius`를 주면 가운데가 뚫린 도넛 차트가 된다. `'donut'` 은 반지름의 50%를 자동으로
* 비워, 숫자를 직접 계산하지 않아도 된다.
*/
const data = [
{ category: '국어', value: 32 },
{ category: '수학', value: 28 },
{ category: '영어', value: 24 },
{ category: '탐구', value: 16 },
];
const frame = css({ width: '100%', height: '[220px]' });
export default function PieChartDonutExample() {
return (
<HStack gap="6" alignItems="flex-start" flexWrap="wrap">
<VStack gap="2" alignItems="stretch" flex="1" minWidth="[220px]">
<Text variant="caption-md" color="neutral.text.low">
기본 파이
</Text>
<div className={frame}>
<PieChart data={data} valueKey="value" labelKey="category" showLabels />
</div>
</VStack>
<VStack gap="2" alignItems="stretch" flex="1" minWidth="[220px]">
<Text variant="caption-md" color="neutral.text.low">
innerRadius="donut"
</Text>
<div className={frame}>
<PieChart data={data} valueKey="value" labelKey="category" innerRadius="donut" showLabels />
</div>
</VStack>
</HStack>
);
}
파이 차트
import { PieChart } from '@mildang/design-system/Chart';
const subjectData = [
{ subject: '수학', score: 35 },
{ subject: '영어', score: 25 },
{ subject: '국어', score: 20 },
{ subject: '과학', score: 12 },
{ subject: '사회', score: 8 },
];
const PieChartPieExample = () => <PieChart data={subjectData} valueKey="score" labelKey="subject" showLabels />;
export default PieChartPieExample;
도넛 차트
import { PieChart } from '@mildang/design-system/Chart';
const subjectData = [
{ subject: '수학', score: 35 },
{ subject: '영어', score: 25 },
{ subject: '국어', score: 20 },
{ subject: '과학', score: 12 },
{ subject: '사회', score: 8 },
];
const PieChartDonutExample = () => (
<PieChart
data={subjectData}
valueKey="score"
labelKey="subject"
innerRadius="donut"
showLabels
showLegend
/>
);
export default PieChartDonutExample;
border 없음 (파이)
import { PieChart } from '@mildang/design-system/Chart';
const subjectData = [
{ subject: '수학', score: 35 },
{ subject: '영어', score: 25 },
{ subject: '국어', score: 20 },
{ subject: '과학', score: 12 },
{ subject: '사회', score: 8 },
];
const noStroke = subjectData.map(() => 'transparent');
const PieChartNoBorderPieExample = () => (
<PieChart data={subjectData} valueKey="score" labelKey="subject" showLabels strokeColors={noStroke} />
);
export default PieChartNoBorderPieExample;
border 없음 (도넛)
import { PieChart } from '@mildang/design-system/Chart';
const subjectData = [
{ subject: '수학', score: 35 },
{ subject: '영어', score: 25 },
{ subject: '국어', score: 20 },
{ subject: '과학', score: 12 },
{ subject: '사회', score: 8 },
];
const noStroke = subjectData.map(() => 'transparent');
const PieChartNoBorderDonutExample = () => (
<PieChart
data={subjectData}
valueKey="score"
labelKey="subject"
innerRadius="donut"
showLabels
showLegend
strokeColors={noStroke}
/>
);
export default PieChartNoBorderDonutExample;
간격으로 구분 (border 없음)
import { PieChart } from '@mildang/design-system/Chart';
const subjectData = [
{ subject: '수학', score: 35 },
{ subject: '영어', score: 25 },
{ subject: '국어', score: 20 },
{ subject: '과학', score: 12 },
{ subject: '사회', score: 8 },
];
const noStroke = subjectData.map(() => 'transparent');
const PieChartWithGapExample = () => (
<PieChart
data={subjectData}
valueKey="score"
labelKey="subject"
padAngle={0.04}
cornerRadius={4}
showLabels
strokeColors={noStroke}
/>
);
export default PieChartWithGapExample;
Hover Tooltip
import { PieChart } from '@mildang/design-system/Chart';
const subjectData = [
{ subject: '수학', score: 35 },
{ subject: '영어', score: 25 },
{ subject: '국어', score: 20 },
{ subject: '과학', score: 12 },
{ subject: '사회', score: 8 },
];
const PieChartHoverTooltipExample = () => (
<PieChart data={subjectData} valueKey="score" labelKey="subject" innerRadius="donut" showTooltip />
);
export default PieChartHoverTooltipExample;
범례 포함
import { PieChart } from '@mildang/design-system/Chart';
const subjectData = [
{ subject: '수학', score: 35 },
{ subject: '영어', score: 25 },
{ subject: '국어', score: 20 },
{ subject: '과학', score: 12 },
{ subject: '사회', score: 8 },
];
const PieChartWithLegendExample = () => (
<PieChart data={subjectData} valueKey="score" labelKey="subject" innerRadius="donut" showLegend />
);
export default PieChartWithLegendExample;
범례 오른쪽
import { PieChart } from '@mildang/design-system/Chart';
const subjectData = [
{ subject: '수학', score: 35 },
{ subject: '영어', score: 25 },
{ subject: '국어', score: 20 },
{ subject: '과학', score: 12 },
{ subject: '사회', score: 8 },
];
const PieChartWithLegendRightExample = () => (
<PieChart
data={subjectData}
valueKey="score"
labelKey="subject"
innerRadius="donut"
showLegend
legendPosition="right"
/>
);
export default PieChartWithLegendRightExample;
도넛 두께 커스텀
import { PieChart } from '@mildang/design-system/Chart';
const subjectData = [
{ subject: '수학', score: 35 },
{ subject: '영어', score: 25 },
{ subject: '국어', score: 20 },
{ subject: '과학', score: 12 },
{ subject: '사회', score: 8 },
];
const PieChartDonutCustomThicknessExample = () => (
<PieChart
data={subjectData}
valueKey="score"
labelKey="subject"
innerRadius={120}
cornerRadius={8}
padAngle={0.04}
/>
);
export default PieChartDonutCustomThicknessExample;
반응형 (너비 자동 조정)
import { PieChart } from '@mildang/design-system/Chart';
const subjectData = [
{ subject: '수학', score: 35 },
{ subject: '영어', score: 25 },
{ subject: '국어', score: 20 },
{ subject: '과학', score: 12 },
{ subject: '사회', score: 8 },
];
const PieChartResponsiveExample = () => (
<div style={{ display: 'flex', gap: 24, alignItems: 'flex-start', flexWrap: 'wrap', width: '100%' }}>
{([160, 280, 400] as const).map((size) => (
<div key={size}>
<div style={{ marginBottom: 4, fontSize: 11, color: '#888' }}>{size}px</div>
<div style={{ width: size, height: size }}>
<PieChart data={subjectData} valueKey="score" labelKey="subject" innerRadius="donut" showLegend />
</div>
</div>
))}
</div>
);
export default PieChartResponsiveExample;
Compound 패턴 (라벨 없이)
import { PieChart } from '@mildang/design-system/Chart';
const subjectData = [
{ subject: '수학', score: 35 },
{ subject: '영어', score: 25 },
{ subject: '국어', score: 20 },
{ subject: '과학', score: 12 },
{ subject: '사회', score: 8 },
];
const PieChartCompoundUsageExample = () => (
<PieChart data={subjectData} valueKey="score" innerRadius="donut">
<PieChart.Arcs />
</PieChart>
);
export default PieChartCompoundUsageExample;
반응형 범례 (Compound — md 이상 오른쪽)
import { getChartColor, PieChart } from '@mildang/design-system/Chart';
import { css } from '@mildang/styled-system/css';
import { token } from '@mildang/styled-system/tokens';
import { ChartLegend } from '@mildang/design-system/Chart';
const TREMOR_9: string[] = ['#3b82f6', '#10b981', '#8b5cf6', '#f59e0b', '#6b7280', '#06b6d4', '#ec4899', '#84cc16', '#d946ef'];
const CHART_STROKE_PRESET: string[] = ['#2563eb', '#059669', '#7c3aed', '#d97706', '#4b5563', '#0891b2', '#db2777', '#65a30d', '#c026d3'];
const CHART_COLOR_PRESET: string[] = TREMOR_9;
const SEMANTIC_CHART_PAINT = [
{ fill: token('colors.positive.surface.high'), stroke: token('colors.positive.border.highest') },
{ fill: token('colors.critical.surface.high'), stroke: token('colors.critical.border.highest') },
{ fill: token('colors.info.surface.high'), stroke: token('colors.info.border.highest') },
{ fill: token('colors.warning.surface.high'), stroke: token('colors.warning.border.highest') },
{ fill: token('colors.brand.surface.highest'), stroke: token('colors.brand.border.highest') },
];
function getChartStroke(fill: string): string {
const index = CHART_COLOR_PRESET.indexOf(fill);
if (index >= 0) return CHART_STROKE_PRESET[index];
return SEMANTIC_CHART_PAINT.find((paint) => paint.fill === fill)?.stroke ?? fill;
}
const subjectData = [
{ subject: '수학', score: 35 },
{ subject: '영어', score: 25 },
{ subject: '국어', score: 20 },
{ subject: '과학', score: 12 },
{ subject: '사회', score: 8 },
];
const PieChartResponsiveLegendExample = () => {
const responsiveWrapperClass = css({
display: 'flex',
flexDirection: { base: 'column', md: 'row' },
width: 'full',
height: 'full',
gap: '22',
});
const responsiveChartAreaClass = css({
w: 'full',
h: { base: '[240px]', md: 'full' },
flexGrow: { base: 0, md: 1 },
flexShrink: { base: 0, md: 1 },
flexBasis: { base: '[240px]', md: '[0px]' },
minW: 0,
minH: 0,
});
const total = subjectData.reduce((s, d) => s + d.score, 0);
const legendItems = subjectData.map((d, i) => ({
key: String(i),
label: d.subject,
value: `${((d.score / total) * 100).toFixed(1)}%`,
color: getChartColor(i),
strokeColor: getChartStroke(getChartColor(i)),
}));
return (
<div className={responsiveWrapperClass}>
<div className={responsiveChartAreaClass}>
<PieChart data={subjectData} valueKey="score" labelKey="subject" innerRadius="donut">
<PieChart.Arcs />
</PieChart>
</div>
<ChartLegend items={legendItems} direction="column" />
</div>
);
};
export default PieChartResponsiveLegendExample;