@iamfiro/design 0.0.0-stage → 0.3.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 (416) hide show
  1. package/AGENTS.md +82 -0
  2. package/README.md +15 -2
  3. package/agents/INDEX.md +157 -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/social-login-button/AGENTS.md +156 -0
  90. package/agents/spark-chart/AGENTS.md +257 -0
  91. package/agents/sparkbar/AGENTS.md +92 -0
  92. package/agents/spinner/AGENTS.md +123 -0
  93. package/agents/split-view/AGENTS.md +290 -0
  94. package/agents/stat/AGENTS.md +138 -0
  95. package/agents/status-icon/AGENTS.md +243 -0
  96. package/agents/step-list/AGENTS.md +313 -0
  97. package/agents/table/AGENTS.md +286 -0
  98. package/agents/tabs/AGENTS.md +305 -0
  99. package/agents/tag-input/AGENTS.md +246 -0
  100. package/agents/time-picker/AGENTS.md +228 -0
  101. package/agents/timeline/AGENTS.md +1018 -0
  102. package/agents/toast/AGENTS.md +266 -0
  103. package/agents/toggle-group/AGENTS.md +216 -0
  104. package/agents/toolbar/AGENTS.md +277 -0
  105. package/agents/tooltip/AGENTS.md +154 -0
  106. package/agents/tracker/AGENTS.md +158 -0
  107. package/agents/tree-diagram/AGENTS.md +260 -0
  108. package/agents/tree-view/AGENTS.md +329 -0
  109. package/agents/typography/AGENTS.md +396 -0
  110. package/lib/components/Accordion/Accordion2.js +135 -0
  111. package/lib/components/ActionBar/ActionBar2.js +65 -0
  112. package/lib/components/Alert/Alert2.js +131 -0
  113. package/lib/components/AppShell/AppShell2.js +94 -0
  114. package/lib/components/AreaChart/AreaChart.js +59 -0
  115. package/lib/components/BarChart/BarChart2.js +243 -0
  116. package/lib/components/BarList/BarList2.js +172 -0
  117. package/lib/components/Breadcrumb/Breadcrumb2.js +87 -0
  118. package/lib/components/Calendar/Calendar2.js +309 -0
  119. package/lib/components/Calendar/DatePicker2.js +229 -0
  120. package/lib/components/Calendar/date.js +121 -0
  121. package/lib/components/Card/Card2.js +104 -0
  122. package/lib/components/CardFrame/CardFrame2.js +72 -0
  123. package/lib/components/Chart/ChartParts.js +150 -0
  124. package/lib/components/Chart/chart2.js +96 -0
  125. package/lib/components/Chart/useChartInteraction.js +94 -0
  126. package/lib/components/ChatBubble/ChatBubble2.js +174 -0
  127. package/lib/components/ChatBubble/formatChatTime.js +53 -0
  128. package/lib/components/ChatComposer/ChatComposer2.js +153 -0
  129. package/lib/components/CodeBlock/CodeBlock2.js +93 -0
  130. package/lib/components/CodeBlock/highlight.js +114 -0
  131. package/lib/components/ColorPicker/ColorPicker2.js +610 -0
  132. package/lib/components/ColorPicker/color.js +180 -0
  133. package/lib/components/ColorPicker/presets.js +75 -0
  134. package/lib/components/Combobox/Combobox2.js +229 -0
  135. package/lib/components/CommandMenu/CommandMenu.js +267 -0
  136. package/lib/components/Comment/Comment2.js +134 -0
  137. package/lib/components/ContextMenu/ContextMenu2.js +168 -0
  138. package/lib/components/DataGrid/DataGrid2.js +1996 -0
  139. package/lib/components/DataGrid/GridPopover.js +154 -0
  140. package/lib/components/DataGrid/anchor.js +50 -0
  141. package/lib/components/DataGrid/model.js +558 -0
  142. package/lib/components/DataGrid/panels.js +703 -0
  143. package/lib/components/DataGrid/typeMeta.js +76 -0
  144. package/lib/components/DataGrid/types.js +54 -0
  145. package/lib/components/DescriptionList/DescriptionList2.js +180 -0
  146. package/lib/components/Dialog/AlertDialog.js +118 -0
  147. package/lib/components/Dialog/Dialog2.js +109 -0
  148. package/lib/components/Dialog/modal.js +92 -0
  149. package/lib/components/Drawer/Drawer2.js +244 -0
  150. package/lib/components/DropdownMenu/DropdownMenu2.js +394 -0
  151. package/lib/components/DropdownMenu/menuCore.js +63 -0
  152. package/lib/components/Dropzone/Dropzone2.js +144 -0
  153. package/lib/components/EmptyState/EmptyState2.js +72 -0
  154. package/lib/components/FileChip/FileChip2.js +118 -0
  155. package/lib/components/FileUpload/FileUpload.js +258 -0
  156. package/lib/components/FilterChips/FilterChips2.js +101 -0
  157. package/lib/components/FunnelChart/FunnelChart2.js +283 -0
  158. package/lib/components/FunnelChart/funnel.js +69 -0
  159. package/lib/components/Highlight/Highlight2.js +100 -0
  160. package/lib/components/HoverCard/HoverCard.js +134 -0
  161. package/lib/components/InlineDiff/InlineDiff2.js +215 -0
  162. package/lib/components/InlineDiff/diff.js +126 -0
  163. package/lib/components/InlineDiff/useDiffReview.js +109 -0
  164. package/lib/components/Kanban/Kanban2.js +691 -0
  165. package/lib/components/Kanban/KanbanCard.js +159 -0
  166. package/lib/components/Kanban/move.js +69 -0
  167. package/lib/components/LineChart/LineChart2.js +342 -0
  168. package/lib/components/List/List2.js +133 -0
  169. package/lib/components/Listbox/Listbox2.js +163 -0
  170. package/lib/components/Marker/Marker2.js +82 -0
  171. package/lib/components/Menubar/Menubar2.js +223 -0
  172. package/lib/components/MultiSelect/MultiSelect2.js +204 -0
  173. package/lib/components/NavigationMenu/NavigationMenu2.js +481 -0
  174. package/lib/components/NoticeStack/NoticeStack2.js +178 -0
  175. package/lib/components/NumberStepper/NumberStepper2.js +129 -0
  176. package/lib/components/OtpInput/OtpInput2.js +122 -0
  177. package/lib/components/Pagination/Pagination2.js +210 -0
  178. package/lib/components/Pagination/paginationRange.js +53 -0
  179. package/lib/components/PhoneInput/PhoneInput.js +175 -0
  180. package/lib/components/PieChart/PieChart2.js +246 -0
  181. package/lib/components/PieChart/arc.js +66 -0
  182. package/lib/components/PieChart/useArcTween.js +94 -0
  183. package/lib/components/Popover/Popover2.js +112 -0
  184. package/lib/components/ProgressTracker/ProgressTracker2.js +226 -0
  185. package/lib/components/Rating/Rating2.js +186 -0
  186. package/lib/components/Resizable/Resizable2.js +236 -0
  187. package/lib/components/Resizable/layout.js +115 -0
  188. package/lib/components/ScaledPreview/ScaledPreview2.js +100 -0
  189. package/lib/components/ScatterChart/ScatterChart2.js +356 -0
  190. package/lib/components/ScatterChart/scatter.js +83 -0
  191. package/lib/components/ScrollArea/ScrollArea2.js +218 -0
  192. package/lib/components/Select/Select2.js +153 -0
  193. package/lib/components/Sidebar/Sidebar2.js +127 -0
  194. package/lib/components/Skeleton/Skeleton2.js +91 -0
  195. package/lib/components/Slider/Slider2.js +142 -0
  196. package/lib/components/SocialLoginButton/SocialLoginButton.js +85 -0
  197. package/lib/components/SparkChart/SparkChart2.js +217 -0
  198. package/lib/components/SparkChart/spark.js +76 -0
  199. package/lib/components/Sparkbar/Sparkbar2.js +111 -0
  200. package/lib/components/SplitView/SplitView2.js +95 -0
  201. package/lib/components/Stat/Stat2.js +164 -0
  202. package/lib/components/StepList/StepList2.js +131 -0
  203. package/lib/components/Table/Table2.js +104 -0
  204. package/lib/components/Tabs/Tabs2.js +131 -0
  205. package/lib/components/TagInput/TagInput2.js +360 -0
  206. package/lib/components/TimePicker/TimePicker2.js +388 -0
  207. package/lib/components/TimePicker/time.js +83 -0
  208. package/lib/components/Timeline/Timeline2.js +369 -0
  209. package/lib/components/Timeline/scale.js +83 -0
  210. package/lib/components/Toast/Toaster.js +212 -0
  211. package/lib/components/Toast/store.js +129 -0
  212. package/lib/components/ToggleGroup/ToggleGroup2.js +125 -0
  213. package/lib/components/Toolbar/Toolbar2.js +281 -0
  214. package/lib/components/Tracker/Tracker2.js +172 -0
  215. package/lib/components/TreeDiagram/TreeDiagram2.js +223 -0
  216. package/lib/components/TreeView/TreeView2.js +284 -0
  217. package/lib/hooks/timing.js +55 -0
  218. package/lib/hooks/useControllableState.js +49 -0
  219. package/lib/hooks/useCountdown.js +80 -0
  220. package/lib/hooks/useDigitInput.js +105 -0
  221. package/lib/hooks/useDismiss.js +71 -0
  222. package/lib/hooks/useElapsed.js +83 -0
  223. package/lib/hooks/useFloating.js +70 -0
  224. package/lib/hooks/useFocusTrap.js +61 -0
  225. package/lib/hooks/usePresence.js +60 -0
  226. package/lib/hooks/usePressToOpen.js +69 -0
  227. package/lib/hooks/useScrollLock.js +70 -0
  228. package/lib/hooks/useSelection.js +68 -0
  229. package/lib/index.js +178 -0
  230. package/lib/primitives/AspectRatio2.js +80 -0
  231. package/lib/primitives/BrandIcon.js +245 -0
  232. package/lib/primitives/ColorDot2.js +70 -0
  233. package/lib/primitives/CopyButton2.js +85 -0
  234. package/lib/primitives/CountBadge2.js +78 -0
  235. package/lib/primitives/Countdown2.js +65 -0
  236. package/lib/primitives/Delta2.js +67 -0
  237. package/lib/primitives/Elapsed.js +62 -0
  238. package/lib/primitives/FileIcon2.js +125 -0
  239. package/lib/primitives/Flag2.js +263 -0
  240. package/lib/primitives/IconStack2.js +85 -0
  241. package/lib/primitives/IconTile2.js +68 -0
  242. package/lib/primitives/Kbd2.js +88 -0
  243. package/lib/primitives/Layout2.js +118 -0
  244. package/lib/primitives/Link2.js +53 -0
  245. package/lib/primitives/MaskedText2.js +120 -0
  246. package/lib/primitives/Progress2.js +137 -0
  247. package/lib/primitives/RelativeTime.js +84 -0
  248. package/lib/primitives/SearchInput2.js +84 -0
  249. package/lib/primitives/SelectorButton2.js +97 -0
  250. package/lib/primitives/Spinner2.js +98 -0
  251. package/lib/primitives/StatusIcon2.js +126 -0
  252. package/lib/primitives/Swatch2.js +79 -0
  253. package/lib/primitives/Tooltip2.js +132 -0
  254. package/lib/primitives/Typography2.js +97 -0
  255. package/lib/primitives/formatCountdown.js +51 -0
  256. package/lib/primitives/formatElapsed.js +66 -0
  257. package/lib/primitives/formatRelativeTime.js +64 -0
  258. package/lib/primitives/hotkey.js +94 -0
  259. package/lib/primitives/index.js +352 -0
  260. package/lib/primitives/maskValue.js +65 -0
  261. package/lib/style.css +2 -0
  262. package/lib/types/components/Accordion/Accordion.d.ts +57 -0
  263. package/lib/types/components/ActionBar/ActionBar.d.ts +24 -0
  264. package/lib/types/components/Alert/Alert.d.ts +36 -0
  265. package/lib/types/components/AppShell/AppShell.d.ts +38 -0
  266. package/lib/types/components/AreaChart/AreaChart.d.ts +17 -0
  267. package/lib/types/components/BarChart/BarChart.d.ts +57 -0
  268. package/lib/types/components/BarList/BarList.d.ts +56 -0
  269. package/lib/types/components/Breadcrumb/Breadcrumb.d.ts +15 -0
  270. package/lib/types/components/Calendar/Calendar.d.ts +34 -0
  271. package/lib/types/components/Calendar/DatePicker.d.ts +45 -0
  272. package/lib/types/components/Calendar/date.d.ts +32 -0
  273. package/lib/types/components/Card/Card.d.ts +64 -0
  274. package/lib/types/components/CardFrame/CardFrame.d.ts +15 -0
  275. package/lib/types/components/Chart/ChartParts.d.ts +38 -0
  276. package/lib/types/components/Chart/chart.d.ts +46 -0
  277. package/lib/types/components/Chart/useChartInteraction.d.ts +39 -0
  278. package/lib/types/components/ChatBubble/ChatBubble.d.ts +61 -0
  279. package/lib/types/components/ChatBubble/formatChatTime.d.ts +2 -0
  280. package/lib/types/components/ChatComposer/ChatComposer.d.ts +42 -0
  281. package/lib/types/components/CodeBlock/CodeBlock.d.ts +18 -0
  282. package/lib/types/components/CodeBlock/highlight.d.ts +10 -0
  283. package/lib/types/components/ColorPicker/ColorPicker.d.ts +61 -0
  284. package/lib/types/components/ColorPicker/color.d.ts +48 -0
  285. package/lib/types/components/ColorPicker/presets.d.ts +7 -0
  286. package/lib/types/components/Combobox/Combobox.d.ts +48 -0
  287. package/lib/types/components/CommandMenu/CommandMenu.d.ts +51 -0
  288. package/lib/types/components/Comment/Comment.d.ts +45 -0
  289. package/lib/types/components/ContextMenu/ContextMenu.d.ts +40 -0
  290. package/lib/types/components/DataGrid/DataGrid.d.ts +43 -0
  291. package/lib/types/components/DataGrid/GridPopover.d.ts +43 -0
  292. package/lib/types/components/DataGrid/anchor.d.ts +5 -0
  293. package/lib/types/components/DataGrid/model.d.ts +61 -0
  294. package/lib/types/components/DataGrid/panels.d.ts +63 -0
  295. package/lib/types/components/DataGrid/typeMeta.d.ts +6 -0
  296. package/lib/types/components/DataGrid/types.d.ts +77 -0
  297. package/lib/types/components/DescriptionList/DescriptionList.d.ts +77 -0
  298. package/lib/types/components/Dialog/AlertDialog.d.ts +26 -0
  299. package/lib/types/components/Dialog/Dialog.d.ts +42 -0
  300. package/lib/types/components/Dialog/modal.d.ts +38 -0
  301. package/lib/types/components/Drawer/Drawer.d.ts +62 -0
  302. package/lib/types/components/DropdownMenu/DropdownMenu.d.ts +97 -0
  303. package/lib/types/components/DropdownMenu/menuCore.d.ts +23 -0
  304. package/lib/types/components/Dropzone/Dropzone.d.ts +30 -0
  305. package/lib/types/components/EmptyState/EmptyState.d.ts +26 -0
  306. package/lib/types/components/FileChip/FileChip.d.ts +32 -0
  307. package/lib/types/components/FileUpload/FileUpload.d.ts +52 -0
  308. package/lib/types/components/FilterChips/FilterChips.d.ts +37 -0
  309. package/lib/types/components/FunnelChart/FunnelChart.d.ts +38 -0
  310. package/lib/types/components/FunnelChart/funnel.d.ts +25 -0
  311. package/lib/types/components/Highlight/Highlight.d.ts +35 -0
  312. package/lib/types/components/HoverCard/HoverCard.d.ts +47 -0
  313. package/lib/types/components/InlineDiff/InlineDiff.d.ts +36 -0
  314. package/lib/types/components/InlineDiff/diff.d.ts +42 -0
  315. package/lib/types/components/InlineDiff/useDiffReview.d.ts +57 -0
  316. package/lib/types/components/Kanban/Kanban.d.ts +64 -0
  317. package/lib/types/components/Kanban/KanbanCard.d.ts +35 -0
  318. package/lib/types/components/Kanban/move.d.ts +25 -0
  319. package/lib/types/components/LineChart/LineChart.d.ts +85 -0
  320. package/lib/types/components/List/List.d.ts +62 -0
  321. package/lib/types/components/Listbox/Listbox.d.ts +60 -0
  322. package/lib/types/components/Marker/Marker.d.ts +28 -0
  323. package/lib/types/components/Menubar/Menubar.d.ts +50 -0
  324. package/lib/types/components/MultiSelect/MultiSelect.d.ts +38 -0
  325. package/lib/types/components/NavigationMenu/NavigationMenu.d.ts +101 -0
  326. package/lib/types/components/NoticeStack/NoticeStack.d.ts +41 -0
  327. package/lib/types/components/NumberStepper/NumberStepper.d.ts +32 -0
  328. package/lib/types/components/OtpInput/OtpInput.d.ts +31 -0
  329. package/lib/types/components/Pagination/Pagination.d.ts +54 -0
  330. package/lib/types/components/Pagination/paginationRange.d.ts +7 -0
  331. package/lib/types/components/PhoneInput/PhoneInput.d.ts +47 -0
  332. package/lib/types/components/PieChart/PieChart.d.ts +67 -0
  333. package/lib/types/components/PieChart/arc.d.ts +26 -0
  334. package/lib/types/components/PieChart/useArcTween.d.ts +15 -0
  335. package/lib/types/components/Popover/Popover.d.ts +28 -0
  336. package/lib/types/components/ProgressTracker/ProgressTracker.d.ts +64 -0
  337. package/lib/types/components/Rating/Rating.d.ts +42 -0
  338. package/lib/types/components/Resizable/Resizable.d.ts +50 -0
  339. package/lib/types/components/Resizable/layout.d.ts +31 -0
  340. package/lib/types/components/ScaledPreview/ScaledPreview.d.ts +38 -0
  341. package/lib/types/components/ScatterChart/ScatterChart.d.ts +81 -0
  342. package/lib/types/components/ScatterChart/scatter.d.ts +14 -0
  343. package/lib/types/components/ScrollArea/ScrollArea.d.ts +34 -0
  344. package/lib/types/components/Select/Select.d.ts +39 -0
  345. package/lib/types/components/Sidebar/Sidebar.d.ts +39 -0
  346. package/lib/types/components/Skeleton/Skeleton.d.ts +47 -0
  347. package/lib/types/components/Slider/Slider.d.ts +36 -0
  348. package/lib/types/components/SocialLoginButton/SocialLoginButton.d.ts +10 -0
  349. package/lib/types/components/SparkChart/SparkChart.d.ts +55 -0
  350. package/lib/types/components/SparkChart/spark.d.ts +16 -0
  351. package/lib/types/components/Sparkbar/Sparkbar.d.ts +39 -0
  352. package/lib/types/components/SplitView/SplitView.d.ts +31 -0
  353. package/lib/types/components/Stat/Stat.d.ts +58 -0
  354. package/lib/types/components/StepList/StepList.d.ts +61 -0
  355. package/lib/types/components/Table/Table.d.ts +39 -0
  356. package/lib/types/components/Tabs/Tabs.d.ts +47 -0
  357. package/lib/types/components/TagInput/TagInput.d.ts +46 -0
  358. package/lib/types/components/TimePicker/TimePicker.d.ts +43 -0
  359. package/lib/types/components/TimePicker/time.d.ts +15 -0
  360. package/lib/types/components/Timeline/Timeline.d.ts +154 -0
  361. package/lib/types/components/Timeline/scale.d.ts +22 -0
  362. package/lib/types/components/Toast/Toaster.d.ts +19 -0
  363. package/lib/types/components/Toast/index.d.ts +4 -0
  364. package/lib/types/components/Toast/store.d.ts +75 -0
  365. package/lib/types/components/ToggleGroup/ToggleGroup.d.ts +28 -0
  366. package/lib/types/components/Toolbar/Toolbar.d.ts +70 -0
  367. package/lib/types/components/Tracker/Tracker.d.ts +43 -0
  368. package/lib/types/components/TreeDiagram/TreeDiagram.d.ts +68 -0
  369. package/lib/types/components/TreeView/TreeView.d.ts +107 -0
  370. package/lib/types/components/index.d.ts +188 -0
  371. package/lib/types/hooks/index.d.ts +17 -0
  372. package/lib/types/hooks/timing.d.ts +29 -0
  373. package/lib/types/hooks/useControllableState.d.ts +5 -0
  374. package/lib/types/hooks/useCountdown.d.ts +19 -0
  375. package/lib/types/hooks/useDigitInput.d.ts +24 -0
  376. package/lib/types/hooks/useDismiss.d.ts +3 -0
  377. package/lib/types/hooks/useElapsed.d.ts +28 -0
  378. package/lib/types/hooks/useFloating.d.ts +21 -0
  379. package/lib/types/hooks/useFocusTrap.d.ts +7 -0
  380. package/lib/types/hooks/usePresence.d.ts +8 -0
  381. package/lib/types/hooks/usePressToOpen.d.ts +20 -0
  382. package/lib/types/hooks/useScrollLock.d.ts +2 -0
  383. package/lib/types/hooks/useSelection.d.ts +29 -0
  384. package/lib/types/index.d.ts +5 -0
  385. package/lib/types/primitives/AspectRatio.d.ts +14 -0
  386. package/lib/types/primitives/BrandIcon.d.ts +12 -0
  387. package/lib/types/primitives/ColorDot.d.ts +20 -0
  388. package/lib/types/primitives/CopyButton.d.ts +12 -0
  389. package/lib/types/primitives/CountBadge.d.ts +28 -0
  390. package/lib/types/primitives/Countdown.d.ts +21 -0
  391. package/lib/types/primitives/Delta.d.ts +33 -0
  392. package/lib/types/primitives/Elapsed.d.ts +23 -0
  393. package/lib/types/primitives/FileIcon.d.ts +17 -0
  394. package/lib/types/primitives/Flag.d.ts +15 -0
  395. package/lib/types/primitives/IconStack.d.ts +27 -0
  396. package/lib/types/primitives/IconTile.d.ts +24 -0
  397. package/lib/types/primitives/Kbd.d.ts +19 -0
  398. package/lib/types/primitives/Layout.d.ts +64 -0
  399. package/lib/types/primitives/Link.d.ts +12 -0
  400. package/lib/types/primitives/MaskedText.d.ts +28 -0
  401. package/lib/types/primitives/Progress.d.ts +52 -0
  402. package/lib/types/primitives/RelativeTime.d.ts +22 -0
  403. package/lib/types/primitives/SearchInput.d.ts +8 -0
  404. package/lib/types/primitives/SelectorButton.d.ts +23 -0
  405. package/lib/types/primitives/Spinner.d.ts +15 -0
  406. package/lib/types/primitives/StatusIcon.d.ts +40 -0
  407. package/lib/types/primitives/Swatch.d.ts +11 -0
  408. package/lib/types/primitives/Tooltip.d.ts +17 -0
  409. package/lib/types/primitives/Typography.d.ts +48 -0
  410. package/lib/types/primitives/formatCountdown.d.ts +2 -0
  411. package/lib/types/primitives/formatElapsed.d.ts +8 -0
  412. package/lib/types/primitives/formatRelativeTime.d.ts +16 -0
  413. package/lib/types/primitives/hotkey.d.ts +10 -0
  414. package/lib/types/primitives/index.d.ts +188 -0
  415. package/lib/types/primitives/maskValue.d.ts +24 -0
  416. package/package.json +55 -4
@@ -0,0 +1,165 @@
1
+ # Rating
2
+
3
+ > 선택 컨트롤 · `@iamfiro/design` · 직접 만들지 말고 이 컴포넌트를 쓴다.
4
+
5
+ 별점. 포인터를 올리면 누를 때 정해질 값을 미리 보여 주고, 벗어나면 원래 값으로 돌아가요. 누르면 확정되며 별이 살짝 튀어요. 같은 값을 다시 누르면 지워져요(allowClear). 키보드는 radiogroup: ←→ 한 칸, Home/End 끝, Backspace 지우기. 평균처럼 보여 주기만 할 땐 readOnly로 4.3처럼 소수까지 채워요.
6
+
7
+ ```tsx
8
+ import { Rating } from '@iamfiro/design'
9
+ ```
10
+
11
+ ## API
12
+
13
+ ### Rating
14
+
15
+ | prop | 타입 | 기본값 | 설명 |
16
+ | --- | --- | --- | --- |
17
+ | `value` | `number \| null` | | 값(제어). null = 아직 평가 안 함. |
18
+ | `defaultValue` | `number \| null` | `null` | 처음 값(비제어). |
19
+ | `onValueChange` | `(value: number \| null) => void` | | 누르거나 키를 누를 때 호출. 지우면 null. |
20
+ | `max` | `number` | `5` | 아이콘 개수. |
21
+ | `allowHalf` | `boolean` | `false` | 0.5 단위 선택. |
22
+ | `allowClear` | `boolean` | `true` | 같은 값을 다시 누르거나 Backspace/Delete로 지우기. |
23
+ | `readOnly` | `boolean` | | 보여 주기만. 소수(4.3)만큼 채우고 포커스되지 않아요. |
24
+ | `disabled` | `boolean` | | 비활성. |
25
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | 아이콘 16 / 20 / 28px. |
26
+ | `tone` | `'amber' \| 'accent' \| 'danger' \| 'neutral'` | `'amber'` | 채움 색. |
27
+ | `icon` | `ReactNode` | `<Star />` | 다른 아이콘(예: <Heart />). 빈 바탕과 채움에 두 번 그려요. |
28
+ | `labels` | `string[]` | | 값마다 옆에 보일 문구. labels[0]이 1점. |
29
+ | `showValue` | `boolean` | | 숫자(4.3)를 옆에 표시. |
30
+ | `count` | `number` | | 평가 수. "(128)"로 표시. |
31
+ | `name` | `string` | | 폼 제출용 hidden input 이름. |
32
+ | `aria-label` | `string` | `'별점'` | radiogroup 이름. |
33
+
34
+ ## 예제
35
+
36
+ 예제를 감싼 `style={{ ... }}` div는 미리보기 배치용이다. 실제 화면 배치는 `Stack` · `Inline` · `Grid`로 한다.
37
+
38
+ ### 기본
39
+
40
+ labels를 주면 값마다 문구가 옆에 붙고, 미리보기 중에는 옅은 글자로 바뀌어요. 같은 별을 다시 누르면 지워져요.
41
+
42
+ ```tsx
43
+ import { useState } from 'react'
44
+ import { Stack, Text, Rating } from '@iamfiro/design'
45
+
46
+ const LABELS = ['별로예요', '그저 그래요', '괜찮아요', '좋아요', '최고예요']
47
+
48
+ export default function Example() {
49
+ const [score, setScore] = useState<number | null>(4)
50
+ return (
51
+ <Stack gap={2}>
52
+ <Text variant="label-md">배송은 만족스러우셨나요?</Text>
53
+ <Rating value={score} onValueChange={setScore} labels={LABELS} aria-label="배송 만족도" />
54
+ <Text variant="body-sm" tone="tertiary" numeric>
55
+ {score === null ? '아직 평가하지 않았어요' : `${score}점을 남겼어요`}
56
+ </Text>
57
+ </Stack>
58
+ )
59
+ }
60
+ ```
61
+
62
+ ### 반 개 단위
63
+
64
+ allowHalf: 별의 왼쪽 반은 .5, 오른쪽 반은 정수. 키보드도 0.5씩 움직여요.
65
+
66
+ ```tsx
67
+ import { useState } from 'react'
68
+ import { Stack, Text, Rating } from '@iamfiro/design'
69
+
70
+ export default function Example() {
71
+ const [score, setScore] = useState<number | null>(3.5)
72
+ return (
73
+ <Stack gap={2}>
74
+ <Text variant="label-md">이 영화를 평가해 주세요</Text>
75
+ <Rating value={score} onValueChange={setScore} allowHalf showValue size="lg" aria-label="영화 별점" />
76
+ </Stack>
77
+ )
78
+ }
79
+ ```
80
+
81
+ ### 읽기 전용 · 평균
82
+
83
+ readOnly는 포커스되지 않는 그림(role="img")이고 소수만큼 별을 잘라 채워요. showValue로 숫자, count로 "(128)".
84
+
85
+ ```tsx
86
+ import { Stack, Text, Rating } from '@iamfiro/design'
87
+
88
+ const PRODUCTS = [
89
+ { name: '무선 키보드 K3', score: 4.3, count: 128 },
90
+ { name: '노이즈 캔슬링 헤드폰', score: 4.8, count: 2041 },
91
+ { name: 'USB-C 허브 7in1', score: 3.6, count: 57 },
92
+ ]
93
+
94
+ export default function Example() {
95
+ return (
96
+ <Stack gap={4}>
97
+ {PRODUCTS.map((p) => (
98
+ <Stack key={p.name} gap={1}>
99
+ <Text variant="label-md">{p.name}</Text>
100
+ <Rating readOnly value={p.score} showValue count={p.count} size="sm" />
101
+ </Stack>
102
+ ))}
103
+ </Stack>
104
+ )
105
+ }
106
+ ```
107
+
108
+ ### 아이콘과 색
109
+
110
+ icon에 다른 lucide 아이콘을, tone으로 amber(기본) · danger · accent · neutral.
111
+
112
+ ```tsx
113
+ import { Flame, Heart, ThumbsUp } from 'lucide-react'
114
+ import { Stack, Text, Rating } from '@iamfiro/design'
115
+
116
+ export default function Example() {
117
+ return (
118
+ <Stack gap={4}>
119
+ <Stack gap={1}>
120
+ <Text variant="label-sm" tone="secondary">관심도</Text>
121
+ <Rating icon={<Heart />} tone="danger" defaultValue={3} aria-label="관심도" />
122
+ </Stack>
123
+ <Stack gap={1}>
124
+ <Text variant="label-sm" tone="secondary">추천 정도</Text>
125
+ <Rating icon={<ThumbsUp />} tone="accent" max={3} defaultValue={2} labels={['글쎄요', '추천해요', '강력 추천']} aria-label="추천 정도" />
126
+ </Stack>
127
+ <Stack gap={1}>
128
+ <Text variant="label-sm" tone="secondary">매운 정도</Text>
129
+ <Rating icon={<Flame />} tone="neutral" defaultValue={1} labels={['순한맛', '약간 매움', '매움', '아주 매움', '극한']} aria-label="매운 정도" />
130
+ </Stack>
131
+ </Stack>
132
+ )
133
+ }
134
+ ```
135
+
136
+ ### 크기와 상태
137
+
138
+ size sm / md / lg, disabled. 폼에는 name으로 값이 실려요.
139
+
140
+ ```tsx
141
+ import { Stack, Text, Rating } from '@iamfiro/design'
142
+
143
+ export default function Example() {
144
+ return (
145
+ <Stack gap={4}>
146
+ <Stack gap={1}>
147
+ <Text variant="label-sm" tone="secondary">작게 (sm)</Text>
148
+ <Rating size="sm" defaultValue={2} aria-label="작은 별점" />
149
+ </Stack>
150
+ <Stack gap={1}>
151
+ <Text variant="label-sm" tone="secondary">기본 (md) · 지우기 없음</Text>
152
+ <Rating defaultValue={3} allowClear={false} name="score" aria-label="기본 별점" />
153
+ </Stack>
154
+ <Stack gap={1}>
155
+ <Text variant="label-sm" tone="secondary">크게 (lg)</Text>
156
+ <Rating size="lg" defaultValue={4} aria-label="큰 별점" />
157
+ </Stack>
158
+ <Stack gap={1}>
159
+ <Text variant="label-sm" tone="secondary">비활성</Text>
160
+ <Rating disabled defaultValue={3} aria-label="비활성 별점" />
161
+ </Stack>
162
+ </Stack>
163
+ )
164
+ }
165
+ ```
@@ -0,0 +1,103 @@
1
+ # RelativeTime
2
+
3
+ > 라벨 & 배지 · `@iamfiro/design` · 직접 만들지 말고 이 컴포넌트를 쓴다.
4
+
5
+ "방금", "2분 전", "어제", "10월 25일"처럼 한국어 상대 시간을 <time dateTime>으로 보여 줘요. 마우스를 올리면 전체 날짜가 툴팁으로 나오고, 화면에 있는 동안 문구가 바뀌어야 하는 순간에 맞춰 스스로 갱신해요.
6
+
7
+ ```tsx
8
+ import { RelativeTime, formatRelativeTime, formatFullDate } from '@iamfiro/design'
9
+ ```
10
+
11
+ ## 디자인 규칙
12
+
13
+ - 메시지·댓글은 `Comment` + `CommentThread`(선 없이 여백, 같은 사람의 연속 메시지는 `continued`로 가깝게). 상대 시간은 직접 만들지 말고 `RelativeTime` / `formatRelativeTime`.
14
+
15
+ ## API
16
+
17
+ ### RelativeTime
18
+
19
+ | prop | 타입 | 기본값 | 설명 |
20
+ | --- | --- | --- | --- |
21
+ | `date` (필수) | `Date \| string \| number` | | 표시할 시각. |
22
+ | `format` | `(date: Date, now: Date) => string` | `formatRelativeTime` | 문구를 직접 만들 때. |
23
+ | `tooltip` | `boolean` | `true` | 올리면 전체 날짜("2026년 10월 25일 오후 3:24"). |
24
+ | `tooltipSide` | `'top' \| 'bottom' \| 'left' \| 'right'` | `'top'` | 툴팁 방향. |
25
+ | `refreshInterval` | `number \| false` | | 고정 갱신 간격(ms). 생략하면 문구가 바뀌는 순간에 맞춰, false면 갱신하지 않아요. |
26
+ | `variant` | `TextVariant` | `'caption'` | 글자 역할. |
27
+ | `tone` | `TextTone` | `'tertiary'` | 글자 색. |
28
+ | `className` | `string` | | <time>에 전달. |
29
+
30
+ ### formatRelativeTime / formatFullDate / nextRelativeTimeChange
31
+
32
+ 컴포넌트 없이 쓰는 함수들.
33
+
34
+ | prop | 타입 | 기본값 | 설명 |
35
+ | --- | --- | --- | --- |
36
+ | `formatRelativeTime(date, now?)` | `string` | | 한국어 상대 시간. |
37
+ | `formatFullDate(date)` | `string` | | 툴팁용 전체 날짜. |
38
+ | `nextRelativeTimeChange(date, now?)` | `number` | | 문구가 다음에 바뀌기까지 남은 ms(1초~1시간). |
39
+
40
+ ## 예제
41
+
42
+ 예제를 감싼 `style={{ ... }}` div는 미리보기 배치용이다. 실제 화면 배치는 `Stack` · `Inline` · `Grid`로 한다.
43
+
44
+ ### 표현 규칙
45
+
46
+ 45초 안은 "방금", 1시간 안은 분, 같은 날(또는 6시간 안)은 시간, 그다음 "어제" · "N일 전", 일주일이 넘으면 날짜. 해가 다르면 연도도 붙어요. 미래는 "N분 후" · "내일".
47
+
48
+ ```tsx
49
+ import { List, ListItem, RelativeTime } from '@iamfiro/design'
50
+
51
+ const MINUTE = 60_000
52
+ const now = Date.now()
53
+
54
+ const ROWS = [
55
+ { label: '방금 전', date: now - 10_000 },
56
+ { label: '몇 분 전', date: now - 2 * MINUTE },
57
+ { label: '몇 시간 전', date: now - 3 * 60 * MINUTE },
58
+ { label: '어제', date: now - 26 * 60 * MINUTE },
59
+ { label: '며칠 전', date: now - 4 * 24 * 60 * MINUTE },
60
+ { label: '올해', date: now - 40 * 24 * 60 * MINUTE },
61
+ { label: '작년 이전', date: now - 400 * 24 * 60 * MINUTE },
62
+ { label: '앞으로', date: now + 3 * 24 * 60 * MINUTE },
63
+ ]
64
+
65
+ export default function Example() {
66
+ return (
67
+ <List density="compact" style={{ width: 320 }}>
68
+ {ROWS.map((r) => (
69
+ <ListItem key={r.label} title={r.label} trailing={<RelativeTime date={r.date} variant="body-sm" tone="secondary" />} />
70
+ ))}
71
+ </List>
72
+ )
73
+ }
74
+ ```
75
+
76
+ ### 실시간 갱신 · 함수
77
+
78
+ 타이머는 문구가 바뀔 때에만 깨어나요. 컴포넌트 밖에서는 formatRelativeTime(date, now) · formatFullDate(date)를 그대로 써요.
79
+
80
+ ```tsx
81
+ import { useState } from 'react'
82
+ import { Button, Inline, RelativeTime, Stack, Text, formatRelativeTime } from '@iamfiro/design'
83
+
84
+ export default function Example() {
85
+ const [savedAt, setSavedAt] = useState(() => new Date())
86
+ const [yesterday] = useState(() => Date.now() - 26 * 60 * 60_000)
87
+ return (
88
+ <Stack gap={4} align="start">
89
+ <Inline gap={2}>
90
+ <Text variant="body-md">마지막 저장</Text>
91
+ <RelativeTime date={savedAt} variant="body-md" />
92
+ </Inline>
93
+ <Button size="sm" onClick={() => setSavedAt(new Date())}>
94
+ 지금 저장
95
+ </Button>
96
+ <Text variant="caption" tone="tertiary">
97
+ 문구가 바뀌는 순간(1분, 1시간 경계)에만 다시 그려요. 함수로도 쓸 수 있어요: formatRelativeTime(어제) → “
98
+ {formatRelativeTime(yesterday)}”
99
+ </Text>
100
+ </Stack>
101
+ )
102
+ }
103
+ ```
@@ -0,0 +1,390 @@
1
+ # Resizable
2
+
3
+ > 컨테이너 · `@iamfiro/design` · 직접 만들지 말고 이 컴포넌트를 쓴다.
4
+
5
+ 경계를 끌어 크기를 나누는 패널 묶음. 가로 · 세로로 여러 칸을 두고, 그룹 안에 그룹을 넣어 IDE · 메일 같은 배치를 만들어요. 크기는 퍼센트(minSize · maxSize)이고, collapsible 패널은 최소 크기를 한참 지나 끌면 접히고 다시 당기면 펴져요. 경계는 자리를 차지하지 않는 9px 손잡이로, 올리면 얇은 선이, 끄는 동안 · 키보드로 고르면 파란 선이 보여요. 끄는 동안은 그대로 따라오고(미리보기), 놓을 때 확정(onLayout)돼요. 접기 · 펴기와 키보드 이동은 부드럽게 움직여요. 높이는 부모가 정해요.
6
+
7
+ ```tsx
8
+ import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@iamfiro/design'
9
+ ```
10
+
11
+ ## API
12
+
13
+ ### ResizablePanelGroup
14
+
15
+ | prop | 타입 | 기본값 | 설명 |
16
+ | --- | --- | --- | --- |
17
+ | `direction` | `'horizontal' \| 'vertical'` | `'horizontal'` | 가로로 나란히 · 세로로 쌓기. |
18
+ | `children` (필수) | `ReactNode` | | ResizablePanel과 그 사이 ResizableHandle. 직계 자식 또는 Fragment 안에. |
19
+ | `autoSaveId` | `string` | | 확정된 배치를 localStorage에 이 이름으로 기억. |
20
+ | `onLayoutChange` | `(sizes: number[]) => void` | | 끄는 동안의 미리보기(퍼센트). Esc로 취소하면 원래 값으로 한 번 더. |
21
+ | `onLayout` | `(sizes: number[]) => void` | | 확정될 때: 끌기를 놓을 때, 키보드, 접기 · 펴기. |
22
+ | `className / style` | `string / CSSProperties` | | 바깥 요소에. 기본 width · height 100%, 세로 flex 부모 안이면 style={{ flex: 1, minHeight: 0 }}. |
23
+
24
+ ### ResizablePanel
25
+
26
+ | prop | 타입 | 기본값 | 설명 |
27
+ | --- | --- | --- | --- |
28
+ | `defaultSize` | `number` | | 처음 크기(%). 없는 패널끼리 남은 만큼 나눠요. |
29
+ | `minSize / maxSize` | `number` | `10 / 100` | 열려 있을 때의 크기 범위(%). |
30
+ | `collapsible` | `boolean` | | 최소 크기를 절반 넘게 지나 끌면 접힘. 접힌 패널은 그만큼 당겨야 최소 크기로 펼쳐져요. |
31
+ | `collapsedSize` | `number` | `0` | 접혔을 때 크기(%). 0이면 내용은 포커스 순서에서도 빠져요(inert). |
32
+ | `collapsed / defaultCollapsed` | `boolean` | | 접힘 상태(제어 / 비제어). |
33
+ | `onCollapsedChange` | `(collapsed: boolean) => void` | | 사용자가 접거나 펼 때(확정 시점). |
34
+ | `id` | `string` | | 경계의 aria-controls가 가리키는 id. 없으면 자동. |
35
+
36
+ ### ResizableHandle
37
+
38
+ | prop | 타입 | 기본값 | 설명 |
39
+ | --- | --- | --- | --- |
40
+ | `withGrip` | `boolean` | | 경계 가운데 작은 손잡이(평소엔 옅은 회색, 끄는 동안 파랑). |
41
+ | `disabled` | `boolean` | | 끌기 · 키보드 끄기. |
42
+ | `aria-label` | `string` | `'패널 크기 조절'` | role="separator"의 이름. aria-valuenow = 앞 패널 크기(%). |
43
+ | `키보드` | `—` | | ← → (세로면 ↑ ↓), Shift는 크게 · Home / End = 앞 패널 최소 / 최대 · Enter = 옆 collapsible 패널 접기 / 펴기(없으면 처음 비율로) · 끄는 중 Esc = 취소. 더블 클릭은 Enter와 같아요. |
44
+
45
+ ## 예제
46
+
47
+ 예제를 감싼 `style={{ ... }}` div는 미리보기 배치용이다. 실제 화면 배치는 `Stack` · `Inline` · `Grid`로 한다.
48
+
49
+ ### IDE 배치 (중첩 · 접기)
50
+
51
+ 가로 그룹 안 오른쪽 패널에 세로 그룹을 넣었어요. 탐색기와 터미널은 collapsible이라 경계를 더블 클릭하거나 포커스 후 Enter로 접고 펴요. 위 버튼은 collapsed / onCollapsedChange로 같은 상태를 제어해요. 각 칸의 스크롤은 ScrollArea로.
52
+
53
+ ```tsx
54
+ import { useState } from 'react'
55
+ import { Folder, FolderOpen, PanelBottom, PanelLeft } from 'lucide-react'
56
+ import { Box, Button, FileIcon, Inline, Stack, Text, Card, CodeBlock, ResizableHandle, ResizablePanel, ResizablePanelGroup, ScrollArea, TreeView, type TreeNode } from '@iamfiro/design'
57
+
58
+ const file = (id: string, name: string): TreeNode => ({ id, label: name, leading: <FileIcon name={name} size={16} /> })
59
+
60
+ const FILES: TreeNode[] = [
61
+ {
62
+ id: 'src',
63
+ label: 'src',
64
+ children: [
65
+ {
66
+ id: 'src/components',
67
+ label: 'components',
68
+ children: [file('src/components/Button.tsx', 'Button.tsx'), file('src/components/Card.tsx', 'Card.tsx'), file('src/components/Dialog.tsx', 'Dialog.tsx')],
69
+ },
70
+ file('src/App.tsx', 'App.tsx'),
71
+ file('src/main.tsx', 'main.tsx'),
72
+ file('src/styles.css', 'styles.css'),
73
+ ],
74
+ },
75
+ file('package.json', 'package.json'),
76
+ file('README.md', 'README.md'),
77
+ ]
78
+
79
+ const withFolderIcons = (nodes: TreeNode[], open: string[]): TreeNode[] =>
80
+ nodes.map((n) => (n.children ? { ...n, leading: open.includes(n.id) ? <FolderOpen /> : <Folder />, children: withFolderIcons(n.children, open) } : n))
81
+
82
+ const CODE = `import { useState } from 'react'
83
+ import { Button } from './components/Button'
84
+ import { Card } from './components/Card'
85
+
86
+ export default function App() {
87
+ const [count, setCount] = useState(0)
88
+
89
+ return (
90
+ <Card>
91
+ <h1>안녕하세요, 김민지 님</h1>
92
+ <p>오늘 처리할 일이 {count}개 남았어요.</p>
93
+ <Button onClick={() => setCount((c) => c + 1)}>
94
+ 할 일 추가
95
+ </Button>
96
+ </Card>
97
+ )
98
+ }`
99
+
100
+ const LOG = `$ npm run dev
101
+
102
+ VITE v8.0.0 ready in 412 ms
103
+
104
+ ➜ Local: http://localhost:5173/
105
+ ➜ Network: use --host to expose
106
+
107
+ 10:42:03 [vite] hmr update /src/App.tsx
108
+ 10:42:19 [vite] hmr update /src/components/Card.tsx
109
+ 10:43:07 [vite] page reload src/main.tsx`
110
+
111
+ export default function Example() {
112
+ const [expanded, setExpanded] = useState(['src', 'src/components'])
113
+ const [selected, setSelected] = useState<string | null>('src/App.tsx')
114
+ const [explorerClosed, setExplorerClosed] = useState(false)
115
+ const [terminalClosed, setTerminalClosed] = useState(false)
116
+
117
+ return (
118
+ <Card padding="none" style={{ width: '100%', maxWidth: 880, height: 480 }}>
119
+ <Box paddingX={3} paddingY={2}>
120
+ <Inline justify="between" wrap={false}>
121
+ <Text variant="label-md">my-app</Text>
122
+ <Inline gap={1} wrap={false}>
123
+ <Button
124
+ size="sm"
125
+ variant="ghost"
126
+ iconOnly
127
+ icon={<PanelLeft />}
128
+ aria-label={explorerClosed ? '탐색기 열기' : '탐색기 닫기'}
129
+ aria-pressed={!explorerClosed}
130
+ onClick={() => setExplorerClosed((v) => !v)}
131
+ />
132
+ <Button
133
+ size="sm"
134
+ variant="ghost"
135
+ iconOnly
136
+ icon={<PanelBottom />}
137
+ aria-label={terminalClosed ? '터미널 열기' : '터미널 닫기'}
138
+ aria-pressed={!terminalClosed}
139
+ onClick={() => setTerminalClosed((v) => !v)}
140
+ />
141
+ </Inline>
142
+ </Inline>
143
+ </Box>
144
+
145
+ <ResizablePanelGroup direction="horizontal" style={{ flex: 1, minHeight: 0, height: 'auto' }}>
146
+ <ResizablePanel
147
+ defaultSize={26}
148
+ minSize={18}
149
+ maxSize={40}
150
+ collapsible
151
+ collapsed={explorerClosed}
152
+ onCollapsedChange={setExplorerClosed}
153
+ >
154
+ <ScrollArea style={{ height: '100%' }}>
155
+ <Box padding={2}>
156
+ <TreeView
157
+ aria-label="파일 탐색기"
158
+ density="compact"
159
+ items={withFolderIcons(FILES, expanded)}
160
+ expanded={expanded}
161
+ onExpandedChange={setExpanded}
162
+ selected={selected}
163
+ onSelectedChange={setSelected}
164
+ expandOnClick
165
+ />
166
+ </Box>
167
+ </ScrollArea>
168
+ </ResizablePanel>
169
+ <ResizableHandle aria-label="탐색기 너비 조절" />
170
+ <ResizablePanel minSize={40}>
171
+ <ResizablePanelGroup direction="vertical">
172
+ <ResizablePanel minSize={25}>
173
+ <ScrollArea orientation="both" fade style={{ height: '100%' }} aria-label="App.tsx 편집기">
174
+ <Box padding={3}>
175
+ <CodeBlock code={CODE} title={selected ?? 'App.tsx'} showLineNumbers copyable={false} />
176
+ </Box>
177
+ </ScrollArea>
178
+ </ResizablePanel>
179
+ <ResizableHandle aria-label="터미널 높이 조절" />
180
+ <ResizablePanel
181
+ defaultSize={34}
182
+ minSize={20}
183
+ collapsible
184
+ collapsed={terminalClosed}
185
+ onCollapsedChange={setTerminalClosed}
186
+ >
187
+ <ScrollArea stickToBottom style={{ height: '100%' }} aria-label="터미널 출력">
188
+ <Box padding={3}>
189
+ <Stack gap={2}>
190
+ <Text variant="overline" tone="tertiary">
191
+ 터미널
192
+ </Text>
193
+ <CodeBlock code={LOG} language="text" copyable={false} />
194
+ </Stack>
195
+ </Box>
196
+ </ScrollArea>
197
+ </ResizablePanel>
198
+ </ResizablePanelGroup>
199
+ </ResizablePanel>
200
+ </ResizablePanelGroup>
201
+ </Card>
202
+ )
203
+ }
204
+ ```
205
+
206
+ ### 메일 (아이콘 레일로 접기)
207
+
208
+ collapsedSize를 주면 0이 아니라 그 크기로 접혀요. 편지함 칸을 왼쪽으로 끌어 접으면 아이콘만 남은 레일이 되고, 다시 당기면 펼쳐져요. withGrip은 경계에 작은 손잡이를 보여 줘요.
209
+
210
+ ```tsx
211
+ import { useState, type ReactNode } from 'react'
212
+ import { Archive, File, Inbox, Send, Star, Trash2 } from 'lucide-react'
213
+ import { Avatar, Box, Button, CountBadge, Inline, Stack, Text, List, ListItem, ResizableHandle, ResizablePanel, ResizablePanelGroup, ScrollArea, Card } from '@iamfiro/design'
214
+
215
+ const FOLDERS: Array<{ id: string; label: string; icon: ReactNode; count?: number }> = [
216
+ { id: 'inbox', label: '받은편지함', icon: <Inbox />, count: 12 },
217
+ { id: 'star', label: '중요', icon: <Star /> },
218
+ { id: 'sent', label: '보낸편지함', icon: <Send /> },
219
+ { id: 'draft', label: '임시보관함', icon: <File />, count: 2 },
220
+ { id: 'archive', label: '보관함', icon: <Archive /> },
221
+ { id: 'trash', label: '휴지통', icon: <Trash2 /> },
222
+ ]
223
+
224
+ const MAILS = [
225
+ { id: 'm1', from: '김민지', subject: '4분기 로드맵 검토 부탁드려요', preview: '지난주 회의에서 나온 의견을 반영해 일정을 조금 당겼어요.', time: '9:41' },
226
+ { id: 'm2', from: '이도현', subject: '배포 결과 공유', preview: '오후 4시 배포가 문제없이 끝났어요. 오류율은 0.05%예요.', time: '어제' },
227
+ { id: 'm3', from: '박서연', subject: '브랜드 가이드 최종본', preview: '색상 표와 로고 여백 규칙을 마지막으로 정리했어요.', time: '어제' },
228
+ { id: 'm4', from: '정하늘', subject: '사용자 인터뷰 일정', preview: '다음 주 화요일과 목요일 오후로 잡아 두었어요.', time: '10월 23일' },
229
+ { id: 'm5', from: '한지우', subject: '디자인 리뷰 메모', preview: '결제 화면의 버튼 위계를 한 번 더 보면 좋겠어요.', time: '10월 22일' },
230
+ { id: 'm6', from: '오세훈', subject: 'API 문서 업데이트', preview: '새 엔드포인트 세 개와 오류 코드 표를 추가했어요.', time: '10월 21일' },
231
+ ]
232
+
233
+ export default function Example() {
234
+ const [folder, setFolder] = useState('inbox')
235
+ const [mailId, setMailId] = useState('m1')
236
+ const [railed, setRailed] = useState(false)
237
+ const mail = MAILS.find((m) => m.id === mailId) ?? MAILS[0]
238
+
239
+ return (
240
+ <Card padding="none" style={{ width: '100%', maxWidth: 920, height: 460 }}>
241
+ <ResizablePanelGroup direction="horizontal">
242
+ {/* 접으면 아이콘만 남는 레일(collapsedSize 7%) */}
243
+ <ResizablePanel defaultSize={20} minSize={16} maxSize={28} collapsible collapsedSize={7} collapsed={railed} onCollapsedChange={setRailed}>
244
+ <ScrollArea style={{ height: '100%' }}>
245
+ <Box padding={2}>
246
+ {railed ? (
247
+ <Stack gap={1} align="center">
248
+ {FOLDERS.map((f) => (
249
+ <Button
250
+ key={f.id}
251
+ size="sm"
252
+ variant={folder === f.id ? 'secondary' : 'ghost'}
253
+ iconOnly
254
+ icon={f.icon}
255
+ aria-label={f.label}
256
+ onClick={() => setFolder(f.id)}
257
+ />
258
+ ))}
259
+ </Stack>
260
+ ) : (
261
+ <List density="compact" aria-label="편지함">
262
+ {FOLDERS.map((f) => (
263
+ <ListItem
264
+ key={f.id}
265
+ leading={f.icon}
266
+ title={f.label}
267
+ trailing={f.count ? <CountBadge count={f.count} variant="soft" /> : undefined}
268
+ selected={folder === f.id}
269
+ onClick={() => setFolder(f.id)}
270
+ />
271
+ ))}
272
+ </List>
273
+ )}
274
+ </Box>
275
+ </ScrollArea>
276
+ </ResizablePanel>
277
+ <ResizableHandle withGrip aria-label="편지함 너비 조절" />
278
+ <ResizablePanel defaultSize={34} minSize={24}>
279
+ <ScrollArea fade style={{ height: '100%' }}>
280
+ <Box padding={2}>
281
+ <List density="compact" aria-label="메일 목록">
282
+ {MAILS.map((m) => (
283
+ <ListItem
284
+ key={m.id}
285
+ leading={<Avatar name={m.from} size={28} />}
286
+ title={m.subject}
287
+ description={m.preview}
288
+ meta={m.time}
289
+ selected={mailId === m.id}
290
+ onClick={() => setMailId(m.id)}
291
+ />
292
+ ))}
293
+ </List>
294
+ </Box>
295
+ </ScrollArea>
296
+ </ResizablePanel>
297
+ <ResizableHandle withGrip aria-label="메일 목록 너비 조절" />
298
+ <ResizablePanel minSize={30}>
299
+ <ScrollArea style={{ height: '100%' }} aria-label="메일 본문">
300
+ <Box padding={5}>
301
+ <Stack gap={4}>
302
+ <Inline gap={3} wrap={false}>
303
+ <Avatar name={mail.from} size={36} />
304
+ <Stack gap={0}>
305
+ <Text variant="label-md">{mail.from}</Text>
306
+ <Text variant="caption" tone="tertiary">
307
+ {mail.time}
308
+ </Text>
309
+ </Stack>
310
+ </Inline>
311
+ <Text variant="heading-sm">{mail.subject}</Text>
312
+ <Text variant="body-md" tone="secondary">
313
+ {mail.preview} 자세한 내용은 첨부한 문서를 확인해 주세요. 궁금한 점이 있으면 편하게 답장 주세요.
314
+ </Text>
315
+ </Stack>
316
+ </Box>
317
+ </ScrollArea>
318
+ </ResizablePanel>
319
+ </ResizablePanelGroup>
320
+ </Card>
321
+ )
322
+ }
323
+ ```
324
+
325
+ ### 미리보기 · 확정 · 기억
326
+
327
+ onLayoutChange는 끄는 동안 계속, onLayout은 놓을 때 · 키를 누를 때 한 번. autoSaveId를 주면 확정된 배치를 localStorage에 기억해요. 패널과 경계는 그룹의 직계 자식(Fragment 안도 가능)이어야 해요.
328
+
329
+ ```tsx
330
+ import { Fragment, useState } from 'react'
331
+ import { RotateCcw } from 'lucide-react'
332
+ import { Box, Button, Inline, Stack, Text, Card, ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@iamfiro/design'
333
+
334
+ const SAVE_KEY = 'docs-resizable-layout'
335
+ const NAMES = ['목차', '문서', '댓글']
336
+
337
+ const format = (sizes: number[]) => sizes.map((s) => `${Math.round(s)}%`).join(' · ')
338
+
339
+ export default function Example() {
340
+ const [live, setLive] = useState<number[] | null>(null)
341
+ const [committed, setCommitted] = useState<number[] | null>(null)
342
+ const [version, setVersion] = useState(0)
343
+
344
+ const reset = () => {
345
+ try {
346
+ window.localStorage.removeItem(`lumen:resizable:${SAVE_KEY}`)
347
+ } catch {
348
+ /* storage blocked */
349
+ }
350
+ setLive(null)
351
+ setCommitted(null)
352
+ setVersion((v) => v + 1)
353
+ }
354
+
355
+ return (
356
+ <Stack gap={3} style={{ width: '100%', maxWidth: 720 }}>
357
+ <Inline justify="between">
358
+ <Stack gap={1}>
359
+ <Text variant="caption" tone="tertiary" numeric>
360
+ 미리보기 {live ? format(live) : '—'}
361
+ </Text>
362
+ <Text variant="caption" numeric>
363
+ 확정 {committed ? format(committed) : '—'}
364
+ </Text>
365
+ </Stack>
366
+ <Button size="sm" variant="ghost" icon={<RotateCcw />} onClick={reset}>
367
+ 처음 배치로
368
+ </Button>
369
+ </Inline>
370
+ <Card padding="none" style={{ height: 240 }}>
371
+ <ResizablePanelGroup key={version} direction="horizontal" autoSaveId={SAVE_KEY} onLayoutChange={setLive} onLayout={setCommitted}>
372
+ {NAMES.map((name, i) => (
373
+ <Fragment key={name}>
374
+ {i > 0 && <ResizableHandle withGrip aria-label={`${NAMES[i - 1]} · ${name} 경계`} />}
375
+ <ResizablePanel minSize={15}>
376
+ <Box padding={4}>
377
+ <Text variant="label-md">{name}</Text>
378
+ </Box>
379
+ </ResizablePanel>
380
+ </Fragment>
381
+ ))}
382
+ </ResizablePanelGroup>
383
+ </Card>
384
+ <Text variant="caption" tone="tertiary">
385
+ 경계를 끌거나 Tab으로 고른 뒤 ← → (Shift는 크게) · Home / End. 끄는 동안은 미리보기만 바뀌고 놓을 때 확정돼요. 끄는 중 Esc는 원래 자리로. 새로고침해도 배치가 남아 있어요.
386
+ </Text>
387
+ </Stack>
388
+ )
389
+ }
390
+ ```