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

+3 -2
View File
@@ -44,6 +44,7 @@
| 시간 흐름의 양·구성비 | `HMAreaChart` | `data` 행 + `categoryKey` + `series[]` |
| 전체 대비 비중 | `HMDonutChart`, `HMPieChart` | `data: { id, label, value, color? }[]` |
| 두 구성의 동심원 비교 | `HMStackedPieChart` | `rings: [내부, 외부]` |
| 두께가 보이는 입체형 비중 | `HMSolidDonutChart`, `HMSolidPieChart` | 위 Donut 데이터 (`HMSolidDonutChart`는 `totalLabel` 선택) |
| 여러 지표의 균형 | `HMRadarChart` | `data` 행 + `categoryKey` + `series[]` |
| 목표 대비 진행률·항목별 막대 | `HMRadialBarChart`, `HMRadialMeterChart`, `HMRadialStackedChart` | 위 Donut 데이터 / `item` / `series[]`+`row` |
@@ -94,7 +95,7 @@ const received = [{ month: "1월", received: 186, completed: 142 }]
도넛 차트(`HMDonutChart`) 옵션: `totalLabel`(가운데에 전체 합계와 캡션), `activeId`(선택한 조각을 바깥쪽으로 확장하고 가운데에 그 이름·값 표시), `onActiveChange`(조각 클릭 시 id 전달; 상태는 소비 앱이 보관하며 호버로는 바뀌지 않음). 가운데 텍스트는 도넛 안쪽을 넘지 않도록 숫자를 줄이고(`1234.6억` 같은 축약) 이름은 `…`로 줄이며, 합계가 0이면 빈 링과 `0`을 표시합니다. 전체 이름은 범례와 툴팁에서 확인합니다.
파이(`HMPieChart`, `HMStackedPieChart`): `labels`(`"outside"` 조각 바깥 "이름 값"과 연결선, `"inside"` 조각 안 값)는 놓을 자리가 없으면 해당 Label만 생략하고 툴팁에는 전체 정보가 나옵니다. `HMStackedPieChart`는 `rings`(안쪽·바깥쪽 두 시리즈, 같은 `id`는 같은 색)로 동심원 링을 그립니다. 방사형(`HMRadarChart`): `filled`(기본 영역, `false`면 선만), `dots`, `customLabels`(축 이름을 두 줄로 줄이거나 `…`로 줄임), `maxValue`(공통 축 상한, 기본 100). 영역(`HMAreaChart`): `curve`(`monotone`·`linear`·`step`), `dots`, `pointLabels`·`pointColorKey`(Line과 같은 Dot·Label·충돌 처리), `stacked`(값 누적), `expanded`(각 시점 합계를 100%로 정규화, Y축 %, 툴팁에 원래 값과 비율; 합계 0이면 0%). 영역 채움은 시리즈 색의 세로 그라데이션(위 진하게, 아래 옅게)입니다.
파이(`HMPieChart`, `HMStackedPieChart`): `labels`(`"outside"` 조각 바깥 "이름 값"과 연결선, `"inside"` 조각 안 값)는 놓을 자리가 없으면 해당 Label만 생략하고 툴팁에는 전체 정보가 나옵니다. `HMStackedPieChart`는 `rings`(안쪽·바깥쪽 두 시리즈, 같은 `id`는 같은 색)로 동심원 링을 그립니다. 입체형 파이·도넛(`HMSolidPieChart`, `HMSolidDonutChart`): 기존 2D 차트와 같은 `data`(`id`·`label`·`value`·`color?`)·항목 순서·조각 각도·범례·툴팁을 쓰고, 조각을 약간 기울여 내려다본 두께 있는 원판(도넛은 바깥 옆면과 구멍 안쪽 벽)으로 그립니다. 회전 가능한 3D가 아니라 SVG로 두께와 깊이감만 표현하며 기존 2D 컴포넌트·Props는 바뀌지 않습니다. 윗면은 항목 색(`--color-<id>`), 옆면은 같은 계열의 한 단계 어두운 HM 토큰(`--hm-chart-1-side` ~ `--hm-chart-5-side`, 공용 `--hm-chart-N` 토큰은 그대로)을 쓰며, HM 색이 아닌 `color`는 그 색을 어둡게 섞어 옆면에 씁니다. 앞쪽 옆면과 도넛 안쪽 뒤 벽은 서로·윗면과 겹치지 않는 영역만 그리므로 가려지는 면이 비치지 않고 조각 사이에 틈이 없습니다. 두께와 크기는 차트 영역 너비·높이에 맞춰 줄어듭니다(차트 높이 `h-72`/`sm:h-80`은 `className`으로 조정). `HMSolidDonutChart`의 `totalLabel`은 기울어진 구멍 안에 합계와 캡션으로 표시하고 없으면 가운데를 비웁니다. 제약: 조각 클릭·Active(`activeId`)·Label(`labels`)은 지원하지 않으며, 값 0인 항목은 그리지 않고(범례에는 남음), 합계가 0이면 빈 원판·링을 표시합니다. 툴팁은 윗면·옆면 어디에서나 같은 항목을 표시하고 포인터를 따라갑니다. 방사형(`HMRadarChart`): `filled`(기본 영역, `false`면 선만), `dots`, `customLabels`(축 이름을 두 줄로 줄이거나 `…`로 줄임), `maxValue`(공통 축 상한, 기본 100). 영역(`HMAreaChart`): `curve`(`monotone`·`linear`·`step`), `dots`, `pointLabels`·`pointColorKey`(Line과 같은 Dot·Label·충돌 처리), `stacked`(값 누적), `expanded`(각 시점 합계를 100%로 정규화, Y축 %, 툴팁에 원래 값과 비율; 합계 0이면 0%). 영역 채움은 시리즈 색의 세로 그라데이션(위 진하게, 아래 옅게)입니다.
래디얼(`HMRadialBarChart`, `HMRadialMeterChart`, `HMRadialStackedChart`)은 공식 shadcn/ui radial 예제(simple·label·grid·text·shape·stacked)의 각도·반지름·트랙·중앙 Text를 따릅니다(`labels`, `grid`, `startAngle`/`endAngle`, `innerRadius`/`outerRadius`, `trackRadii`, `cornerRadius`). 중앙 숫자는 `CenterText`가 구멍 폭에 맞춰 줄이거나 `1234.6억`처럼 축약합니다. 도넛·래디얼의 링·막대 두께는 공식 예제의 1.5배입니다(2026-11-06): 단일 막대(text·shape·stacked)는 바깥 반지름을 두고 안쪽 반지름을 줄였고, 여러 막대(simple·label·grid)는 `barSize`(18, grid 16.5)·`ringGap`(4, grid 3, 기존 간격)과 기존 중앙 구멍(`innerRadius` 30)을 유지해 `outerRadius`를 `innerRadius + 항목 수 × (barSize + ringGap)`(5개: 140, grid 127.5)로 계산합니다. 래디얼 차트 영역 높이는 300px로 같고 범례는 차트 영역 아래에 따로 놓이며, 폭이 모자라면(320px) simple·label·grid의 반지름·두께·간격이 비례해 줄어듭니다. `HMRadialMeterChart`의 `trackRadii`는 막대와 같은 두께로 맞춥니다. `labels`(항목명 Label)는 막대 안쪽 시작 부분에 호를 따라 그려지며(`insideStart`) 막대 호 길이에 들어가지 않는 긴 이름은 `…`로 줄입니다(전체 이름은 범례와 툴팁).
@@ -115,7 +116,7 @@ npm run storybook # Chart 그룹: Bar / Line / Donut / P
### 예제 컴포넌트
소비 예제(`examples/chart/main.tsx`)와 Storybook은 `@hm-design/chart/examples`의 예제 카드를 사용합니다: Bar(`BarChartExample`, `StackedBarChartExample`, `HorizontalBarChartExample`, `SignedBarChartExample`), Line(`LineChartExample` 외 Curve·Straight·Step 변형), Donut(`DonutChartExample` 외 Plain·Center·Active·Interactive), Pie(`PiePlainExample`, `PieLabelExample`, `PieLabelListExample`, `PieStackedExample`), Radar(`RadarPlainExample`, `RadarDotExample`, `RadarLinesOnlyExample`, `RadarCustomLabelExample`), Area(`AreaCurveExample` 외 Straight·Step·Stacked·Expanded), Radial(`RadialSimpleExample`, `RadialLabelExample`, `RadialGridExample`, `RadialTextExample`, `RadialShapeExample`, `RadialStackedExample`), 묶음 `ChartExamples`. 예시 데이터(`sampleBar`, `samplePie`, `sampleRadial` 등)는 같은 진입점에서 export됩니다. 예제 카드는 `ExampleCard`(내부 컴포넌트)로 감싸며 실제 업무 데이터가 아님을 표시합니다.
소비 예제(`examples/chart/main.tsx`)와 Storybook은 `@hm-design/chart/examples`의 예제 카드를 사용합니다: Bar(`BarChartExample`, `StackedBarChartExample`, `HorizontalBarChartExample`, `SignedBarChartExample`), Line(`LineChartExample` 외 Curve·Straight·Step 변형), Donut(`DonutChartExample` 외 Plain·Center·Active·Interactive·`DonutSolidExample`(입체형)), Pie(`PiePlainExample`, `PieLabelExample`, `PieLabelListExample`, `PieStackedExample`, `PieSolidExample`(입체형)), Radar(`RadarPlainExample`, `RadarDotExample`, `RadarLinesOnlyExample`, `RadarCustomLabelExample`), Area(`AreaCurveExample` 외 Straight·Step·Stacked·Expanded), Radial(`RadialSimpleExample`, `RadialLabelExample`, `RadialGridExample`, `RadialTextExample`, `RadialShapeExample`, `RadialStackedExample`), 묶음 `ChartExamples`. 예시 데이터(`sampleBar`, `samplePie`, `sampleRadial` 등)는 같은 진입점에서 export됩니다. 예제 카드는 `ExampleCard`(내부 컴포넌트)로 감싸며 실제 업무 데이터가 아님을 표시합니다.
## 공식 shadcn Chart와의 차이