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:
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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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: "전체(건)",
|
||||
}
|
||||
Reference in new issue
Block a user