feat(chart): add @hm-design/chart package with Bar, Line, Donut, Pie, Radar, Area and Radial charts

- new workspace package packages/chart (shadcn/ui Chart + Recharts, HM Theme tokens), examples entry with sample data
- Storybook Chart group (src/chart), consumer example (examples/chart), build:chart / build:chart-example scripts
- README rewritten as the AI usage guide (install, CSS wiring, chart selection, data rules, verification)
- docs: package count/list, Submodule guide (chart workspace, React dedupe), shared AI_GUIDE row, MAINTAINING, story inventory
This commit is contained in:
shkim4 committed 2026-11-06 15:52:47 +09:00
1 parent 228944a06b
commit dc9258599c
56 files changed
+3979 -28

No files matched your search

@@ -0,0 +1,205 @@
import { useState, type ReactNode } from "react"
import { cn } from "cn"
import {
Badge,
Card,
CardAction,
CardContent,
CardDescription,
CardHeader,
CardTitle,
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@hm-design/ui"
import { HMBarChart, HMDonutChart, HMHorizontalBarChart, HMLineChart, HMSignedBarChart } from "../index"
import { sampleBar, sampleDataNotice, sampleDonut, sampleDonutStates, sampleHorizontalBar, sampleLine, sampleLineCompare, sampleLinePointColors, sampleSignedBar } from "./sample-data"
/** Marks a chart as sample data. Use it next to every chart that is fed with `sample-data`. */
export function SampleDataBadge() {
return <Badge variant="outline" data-slot="chart-sample-badge" title={sampleDataNotice}>예시 데이터</Badge>
}
export function ExampleCard({ title, description, className, toolbar, children }: { title: string; description: string; className?: string; toolbar?: ReactNode; children: ReactNode }) {
return (
// h-full lets a card fill its grid cell; the notice sits at the card bottom so cards of one row line up.
<Card className={cn("h-full", className)} data-chart-example={title}>
<CardHeader>
<CardTitle className="break-keep">{title}</CardTitle>
<CardDescription className="break-keep">{description}</CardDescription>
<CardAction><SampleDataBadge /></CardAction>
</CardHeader>
<CardContent className="flex flex-1 flex-col">
{toolbar !== undefined ? <div data-slot="chart-example-toolbar" className="mb-3 flex min-h-[34px] items-center text-sm text-muted-foreground">{toolbar}</div> : null}
{children}
<p data-slot="chart-sample-notice" className="mt-auto pt-3">{sampleDataNotice}</p>
</CardContent>
</Card>
)
}
export function BarChartExample({ className }: { className?: string }) {
return (
<ExampleCard title="월별 접수·완료 현황" description="막대 차트 · 상반기 월별 건수" className={className}>
<HMBarChart {...sampleBar} label="월별 접수·완료 건수 막대 차트(예시 데이터)" />
</ExampleCard>
)
}
export function StackedBarChartExample({ className }: { className?: string }) {
return (
<ExampleCard title="월별 접수·완료 현황(누적)" description="막대 차트 · stacked" className={className}>
<HMBarChart {...sampleBar} stacked label="월별 접수·완료 건수 누적 막대 차트(예시 데이터)" />
</ExampleCard>
)
}
export function HorizontalBarChartExample({ className }: { className?: string }) {
return (
<ExampleCard title="항목별 처리 건수" description="가로 막대 차트 · 항목별 건수" className={className}>
<HMHorizontalBarChart {...sampleHorizontalBar} label="항목별 처리 건수 가로 막대 차트(예시 데이터)" />
</ExampleCard>
)
}
export function SignedBarChartExample({ className }: { className?: string }) {
return (
<ExampleCard title="월별 순증감" description="막대 차트 · 0 기준 양수·음수" className={className}>
<HMSignedBarChart {...sampleSignedBar} label="월별 순증감 양수·음수 막대 차트(예시 데이터)" />
</ExampleCard>
)
}
export function LineChartExample({ className }: { className?: string }) {
return (
<ExampleCard title="월별 처리 시간 추이" description="선 차트 · 단위: 시간" className={className}>
<HMLineChart {...sampleLine} label="월별 평균 처리 시간 선 차트(예시 데이터)" />
</ExampleCard>
)
}
export function LineCurveExample({ className }: { className?: string }) {
return (
<ExampleCard title="곡선형" description="monotone · Dot 없음" className={className}>
<HMLineChart {...sampleLineCompare} curve="monotone" dots={false} label="월별 접수·완료 곡선형 선 차트(예시 데이터)" />
</ExampleCard>
)
}
export function LineCurveDotLabelExample({ className }: { className?: string }) {
return (
<ExampleCard title="곡선형 · Dot + Label" description="monotone · Dot과 값 Label" className={className}>
<HMLineChart {...sampleLineCompare} curve="monotone" pointLabels label="월별 접수·완료 곡선형 Dot·값 라벨 선 차트(예시 데이터)" />
</ExampleCard>
)
}
export function LineStraightExample({ className }: { className?: string }) {
return (
<ExampleCard title="직선형" description="linear · Dot 없음" className={className}>
<HMLineChart {...sampleLineCompare} curve="linear" dots={false} label="월별 접수·완료 직선형 선 차트(예시 데이터)" />
</ExampleCard>
)
}
export function LineStraightColorLabelExample({ className }: { className?: string }) {
return (
<ExampleCard title="직선형 · Color Dot" description="linear · Dot 개별 색상 + 값 Label" className={className}>
<HMLineChart {...sampleLinePointColors} curve="linear" pointLabels label="월별 접수·완료 직선형 포인트별 색상 선 차트(예시 데이터)" />
</ExampleCard>
)
}
export function LineStepExample({ className }: { className?: string }) {
return (
<ExampleCard title="계단형" description="step · Dot 없음" className={className}>
<HMLineChart {...sampleLineCompare} curve="step" dots={false} label="월별 접수·완료 계단형 선 차트(예시 데이터)" />
</ExampleCard>
)
}
export function LineStepDotLabelExample({ className }: { className?: string }) {
return (
<ExampleCard title="계단형 · Dot + Label" description="step · Dot과 값 Label" className={className}>
<HMLineChart {...sampleLineCompare} curve="step" pointLabels label="월별 접수·완료 계단형 Dot·값 라벨 선 차트(예시 데이터)" />
</ExampleCard>
)
}
export function DonutChartExample({ className }: { className?: string }) {
return (
<ExampleCard title="업무 상태 비중" description="도넛 차트 · 상태별 건수" className={className}>
<HMDonutChart {...sampleDonut} label="업무 상태별 비중 도넛 차트(예시 데이터)" />
</ExampleCard>
)
}
/** Bar, line and donut examples: two per row from 1024px (HM desktop boundary), one per row below. With an odd count the last card keeps one column's width. */
export function ChartExamples() {
return (
<div className="grid grid-cols-1 gap-6 lg:grid-cols-2" data-chart-examples="">
<BarChartExample className="min-w-0" />
<LineChartExample className="min-w-0" />
<DonutChartExample className="min-w-0" />
</div>
)
}
// Four donut states on one data set: plain, center total, active (fixed selection) and interactive (Select + slice click).
// The toolbar row has the Select height in every card, so the donuts of a row start at the same height.
export function DonutPlainExample({ className }: { className?: string }) {
return (
<ExampleCard title="기본형" description="도넛 차트 · 기본" className={className} toolbar="중앙 표시 없음">
<HMDonutChart data={sampleDonutStates.data} label="업무 상태 도넛 차트, 가운데 값 없음(예시 데이터)" />
</ExampleCard>
)
}
export function DonutCenterExample({ className }: { className?: string }) {
return (
<ExampleCard title="기본형 + 텍스트" description="도넛 차트 · 전체 합계" className={className} toolbar="중앙 표시: 전체 합계">
<HMDonutChart data={sampleDonutStates.data} totalLabel={sampleDonutStates.totalLabel} label="업무 상태 도넛 차트, 가운데 전체 합계(예시 데이터)" />
</ExampleCard>
)
}
export function DonutActiveExample({ className }: { className?: string }) {
const selected = sampleDonutStates.data.find(item => item.id === sampleDonutStates.activeId)
return (
<ExampleCard title="Active 상태" description="도넛 차트 · 선택된 항목" className={className} toolbar={`선택 항목: ${selected?.label ?? ""}`}>
<HMDonutChart data={sampleDonutStates.data} activeId={sampleDonutStates.activeId} label="업무 상태 도넛 차트, 선택된 항목 강조(예시 데이터)" />
</ExampleCard>
)
}
export function DonutInteractiveExample({ className }: { className?: string }) {
const [activeId, setActiveId] = useState(sampleDonutStates.activeId)
return (
<ExampleCard
title="Interactive"
description="도넛 차트 · 선택 또는 클릭"
className={className}
toolbar={
<Select value={activeId} onValueChange={setActiveId}>
<SelectTrigger size="medium" aria-label="강조할 업무 상태 선택" className="w-full max-w-64">
<SelectValue />
</SelectTrigger>
<SelectContent>
{sampleDonutStates.data.map(item => (
<SelectItem key={item.id} value={item.id}>{item.label}</SelectItem>
))}
</SelectContent>
</Select>
}
>
<HMDonutChart
data={sampleDonutStates.data}
activeId={activeId}
onActiveChange={setActiveId}
label="업무 상태 도넛 차트, 항목을 선택하거나 조각을 클릭해 강조(예시 데이터)"
/>
</ExampleCard>
)
}
+31
View File
@@ -0,0 +1,31 @@
// Sample data and ready-made example cards. Everything here is illustrative, not business data.
export { sampleBar, sampleLine, sampleDonut, sampleDonutStates, sampleHorizontalBar, sampleSignedBar, sampleLineCompare, sampleLinePointColors, sampleDataNotice } from "./sample-data"
export { BarChartExample, StackedBarChartExample, HorizontalBarChartExample, SignedBarChartExample, LineChartExample, LineCurveExample, LineCurveDotLabelExample, LineStraightExample, LineStraightColorLabelExample, LineStepExample, LineStepDotLabelExample, DonutChartExample, DonutPlainExample, DonutCenterExample, DonutActiveExample, DonutInteractiveExample, ChartExamples, SampleDataBadge } from "./chart-examples"
export { samplePie, sampleStackedPie, sampleRadar } from "./sample-data"
export {
PiePlainExample,
PieLabelExample,
PieLabelListExample,
PieStackedExample,
RadarPlainExample,
RadarDotExample,
RadarLinesOnlyExample,
RadarCustomLabelExample,
AreaCurveExample,
AreaCurveDotLabelExample,
AreaStraightExample,
AreaStraightColorLabelExample,
AreaStepExample,
AreaStepDotLabelExample,
AreaStackedExample,
AreaExpandedExample,
} from "./pie-radar-area-examples"
export { sampleRadial, sampleRadialText, sampleRadialShape, sampleRadialStacked } from "./sample-data"
export {
RadialSimpleExample,
RadialLabelExample,
RadialGridExample,
RadialTextExample,
RadialShapeExample,
RadialStackedExample,
} from "./radial-examples"
@@ -0,0 +1,121 @@
import { HMAreaChart, HMPieChart, HMRadarChart, HMStackedPieChart } from "../index"
import { ExampleCard } from "./chart-examples"
import { sampleLineCompare, sampleLinePointColors, samplePie, sampleRadar, sampleStackedPie } from "./sample-data"
// Pie, Radar and Area examples on sample data. Titles follow the Line and Donut examples.
type Props = { className?: string }
export function PiePlainExample({ className }: Props) {
return (
<ExampleCard title="기본 Pie" description="파이 차트 · 기본" className={className}>
<HMPieChart {...samplePie} label="업무 상태 파이 차트(예시 데이터)" />
</ExampleCard>
)
}
export function PieLabelExample({ className }: Props) {
return (
<ExampleCard title="Pie + Label" description="파이 차트 · 이름과 값" className={className}>
<HMPieChart {...samplePie} labels="outside" label="업무 상태 파이 차트, 조각 바깥 이름·값 Label(예시 데이터)" />
</ExampleCard>
)
}
export function PieLabelListExample({ className }: Props) {
return (
<ExampleCard title="Pie + Label List" description="파이 차트 · 조각 안 값" className={className}>
<HMPieChart {...samplePie} labels="inside" label="업무 상태 파이 차트, 조각 안 값 Label(예시 데이터)" />
</ExampleCard>
)
}
export function PieStackedExample({ className }: Props) {
return (
<ExampleCard title="Pie + Stacked" description="파이 차트 · 두 시리즈 링" className={className}>
<HMStackedPieChart {...sampleStackedPie} label="분기별 업무 상태 Stacked 파이 차트(예시 데이터)" />
</ExampleCard>
)
}
export function RadarPlainExample({ className }: Props) {
return (
<ExampleCard title="Radar" description="방사형 차트 · 영역" className={className}>
<HMRadarChart {...sampleRadar} label="단계별 처리 수준 방사형 차트(예시 데이터)" />
</ExampleCard>
)
}
export function RadarDotExample({ className }: Props) {
return (
<ExampleCard title="Radar + Dot" description="방사형 차트 · 축별 Dot" className={className}>
<HMRadarChart {...sampleRadar} dots label="단계별 처리 수준 방사형 차트, Dot(예시 데이터)" />
</ExampleCard>
)
}
export function RadarLinesOnlyExample({ className }: Props) {
return (
<ExampleCard title="Radar + Lines Only" description="방사형 차트 · 선만" className={className}>
<HMRadarChart {...sampleRadar} filled={false} label="단계별 처리 수준 방사형 차트, 선만(예시 데이터)" />
</ExampleCard>
)
}
export function RadarCustomLabelExample({ className }: Props) {
return (
<ExampleCard title="Radar + Custom Label" description="방사형 차트 · 축 Label" className={className}>
<HMRadarChart {...sampleRadar} customLabels label="단계별 처리 수준 방사형 차트, 축 Label(예시 데이터)" />
</ExampleCard>
)
}
export function AreaCurveExample({ className }: Props) {
return (
<ExampleCard title="곡선형" description="영역 차트 · monotone" className={className}>
<HMAreaChart {...sampleLineCompare} curve="monotone" label="월별 접수·완료 곡선형 영역 차트(예시 데이터)" />
</ExampleCard>
)
}
export function AreaCurveDotLabelExample({ className }: Props) {
return (
<ExampleCard title="곡선형 · Dot + Label" description="monotone · Dot과 값 Label" className={className}>
<HMAreaChart {...sampleLineCompare} curve="monotone" pointLabels label="월별 접수·완료 곡선형 Dot·값 Label 영역 차트(예시 데이터)" />
</ExampleCard>
)
}
export function AreaStraightExample({ className }: Props) {
return (
<ExampleCard title="직선형" description="영역 차트 · linear" className={className}>
<HMAreaChart {...sampleLineCompare} curve="linear" label="월별 접수·완료 직선형 영역 차트(예시 데이터)" />
</ExampleCard>
)
}
export function AreaStraightColorLabelExample({ className }: Props) {
return (
<ExampleCard title="직선형 · Color Dot" description="linear · Dot 개별 색상 + 값 Label" className={className}>
<HMAreaChart {...sampleLinePointColors} curve="linear" pointLabels label="월별 접수·완료 직선형 포인트별 색상 영역 차트(예시 데이터)" />
</ExampleCard>
)
}
export function AreaStepExample({ className }: Props) {
return (
<ExampleCard title="계단형" description="영역 차트 · step" className={className}>
<HMAreaChart {...sampleLineCompare} curve="step" label="월별 접수·완료 계단형 영역 차트(예시 데이터)" />
</ExampleCard>
)
}
export function AreaStepDotLabelExample({ className }: Props) {
return (
<ExampleCard title="계단형 · Dot + Label" description="step · Dot과 값 Label" className={className}>
<HMAreaChart {...sampleLineCompare} curve="step" pointLabels label="월별 접수·완료 계단형 Dot·값 Label 영역 차트(예시 데이터)" />
</ExampleCard>
)
}
export function AreaStackedExample({ className }: Props) {
return (
<ExampleCard title="Stacked" description="영역 차트 · 값 누적" className={className}>
<HMAreaChart {...sampleLineCompare} stacked label="월별 접수·완료 Stacked 영역 차트(예시 데이터)" />
</ExampleCard>
)
}
export function AreaExpandedExample({ className }: Props) {
return (
<ExampleCard title="Expanded" description="영역 차트 · 합계 100%" className={className}>
<HMAreaChart {...sampleLineCompare} expanded label="월별 접수·완료 Expanded 영역 차트(예시 데이터)" />
</ExampleCard>
)
}
@@ -0,0 +1,49 @@
import { HMRadialBarChart, HMRadialMeterChart, HMRadialStackedChart } from "../index"
import { ExampleCard } from "./chart-examples"
import { sampleRadial, sampleRadialShape, sampleRadialStacked, sampleRadialText } from "./sample-data"
// Radial examples on sample data, one per official shadcn/ui radial example.
type Props = { className?: string }
export function RadialSimpleExample({ className }: Props) {
return (
<ExampleCard title="Radial Chart" description="radial-simple · 항목별 막대" className={className}>
<HMRadialBarChart {...sampleRadial} label="업무 상태 방사형 막대 차트(예시 데이터)" />
</ExampleCard>
)
}
export function RadialLabelExample({ className }: Props) {
return (
<ExampleCard title="Radial Chart + Label" description="radial-label · 막대 안 항목명" className={className}>
<HMRadialBarChart {...sampleRadial} labels startAngle={-90} endAngle={380} label="업무 상태 방사형 막대 차트, 항목명 Label(예시 데이터)" />
</ExampleCard>
)
}
export function RadialGridExample({ className }: Props) {
return (
<ExampleCard title="Radial Chart - Grid" description="radial-grid · 원형 Grid" className={className}>
<HMRadialBarChart {...sampleRadial} grid label="업무 상태 방사형 막대 차트, Grid(예시 데이터)" />
</ExampleCard>
)
}
export function RadialTextExample({ className }: Props) {
return (
<ExampleCard title="Radial Chart + Text" description="radial-text · 중앙 Text" className={className}>
<HMRadialMeterChart {...sampleRadialText} startAngle={0} endAngle={250} innerRadius={75} outerRadius={90} trackRadii={[88.5, 76.5]} cornerRadius={10} label="진행 중 건수 방사형 차트, 중앙 Text(예시 데이터)" />
</ExampleCard>
)
}
export function RadialShapeExample({ className }: Props) {
return (
<ExampleCard title="Radial Chart - Shape" description="radial-shape · 링 트랙" className={className}>
<HMRadialMeterChart {...sampleRadialShape} startAngle={0} endAngle={100} innerRadius={50} outerRadius={95} trackRadii={[90.5, 54.5]} label="진행 중 건수 방사형 차트, Shape(예시 데이터)" />
</ExampleCard>
)
}
export function RadialStackedExample({ className }: Props) {
return (
<ExampleCard title="Radial Chart + Stacked" description="radial-stacked · 반원 누적" className={className}>
<HMRadialStackedChart {...sampleRadialStacked} label="업무 상태 방사형 Stacked 차트(예시 데이터)" />
</ExampleCard>
)
}
+189
View File
@@ -0,0 +1,189 @@
import { chartColors, type ChartSeries, type HMDonutDatum, type HMPieRing } from "../index"
// SAMPLE DATA ONLY. These values are invented to show the charts; they are not business data and must not be reported as such.
// Real data belongs to the consuming app and is passed to HMBarChart / HMLineChart / HMDonutChart as props.
export const sampleDataNotice = "예시 데이터 · 실제 업무 데이터가 아닙니다"
export const sampleBar = {
categoryKey: "month",
series: [
{ key: "received", label: "접수" },
{ key: "completed", label: "완료" },
] satisfies ChartSeries[],
data: [
{ month: "1월", received: 186, completed: 142 },
{ month: "2월", received: 305, completed: 238 },
{ month: "3월", received: 237, completed: 221 },
{ month: "4월", received: 173, completed: 190 },
{ month: "5월", received: 209, completed: 196 },
{ month: "6월", received: 264, completed: 231 },
],
}
export const sampleLine = {
categoryKey: "month",
series: [
{ key: "average", label: "평균 처리 시간" },
{ key: "target", label: "목표" },
] satisfies ChartSeries[],
data: [
{ month: "1월", average: 4.8, target: 4 },
{ month: "2월", average: 4.4, target: 4 },
{ month: "3월", average: 4.1, target: 4 },
{ month: "4월", average: 3.7, target: 4 },
{ month: "5월", average: 3.9, target: 4 },
{ month: "6월", average: 3.4, target: 4 },
],
}
export const sampleDonut = {
totalLabel: "전체(건)",
data: [
{ id: "done", label: "완료", value: 118 },
{ id: "inProgress", label: "진행 중", value: 42 },
{ id: "delayed", label: "지연", value: 12 },
{ id: "onHold", label: "보류", value: 8 },
] satisfies HMDonutDatum[],
}
// Item names are deliberately uneven: one very long name, short bars and a zero value exercise the label rules.
export const sampleHorizontalBar = {
categoryKey: "item",
valueKey: "count",
valueLabel: "처리 건수",
data: [
{ item: "종합 민원 접수 및 처리 현황 보고서 작성", count: 186 },
{ item: "시설 점검", count: 142 },
{ item: "예산 집행 검토", count: 96 },
{ item: "계약 변경 승인 요청 건", count: 38 },
{ item: "보류 항목", count: 3 },
{ item: "반려", count: 0 },
],
}
// Mixed signs with a zero value. A positive-only or negative-only list works the same way.
export const sampleSignedBar = {
categoryKey: "month",
valueKey: "change",
valueLabel: "순증감",
data: [
{ month: "1월", change: 120 },
{ month: "2월", change: -80 },
{ month: "3월", change: 45 },
{ month: "4월", change: -150 },
{ month: "5월", change: 0 },
{ month: "6월", change: 95 },
],
}
// One data set for all line examples (curve, straight, dots, per-point colors, step), so the connection styles compare directly on the same axis range.
// It is built to exercise the label rules: 3월 is a tie (40 = 40), 2월 and 4월 are close, the lines cross between 5월 and 6월, and 완료 drops to 0 in 5월.
const lineCompareRows = [
{ month: "1월", received: 48, completed: 30 },
{ month: "2월", received: 72, completed: 60 },
{ month: "3월", received: 40, completed: 40 },
{ month: "4월", received: 85, completed: 70 },
{ month: "5월", received: 20, completed: 0 },
{ month: "6월", received: 56, completed: 62 },
]
export const sampleLineCompare = {
categoryKey: "month",
series: [
{ key: "received", label: "접수" },
{ key: "completed", label: "완료" },
] satisfies ChartSeries[],
data: lineCompareRows,
}
// Per-point colors are HM palette tokens by value, one family per series: 접수 in greens, 완료 in browns, and the orange token below 30
// (all dark enough for the value label to read on the card).
const receivedColor = (value: number) =>
value >= 60 ? "var(--hm-sem-palette-green-light-1)"
: value >= 30 ? "var(--hm-sem-palette-green-tone-1)"
: "var(--hm-sem-palette-orange-default)"
const completedColor = (value: number) =>
value >= 60 ? "var(--hm-sem-palette-brown-light-1)"
: value >= 30 ? "var(--hm-sem-palette-brown-light-2)"
: "var(--hm-sem-palette-orange-default)"
export const sampleLinePointColors = {
...sampleLineCompare,
pointColorKey: { received: "receivedColor", completed: "completedColor" },
data: lineCompareRows.map(row => ({
...row,
receivedColor: receivedColor(row.received),
completedColor: completedColor(row.completed),
})),
}
// One data set for the four donut states (plain, center total, active, interactive), so they compare directly.
// It includes a long name (shortened in the center, complete in the legend and tooltip) and a zero item.
export const sampleDonutStates = {
totalLabel: "전체(건)",
/** The selected slice of the active and interactive examples. */
activeId: "inProgress",
data: [
{ id: "done", label: "완료", value: 118 },
{ id: "inProgress", label: "진행 중", value: 42 },
{ id: "review", label: "관계 기관 협의 및 보완 요청 대기", value: 24 },
{ id: "delayed", label: "지연", value: 12 },
{ id: "rejected", label: "반려", value: 0 },
] satisfies HMDonutDatum[],
}
// Pie examples. Items keep one color by id (the order of this list), in every pie and in both rings of the stacked pie.
// It includes a long name (omitted or shortened where it does not fit) and a very small slice.
const pieMaster = [
{ id: "done", label: "완료" },
{ id: "inProgress", label: "진행 중" },
{ id: "review", label: "관계 기관 협의 및 보완 요청 대기" },
{ id: "delayed", label: "지연" },
{ id: "onHold", label: "보류" },
]
const pieItem = (id: string, value: number): HMDonutDatum => {
const index = pieMaster.findIndex(item => item.id === id)
return { id, label: pieMaster[index].label, value, color: chartColors[index % chartColors.length] }
}
export const samplePie = {
data: [pieItem("done", 118), pieItem("inProgress", 42), pieItem("review", 24), pieItem("delayed", 12), pieItem("onHold", 3)],
}
export const sampleStackedPie = {
rings: [
{ id: "q1", label: "1분기", data: [pieItem("done", 80), pieItem("inProgress", 30), pieItem("delayed", 8)] },
{ id: "q2", label: "2분기", data: [pieItem("done", 118), pieItem("inProgress", 42), pieItem("review", 24), pieItem("delayed", 12)] },
] satisfies [HMPieRing, HMPieRing],
}
// Radar examples: six axes, two series on one 0-100 range. The first axis (top) has a long name to exercise the custom label.
export const sampleRadar = {
categoryKey: "area",
maxValue: 100,
series: [
{ key: "current", label: "이번 분기" },
{ key: "previous", label: "지난 분기" },
] satisfies ChartSeries[],
data: [
{ area: "관계 기관 협의 및 보완 요청", current: 62, previous: 48 },
{ area: "접수", current: 88, previous: 72 },
{ area: "검토", current: 70, previous: 82 },
{ area: "승인", current: 55, previous: 60 },
{ area: "배정", current: 92, previous: 65 },
{ area: "완료", current: 76, previous: 58 },
],
}
// Radial examples. Same items and color mapping as the pies (by id). Values are on the scale of the official examples so the arcs read
// the same way: five bars (simple, label, grid), one value (text: 200, shape: 1260) and one stacked row (570 + 1260).
const radialItem = (id: string, value: number) => pieItem(id, value)
export const sampleRadial = {
data: [radialItem("done", 275), radialItem("inProgress", 200), radialItem("review", 187), radialItem("delayed", 173), radialItem("onHold", 90)],
}
export const sampleRadialText = { item: radialItem("inProgress", 200), caption: "진행 중 건수" }
export const sampleRadialShape = { item: radialItem("inProgress", 1260), caption: "진행 중 건수" }
export const sampleRadialStacked = {
series: [
{ key: "inProgress", label: "진행 중", color: chartColors[1] },
{ key: "done", label: "완료", color: chartColors[0] },
] satisfies ChartSeries[],
row: { inProgress: 570, done: 1260 },
totalLabel: "전체(건)",
}