@iamfiro/design 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. package/AGENTS.md +81 -0
  2. package/README.md +15 -2
  3. package/agents/INDEX.md +156 -0
  4. package/agents/accordion/AGENTS.md +279 -0
  5. package/agents/action-bar/AGENTS.md +90 -0
  6. package/agents/alert/AGENTS.md +212 -0
  7. package/agents/alert-dialog/AGENTS.md +193 -0
  8. package/agents/app-shell/AGENTS.md +409 -0
  9. package/agents/area-chart/AGENTS.md +183 -0
  10. package/agents/aspect-ratio/AGENTS.md +143 -0
  11. package/agents/avatar/AGENTS.md +130 -0
  12. package/agents/badge/AGENTS.md +217 -0
  13. package/agents/bar-chart/AGENTS.md +386 -0
  14. package/agents/bar-list/AGENTS.md +223 -0
  15. package/agents/brand-icon/AGENTS.md +82 -0
  16. package/agents/breadcrumb/AGENTS.md +133 -0
  17. package/agents/button/AGENTS.md +195 -0
  18. package/agents/card/AGENTS.md +141 -0
  19. package/agents/card-frame/AGENTS.md +227 -0
  20. package/agents/chat-bubble/AGENTS.md +266 -0
  21. package/agents/chat-composer/AGENTS.md +251 -0
  22. package/agents/checkbox/AGENTS.md +113 -0
  23. package/agents/code-block/AGENTS.md +144 -0
  24. package/agents/color-dot/AGENTS.md +120 -0
  25. package/agents/color-picker/AGENTS.md +224 -0
  26. package/agents/colors/AGENTS.md +168 -0
  27. package/agents/combobox/AGENTS.md +218 -0
  28. package/agents/command-menu/AGENTS.md +169 -0
  29. package/agents/comment/AGENTS.md +206 -0
  30. package/agents/context-menu/AGENTS.md +303 -0
  31. package/agents/copy-button/AGENTS.md +84 -0
  32. package/agents/count-badge/AGENTS.md +115 -0
  33. package/agents/countdown/AGENTS.md +101 -0
  34. package/agents/data-grid/AGENTS.md +460 -0
  35. package/agents/date-picker/AGENTS.md +197 -0
  36. package/agents/delta/AGENTS.md +114 -0
  37. package/agents/description-list/AGENTS.md +251 -0
  38. package/agents/dialog/AGENTS.md +391 -0
  39. package/agents/drawer/AGENTS.md +471 -0
  40. package/agents/dropdown-menu/AGENTS.md +322 -0
  41. package/agents/dropzone/AGENTS.md +116 -0
  42. package/agents/elapsed/AGENTS.md +160 -0
  43. package/agents/empty-state/AGENTS.md +203 -0
  44. package/agents/file-chip/AGENTS.md +79 -0
  45. package/agents/file-icon/AGENTS.md +66 -0
  46. package/agents/file-upload/AGENTS.md +168 -0
  47. package/agents/filter-chips/AGENTS.md +167 -0
  48. package/agents/flag/AGENTS.md +67 -0
  49. package/agents/funnel-chart/AGENTS.md +131 -0
  50. package/agents/highlight/AGENTS.md +119 -0
  51. package/agents/hover-card/AGENTS.md +271 -0
  52. package/agents/icon-stack/AGENTS.md +120 -0
  53. package/agents/icon-tile/AGENTS.md +94 -0
  54. package/agents/inline-diff/AGENTS.md +211 -0
  55. package/agents/input/AGENTS.md +261 -0
  56. package/agents/kanban/AGENTS.md +352 -0
  57. package/agents/kbd/AGENTS.md +160 -0
  58. package/agents/layout/AGENTS.md +351 -0
  59. package/agents/line-chart/AGENTS.md +393 -0
  60. package/agents/link/AGENTS.md +53 -0
  61. package/agents/list/AGENTS.md +392 -0
  62. package/agents/marker/AGENTS.md +142 -0
  63. package/agents/masked-text/AGENTS.md +127 -0
  64. package/agents/menubar/AGENTS.md +276 -0
  65. package/agents/multi-select/AGENTS.md +159 -0
  66. package/agents/navigation-menu/AGENTS.md +315 -0
  67. package/agents/notice-stack/AGENTS.md +101 -0
  68. package/agents/number-stepper/AGENTS.md +96 -0
  69. package/agents/otp-input/AGENTS.md +162 -0
  70. package/agents/pagination/AGENTS.md +189 -0
  71. package/agents/phone-input/AGENTS.md +131 -0
  72. package/agents/pie-chart/AGENTS.md +243 -0
  73. package/agents/popover/AGENTS.md +190 -0
  74. package/agents/progress/AGENTS.md +273 -0
  75. package/agents/progress-tracker/AGENTS.md +305 -0
  76. package/agents/rating/AGENTS.md +165 -0
  77. package/agents/relative-time/AGENTS.md +103 -0
  78. package/agents/resizable/AGENTS.md +390 -0
  79. package/agents/scaled-preview/AGENTS.md +111 -0
  80. package/agents/scatter-chart/AGENTS.md +160 -0
  81. package/agents/scroll-area/AGENTS.md +271 -0
  82. package/agents/search-input/AGENTS.md +105 -0
  83. package/agents/segmented-control/AGENTS.md +153 -0
  84. package/agents/select/AGENTS.md +159 -0
  85. package/agents/selector-button/AGENTS.md +122 -0
  86. package/agents/sidebar/AGENTS.md +201 -0
  87. package/agents/skeleton/AGENTS.md +166 -0
  88. package/agents/slider/AGENTS.md +181 -0
  89. package/agents/spark-chart/AGENTS.md +257 -0
  90. package/agents/sparkbar/AGENTS.md +92 -0
  91. package/agents/spinner/AGENTS.md +123 -0
  92. package/agents/split-view/AGENTS.md +290 -0
  93. package/agents/stat/AGENTS.md +138 -0
  94. package/agents/status-icon/AGENTS.md +243 -0
  95. package/agents/step-list/AGENTS.md +313 -0
  96. package/agents/table/AGENTS.md +286 -0
  97. package/agents/tabs/AGENTS.md +305 -0
  98. package/agents/tag-input/AGENTS.md +246 -0
  99. package/agents/time-picker/AGENTS.md +228 -0
  100. package/agents/timeline/AGENTS.md +1018 -0
  101. package/agents/toast/AGENTS.md +266 -0
  102. package/agents/toggle-group/AGENTS.md +216 -0
  103. package/agents/toolbar/AGENTS.md +277 -0
  104. package/agents/tooltip/AGENTS.md +154 -0
  105. package/agents/tracker/AGENTS.md +158 -0
  106. package/agents/tree-diagram/AGENTS.md +260 -0
  107. package/agents/tree-view/AGENTS.md +329 -0
  108. package/agents/typography/AGENTS.md +396 -0
  109. package/lib/index.js +17914 -0
  110. package/lib/style.css +2 -0
  111. package/lib/types/components/Accordion/Accordion.d.ts +57 -0
  112. package/lib/types/components/ActionBar/ActionBar.d.ts +24 -0
  113. package/lib/types/components/Alert/Alert.d.ts +36 -0
  114. package/lib/types/components/AppShell/AppShell.d.ts +38 -0
  115. package/lib/types/components/AreaChart/AreaChart.d.ts +17 -0
  116. package/lib/types/components/BarChart/BarChart.d.ts +57 -0
  117. package/lib/types/components/BarList/BarList.d.ts +56 -0
  118. package/lib/types/components/Breadcrumb/Breadcrumb.d.ts +15 -0
  119. package/lib/types/components/Calendar/Calendar.d.ts +34 -0
  120. package/lib/types/components/Calendar/DatePicker.d.ts +45 -0
  121. package/lib/types/components/Calendar/date.d.ts +32 -0
  122. package/lib/types/components/Card/Card.d.ts +64 -0
  123. package/lib/types/components/CardFrame/CardFrame.d.ts +15 -0
  124. package/lib/types/components/Chart/ChartParts.d.ts +38 -0
  125. package/lib/types/components/Chart/chart.d.ts +46 -0
  126. package/lib/types/components/Chart/useChartInteraction.d.ts +39 -0
  127. package/lib/types/components/ChatBubble/ChatBubble.d.ts +61 -0
  128. package/lib/types/components/ChatBubble/formatChatTime.d.ts +2 -0
  129. package/lib/types/components/ChatComposer/ChatComposer.d.ts +42 -0
  130. package/lib/types/components/CodeBlock/CodeBlock.d.ts +18 -0
  131. package/lib/types/components/CodeBlock/highlight.d.ts +10 -0
  132. package/lib/types/components/ColorPicker/ColorPicker.d.ts +61 -0
  133. package/lib/types/components/ColorPicker/color.d.ts +48 -0
  134. package/lib/types/components/ColorPicker/presets.d.ts +7 -0
  135. package/lib/types/components/Combobox/Combobox.d.ts +48 -0
  136. package/lib/types/components/CommandMenu/CommandMenu.d.ts +51 -0
  137. package/lib/types/components/Comment/Comment.d.ts +45 -0
  138. package/lib/types/components/ContextMenu/ContextMenu.d.ts +40 -0
  139. package/lib/types/components/DataGrid/DataGrid.d.ts +43 -0
  140. package/lib/types/components/DataGrid/GridPopover.d.ts +43 -0
  141. package/lib/types/components/DataGrid/anchor.d.ts +5 -0
  142. package/lib/types/components/DataGrid/model.d.ts +61 -0
  143. package/lib/types/components/DataGrid/panels.d.ts +63 -0
  144. package/lib/types/components/DataGrid/typeMeta.d.ts +6 -0
  145. package/lib/types/components/DataGrid/types.d.ts +77 -0
  146. package/lib/types/components/DescriptionList/DescriptionList.d.ts +77 -0
  147. package/lib/types/components/Dialog/AlertDialog.d.ts +26 -0
  148. package/lib/types/components/Dialog/Dialog.d.ts +42 -0
  149. package/lib/types/components/Dialog/modal.d.ts +38 -0
  150. package/lib/types/components/Drawer/Drawer.d.ts +62 -0
  151. package/lib/types/components/DropdownMenu/DropdownMenu.d.ts +97 -0
  152. package/lib/types/components/DropdownMenu/menuCore.d.ts +23 -0
  153. package/lib/types/components/Dropzone/Dropzone.d.ts +30 -0
  154. package/lib/types/components/EmptyState/EmptyState.d.ts +26 -0
  155. package/lib/types/components/FileChip/FileChip.d.ts +32 -0
  156. package/lib/types/components/FileUpload/FileUpload.d.ts +52 -0
  157. package/lib/types/components/FilterChips/FilterChips.d.ts +37 -0
  158. package/lib/types/components/FunnelChart/FunnelChart.d.ts +38 -0
  159. package/lib/types/components/FunnelChart/funnel.d.ts +25 -0
  160. package/lib/types/components/Highlight/Highlight.d.ts +35 -0
  161. package/lib/types/components/HoverCard/HoverCard.d.ts +47 -0
  162. package/lib/types/components/InlineDiff/InlineDiff.d.ts +36 -0
  163. package/lib/types/components/InlineDiff/diff.d.ts +42 -0
  164. package/lib/types/components/InlineDiff/useDiffReview.d.ts +57 -0
  165. package/lib/types/components/Kanban/Kanban.d.ts +64 -0
  166. package/lib/types/components/Kanban/KanbanCard.d.ts +35 -0
  167. package/lib/types/components/Kanban/move.d.ts +25 -0
  168. package/lib/types/components/LineChart/LineChart.d.ts +85 -0
  169. package/lib/types/components/List/List.d.ts +62 -0
  170. package/lib/types/components/Listbox/Listbox.d.ts +60 -0
  171. package/lib/types/components/Marker/Marker.d.ts +28 -0
  172. package/lib/types/components/Menubar/Menubar.d.ts +50 -0
  173. package/lib/types/components/MultiSelect/MultiSelect.d.ts +38 -0
  174. package/lib/types/components/NavigationMenu/NavigationMenu.d.ts +101 -0
  175. package/lib/types/components/NoticeStack/NoticeStack.d.ts +41 -0
  176. package/lib/types/components/NumberStepper/NumberStepper.d.ts +32 -0
  177. package/lib/types/components/OtpInput/OtpInput.d.ts +31 -0
  178. package/lib/types/components/Pagination/Pagination.d.ts +54 -0
  179. package/lib/types/components/Pagination/paginationRange.d.ts +7 -0
  180. package/lib/types/components/PhoneInput/PhoneInput.d.ts +47 -0
  181. package/lib/types/components/PieChart/PieChart.d.ts +67 -0
  182. package/lib/types/components/PieChart/arc.d.ts +26 -0
  183. package/lib/types/components/PieChart/useArcTween.d.ts +15 -0
  184. package/lib/types/components/Popover/Popover.d.ts +28 -0
  185. package/lib/types/components/ProgressTracker/ProgressTracker.d.ts +64 -0
  186. package/lib/types/components/Rating/Rating.d.ts +42 -0
  187. package/lib/types/components/Resizable/Resizable.d.ts +50 -0
  188. package/lib/types/components/Resizable/layout.d.ts +31 -0
  189. package/lib/types/components/ScaledPreview/ScaledPreview.d.ts +38 -0
  190. package/lib/types/components/ScatterChart/ScatterChart.d.ts +81 -0
  191. package/lib/types/components/ScatterChart/scatter.d.ts +14 -0
  192. package/lib/types/components/ScrollArea/ScrollArea.d.ts +34 -0
  193. package/lib/types/components/Select/Select.d.ts +39 -0
  194. package/lib/types/components/Sidebar/Sidebar.d.ts +39 -0
  195. package/lib/types/components/Skeleton/Skeleton.d.ts +47 -0
  196. package/lib/types/components/Slider/Slider.d.ts +36 -0
  197. package/lib/types/components/SparkChart/SparkChart.d.ts +55 -0
  198. package/lib/types/components/SparkChart/spark.d.ts +16 -0
  199. package/lib/types/components/Sparkbar/Sparkbar.d.ts +39 -0
  200. package/lib/types/components/SplitView/SplitView.d.ts +31 -0
  201. package/lib/types/components/Stat/Stat.d.ts +58 -0
  202. package/lib/types/components/StepList/StepList.d.ts +61 -0
  203. package/lib/types/components/Table/Table.d.ts +39 -0
  204. package/lib/types/components/Tabs/Tabs.d.ts +47 -0
  205. package/lib/types/components/TagInput/TagInput.d.ts +46 -0
  206. package/lib/types/components/TimePicker/TimePicker.d.ts +43 -0
  207. package/lib/types/components/TimePicker/time.d.ts +15 -0
  208. package/lib/types/components/Timeline/Timeline.d.ts +154 -0
  209. package/lib/types/components/Timeline/scale.d.ts +22 -0
  210. package/lib/types/components/Toast/Toaster.d.ts +19 -0
  211. package/lib/types/components/Toast/index.d.ts +4 -0
  212. package/lib/types/components/Toast/store.d.ts +75 -0
  213. package/lib/types/components/ToggleGroup/ToggleGroup.d.ts +28 -0
  214. package/lib/types/components/Toolbar/Toolbar.d.ts +70 -0
  215. package/lib/types/components/Tracker/Tracker.d.ts +43 -0
  216. package/lib/types/components/TreeDiagram/TreeDiagram.d.ts +68 -0
  217. package/lib/types/components/TreeView/TreeView.d.ts +107 -0
  218. package/lib/types/components/index.d.ts +186 -0
  219. package/lib/types/hooks/index.d.ts +17 -0
  220. package/lib/types/hooks/timing.d.ts +29 -0
  221. package/lib/types/hooks/useControllableState.d.ts +5 -0
  222. package/lib/types/hooks/useCountdown.d.ts +19 -0
  223. package/lib/types/hooks/useDigitInput.d.ts +24 -0
  224. package/lib/types/hooks/useDismiss.d.ts +3 -0
  225. package/lib/types/hooks/useElapsed.d.ts +28 -0
  226. package/lib/types/hooks/useFloating.d.ts +21 -0
  227. package/lib/types/hooks/useFocusTrap.d.ts +7 -0
  228. package/lib/types/hooks/usePresence.d.ts +8 -0
  229. package/lib/types/hooks/usePressToOpen.d.ts +20 -0
  230. package/lib/types/hooks/useScrollLock.d.ts +2 -0
  231. package/lib/types/hooks/useSelection.d.ts +29 -0
  232. package/lib/types/index.d.ts +5 -0
  233. package/lib/types/primitives/AspectRatio.d.ts +14 -0
  234. package/lib/types/primitives/BrandIcon.d.ts +12 -0
  235. package/lib/types/primitives/ColorDot.d.ts +20 -0
  236. package/lib/types/primitives/CopyButton.d.ts +12 -0
  237. package/lib/types/primitives/CountBadge.d.ts +28 -0
  238. package/lib/types/primitives/Countdown.d.ts +21 -0
  239. package/lib/types/primitives/Delta.d.ts +33 -0
  240. package/lib/types/primitives/Elapsed.d.ts +23 -0
  241. package/lib/types/primitives/FileIcon.d.ts +17 -0
  242. package/lib/types/primitives/Flag.d.ts +15 -0
  243. package/lib/types/primitives/IconStack.d.ts +27 -0
  244. package/lib/types/primitives/IconTile.d.ts +24 -0
  245. package/lib/types/primitives/Kbd.d.ts +19 -0
  246. package/lib/types/primitives/Layout.d.ts +64 -0
  247. package/lib/types/primitives/Link.d.ts +12 -0
  248. package/lib/types/primitives/MaskedText.d.ts +28 -0
  249. package/lib/types/primitives/Progress.d.ts +52 -0
  250. package/lib/types/primitives/RelativeTime.d.ts +22 -0
  251. package/lib/types/primitives/SearchInput.d.ts +8 -0
  252. package/lib/types/primitives/SelectorButton.d.ts +23 -0
  253. package/lib/types/primitives/Spinner.d.ts +15 -0
  254. package/lib/types/primitives/StatusIcon.d.ts +40 -0
  255. package/lib/types/primitives/Swatch.d.ts +11 -0
  256. package/lib/types/primitives/Tooltip.d.ts +17 -0
  257. package/lib/types/primitives/Typography.d.ts +48 -0
  258. package/lib/types/primitives/formatCountdown.d.ts +2 -0
  259. package/lib/types/primitives/formatElapsed.d.ts +8 -0
  260. package/lib/types/primitives/formatRelativeTime.d.ts +16 -0
  261. package/lib/types/primitives/hotkey.d.ts +10 -0
  262. package/lib/types/primitives/index.d.ts +188 -0
  263. package/lib/types/primitives/maskValue.d.ts +24 -0
  264. package/package.json +54 -4
@@ -0,0 +1,243 @@
1
+ # PieChart
2
+
3
+ > 차트 · `@iamfiro/design` · 직접 만들지 말고 이 컴포넌트를 쓴다.
4
+
5
+ 전체 중 각 부분의 비율을 보여주는 도넛·원 차트. 얇은 링의 조각 사이엔 같은 폭의 틈이 있고 모서리는 살짝 둥글어요. 12시 방향에서 시계 방향으로 펼쳐지며 그려지고, 데이터가 바뀌면 조각이 제자리에서 모양을 바꿔요. 마우스를 올린 조각은 바깥으로 살짝 커지고 나머지는 흐려지며, 가운데 숫자가 그 조각의 값과 이름으로 바뀌어요.
6
+
7
+ ```tsx
8
+ import { PieChart, type PieSlice } from '@iamfiro/design'
9
+ ```
10
+
11
+ ## 디자인 규칙
12
+
13
+ - 차트: 시리즈 색은 `--chart-1…6`을 순서대로, 그리드는 `--chart-grid`로 아주 옅게(세로선·축선 없음). 범례·툴팁의 색 표시는 `ColorDot`, 툴팁은 `Card raised` + `Separator`로 조립. 축·툴팁·범례·호버/키보드는 `components/Chart/`의 공용 조각(`ChartTooltip`, `ChartLegend`, `useChartInteraction`)을 쓴다. → `LineChart` · `BarChart` · `PieChart` · `Sparkbar`
14
+ - 도넛은 얇은 링(`thickness` ≈ 지름 9%) + 조각 사이 같은 폭의 틈(`padAngle`) + 살짝 둥근 모서리(`cornerRadius`, 얇은 조각에선 저절로 줄어 뒤집히지 않게). 가운데는 큰 `number` 숫자 + 아래 옅은 caption, hover한 조각은 바깥으로 커지고 나머지는 흐려지며 가운데가 그 조각 값으로 바뀐다. 진행률은 `max` + 옅은 트랙. 마운트 시 12시부터 펼치고 데이터가 바뀌면 같은 키끼리 모양을 이어 간다(rAF, reduced motion에서도). → `PieChart`
15
+
16
+ ## API
17
+
18
+ ### PieChart
19
+
20
+ | prop | 타입 | 기본값 | 설명 |
21
+ | --- | --- | --- | --- |
22
+ | `data` (필수) | `PieSlice[]` | | 조각 목록. 0 이하는 그리지 않아요. |
23
+ | `variant` | `'donut' \| 'pie'` | `'donut'` | 도넛 또는 구멍 없는 원. |
24
+ | `size` | `number` | `200` | 지름(px). |
25
+ | `thickness` | `number` | `지름의 약 9%` | 링 두께(px). |
26
+ | `innerRadius` | `number` | | 구멍 크기(반지름 비율, 이전 API). thickness가 우선이고 0이면 원. |
27
+ | `padAngle` | `number` | `2` | 조각 사이 틈(도, 링 가운데 기준). 틈은 안팎 같은 폭이에요. |
28
+ | `cornerRadius` | `number` | `3` | 조각 모서리 둥글기(px). 얇거나 짧은 조각에선 저절로 줄어요. |
29
+ | `max` | `number` | | 링 전체가 나타내는 값(진행률 도넛). 남은 부분은 비워 둬요. |
30
+ | `track` | `boolean` | `max가 있으면 true` | 조각 뒤의 옅은 링. |
31
+ | `formatValue` | `(value, slice) => string` | `'1,234'` | 값 형식. 조각 없이 부르면 합계. |
32
+ | `centerLabel` | `ReactNode \| (ctx: PieCenterContext) => ReactNode` | `'합계'` | 가운데 숫자 아래 문구. 함수면 가운데 전체를 직접 그려요(ctx.total · ctx.active). |
33
+ | `centerValue` | `ReactNode` | | 가운데 숫자. 기본은 합계(max가 있으면 비율). |
34
+ | `legend` | `boolean \| 'side' \| 'bottom'` | `true` | true · 'side' = 옆 목록(값 · 비율), 'bottom' = 아래 한 줄. |
35
+ | `showPercent` | `boolean` | `true` | 옆 범례에 비율 표시. |
36
+ | `tooltip` | `boolean` | `원이면 true` | 조각 옆 Card 툴팁. 도넛은 기본으로 가운데에서 읽어 줘요. |
37
+ | `activeIndex / defaultActiveIndex / onActiveIndexChange` | `number \| null` | | 하이라이트한 조각(제어/비제어). |
38
+ | `aria-label` | `string` | `'원 차트'` | 차트 이름. 하이라이트한 조각은 aria-live로 읽어요. |
39
+
40
+ ### PieSlice
41
+
42
+ | prop | 타입 | 기본값 | 설명 |
43
+ | --- | --- | --- | --- |
44
+ | `key` (필수) | `string` | | 고유 키. 데이터가 바뀔 때 같은 키끼리 모양이 이어져요. |
45
+ | `label` (필수) | `string` | | 이름. |
46
+ | `value` (필수) | `number` | | 값. |
47
+ | `color` | `string` | | CSS 변수('--chart-3') 또는 색. 기본은 --chart-1부터 차례로. |
48
+
49
+ ## 예제
50
+
51
+ 예제를 감싼 `style={{ ... }}` div는 미리보기 배치용이다. 실제 화면 배치는 `Stack` · `Inline` · `Grid`로 한다.
52
+
53
+ ### 도넛 · 옆 범례
54
+
55
+ 분류별 지출. 가운데엔 합계, 조각이나 범례에 마우스를 올리면 그 조각의 값 · 이름 · 비율로 바뀌어요. 기간을 바꾸면 조각이 이어서 움직여요. ←/→로 조각을 돌아가며 고르고 화면 읽기 프로그램이 값을 읽어요.
56
+
57
+ ```tsx
58
+ import { useState } from 'react'
59
+ import { PieChart, type PieSlice, SegmentedControl, Stack } from '@iamfiro/design'
60
+
61
+ const MONTHS: Record<'oct' | 'sep', PieSlice[]> = {
62
+ oct: [
63
+ { key: 'food', label: '식비', value: 86 },
64
+ { key: 'home', label: '주거', value: 72 },
65
+ { key: 'move', label: '교통', value: 31 },
66
+ { key: 'shop', label: '쇼핑', value: 44 },
67
+ { key: 'fun', label: '여가', value: 23 },
68
+ { key: 'etc', label: '기타', value: 9 },
69
+ ],
70
+ sep: [
71
+ { key: 'food', label: '식비', value: 78 },
72
+ { key: 'home', label: '주거', value: 72 },
73
+ { key: 'move', label: '교통', value: 26 },
74
+ { key: 'shop', label: '쇼핑', value: 61 },
75
+ { key: 'fun', label: '여가', value: 38 },
76
+ { key: 'etc', label: '기타', value: 14 },
77
+ ],
78
+ }
79
+
80
+ export default function Example() {
81
+ const [month, setMonth] = useState<'oct' | 'sep'>('oct')
82
+ return (
83
+ <Stack gap={5} align="start">
84
+ <SegmentedControl
85
+ value={month}
86
+ onChange={setMonth}
87
+ ariaLabel="기간"
88
+ options={[
89
+ { value: 'oct', label: '10월' },
90
+ { value: 'sep', label: '9월' },
91
+ ]}
92
+ />
93
+ <PieChart data={MONTHS[month]} size={208} formatValue={(v) => `${v}만 원`} centerLabel="이번 달 지출" aria-label="분류별 지출" />
94
+ </Stack>
95
+ )
96
+ }
97
+ ```
98
+
99
+ ### 진행률 · 트랙
100
+
101
+ max를 주면 단일 값의 진행 링이 돼요. 남은 부분엔 옅은 트랙이 보이고, cornerRadius를 두께의 절반으로 하면 끝이 둥근 막대가 돼요.
102
+
103
+ ```tsx
104
+ import { useState } from 'react'
105
+ import { PieChart, Slider, Stack } from '@iamfiro/design'
106
+
107
+ export default function Example() {
108
+ const [done, setDone] = useState(68)
109
+ return (
110
+ <Stack gap={5} style={{ width: 240 }}>
111
+ <PieChart
112
+ data={[{ key: 'done', label: '달성', value: done, color: '--chart-2' }]}
113
+ max={100}
114
+ size={176}
115
+ thickness={14}
116
+ cornerRadius={7}
117
+ padAngle={0}
118
+ legend={false}
119
+ formatValue={(v) => `${v}%`}
120
+ centerLabel="분기 목표 달성"
121
+ aria-label="분기 목표 달성률"
122
+ style={{ alignSelf: 'center' }}
123
+ />
124
+ <Slider value={done} onValueChange={setDone} min={0} max={100} step={1} formatValue={(v) => `${v}%`} aria-label="달성률" />
125
+ </Stack>
126
+ )
127
+ }
128
+ ```
129
+
130
+ ### 크기 · 범례 없음
131
+
132
+ size로 지름을 정하면 링 두께와 가운데 숫자 크기가 따라 바뀌어요. 카드 안 작은 지표엔 범례를 꺼요.
133
+
134
+ ```tsx
135
+ import { Card, CardBody, PieChart, Grid, Inline, Stack, Text } from '@iamfiro/design'
136
+
137
+ const METRICS = [
138
+ { title: '저장 공간', used: 72, color: '--chart-2' },
139
+ { title: '이번 달 크레딧', used: 38, color: '--chart-3' },
140
+ { title: '좌석', used: 90, color: '--chart-1' },
141
+ ]
142
+
143
+ const SIZES = [96, 128, 160]
144
+
145
+ export default function Example() {
146
+ return (
147
+ <Stack gap={6} style={{ width: '100%', maxWidth: 560 }}>
148
+ <Grid minItemWidth={160} gap={4}>
149
+ {METRICS.map((m) => (
150
+ <Card key={m.title}>
151
+ <CardBody>
152
+ <Stack gap={3} align="center">
153
+ <PieChart
154
+ size={112}
155
+ max={100}
156
+ legend={false}
157
+ data={[{ key: 'used', label: '사용', value: m.used, color: m.color }]}
158
+ formatValue={(v) => `${v}%`}
159
+ centerLabel="사용"
160
+ aria-label={`${m.title} 사용량`}
161
+ />
162
+ <Text variant="label-md">{m.title}</Text>
163
+ </Stack>
164
+ </CardBody>
165
+ </Card>
166
+ ))}
167
+ </Grid>
168
+ <Inline gap={6} justify="center">
169
+ {SIZES.map((s) => (
170
+ <PieChart
171
+ key={s}
172
+ size={s}
173
+ legend={false}
174
+ data={[
175
+ { key: 'a', label: '완료', value: 54 },
176
+ { key: 'b', label: '진행 중', value: 28 },
177
+ { key: 'c', label: '대기', value: 18 },
178
+ ]}
179
+ centerLabel={`${s}px`}
180
+ aria-label={`${s}px 도넛`}
181
+ />
182
+ ))}
183
+ </Inline>
184
+ </Stack>
185
+ )
186
+ }
187
+ ```
188
+
189
+ ### 툴팁 · 아래 범례 · 가운데 직접 그리기
190
+
191
+ tooltip이면 조각 옆 Card에 값을 보여 주고 가운데는 그대로 둬요. legend="bottom"은 아래 한 줄 범례, centerLabel에 함수를 주면 가운데를 직접 그려요.
192
+
193
+ ```tsx
194
+ import { PieChart, type PieSlice, Stack, Text } from '@iamfiro/design'
195
+
196
+ const DATA: PieSlice[] = [
197
+ { key: 'paid', label: '유료 광고', value: 4800 },
198
+ { key: 'organic', label: '검색 유입', value: 4600 },
199
+ { key: 'email', label: '이메일', value: 2100 },
200
+ { key: 'referral', label: '추천', value: 1500 },
201
+ { key: 'direct', label: '직접 방문', value: 800 },
202
+ ]
203
+
204
+ export default function Example() {
205
+ return (
206
+ <PieChart
207
+ data={DATA}
208
+ size={200}
209
+ legend="bottom"
210
+ tooltip
211
+ formatValue={(v) => `${v.toLocaleString('ko-KR')}만 원`}
212
+ centerLabel={({ total }) => (
213
+ <Stack gap={0} align="center">
214
+ <Text variant="overline" tone="tertiary">
215
+ 이번 주 매출
216
+ </Text>
217
+ <Text variant="number-lg">{(total / 10000).toFixed(1)}억</Text>
218
+ </Stack>
219
+ )}
220
+ aria-label="유입 경로별 매출 비중"
221
+ />
222
+ )
223
+ }
224
+ ```
225
+
226
+ ### 원
227
+
228
+ variant="pie"(또는 innerRadius={0})이면 구멍 없는 원. 값은 조각 옆 툴팁으로 보여줘요.
229
+
230
+ ```tsx
231
+ import { PieChart, type PieSlice } from '@iamfiro/design'
232
+
233
+ const DATA: PieSlice[] = [
234
+ { key: 'mac', label: 'macOS', value: 46 },
235
+ { key: 'win', label: 'Windows', value: 31 },
236
+ { key: 'ios', label: 'iOS', value: 14 },
237
+ { key: 'android', label: 'Android', value: 9 },
238
+ ]
239
+
240
+ export default function Example() {
241
+ return <PieChart data={DATA} variant="pie" size={180} formatValue={(v) => `${v}명`} aria-label="운영체제별 사용자" />
242
+ }
243
+ ```
@@ -0,0 +1,190 @@
1
+ # Popover
2
+
3
+ > 메뉴 & 오버레이 · `@iamfiro/design` · 직접 만들지 말고 이 컴포넌트를 쓴다.
4
+
5
+ 트리거 옆에 붙어 뜨는 작은 패널. 작은 폼, 미리보기 카드, 알림 목록처럼 페이지를 떠나지 않고 잠깐 보는 내용에 써요. 부모의 overflow: hidden에 잘리지 않고, 아래 공간이 부족하면 위로 뒤집히며, 열리고 닫힐 때 모두 애니메이션돼요. 바깥을 누르거나 Esc로 닫히고, 안에 포커스가 있던 채로 닫히면 트리거로 포커스가 돌아가요.
6
+
7
+ ```tsx
8
+ import { Popover } from '@iamfiro/design'
9
+ ```
10
+
11
+ ## 디자인 규칙
12
+
13
+ - 팝오버는 `ListboxPopover` / `Popover` / `DropdownMenu`(portal + fixed, 공간 부족 시 위로, `useFloating`의 `align`). 부모 `overflow: hidden`에 잘리면 안 된다.
14
+
15
+ ## API
16
+
17
+ ### Popover
18
+
19
+ | prop | 타입 | 기본값 | 설명 |
20
+ | --- | --- | --- | --- |
21
+ | `trigger` (필수) | `ReactNode` | | 여는 요소(보통 Button). |
22
+ | `children` (필수) | `ReactNode \| (close) => ReactNode` | | 패널 내용. 함수면 close를 받아요. |
23
+ | `openOn` | `'click' \| 'hover'` | `'click'` | click: 누르면 토글 · hover: 머물면 열리고 유예 뒤 닫힘. |
24
+ | `align` | `'start' \| 'center' \| 'end'` | `'start'` | 트리거 기준 가로 정렬. |
25
+ | `width` | `number \| string` | | 패널 폭(px 또는 CSS 값). 최대 화면 폭 - 16px. |
26
+ | `padding` | `'none' \| 'sm' \| 'md'` | `'md'` | 안쪽 여백. 자체 여백이 있는 목록·미디어는 none. |
27
+ | `autoFocus` | `boolean` | `true` | click 모드에서 열릴 때 패널 안 첫 요소로 포커스 이동. |
28
+ | `open / onOpenChange` | `boolean / (open) => void` | | 열림 상태를 밖에서 제어할 때. |
29
+ | `defaultOpen` | `boolean` | `false` | 처음 열림 상태(비제어). |
30
+ | `aria-label` | `string` | | 패널(role="dialog")의 접근성 이름. |
31
+ | `className` | `string` | | 패널에 붙는 클래스. |
32
+
33
+ ## 예제
34
+
35
+ 예제를 감싼 `style={{ ... }}` div는 미리보기 배치용이다. 실제 화면 배치는 `Stack` · `Inline` · `Grid`로 한다.
36
+
37
+ ### 작은 폼
38
+
39
+ 클릭하면 열리고 첫 입력칸으로 포커스가 들어가요. children에 함수를 넘기면 close를 받아 저장·취소 뒤에 닫을 수 있어요.
40
+
41
+ ```tsx
42
+ import { useState } from 'react'
43
+ import { Link2 } from 'lucide-react'
44
+ import { Button, Field, Inline, Input, Stack, Popover, toast } from '@iamfiro/design'
45
+
46
+ export default function Example() {
47
+ const [url, setUrl] = useState('')
48
+
49
+ return (
50
+ <Popover trigger={<Button icon={<Link2 />}>링크 추가</Button>} width={320} aria-label="링크 추가">
51
+ {(close) => (
52
+ <Stack
53
+ as="form"
54
+ gap={4}
55
+ onSubmit={(e) => {
56
+ e.preventDefault()
57
+ toast.success('링크를 추가했어요', { description: url })
58
+ setUrl('')
59
+ close()
60
+ }}
61
+ >
62
+ <Field label="주소" htmlFor="link-url">
63
+ <Input id="link-url" type="url" placeholder="https://" value={url} onChange={(e) => setUrl(e.target.value)} />
64
+ </Field>
65
+ <Inline justify="end" gap={2}>
66
+ <Button size="sm" variant="ghost" onClick={close}>
67
+ 취소
68
+ </Button>
69
+ <Button size="sm" variant="primary" type="submit" disabled={!url}>
70
+ 추가
71
+ </Button>
72
+ </Inline>
73
+ </Stack>
74
+ )}
75
+ </Popover>
76
+ )
77
+ }
78
+ ```
79
+
80
+ ### 정렬
81
+
82
+ align으로 트리거의 왼쪽 · 가운데 · 오른쪽 끝에 맞춰요. 화면 오른쪽 끝의 버튼이면 end를 쓰세요.
83
+
84
+ ```tsx
85
+ import { Button, Inline, Text, Popover } from '@iamfiro/design'
86
+
87
+ const ALIGNS = [
88
+ { align: 'start', label: '왼쪽 맞춤 (start)' },
89
+ { align: 'center', label: '가운데 (center)' },
90
+ { align: 'end', label: '오른쪽 맞춤 (end)' },
91
+ ] as const
92
+
93
+ export default function Example() {
94
+ return (
95
+ <Inline gap={3}>
96
+ {ALIGNS.map((a) => (
97
+ <Popover key={a.align} align={a.align} width={240} trigger={<Button>{a.label}</Button>}>
98
+ <Text variant="body-sm" tone="secondary">
99
+ 트리거의 {a.align === 'start' ? '왼쪽' : a.align === 'center' ? '가운데' : '오른쪽'}에 맞춰 열려요. 아래 공간이 부족하면 위로 뒤집혀요.
100
+ </Text>
101
+ </Popover>
102
+ ))}
103
+ </Inline>
104
+ )
105
+ }
106
+ ```
107
+
108
+ ### 호버 미리보기
109
+
110
+ openOn="hover"는 포인터가 잠깐(200ms) 머물러야 열려서 지나가기만 하면 뜨지 않아요. 벗어나도 300ms 유예가 있어 패널로 옮겨 가는 동안 닫히지 않아요. 키보드 포커스로도 바로 열려요. 터치 기기에는 호버가 없으니 꼭 필요한 정보는 여기에만 두지 마세요.
111
+
112
+ ```tsx
113
+ import { Avatar, Badge, Button, Inline, Stack, Text, Popover } from '@iamfiro/design'
114
+
115
+ export default function Example() {
116
+ return (
117
+ <Text as="div" variant="body-md">
118
+ 이 문서는{' '}
119
+ <Popover
120
+ openOn="hover"
121
+ width={280}
122
+ aria-label="김민지 프로필"
123
+ trigger={
124
+ <Button variant="ghost" size="sm" icon={<Avatar name="김민지" size={20} />}>
125
+ 김민지
126
+ </Button>
127
+ }
128
+ >
129
+ <Stack gap={4}>
130
+ <Inline gap={3} wrap={false}>
131
+ <Avatar name="김민지" size={40} online />
132
+ <Stack gap={0}>
133
+ <Text variant="label-lg">김민지</Text>
134
+ <Text variant="body-sm" tone="secondary">
135
+ 디자인 시스템 팀 · 리드
136
+ </Text>
137
+ </Stack>
138
+ </Inline>
139
+ <Text variant="body-sm" tone="secondary">
140
+ Lumen 컴포넌트와 토큰을 관리해요. 오후 6시 이후엔 답이 늦을 수 있어요.
141
+ </Text>
142
+ <Inline gap={2}>
143
+ <Badge tone="success" dot>
144
+ 온라인
145
+ </Badge>
146
+ <Badge>서울</Badge>
147
+ </Inline>
148
+ </Stack>
149
+ </Popover>
150
+ 님이 마지막으로 수정했어요.
151
+ </Text>
152
+ )
153
+ }
154
+ ```
155
+
156
+ ### 여백과 폭
157
+
158
+ 목록·메뉴처럼 자체 여백이 있는 내용은 padding='none'. width는 px 숫자나 CSS 값이고, 화면보다 넓어지지 않아요.
159
+
160
+ ```tsx
161
+ import { Bell, CircleHelp } from 'lucide-react'
162
+ import { Avatar, Button, Inline, Text, List, ListItem, Popover } from '@iamfiro/design'
163
+
164
+ const NOTIFICATIONS = [
165
+ { name: '이도현', text: '디자인 리뷰에 댓글을 남겼어요', time: '2분 전' },
166
+ { name: '최서연', text: '10월 회의록을 공유했어요', time: '1시간 전' },
167
+ { name: '정우진', text: '배포 승인을 요청했어요', time: '어제' },
168
+ ]
169
+
170
+ export default function Example() {
171
+ return (
172
+ <Inline gap={3}>
173
+ <Popover padding="none" width={340} aria-label="알림" trigger={<Button icon={<Bell />}>알림 (padding none)</Button>}>
174
+ {(close) => (
175
+ <List density="comfortable" label="알림">
176
+ {NOTIFICATIONS.map((n) => (
177
+ <ListItem key={n.name} leading={<Avatar name={n.name} size={32} />} title={n.text} description={n.name} meta={n.time} onClick={close} />
178
+ ))}
179
+ </List>
180
+ )}
181
+ </Popover>
182
+ <Popover padding="sm" width={220} trigger={<Button icon={<CircleHelp />}>도움말 (padding sm)</Button>}>
183
+ <Text variant="body-sm" tone="secondary">
184
+ 작은 안내 문구에는 sm, 폼이나 카드형 내용에는 기본값 md를 써요.
185
+ </Text>
186
+ </Popover>
187
+ </Inline>
188
+ )
189
+ }
190
+ ```