@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
+ # StatusIcon
2
+
3
+ > 아이콘 & 아바타 · `@iamfiro/design` · 직접 만들지 말고 이 컴포넌트를 쓴다.
4
+
5
+ 체크리스트 · 일정 행 앞에 붙는 작은 단계 아이콘. 대기는 회색 점선 원, 진행 중은 파란 링(돌거나 채워짐), 완료는 회색 체크, 오류는 빨간 ×, 확인 필요는 주황 !예요. 모든 상태가 svg 하나에 들어 있어 상태가 바뀌면 링이 차오르고 체크가 그려지며 자연스럽게 넘어가요.
6
+
7
+ ```tsx
8
+ import { StatusIcon } from '@iamfiro/design'
9
+ ```
10
+
11
+ ## 디자인 규칙
12
+
13
+ - 단계 행 앞 아이콘은 `StatusIcon`(대기 = 회색 점선 원, 진행 = 파란 링, 완료 = 회색 체크, 오류 · 확인 필요). 상태마다 아이콘을 바꿔 끼우지 않고 한 svg 안에서 전환한다.
14
+ - 읽기 전용 단계 타임라인(계획 · 활동 기록 · 승인 흐름)은 `StepList`(`ol`, 마커 + 연결선, `meta` 시간, 아래 내용엔 Collapsible inline). 번호 원은 `StatusIcon number`(`persistNumber`면 모든 상태에서 번호). 사용자가 체크하는 목록은 `ProgressTracker`. 카드 아래 상태 띠는 `CardFooter band`.
15
+
16
+ ## API
17
+
18
+ ### StatusIcon
19
+
20
+ | prop | 타입 | 기본값 | 설명 |
21
+ | --- | --- | --- | --- |
22
+ | `status` (필수) | `'pending' \| 'active' \| 'done' \| 'error' \| 'warning'` | | 단계 상태. |
23
+ | `size` | `'sm' \| 'md' \| 'lg' \| number` | `'md'` | 크기(px 숫자도 가능). |
24
+ | `progress` | `number` | | active일 때 0–100. 생략하면 링이 계속 돌아요. |
25
+ | `colored` | `boolean` | | 완료를 회색 대신 초록으로. |
26
+ | `number` | `number \| string` | | 대기 상태를 회색 실선 원 + 번호("5")로. 다른 상태에선 링 · 기호로 넘어가요. |
27
+ | `persistNumber` | `boolean` | | number와 함께: 모든 상태에서 번호를 남기고 테두리가 상태 색(완료 회색 · 진행 파랑 · 오류 빨강 · 확인 주황)이 돼요. |
28
+ | `label` | `string` | | 스크린 리더가 읽을 이름. 기본은 "완료", "진행 중 40%"처럼 상태 이름이에요. |
29
+ | `decorative` | `boolean` | | 옆 글자가 이미 상태를 말할 때 스크린 리더에서 숨겨요. |
30
+ | `className` | `string` | | 추가 클래스. |
31
+ | `style` | `CSSProperties` | | 인라인 스타일. |
32
+
33
+ ## 예제
34
+
35
+ 예제를 감싼 `style={{ ... }}` div는 미리보기 배치용이다. 실제 화면 배치는 `Stack` · `Inline` · `Grid`로 한다.
36
+
37
+ ### 상태
38
+
39
+ pending · active · done · error · warning. colored를 주면 완료가 초록이 돼요.
40
+
41
+ ```tsx
42
+ import { Inline, StatusIcon, Text, type StatusIconStatus } from '@iamfiro/design'
43
+
44
+ const STATES: Array<{ status: StatusIconStatus; label: string }> = [
45
+ { status: 'pending', label: '대기' },
46
+ { status: 'active', label: '진행 중' },
47
+ { status: 'done', label: '완료' },
48
+ { status: 'error', label: '오류' },
49
+ { status: 'warning', label: '확인 필요' },
50
+ ]
51
+
52
+ export default function Example() {
53
+ return (
54
+ <Inline gap={6}>
55
+ {STATES.map((s) => (
56
+ <Inline key={s.status} gap={2} wrap={false}>
57
+ <StatusIcon status={s.status} decorative />
58
+ <Text variant="label-sm">{s.label}</Text>
59
+ </Inline>
60
+ ))}
61
+ <Inline gap={2} wrap={false}>
62
+ <StatusIcon status="done" colored decorative />
63
+ <Text variant="label-sm">완료 (colored)</Text>
64
+ </Inline>
65
+ </Inline>
66
+ )
67
+ }
68
+ ```
69
+
70
+ ### 작업 단계
71
+
72
+ ListItem의 leading에 놓아 단계 목록을 만들어요. 버튼을 눌러 상태가 바뀌는 움직임을 확인해 보세요.
73
+
74
+ ```tsx
75
+ import { useState } from 'react'
76
+ import { Button, Inline, Stack, StatusIcon, type StatusIconStatus, List, ListItem } from '@iamfiro/design'
77
+
78
+ const STEPS = [
79
+ { id: 'lint', title: '코드 검사', description: '규칙 위반 0건' },
80
+ { id: 'test', title: '테스트', description: '412개 통과' },
81
+ { id: 'build', title: '빌드', description: '번들 크기 확인 중' },
82
+ { id: 'deploy', title: '운영 배포', description: '서울 리전' },
83
+ { id: 'notify', title: '팀에 알리기', description: '#release 채널' },
84
+ ]
85
+
86
+ export default function Example() {
87
+ // Index of the step in progress; steps before it are done.
88
+ const [current, setCurrent] = useState(2)
89
+ const [failed, setFailed] = useState(false)
90
+
91
+ const statusOf = (i: number): StatusIconStatus => {
92
+ if (i < current) return 'done'
93
+ if (i === current) return failed ? 'error' : 'active'
94
+ return 'pending'
95
+ }
96
+
97
+ return (
98
+ <Stack gap={4} style={{ width: '100%', maxWidth: 420 }}>
99
+ <List label="배포 단계">
100
+ {STEPS.map((s, i) => (
101
+ <ListItem
102
+ key={s.id}
103
+ leading={<StatusIcon status={statusOf(i)} />}
104
+ title={s.title}
105
+ description={i === current && failed ? '빌드 스크립트가 종료 코드 1로 끝났어요' : s.description}
106
+ />
107
+ ))}
108
+ </List>
109
+ <Inline gap={2}>
110
+ <Button
111
+ variant="primary"
112
+ size="sm"
113
+ disabled={current >= STEPS.length}
114
+ onClick={() => {
115
+ setFailed(false)
116
+ setCurrent((c) => c + 1)
117
+ }}
118
+ >
119
+ 다음 단계
120
+ </Button>
121
+ <Button size="sm" disabled={current >= STEPS.length} onClick={() => setFailed((f) => !f)}>
122
+ {failed ? '다시 시도' : '실패로 표시'}
123
+ </Button>
124
+ <Button
125
+ variant="ghost"
126
+ size="sm"
127
+ onClick={() => {
128
+ setFailed(false)
129
+ setCurrent(0)
130
+ }}
131
+ >
132
+ 처음부터
133
+ </Button>
134
+ </Inline>
135
+ </Stack>
136
+ )
137
+ }
138
+ ```
139
+
140
+ ### 번호
141
+
142
+ number를 주면 대기 상태가 점선 원 대신 회색 실선 원 + 번호가 돼요. 시작하면 링으로, 끝나면 체크로 넘어가요. persistNumber면 모든 상태에서 번호가 남고 테두리 색만 상태를 따라가요(번호 타임라인).
143
+
144
+ ```tsx
145
+ import { useState } from 'react'
146
+ import { Button, Inline, Stack, StatusIcon, Text, type StatusIconStatus } from '@iamfiro/design'
147
+
148
+ const ORDER: StatusIconStatus[] = ['pending', 'active', 'done']
149
+
150
+ export default function Example() {
151
+ const [step, setStep] = useState(0)
152
+ const status = ORDER[step]
153
+ return (
154
+ <Stack gap={5}>
155
+ <Inline gap={6}>
156
+ <Inline gap={2} wrap={false}>
157
+ <StatusIcon status="pending" number={5} size="lg" decorative />
158
+ <Text variant="label-sm">대기 (number)</Text>
159
+ </Inline>
160
+ <Inline gap={2} wrap={false}>
161
+ <StatusIcon status="pending" number={12} size="lg" decorative />
162
+ <Text variant="label-sm">두 자리</Text>
163
+ </Inline>
164
+ </Inline>
165
+ <Inline gap={4}>
166
+ {(['pending', 'active', 'done', 'error', 'warning'] as const).map((s, i) => (
167
+ <StatusIcon key={s} status={s} number={i + 1} persistNumber size="lg" />
168
+ ))}
169
+ <Text variant="label-sm" tone="secondary">
170
+ persistNumber
171
+ </Text>
172
+ </Inline>
173
+ <Inline gap={3}>
174
+ <StatusIcon status={status} number={3} size="lg" />
175
+ <Button size="sm" onClick={() => setStep((s) => (s + 1) % ORDER.length)}>
176
+ {status === 'pending' ? '시작하기' : status === 'active' ? '완료하기' : '처음으로'}
177
+ </Button>
178
+ </Inline>
179
+ </Stack>
180
+ )
181
+ }
182
+ ```
183
+
184
+ ### 진행률
185
+
186
+ active에 progress(0–100)를 주면 링이 그만큼 채워지고, 없으면 Spinner처럼 돌아요.
187
+
188
+ ```tsx
189
+ import { useState } from 'react'
190
+ import { Button, Inline, Stack, StatusIcon, Text } from '@iamfiro/design'
191
+
192
+ export default function Example() {
193
+ const [progress, setProgress] = useState(35)
194
+ const done = progress >= 100
195
+
196
+ return (
197
+ <Stack gap={4} align="start">
198
+ <Inline gap={6}>
199
+ <Inline gap={2} wrap={false}>
200
+ <StatusIcon status="active" size="lg" />
201
+ <Text variant="label-sm">파일 정리 중</Text>
202
+ </Inline>
203
+ <Inline gap={2} wrap={false}>
204
+ <StatusIcon status={done ? 'done' : 'active'} progress={progress} size="lg" />
205
+ <Text variant="label-sm" numeric>
206
+ {done ? '업로드 완료' : `업로드 ${progress}%`}
207
+ </Text>
208
+ </Inline>
209
+ </Inline>
210
+ <Inline gap={2}>
211
+ <Button size="sm" disabled={done} onClick={() => setProgress((p) => Math.min(100, p + 25))}>
212
+ 25% 올리기
213
+ </Button>
214
+ <Button variant="ghost" size="sm" onClick={() => setProgress(0)}>
215
+ 초기화
216
+ </Button>
217
+ </Inline>
218
+ </Stack>
219
+ )
220
+ }
221
+ ```
222
+
223
+ ### 크기
224
+
225
+ sm 14 · md 16 · lg 20px, 또는 px 숫자. 텍스트 옆에서도 찌그러지지 않아요(flex: none).
226
+
227
+ ```tsx
228
+ import { Inline, StatusIcon } from '@iamfiro/design'
229
+
230
+ export default function Example() {
231
+ return (
232
+ <Inline gap={6}>
233
+ {(['sm', 'md', 'lg', 28] as const).map((size) => (
234
+ <Inline key={size} gap={2} wrap={false}>
235
+ <StatusIcon status="done" size={size} />
236
+ <StatusIcon status="active" progress={60} size={size} />
237
+ <StatusIcon status="pending" size={size} />
238
+ </Inline>
239
+ ))}
240
+ </Inline>
241
+ )
242
+ }
243
+ ```
@@ -0,0 +1,313 @@
1
+ # StepList
2
+
3
+ > 진행 표시 · `@iamfiro/design` · 직접 만들지 말고 이 컴포넌트를 쓴다.
4
+
5
+ 단계를 세로로 이어 보여 주는 읽기 전용 타임라인. 단계마다 왼쪽에 상태 마커(StatusIcon 또는 번호 원)가 있고 가는 연결선으로 이어져요. 제목 오른쪽엔 걸린 시간 같은 meta, 아래엔 회색 설명과 접는 줄 · 경고 줄 같은 내용을 넣어요. 상태가 바뀌면 마커가 자연스럽게 넘어가고, connector="progress"면 끝난 구간의 선이 아래로 차올라요. 사용자가 직접 체크하는 목록은 ProgressTracker를 써요.
6
+
7
+ ```tsx
8
+ import { StepList, StepListItem } from '@iamfiro/design'
9
+ ```
10
+
11
+ ## 디자인 규칙
12
+
13
+ - 읽기 전용 단계 타임라인(계획 · 활동 기록 · 승인 흐름)은 `StepList`(`ol`, 마커 + 연결선, `meta` 시간, 아래 내용엔 Collapsible inline). 번호 원은 `StatusIcon number`(`persistNumber`면 모든 상태에서 번호). 사용자가 체크하는 목록은 `ProgressTracker`. 카드 아래 상태 띠는 `CardFooter band`.
14
+
15
+ ## API
16
+
17
+ ### StepList
18
+
19
+ <ol>로 그려지고 진행 중(active) 단계엔 aria-current="step"이 붙어요.
20
+
21
+ | prop | 타입 | 기본값 | 설명 |
22
+ | --- | --- | --- | --- |
23
+ | `steps` | `StepListStep[]` | | 단계 데이터. 대신 StepListItem을 children으로 조립해도 돼요. |
24
+ | `marker` | `'status' \| 'number' \| 'status-number'` | `'status'` | status: StatusIcon · number: 모든 단계에 번호(테두리가 상태 색) · status-number: StatusIcon인데 대기 단계만 번호. |
25
+ | `connector` | `'default' \| 'progress' \| 'none'` | `'default'` | default: 회색 선 · progress: 끝난 단계 아래 구간이 파랗게 차올라요 · none: 선 없음. |
26
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | 마커 16 · 18 · 22px와 제목 · 설명 글자 크기. |
27
+ | `density` | `'compact' \| 'default' \| 'comfortable'` | `'default'` | 단계 사이 간격 12 · 20 · 32px. |
28
+ | `start` | `number` | `1` | 번호 마커의 첫 번호. |
29
+ | `aria-label` | `string` | | 목록 이름. |
30
+ | `className / style` | `string / CSSProperties` | | <ol>에 전달. |
31
+
32
+ ### StepListStep / StepListItem
33
+
34
+ steps 배열의 항목(id 필수)과 StepListItem의 props는 같아요.
35
+
36
+ | prop | 타입 | 기본값 | 설명 |
37
+ | --- | --- | --- | --- |
38
+ | `id` (필수) | `string` | | steps 배열에서만. key로 쓰여요. |
39
+ | `status` (필수) | `'done' \| 'active' \| 'pending' \| 'error' \| 'warning'` | | 단계 상태. |
40
+ | `title` (필수) | `ReactNode` | | 제목. |
41
+ | `description` | `ReactNode` | | 제목 아래 회색 글. Text variant="code" 칩을 함께 넣을 수 있어요. |
42
+ | `meta` | `ReactNode` | | 제목 줄 오른쪽 끝(걸린 시간 "1.9초", Elapsed). |
43
+ | `children` | `ReactNode` | | 설명 아래 내용(Collapsible inline, 경고 줄, FileChip). |
44
+ | `marker` | `'status' \| 'number' \| 'status-number' \| ReactNode` | | 이 단계만 다른 마커. 노드를 넘기면 그대로 그려요. |
45
+ | `number` | `number \| string` | | 번호 마커의 숫자. 기본은 순서(start부터). |
46
+ | `progress` | `number` | | active일 때 0–100. 링이 돌지 않고 그만큼 채워져요. |
47
+
48
+ ## 예제
49
+
50
+ 예제를 감싼 `style={{ ... }}` div는 미리보기 배치용이다. 실제 화면 배치는 `Stack` · `Inline` · `Grid`로 한다.
51
+
52
+ ### 계획 카드
53
+
54
+ Card + CardHeader(eyebrow 오버라인) + StepList + CardFooter band. marker="status-number"라 대기 단계는 번호, 진행 중은 파란 링, 완료는 회색 체크예요. 진행 중 단계와 아래 띠의 경과 시간은 Elapsed로 계속 올라가요.
55
+
56
+ ```tsx
57
+ import { ListChecks } from 'lucide-react'
58
+ import { Elapsed, Inline, StatusIcon, Text, formatElapsed, Card, CardBody, CardFooter, CardHeader, StepList, type StepListStep } from '@iamfiro/design'
59
+
60
+ const STEPS: StepListStep[] = [
61
+ {
62
+ id: 'collect',
63
+ status: 'done',
64
+ title: '분기 실적 자료 모으기',
65
+ description: (
66
+ <>
67
+ 재무팀 공유 폴더에서 <Text variant="code">q3-actuals.xlsx</Text> 불러옴
68
+ </>
69
+ ),
70
+ meta: '1.9초',
71
+ },
72
+ {
73
+ id: 'compare',
74
+ status: 'done',
75
+ title: '계획 대비 실적 비교',
76
+ description: (
77
+ <>
78
+ 부서별 차이를 <Text variant="code">variance</Text> 시트로 정리
79
+ </>
80
+ ),
81
+ meta: '42초',
82
+ },
83
+ {
84
+ id: 'charts',
85
+ status: 'done',
86
+ title: '핵심 지표 차트 만들기',
87
+ description: '매출 · 영업이익 · 현금흐름 차트 3개',
88
+ meta: '3분 12초',
89
+ },
90
+ {
91
+ id: 'draft',
92
+ status: 'active',
93
+ title: '보고서 초안 작성',
94
+ description: (
95
+ <>
96
+ <Text variant="code">board-update-q3.docx</Text>에 요약과 차트를 넣는 중
97
+ </>
98
+ ),
99
+ // The running step keeps its own timer.
100
+ meta: <Elapsed initial={1052} format={(s) => formatElapsed(s, 'text')} />,
101
+ },
102
+ { id: 'review', status: 'pending', title: '검토 요청 보내기', description: '김민지 팀장에게 검토 요청' },
103
+ ]
104
+
105
+ export default function Example() {
106
+ const done = STEPS.filter((s) => s.status === 'done').length
107
+ return (
108
+ <Card style={{ width: '100%', maxWidth: 480 }}>
109
+ <CardHeader
110
+ eyebrow={
111
+ <Inline gap={1} wrap={false}>
112
+ <ListChecks size={14} aria-hidden />
113
+ 계획 · {STEPS.length}단계
114
+ </Inline>
115
+ }
116
+ title="3분기 이사회 보고서 준비"
117
+ />
118
+ <CardBody>
119
+ <StepList steps={STEPS} marker="status-number" aria-label="보고서 준비 단계" />
120
+ </CardBody>
121
+ <CardFooter band align="between">
122
+ <Inline gap={2} wrap={false}>
123
+ <StatusIcon status="active" size="sm" decorative />
124
+ <Text variant="label-sm" tone="secondary" numeric>
125
+ 실행 중 · {done + 1}/{STEPS.length}
126
+ </Text>
127
+ </Inline>
128
+ <Text variant="label-sm" tone="tertiary">
129
+ <Elapsed initial={1060} />
130
+ </Text>
131
+ </CardFooter>
132
+ </Card>
133
+ )
134
+ }
135
+ ```
136
+
137
+ ### 활동 기록
138
+
139
+ StepListItem을 직접 조립해요. marker="number"는 모든 단계에 번호를 두고 테두리 색만 상태를 따라가요. 내용 자리엔 IconStack 타일을 단 Collapsible variant="inline"과 주황 경고 줄을 넣었어요. 마지막 단계처럼 marker에 노드를 넘기면 원하는 아이콘을 써요.
140
+
141
+ ```tsx
142
+ import type { ReactNode } from 'react'
143
+ import { FileSearch, ListChecks, Server, ShieldCheck, TestTube } from 'lucide-react'
144
+ import { IconStack, IconTile, Inline, Stack, Text, Collapsible, StepList, StepListItem } from '@iamfiro/design'
145
+
146
+ const tile = (key: string, icon: ReactNode) => (
147
+ <IconTile key={key} size={18}>
148
+ {icon}
149
+ </IconTile>
150
+ )
151
+
152
+ export default function Example() {
153
+ return (
154
+ <Stack gap={3} style={{ width: '100%', maxWidth: 480 }}>
155
+ <Text as="h3" variant="heading-xs">
156
+ 활동
157
+ </Text>
158
+ <StepList marker="number" aria-label="배포 활동">
159
+ <StepListItem status="done" title="배포 설정 확인">
160
+ <Collapsible
161
+ variant="inline"
162
+ icon={
163
+ <IconStack
164
+ shape="square"
165
+ size={18}
166
+ overlap={4}
167
+ icons={[tile('config', <FileSearch size={11} />), tile('security', <ShieldCheck size={11} />)]}
168
+ label="설정 검사, 보안 검사"
169
+ />
170
+ }
171
+ label="검사 2개 · 1.2초"
172
+ >
173
+ <Text variant="body-sm" tone="secondary">
174
+ 환경 변수 14개, 보안 정책 3개 모두 통과
175
+ </Text>
176
+ </Collapsible>
177
+ </StepListItem>
178
+ <StepListItem status="done" title="테스트 실행">
179
+ <Collapsible
180
+ variant="inline"
181
+ icon={<IconStack shape="square" size={18} icons={[tile('unit', <TestTube size={11} />)]} label="단위 테스트" />}
182
+ label="테스트 묶음 1개 · 2분 4초"
183
+ >
184
+ <Text variant="body-sm" tone="secondary">
185
+ 812개 통과 · 건너뜀 3개
186
+ </Text>
187
+ </Collapsible>
188
+ </StepListItem>
189
+ <StepListItem status="done" title="스테이징 서버에 배포" description="v2.14.0 · 서버 4대" />
190
+ <StepListItem status="warning" title="운영 서버에 배포">
191
+ <Inline gap={1} wrap={false}>
192
+ <Text as="span" tone="warning" style={{ display: 'inline-flex' }}>
193
+ <ListChecks size={14} aria-hidden />
194
+ </Text>
195
+ <Text variant="label-sm" tone="warning">
196
+ 승인을 기다리고 있어요
197
+ </Text>
198
+ </Inline>
199
+ </StepListItem>
200
+ <StepListItem
201
+ status="pending"
202
+ title="배포 후 모니터링"
203
+ description="마커는 직접 넣을 수도 있어요"
204
+ marker={
205
+ <IconTile size={18} round>
206
+ <Server size={11} />
207
+ </IconTile>
208
+ }
209
+ />
210
+ </StepList>
211
+ </Stack>
212
+ )
213
+ }
214
+ ```
215
+
216
+ ### 상태 변화
217
+
218
+ "다음 단계"를 누르면 마커가 링 → 체크로 넘어가고, connector="progress"의 파란 선이 다음 단계까지 위에서 아래로 차올라요.
219
+
220
+ ```tsx
221
+ import { useState } from 'react'
222
+ import { RotateCcw } from 'lucide-react'
223
+ import { Button, Inline, Stack, StepList, type StepListStatus, type StepListStep } from '@iamfiro/design'
224
+
225
+ const TITLES = ['코드 가져오기', '빌드', '테스트', '이미지 올리기', '배포']
226
+
227
+ export default function Example() {
228
+ // How many steps are finished; the one after them is running.
229
+ const [done, setDone] = useState(1)
230
+ const steps: StepListStep[] = TITLES.map((title, i) => {
231
+ const status: StepListStatus = i < done ? 'done' : i === done ? 'active' : 'pending'
232
+ return { id: title, status, title, meta: i < done ? `${(i + 1) * 7}초` : undefined }
233
+ })
234
+ return (
235
+ <Stack gap={4} style={{ width: '100%', maxWidth: 360 }}>
236
+ <StepList steps={steps} connector="progress" marker="status-number" density="compact" aria-label="배포 파이프라인" />
237
+ <Inline gap={2}>
238
+ <Button size="sm" variant="primary" disabled={done >= TITLES.length} onClick={() => setDone((d) => d + 1)}>
239
+ 다음 단계
240
+ </Button>
241
+ <Button size="sm" variant="ghost" icon={<RotateCcw />} onClick={() => setDone(0)}>
242
+ 처음부터
243
+ </Button>
244
+ </Inline>
245
+ </Stack>
246
+ )
247
+ }
248
+ ```
249
+
250
+ ### 상태
251
+
252
+ done · active · pending · error · warning. 기본 marker="status"는 대기를 회색 점선 원으로 그려요.
253
+
254
+ ```tsx
255
+ import { Text, StepList, type StepListStep } from '@iamfiro/design'
256
+
257
+ const STEPS: StepListStep[] = [
258
+ { id: 'submit', status: 'done', title: '휴가 신청서 제출', description: '이도현 · 10월 25일 오전 9:30' },
259
+ { id: 'lead', status: 'done', title: '팀장 승인', description: '김민지', meta: '12분' },
260
+ {
261
+ id: 'hr',
262
+ status: 'error',
263
+ title: '인사팀 확인',
264
+ description: (
265
+ <>
266
+ 남은 연차가 부족해요 · <Text variant="code">잔여 1.5일</Text>
267
+ </>
268
+ ),
269
+ meta: '3분',
270
+ },
271
+ { id: 'fix', status: 'warning', title: '신청 기간 수정', description: '3일 → 1.5일로 바꿔 주세요' },
272
+ { id: 'final', status: 'pending', title: '최종 승인' },
273
+ ]
274
+
275
+ export default function Example() {
276
+ return <StepList steps={STEPS} aria-label="휴가 승인 단계" style={{ width: '100%', maxWidth: 400 }} />
277
+ }
278
+ ```
279
+
280
+ ### 크기 · 밀도
281
+
282
+ size는 마커와 글자 크기(sm 16 · md 18 · lg 22px), density는 단계 사이 간격(compact 12 · default 20 · comfortable 32px)이에요.
283
+
284
+ ```tsx
285
+ import { Grid, Stack, Text, StepList, type StepListDensity, type StepListSize, type StepListStep } from '@iamfiro/design'
286
+
287
+ const STEPS: StepListStep[] = [
288
+ { id: 'quote', status: 'done', title: '견적 요청', meta: '2분' },
289
+ { id: 'review', status: 'active', title: '견적서 검토' },
290
+ { id: 'sign', status: 'pending', title: '계약서 서명' },
291
+ ]
292
+
293
+ const VARIANTS: Array<{ size: StepListSize; density: StepListDensity }> = [
294
+ { size: 'sm', density: 'compact' },
295
+ { size: 'md', density: 'default' },
296
+ { size: 'lg', density: 'comfortable' },
297
+ ]
298
+
299
+ export default function Example() {
300
+ return (
301
+ <Grid minItemWidth={160} gap={6} style={{ width: '100%' }}>
302
+ {VARIANTS.map(({ size, density }) => (
303
+ <Stack key={size} gap={3}>
304
+ <Text variant="overline">
305
+ {size} · {density}
306
+ </Text>
307
+ <StepList steps={STEPS} size={size} density={density} marker="number" aria-label={`${size} 크기`} />
308
+ </Stack>
309
+ ))}
310
+ </Grid>
311
+ )
312
+ }
313
+ ```