feat(chart): add solid (thick) pie and donut charts

Add HMSolidPieChart and HMSolidDonutChart: a tilted, SVG-drawn disc with an
outer wall and, for the donut, an inner wall seen through the hole. Same data,
slice angles, order, legend and tooltip as the 2D charts; the tooltip is the
same on the top face and the walls and follows the pointer. The existing 2D
charts and their props are unchanged.

- Side walls use darker HM tokens (--hm-chart-1-side .. --hm-chart-5-side) and
  a color-mix darkening for custom colors; shared chart tokens are untouched.
- Add a "입체형" example to the Pie Chart and Donut Chart stories
  (PieSolidExample, DonutSolidExample); ExampleCard description is now optional.
- Document usage and constraints in the chart package README.
This commit is contained in:
shkim4 committed 2026-11-06 17:44:49 +09:00
1 parent dc9258599c
commit db0bfe2d34
10 files changed
+332 -8

No files matched your search

+11 -3
View File
@@ -14,7 +14,7 @@ import {
SelectTrigger,
SelectValue,
} from "@hm-design/ui"
import { HMBarChart, HMDonutChart, HMHorizontalBarChart, HMLineChart, HMSignedBarChart } from "../index"
import { HMBarChart, HMDonutChart, HMHorizontalBarChart, HMLineChart, HMSignedBarChart, HMSolidDonutChart } 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`. */
@@ -22,13 +22,13 @@ 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 }) {
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>
{description ? <CardDescription className="break-keep">{description}</CardDescription> : null}
<CardAction><SampleDataBadge /></CardAction>
</CardHeader>
<CardContent className="flex flex-1 flex-col">
@@ -165,6 +165,14 @@ export function DonutCenterExample({ className }: { className?: string }) {
)
}
export function DonutSolidExample({ className }: { className?: string }) {
return (
<ExampleCard title="입체형" className={className}>
<HMSolidDonutChart 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 (
+2 -1
View File
@@ -1,12 +1,13 @@
// 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 { BarChartExample, StackedBarChartExample, HorizontalBarChartExample, SignedBarChartExample, LineChartExample, LineCurveExample, LineCurveDotLabelExample, LineStraightExample, LineStraightColorLabelExample, LineStepExample, LineStepDotLabelExample, DonutChartExample, DonutPlainExample, DonutCenterExample, DonutSolidExample, DonutActiveExample, DonutInteractiveExample, ChartExamples, SampleDataBadge } from "./chart-examples"
export { samplePie, sampleStackedPie, sampleRadar } from "./sample-data"
export {
PiePlainExample,
PieLabelExample,
PieLabelListExample,
PieStackedExample,
PieSolidExample,
RadarPlainExample,
RadarDotExample,
RadarLinesOnlyExample,
@@ -1,4 +1,4 @@
import { HMAreaChart, HMPieChart, HMRadarChart, HMStackedPieChart } from "../index"
import { HMAreaChart, HMPieChart, HMRadarChart, HMSolidPieChart, HMStackedPieChart } from "../index"
import { ExampleCard } from "./chart-examples"
import { sampleLineCompare, sampleLinePointColors, samplePie, sampleRadar, sampleStackedPie } from "./sample-data"
@@ -34,6 +34,14 @@ export function PieStackedExample({ className }: Props) {
)
}
export function PieSolidExample({ className }: Props) {
return (
<ExampleCard title="입체형" className={className}>
<HMSolidPieChart {...samplePie} label="업무 상태 입체형 파이 차트(예시 데이터)" />
</ExampleCard>
)
}
export function RadarPlainExample({ className }: Props) {
return (
<ExampleCard title="Radar" description="방사형 차트 · 영역" className={className}>