@elabs-ai/components-ui 4.0.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 (449) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +78 -0
  3. package/dist/index.d.ts +4497 -0
  4. package/dist/index.js +15192 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/lib/cn.d.ts +10 -0
  7. package/dist/lib/cn.js +20 -0
  8. package/dist/lib/cn.js.map +1 -0
  9. package/package.json +106 -0
  10. package/src/blocks/sidebar-02/app-sidebar.tsx +160 -0
  11. package/src/blocks/sidebar-02/logo.tsx +18 -0
  12. package/src/blocks/sidebar-02/nav-main.tsx +13 -0
  13. package/src/blocks/sidebar-02/nav-notifications.tsx +7 -0
  14. package/src/blocks/sidebar-02/sidebar-02.stories.tsx +23 -0
  15. package/src/blocks/sidebar-02/team-switcher.tsx +7 -0
  16. package/src/blocks/sidebar-04/app-sidebar.tsx +236 -0
  17. package/src/blocks/sidebar-04/mail-context.tsx +29 -0
  18. package/src/blocks/sidebar-04/nav-user.tsx +6 -0
  19. package/src/blocks/sidebar-04/sidebar-04.stories.tsx +150 -0
  20. package/src/blocks/sidebar-05/app-sidebar.tsx +375 -0
  21. package/src/blocks/sidebar-05/sidebar-05.stories.tsx +18 -0
  22. package/src/blocks/sidebar-05/team-switcher.tsx +6 -0
  23. package/src/blocks-comparison-table.stories.tsx +165 -0
  24. package/src/components/accordion/accordion.stories.tsx +71 -0
  25. package/src/components/accordion/accordion.test.tsx +18 -0
  26. package/src/components/accordion/accordion.tsx +49 -0
  27. package/src/components/accordion/index.ts +1 -0
  28. package/src/components/advanced-group/advanced-group.stories.tsx +143 -0
  29. package/src/components/advanced-group/advanced-group.test.tsx +81 -0
  30. package/src/components/advanced-group/advanced-group.tsx +111 -0
  31. package/src/components/advanced-group/index.ts +2 -0
  32. package/src/components/alert/alert.stories.tsx +156 -0
  33. package/src/components/alert/alert.test.tsx +57 -0
  34. package/src/components/alert/alert.tsx +91 -0
  35. package/src/components/alert/index.ts +8 -0
  36. package/src/components/alert-dialog/alert-dialog.stories.tsx +73 -0
  37. package/src/components/alert-dialog/alert-dialog.tsx +104 -0
  38. package/src/components/alert-dialog/index.ts +12 -0
  39. package/src/components/app-shell/app-shell.stories.tsx +61 -0
  40. package/src/components/app-shell/app-shell.tsx +31 -0
  41. package/src/components/app-shell/index.ts +1 -0
  42. package/src/components/app-sidebar/app-sidebar.stories.tsx +121 -0
  43. package/src/components/app-sidebar/app-sidebar.test.tsx +55 -0
  44. package/src/components/app-sidebar/app-sidebar.tsx +53 -0
  45. package/src/components/app-sidebar/index.ts +1 -0
  46. package/src/components/aspect-ratio/aspect-ratio.stories.tsx +46 -0
  47. package/src/components/aspect-ratio/aspect-ratio.tsx +3 -0
  48. package/src/components/aspect-ratio/index.ts +1 -0
  49. package/src/components/attribution-panel/attribution-panel.stories.tsx +83 -0
  50. package/src/components/attribution-panel/attribution-panel.test.tsx +123 -0
  51. package/src/components/attribution-panel/attribution-panel.tsx +191 -0
  52. package/src/components/attribution-panel/attribution-types.ts +44 -0
  53. package/src/components/attribution-panel/attributions.generated.ts +1422 -0
  54. package/src/components/attribution-panel/index.ts +3 -0
  55. package/src/components/avatar/avatar.stories.tsx +45 -0
  56. package/src/components/avatar/avatar.test.tsx +13 -0
  57. package/src/components/avatar/avatar.tsx +46 -0
  58. package/src/components/avatar/index.ts +1 -0
  59. package/src/components/badge/badge.stories.tsx +44 -0
  60. package/src/components/badge/badge.test.tsx +10 -0
  61. package/src/components/badge/badge.tsx +28 -0
  62. package/src/components/badge/index.ts +1 -0
  63. package/src/components/bento-grid/bento-grid.stories.tsx +165 -0
  64. package/src/components/bento-grid/bento-grid.test.tsx +119 -0
  65. package/src/components/bento-grid/bento-grid.tsx +339 -0
  66. package/src/components/bento-grid/index.ts +9 -0
  67. package/src/components/bounded-number/bounded-number.stories.tsx +118 -0
  68. package/src/components/bounded-number/bounded-number.test.tsx +73 -0
  69. package/src/components/bounded-number/bounded-number.tsx +110 -0
  70. package/src/components/bounded-number/index.ts +1 -0
  71. package/src/components/breadcrumb/breadcrumb.stories.tsx +47 -0
  72. package/src/components/breadcrumb/breadcrumb.test.tsx +17 -0
  73. package/src/components/breadcrumb/breadcrumb.tsx +86 -0
  74. package/src/components/breadcrumb/index.ts +9 -0
  75. package/src/components/button/button.stories.tsx +70 -0
  76. package/src/components/button/button.test.tsx +50 -0
  77. package/src/components/button/button.tsx +63 -0
  78. package/src/components/button/index.ts +1 -0
  79. package/src/components/button-group/button-group.stories.tsx +45 -0
  80. package/src/components/button-group/button-group.tsx +70 -0
  81. package/src/components/button-group/index.ts +6 -0
  82. package/src/components/calendar/calendar.stories.tsx +95 -0
  83. package/src/components/calendar/calendar.test.tsx +140 -0
  84. package/src/components/calendar/calendar.tsx +70 -0
  85. package/src/components/calendar/index.ts +1 -0
  86. package/src/components/card/card.stories.tsx +438 -0
  87. package/src/components/card/card.test.tsx +153 -0
  88. package/src/components/card/card.tsx +332 -0
  89. package/src/components/card/index.ts +13 -0
  90. package/src/components/carousel/carousel.stories.tsx +102 -0
  91. package/src/components/carousel/carousel.tsx +225 -0
  92. package/src/components/carousel/index.ts +8 -0
  93. package/src/components/change-review/change-review.stories.tsx +288 -0
  94. package/src/components/change-review/change-review.test.tsx +261 -0
  95. package/src/components/change-review/change-review.tsx +637 -0
  96. package/src/components/change-review/index.ts +23 -0
  97. package/src/components/checkbox/checkbox.stories.tsx +119 -0
  98. package/src/components/checkbox/checkbox.test.tsx +40 -0
  99. package/src/components/checkbox/checkbox.tsx +34 -0
  100. package/src/components/checkbox/index.ts +1 -0
  101. package/src/components/collapsible/collapsible.stories.tsx +62 -0
  102. package/src/components/collapsible/collapsible.tsx +22 -0
  103. package/src/components/collapsible/index.ts +1 -0
  104. package/src/components/collapsible-panel/index.ts +8 -0
  105. package/src/components/collapsible-panel/use-collapsible-panel.test.tsx +93 -0
  106. package/src/components/collapsible-panel/use-collapsible-panel.ts +159 -0
  107. package/src/components/color-picker/color-picker.stories.tsx +93 -0
  108. package/src/components/color-picker/color-picker.test.tsx +79 -0
  109. package/src/components/color-picker/color-picker.tsx +341 -0
  110. package/src/components/color-picker/index.ts +1 -0
  111. package/src/components/combobox/combobox.stories.tsx +154 -0
  112. package/src/components/combobox/combobox.test.tsx +246 -0
  113. package/src/components/combobox/combobox.tsx +210 -0
  114. package/src/components/combobox/index.ts +1 -0
  115. package/src/components/command/command.stories.tsx +371 -0
  116. package/src/components/command/command.test.tsx +280 -0
  117. package/src/components/command/command.tsx +301 -0
  118. package/src/components/command/index.ts +13 -0
  119. package/src/components/confirm-dialog/confirm-dialog.stories.tsx +183 -0
  120. package/src/components/confirm-dialog/confirm-dialog.test.tsx +147 -0
  121. package/src/components/confirm-dialog/confirm-dialog.tsx +146 -0
  122. package/src/components/confirm-dialog/index.ts +2 -0
  123. package/src/components/context-menu/context-menu.stories.tsx +70 -0
  124. package/src/components/context-menu/context-menu.tsx +172 -0
  125. package/src/components/context-menu/index.ts +16 -0
  126. package/src/components/copyable-value/copyable-value.stories.tsx +80 -0
  127. package/src/components/copyable-value/copyable-value.test.tsx +128 -0
  128. package/src/components/copyable-value/copyable-value.tsx +101 -0
  129. package/src/components/copyable-value/index.ts +1 -0
  130. package/src/components/date-picker/date-picker.stories.tsx +42 -0
  131. package/src/components/date-picker/date-picker.tsx +49 -0
  132. package/src/components/date-picker/index.ts +1 -0
  133. package/src/components/date-range-picker/date-range-picker.stories.tsx +151 -0
  134. package/src/components/date-range-picker/date-range-picker.test.tsx +62 -0
  135. package/src/components/date-range-picker/date-range-picker.tsx +126 -0
  136. package/src/components/date-range-picker/index.ts +6 -0
  137. package/src/components/descriptions/descriptions.stories.tsx +43 -0
  138. package/src/components/descriptions/descriptions.test.tsx +41 -0
  139. package/src/components/descriptions/descriptions.tsx +95 -0
  140. package/src/components/descriptions/index.ts +6 -0
  141. package/src/components/dialog/dialog.stories.tsx +728 -0
  142. package/src/components/dialog/dialog.test.tsx +285 -0
  143. package/src/components/dialog/dialog.tsx +340 -0
  144. package/src/components/dialog/index.ts +18 -0
  145. package/src/components/dialog/use-dialog-dismiss-guard.test.tsx +80 -0
  146. package/src/components/dialog/use-dialog-dismiss-guard.ts +109 -0
  147. package/src/components/drawer/drawer.stories.tsx +76 -0
  148. package/src/components/drawer/drawer.tsx +85 -0
  149. package/src/components/drawer/index.ts +11 -0
  150. package/src/components/dropdown-menu/dropdown-menu.stories.tsx +176 -0
  151. package/src/components/dropdown-menu/dropdown-menu.test.tsx +109 -0
  152. package/src/components/dropdown-menu/dropdown-menu.tsx +179 -0
  153. package/src/components/dropdown-menu/index.ts +17 -0
  154. package/src/components/empty-state/empty-state.stories.tsx +54 -0
  155. package/src/components/empty-state/empty-state.test.tsx +11 -0
  156. package/src/components/empty-state/empty-state.tsx +30 -0
  157. package/src/components/empty-state/index.ts +1 -0
  158. package/src/components/error-state/error-state.stories.tsx +51 -0
  159. package/src/components/error-state/error-state.tsx +27 -0
  160. package/src/components/error-state/index.ts +1 -0
  161. package/src/components/expand-dialog/expand-dialog.stories.tsx +246 -0
  162. package/src/components/expand-dialog/expand-dialog.test.tsx +185 -0
  163. package/src/components/expand-dialog/expand-dialog.tsx +387 -0
  164. package/src/components/expand-dialog/index.ts +11 -0
  165. package/src/components/field-row/field-row.stories.tsx +114 -0
  166. package/src/components/field-row/field-row.test.tsx +188 -0
  167. package/src/components/field-row/field-row.tsx +115 -0
  168. package/src/components/field-row/index.ts +1 -0
  169. package/src/components/file-upload/file-upload.stories.tsx +183 -0
  170. package/src/components/file-upload/file-upload.test.tsx +169 -0
  171. package/src/components/file-upload/file-upload.tsx +459 -0
  172. package/src/components/file-upload/index.ts +13 -0
  173. package/src/components/form/form.stories.tsx +71 -0
  174. package/src/components/form/form.tsx +141 -0
  175. package/src/components/form/index.ts +10 -0
  176. package/src/components/hover-card/hover-card.stories.tsx +57 -0
  177. package/src/components/hover-card/hover-card.tsx +25 -0
  178. package/src/components/hover-card/index.ts +1 -0
  179. package/src/components/icon-button/icon-button.stories.tsx +108 -0
  180. package/src/components/icon-button/icon-button.test.tsx +93 -0
  181. package/src/components/icon-button/icon-button.tsx +139 -0
  182. package/src/components/icon-button/index.ts +1 -0
  183. package/src/components/input/index.ts +1 -0
  184. package/src/components/input/input.stories.tsx +44 -0
  185. package/src/components/input/input.test.tsx +19 -0
  186. package/src/components/input/input.tsx +26 -0
  187. package/src/components/input-group/index.ts +11 -0
  188. package/src/components/input-group/input-group.stories.tsx +87 -0
  189. package/src/components/input-group/input-group.test.tsx +48 -0
  190. package/src/components/input-group/input-group.tsx +170 -0
  191. package/src/components/input-otp/index.ts +1 -0
  192. package/src/components/input-otp/input-otp.stories.tsx +66 -0
  193. package/src/components/input-otp/input-otp.tsx +68 -0
  194. package/src/components/kbd/index.ts +1 -0
  195. package/src/components/kbd/kbd.stories.tsx +45 -0
  196. package/src/components/kbd/kbd.tsx +20 -0
  197. package/src/components/key-value-editor/index.ts +1 -0
  198. package/src/components/key-value-editor/key-value-editor.stories.tsx +130 -0
  199. package/src/components/key-value-editor/key-value-editor.test.tsx +231 -0
  200. package/src/components/key-value-editor/key-value-editor.tsx +263 -0
  201. package/src/components/label/index.ts +1 -0
  202. package/src/components/label/label.stories.tsx +35 -0
  203. package/src/components/label/label.tsx +19 -0
  204. package/src/components/link-preview/index.ts +2 -0
  205. package/src/components/link-preview/link-preview.stories.tsx +178 -0
  206. package/src/components/link-preview/link-preview.test.tsx +123 -0
  207. package/src/components/link-preview/link-preview.tsx +194 -0
  208. package/src/components/list-editor/index.ts +1 -0
  209. package/src/components/list-editor/list-editor.stories.tsx +119 -0
  210. package/src/components/list-editor/list-editor.test.tsx +96 -0
  211. package/src/components/list-editor/list-editor.tsx +192 -0
  212. package/src/components/loading-state/index.ts +1 -0
  213. package/src/components/loading-state/loading-state.stories.tsx +38 -0
  214. package/src/components/loading-state/loading-state.tsx +26 -0
  215. package/src/components/locale-provider/index.ts +8 -0
  216. package/src/components/locale-provider/locale-provider.stories.tsx +83 -0
  217. package/src/components/locale-provider/locale-provider.test.tsx +238 -0
  218. package/src/components/locale-provider/locale-provider.tsx +187 -0
  219. package/src/components/locale-provider/messages.ts +300 -0
  220. package/src/components/match-highlight/index.ts +7 -0
  221. package/src/components/match-highlight/match-highlight.stories.tsx +146 -0
  222. package/src/components/match-highlight/match-highlight.test.tsx +202 -0
  223. package/src/components/match-highlight/match-highlight.tsx +145 -0
  224. package/src/components/mention-input/index.ts +28 -0
  225. package/src/components/mention-input/mention-input-mirror.stories.tsx +171 -0
  226. package/src/components/mention-input/mention-input.stories.tsx +463 -0
  227. package/src/components/mention-input/mention-input.test.tsx +617 -0
  228. package/src/components/mention-input/mention-input.tsx +1102 -0
  229. package/src/components/mention-input/mention-value.test.ts +253 -0
  230. package/src/components/mention-input/mention-value.ts +193 -0
  231. package/src/components/menubar/index.ts +17 -0
  232. package/src/components/menubar/menubar.stories.tsx +67 -0
  233. package/src/components/menubar/menubar.tsx +198 -0
  234. package/src/components/metric-card/index.ts +1 -0
  235. package/src/components/metric-card/metric-card.stories.tsx +224 -0
  236. package/src/components/metric-card/metric-card.test.tsx +198 -0
  237. package/src/components/metric-card/metric-card.tsx +229 -0
  238. package/src/components/model-picker/index.ts +11 -0
  239. package/src/components/model-picker/model-picker-state.ts +85 -0
  240. package/src/components/model-picker/model-picker.stories.tsx +128 -0
  241. package/src/components/model-picker/model-picker.test.tsx +241 -0
  242. package/src/components/model-picker/model-picker.tsx +270 -0
  243. package/src/components/nav-main/index.ts +1 -0
  244. package/src/components/nav-main/nav-main.tsx +147 -0
  245. package/src/components/nav-notifications/index.ts +5 -0
  246. package/src/components/nav-notifications/nav-notifications.test.tsx +15 -0
  247. package/src/components/nav-notifications/nav-notifications.tsx +70 -0
  248. package/src/components/nav-user/index.ts +1 -0
  249. package/src/components/nav-user/nav-user.test.tsx +29 -0
  250. package/src/components/nav-user/nav-user.tsx +114 -0
  251. package/src/components/navigation-menu/index.ts +10 -0
  252. package/src/components/navigation-menu/navigation-menu.stories.tsx +92 -0
  253. package/src/components/navigation-menu/navigation-menu.tsx +99 -0
  254. package/src/components/number-input/index.ts +1 -0
  255. package/src/components/number-input/number-input.stories.tsx +120 -0
  256. package/src/components/number-input/number-input.test.tsx +96 -0
  257. package/src/components/number-input/number-input.tsx +219 -0
  258. package/src/components/page-shell/index.ts +1 -0
  259. package/src/components/page-shell/page-shell.stories.tsx +105 -0
  260. package/src/components/page-shell/page-shell.test.tsx +57 -0
  261. package/src/components/page-shell/page-shell.tsx +62 -0
  262. package/src/components/pagination/index.ts +9 -0
  263. package/src/components/pagination/pagination.stories.tsx +57 -0
  264. package/src/components/pagination/pagination.tsx +77 -0
  265. package/src/components/popover/index.ts +1 -0
  266. package/src/components/popover/popover.stories.tsx +28 -0
  267. package/src/components/popover/popover.test.tsx +16 -0
  268. package/src/components/popover/popover.tsx +29 -0
  269. package/src/components/progress/index.ts +1 -0
  270. package/src/components/progress/progress.stories.tsx +80 -0
  271. package/src/components/progress/progress.test.tsx +35 -0
  272. package/src/components/progress/progress.tsx +50 -0
  273. package/src/components/radio-group/index.ts +1 -0
  274. package/src/components/radio-group/radio-group.stories.tsx +75 -0
  275. package/src/components/radio-group/radio-group.test.tsx +16 -0
  276. package/src/components/radio-group/radio-group.tsx +33 -0
  277. package/src/components/rating/index.ts +1 -0
  278. package/src/components/rating/rating.stories.tsx +111 -0
  279. package/src/components/rating/rating.test.tsx +41 -0
  280. package/src/components/rating/rating.tsx +226 -0
  281. package/src/components/resizable/index.ts +1 -0
  282. package/src/components/resizable/resizable.stories.tsx +25 -0
  283. package/src/components/resizable/resizable.tsx +40 -0
  284. package/src/components/reveal/index.ts +7 -0
  285. package/src/components/reveal/reveal.stories.tsx +90 -0
  286. package/src/components/reveal/reveal.test.tsx +43 -0
  287. package/src/components/reveal/reveal.tsx +95 -0
  288. package/src/components/revision-timeline/index.ts +10 -0
  289. package/src/components/revision-timeline/revision-timeline.stories.tsx +785 -0
  290. package/src/components/revision-timeline/revision-timeline.test.tsx +509 -0
  291. package/src/components/revision-timeline/revision-timeline.tsx +1107 -0
  292. package/src/components/scroll-area/index.ts +1 -0
  293. package/src/components/scroll-area/scroll-area.stories.tsx +45 -0
  294. package/src/components/scroll-area/scroll-area.tsx +43 -0
  295. package/src/components/section-header/index.ts +1 -0
  296. package/src/components/section-header/section-header.stories.tsx +20 -0
  297. package/src/components/section-header/section-header.tsx +36 -0
  298. package/src/components/segmented-field/index.ts +5 -0
  299. package/src/components/segmented-field/segmented-field.stories.tsx +121 -0
  300. package/src/components/segmented-field/segmented-field.test.tsx +163 -0
  301. package/src/components/segmented-field/segmented-field.tsx +235 -0
  302. package/src/components/select/index.ts +10 -0
  303. package/src/components/select/select.stories.tsx +106 -0
  304. package/src/components/select/select.test.tsx +98 -0
  305. package/src/components/select/select.tsx +230 -0
  306. package/src/components/separator/index.ts +2 -0
  307. package/src/components/separator/separator.stories.tsx +87 -0
  308. package/src/components/separator/separator.tsx +51 -0
  309. package/src/components/sheet/index.ts +12 -0
  310. package/src/components/sheet/sheet.stories.tsx +65 -0
  311. package/src/components/sheet/sheet.tsx +109 -0
  312. package/src/components/sidebar/index.ts +27 -0
  313. package/src/components/sidebar/sidebar.stories.tsx +69 -0
  314. package/src/components/sidebar/sidebar.test.tsx +43 -0
  315. package/src/components/sidebar/sidebar.tsx +688 -0
  316. package/src/components/skeleton/index.ts +1 -0
  317. package/src/components/skeleton/skeleton.stories.tsx +28 -0
  318. package/src/components/skeleton/skeleton.tsx +15 -0
  319. package/src/components/slider/index.ts +1 -0
  320. package/src/components/slider/slider.stories.tsx +211 -0
  321. package/src/components/slider/slider.test.tsx +168 -0
  322. package/src/components/slider/slider.tsx +136 -0
  323. package/src/components/slider-number/index.ts +1 -0
  324. package/src/components/slider-number/slider-number.stories.tsx +123 -0
  325. package/src/components/slider-number/slider-number.test.tsx +165 -0
  326. package/src/components/slider-number/slider-number.tsx +191 -0
  327. package/src/components/sonner/index.ts +1 -0
  328. package/src/components/sonner/sonner.stories.tsx +124 -0
  329. package/src/components/sonner/sonner.test.tsx +76 -0
  330. package/src/components/sonner/sonner.tsx +101 -0
  331. package/src/components/spinner/index.ts +1 -0
  332. package/src/components/spinner/spinner.stories.tsx +27 -0
  333. package/src/components/spinner/spinner.tsx +19 -0
  334. package/src/components/split-panel/index.ts +1 -0
  335. package/src/components/split-panel/split-panel.stories.tsx +60 -0
  336. package/src/components/split-panel/split-panel.test.tsx +38 -0
  337. package/src/components/split-panel/split-panel.tsx +95 -0
  338. package/src/components/state-panel/index.ts +1 -0
  339. package/src/components/state-panel/state-panel.stories.tsx +80 -0
  340. package/src/components/state-panel/state-panel.test.tsx +64 -0
  341. package/src/components/state-panel/state-panel.tsx +167 -0
  342. package/src/components/status-badge/index.ts +18 -0
  343. package/src/components/status-badge/status-badge.stories.tsx +132 -0
  344. package/src/components/status-badge/status-badge.test.tsx +189 -0
  345. package/src/components/status-badge/status-badge.tsx +330 -0
  346. package/src/components/switch/index.ts +1 -0
  347. package/src/components/switch/switch.stories.tsx +58 -0
  348. package/src/components/switch/switch.test.tsx +12 -0
  349. package/src/components/switch/switch.tsx +29 -0
  350. package/src/components/table/index.ts +10 -0
  351. package/src/components/table/table.stories.tsx +53 -0
  352. package/src/components/table/table.test.tsx +17 -0
  353. package/src/components/table/table.tsx +106 -0
  354. package/src/components/tabs/index.ts +1 -0
  355. package/src/components/tabs/tabs.stories.tsx +267 -0
  356. package/src/components/tabs/tabs.test.tsx +94 -0
  357. package/src/components/tabs/tabs.tsx +170 -0
  358. package/src/components/tag-input/index.ts +1 -0
  359. package/src/components/tag-input/tag-input.stories.tsx +120 -0
  360. package/src/components/tag-input/tag-input.test.tsx +97 -0
  361. package/src/components/tag-input/tag-input.tsx +253 -0
  362. package/src/components/team-switcher/index.ts +1 -0
  363. package/src/components/team-switcher/team-switcher.test.tsx +39 -0
  364. package/src/components/team-switcher/team-switcher.tsx +100 -0
  365. package/src/components/textarea/index.ts +1 -0
  366. package/src/components/textarea/textarea.stories.tsx +42 -0
  367. package/src/components/textarea/textarea.tsx +24 -0
  368. package/src/components/theme-switcher/index.ts +2 -0
  369. package/src/components/theme-switcher/theme-switcher.stories.tsx +146 -0
  370. package/src/components/theme-switcher/theme-switcher.test.tsx +253 -0
  371. package/src/components/theme-switcher/theme-switcher.tsx +280 -0
  372. package/src/components/theme-switcher/use-theme-transition.ts +62 -0
  373. package/src/components/timeline/index.ts +13 -0
  374. package/src/components/timeline/timeline.stories.tsx +99 -0
  375. package/src/components/timeline/timeline.test.tsx +196 -0
  376. package/src/components/timeline/timeline.tsx +188 -0
  377. package/src/components/toggle/index.ts +1 -0
  378. package/src/components/toggle/toggle.stories.tsx +116 -0
  379. package/src/components/toggle/toggle.test.tsx +119 -0
  380. package/src/components/toggle/toggle.tsx +64 -0
  381. package/src/components/toggle-group/index.ts +1 -0
  382. package/src/components/toggle-group/toggle-group.stories.tsx +110 -0
  383. package/src/components/toggle-group/toggle-group.tsx +59 -0
  384. package/src/components/toolbar/index.ts +14 -0
  385. package/src/components/toolbar/toolbar.stories.tsx +126 -0
  386. package/src/components/toolbar/toolbar.test.tsx +73 -0
  387. package/src/components/toolbar/toolbar.tsx +177 -0
  388. package/src/components/tooltip/index.ts +1 -0
  389. package/src/components/tooltip/tooltip.stories.tsx +67 -0
  390. package/src/components/tooltip/tooltip.tsx +27 -0
  391. package/src/components/top-nav/index.ts +1 -0
  392. package/src/components/top-nav/top-nav.stories.tsx +18 -0
  393. package/src/components/top-nav/top-nav.tsx +34 -0
  394. package/src/components/transfer/index.ts +2 -0
  395. package/src/components/transfer/transfer.stories.tsx +162 -0
  396. package/src/components/transfer/transfer.test.tsx +177 -0
  397. package/src/components/transfer/transfer.tsx +481 -0
  398. package/src/components/tree/index.ts +6 -0
  399. package/src/components/tree/tree.stories.tsx +618 -0
  400. package/src/components/tree/tree.test.tsx +699 -0
  401. package/src/components/tree/tree.tsx +1149 -0
  402. package/src/components/tree/use-tree-keyboard.ts +314 -0
  403. package/src/components/tree-select/index.ts +1 -0
  404. package/src/components/tree-select/tree-select.stories.tsx +127 -0
  405. package/src/components/tree-select/tree-select.test.tsx +40 -0
  406. package/src/components/tree-select/tree-select.tsx +137 -0
  407. package/src/components/typography/index.ts +31 -0
  408. package/src/components/typography/prose.test.tsx +48 -0
  409. package/src/components/typography/prose.tsx +194 -0
  410. package/src/components/typography/typography.stories.tsx +244 -0
  411. package/src/components/typography/typography.test.tsx +140 -0
  412. package/src/components/typography/typography.tsx +126 -0
  413. package/src/components/view-toolbar/index.ts +10 -0
  414. package/src/components/view-toolbar/view-toolbar.stories.tsx +342 -0
  415. package/src/components/view-toolbar/view-toolbar.test.tsx +262 -0
  416. package/src/components/view-toolbar/view-toolbar.tsx +320 -0
  417. package/src/components/virtual-select/index.ts +2 -0
  418. package/src/components/virtual-select/use-virtual-listbox.ts +132 -0
  419. package/src/components/virtual-select/virtual-select.stories.tsx +203 -0
  420. package/src/components/virtual-select/virtual-select.test.tsx +106 -0
  421. package/src/components/virtual-select/virtual-select.tsx +361 -0
  422. package/src/components/wizard/index.ts +12 -0
  423. package/src/components/wizard/wizard.stories.tsx +148 -0
  424. package/src/components/wizard/wizard.test.tsx +67 -0
  425. package/src/components/wizard/wizard.tsx +354 -0
  426. package/src/decoration.stories.tsx +236 -0
  427. package/src/index.ts +174 -0
  428. package/src/lib/cn.test.ts +39 -0
  429. package/src/lib/cn.ts +31 -0
  430. package/src/lib/compact-number.ts +61 -0
  431. package/src/lib/document-address.test.ts +75 -0
  432. package/src/lib/document-address.ts +265 -0
  433. package/src/lib/download.ts +93 -0
  434. package/src/lib/file-icon.test.ts +45 -0
  435. package/src/lib/file-icon.ts +58 -0
  436. package/src/lib/file-kind.test.ts +98 -0
  437. package/src/lib/file-kind.ts +303 -0
  438. package/src/lib/file-source.test.ts +217 -0
  439. package/src/lib/file-source.ts +269 -0
  440. package/src/lib/merge-refs.test.ts +37 -0
  441. package/src/lib/merge-refs.ts +17 -0
  442. package/src/lib/streamdown-translations.ts +99 -0
  443. package/src/lib/use-copy-to-clipboard.ts +75 -0
  444. package/src/lib/use-mobile.ts +16 -0
  445. package/src/motion.stories.tsx +266 -0
  446. package/src/taste-profile.test.tsx +252 -0
  447. package/src/templates-object-detail-hub.stories.tsx +466 -0
  448. package/src/templates-screen-states.stories.tsx +306 -0
  449. package/src/templates-settings.stories.tsx +245 -0
@@ -0,0 +1,170 @@
1
+ import { forwardRef, type ComponentProps } from "react";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import { cn } from "../../lib/cn";
4
+ import { Button } from "../button";
5
+ import { Input } from "../input";
6
+ import { Textarea } from "../textarea";
7
+
8
+ export const inputGroupVariants = cva(
9
+ cn(
10
+ "group/input-group relative flex w-full flex-wrap items-center rounded-md outline-none transition-[color,box-shadow] duration-fast ease-standard",
11
+ // Grow to fit a textarea even when it's nested (e.g. wrapped in a
12
+ // `display:contents` body like PromptInputBody). `:has(>textarea)`
13
+ // misses through that wrapper, so match any descendant textarea.
14
+ "h-9 has-[textarea]:h-auto",
15
+ "has-[[data-slot=input-group-control]:focus-visible]:ring-2 has-[[data-slot=input-group-control]:focus-visible]:ring-ring has-[[data-slot=input-group-control]:focus-visible]:ring-offset-1 has-[[data-slot=input-group-control]:focus-visible]:ring-offset-background",
16
+ "has-[[data-slot=input-group-control][aria-invalid=true]]:ring-2 has-[[data-slot=input-group-control][aria-invalid=true]]:ring-destructive",
17
+ ),
18
+ {
19
+ variants: {
20
+ variant: {
21
+ // The form-field look: the strong `--input` boundary is the field's
22
+ // sole structural cue, so it stays on the strong rung.
23
+ outline: "border border-input shadow-sm",
24
+ // The composer look (#194, research 08 §C.1/§D): a soft fill with the
25
+ // focus-within ring carrying focus — NO hard border. Separation comes
26
+ // from the fill (the chat footer already draws its own `border-t`).
27
+ surface: "bg-surface-muted",
28
+ // The double-card well (#254): a `bg-card` fill meant to nest inside
29
+ // an ALREADY-tinted outer frame (e.g. `bg-surface-muted`) — the outer
30
+ // tint is the redundant/complementary cue, so the well itself needs no
31
+ // border either. NOT universally "white": `--card`'s lightness
32
+ // relative to `--surface-muted` is theme-driven, same trade-off as the
33
+ // `bg-surface-muted`-as-a-well note above — raised (lighter than the
34
+ // outer frame) on light themes, recessed (darker) on dark and
35
+ // a low-chroma palette. The well still reads as a distinct, legible tone in every
36
+ // theme; only the light-themes' "raised" framing is theme-specific.
37
+ card: "bg-card",
38
+ },
39
+ },
40
+ defaultVariants: { variant: "outline" },
41
+ },
42
+ );
43
+
44
+ export type InputGroupProps = ComponentProps<"div"> & VariantProps<typeof inputGroupVariants>;
45
+
46
+ export const InputGroup = forwardRef<HTMLDivElement, InputGroupProps>(function InputGroup(
47
+ { className, variant, ...props },
48
+ ref,
49
+ ) {
50
+ return (
51
+ <div
52
+ ref={ref}
53
+ role="group"
54
+ data-slot="input-group"
55
+ className={cn(inputGroupVariants({ variant }), className)}
56
+ {...props}
57
+ />
58
+ );
59
+ });
60
+
61
+ export const inputGroupAddonVariants = cva(
62
+ "flex h-auto cursor-text items-center gap-2 py-1.5 text-sm font-medium text-muted-foreground select-none [&>svg:not([class*='size-'])]:size-4",
63
+ {
64
+ variants: {
65
+ align: {
66
+ "inline-start": "order-first ps-3 has-[>button]:ms-[-0.45rem]",
67
+ "inline-end": "order-last pe-3 has-[>button]:me-[-0.45rem]",
68
+ "block-start": "order-first w-full justify-start px-3 pt-2.5",
69
+ "block-end": "order-last w-full justify-start px-3 pt-0 pb-2.5",
70
+ },
71
+ },
72
+ defaultVariants: { align: "inline-start" },
73
+ },
74
+ );
75
+
76
+ export const InputGroupAddon = forwardRef<
77
+ HTMLDivElement,
78
+ ComponentProps<"div"> & VariantProps<typeof inputGroupAddonVariants>
79
+ >(function InputGroupAddon({ className, align = "inline-start", ...props }, ref) {
80
+ return (
81
+ <div
82
+ ref={ref}
83
+ role="group"
84
+ data-slot="input-group-addon"
85
+ data-align={align}
86
+ className={cn(inputGroupAddonVariants({ align }), className)}
87
+ {...props}
88
+ />
89
+ );
90
+ });
91
+
92
+ const inputGroupButtonVariants = cva("flex items-center gap-2 text-sm shadow-none", {
93
+ variants: {
94
+ size: {
95
+ xs: "h-6 gap-1 rounded-sm px-2 has-[>svg]:px-2 [&>svg:not([class*='size-'])]:size-3.5",
96
+ sm: "h-8 gap-1.5 rounded-md px-2.5 has-[>svg]:px-2.5",
97
+ "icon-xs": "size-6 rounded-sm p-0 has-[>svg]:p-0",
98
+ "icon-sm": "size-8 p-0 has-[>svg]:p-0",
99
+ },
100
+ },
101
+ defaultVariants: { size: "xs" },
102
+ });
103
+
104
+ export const InputGroupButton = forwardRef<
105
+ HTMLButtonElement,
106
+ Omit<ComponentProps<typeof Button>, "size"> & {
107
+ size?: "xs" | "sm" | "icon-xs" | "icon-sm";
108
+ }
109
+ >(function InputGroupButton(
110
+ { className, type = "button", variant = "ghost", size = "xs", ...props },
111
+ ref,
112
+ ) {
113
+ return (
114
+ <Button
115
+ ref={ref}
116
+ type={type}
117
+ data-size={size}
118
+ variant={variant}
119
+ className={cn(inputGroupButtonVariants({ size }), className)}
120
+ {...props}
121
+ />
122
+ );
123
+ });
124
+
125
+ export const InputGroupText = forwardRef<HTMLSpanElement, ComponentProps<"span">>(
126
+ function InputGroupText({ className, ...props }, ref) {
127
+ return (
128
+ <span
129
+ ref={ref}
130
+ className={cn(
131
+ "flex items-center gap-2 text-sm text-muted-foreground [&_svg:not([class*='size-'])]:size-4",
132
+ className,
133
+ )}
134
+ {...props}
135
+ />
136
+ );
137
+ },
138
+ );
139
+
140
+ export const InputGroupInput = forwardRef<HTMLInputElement, ComponentProps<typeof Input>>(
141
+ function InputGroupInput({ className, ...props }, ref) {
142
+ return (
143
+ <Input
144
+ ref={ref}
145
+ data-slot="input-group-control"
146
+ className={cn(
147
+ "flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 focus-visible:ring-offset-0",
148
+ className,
149
+ )}
150
+ {...props}
151
+ />
152
+ );
153
+ },
154
+ );
155
+
156
+ export const InputGroupTextarea = forwardRef<HTMLTextAreaElement, ComponentProps<typeof Textarea>>(
157
+ function InputGroupTextarea({ className, ...props }, ref) {
158
+ return (
159
+ <Textarea
160
+ ref={ref}
161
+ data-slot="input-group-control"
162
+ className={cn(
163
+ "flex-1 resize-none rounded-none border-0 bg-transparent py-2.5 shadow-none focus-visible:ring-0 focus-visible:ring-offset-0",
164
+ className,
165
+ )}
166
+ {...props}
167
+ />
168
+ );
169
+ },
170
+ );
@@ -0,0 +1 @@
1
+ export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator } from "./input-otp";
@@ -0,0 +1,66 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { expect } from "storybook/test";
3
+ import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from "./input-otp";
4
+ const meta = {
5
+ title: "Forms/InputOTP",
6
+ component: InputOTP,
7
+ tags: ["autodocs"],
8
+ argTypes: {
9
+ maxLength: {
10
+ description: "Total number of OTP digits.",
11
+ control: "number",
12
+ table: { category: "Behavior" },
13
+ },
14
+ value: {
15
+ description: "Controlled OTP value string.",
16
+ control: "text",
17
+ table: { category: "State" },
18
+ },
19
+ onChange: {
20
+ description: "Called when the OTP value changes.",
21
+ control: false,
22
+ table: { category: "Behavior" },
23
+ },
24
+ disabled: {
25
+ description: "Disables the OTP input.",
26
+ control: "boolean",
27
+ table: { category: "State" },
28
+ },
29
+ pattern: {
30
+ description: "Regex pattern to validate each character (e.g. digits-only).",
31
+ control: false,
32
+ table: { category: "Behavior" },
33
+ },
34
+ className: {
35
+ description: "Additional CSS classes applied to the input element.",
36
+ control: "text",
37
+ table: { category: "Appearance" },
38
+ },
39
+ },
40
+ } satisfies Meta<typeof InputOTP>;
41
+ export default meta;
42
+ type Story = StoryObj<typeof meta>;
43
+ export const Default: Story = {
44
+ render: () => (
45
+ <InputOTP maxLength={6}>
46
+ <InputOTPGroup>
47
+ <InputOTPSlot index={0} />
48
+ <InputOTPSlot index={1} />
49
+ <InputOTPSlot index={2} />
50
+ </InputOTPGroup>
51
+ <InputOTPSeparator />
52
+ <InputOTPGroup>
53
+ <InputOTPSlot index={3} />
54
+ <InputOTPSlot index={4} />
55
+ <InputOTPSlot index={5} />
56
+ </InputOTPGroup>
57
+ </InputOTP>
58
+ ),
59
+ // Types three digits into the OTP and confirms the input reflects the value.
60
+ play: async ({ canvas, userEvent }) => {
61
+ const input = canvas.getByRole("textbox");
62
+ await userEvent.click(input);
63
+ await userEvent.type(input, "123");
64
+ await expect(input).toHaveValue("123");
65
+ },
66
+ };
@@ -0,0 +1,68 @@
1
+ import { forwardRef, useContext, type ComponentPropsWithoutRef, type ElementRef } from "react";
2
+ import { OTPInput, OTPInputContext } from "input-otp";
3
+ import { Minus } from "lucide-react";
4
+ import { cn } from "../../lib/cn";
5
+
6
+ export const InputOTP = forwardRef<
7
+ ElementRef<typeof OTPInput>,
8
+ ComponentPropsWithoutRef<typeof OTPInput>
9
+ >(function InputOTP({ className, containerClassName, ...props }, ref) {
10
+ return (
11
+ <OTPInput
12
+ ref={ref}
13
+ containerClassName={cn(
14
+ "flex items-center gap-2 has-[:disabled]:opacity-50",
15
+ containerClassName,
16
+ )}
17
+ className={cn("disabled:cursor-not-allowed", className)}
18
+ {...props}
19
+ />
20
+ );
21
+ });
22
+
23
+ export const InputOTPGroup = forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
24
+ function InputOTPGroup({ className, ...props }, ref) {
25
+ return <div ref={ref} className={cn("flex items-center", className)} {...props} />;
26
+ },
27
+ );
28
+
29
+ export const InputOTPSlot = forwardRef<
30
+ HTMLDivElement,
31
+ React.HTMLAttributes<HTMLDivElement> & { index: number }
32
+ >(function InputOTPSlot({ index, className, ...props }, ref) {
33
+ const ctx = useContext(OTPInputContext);
34
+ const slot = ctx.slots[index];
35
+ const { char, hasFakeCaret, isActive } = slot ?? {
36
+ char: null,
37
+ hasFakeCaret: false,
38
+ isActive: false,
39
+ };
40
+ return (
41
+ <div
42
+ ref={ref}
43
+ className={cn(
44
+ "relative flex size-9 items-center justify-center border-y border-e border-input text-sm shadow-sm transition-[color,border-color,box-shadow] duration-fast ease-standard first:rounded-s-md first:border-s last:rounded-e-md",
45
+ isActive && "z-10 ring-2 ring-ring",
46
+ className,
47
+ )}
48
+ {...props}
49
+ >
50
+ {char}
51
+ {hasFakeCaret && (
52
+ <div className="pointer-events-none absolute inset-0 flex items-center justify-center">
53
+ <div className="h-4 w-px animate-caret-blink bg-foreground" />
54
+ </div>
55
+ )}
56
+ </div>
57
+ );
58
+ });
59
+
60
+ export const InputOTPSeparator = forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
61
+ function InputOTPSeparator(props, ref) {
62
+ return (
63
+ <div ref={ref} role="separator" {...props}>
64
+ <Minus className="size-4 text-muted-foreground" />
65
+ </div>
66
+ );
67
+ },
68
+ );
@@ -0,0 +1 @@
1
+ export { Kbd } from "./kbd";
@@ -0,0 +1,45 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { Kbd } from "./kbd";
3
+ const meta = {
4
+ title: "Display/Kbd",
5
+ component: Kbd,
6
+ tags: ["autodocs"],
7
+ argTypes: {
8
+ children: {
9
+ description:
10
+ 'Key label — a glyph (⌘, ⇧) or short text (Enter, K). Wrapped in `translate="no"`.',
11
+ control: "text",
12
+ table: { category: "Content" },
13
+ },
14
+ className: {
15
+ description: "Extra Tailwind classes merged via cn().",
16
+ control: "text",
17
+ table: { category: "Appearance" },
18
+ },
19
+ },
20
+ } satisfies Meta<typeof Kbd>;
21
+ export default meta;
22
+ type Story = StoryObj<typeof meta>;
23
+ export const Default: Story = {
24
+ render: () => (
25
+ <span className="text-body">
26
+ Press <Kbd>⌘</Kbd> <Kbd>K</Kbd>
27
+ </span>
28
+ ),
29
+ };
30
+
31
+ export const Combination: Story = {
32
+ render: () => (
33
+ <span className="text-body">
34
+ <Kbd>⇧</Kbd> <Kbd>⌘</Kbd> <Kbd>P</Kbd>
35
+ </span>
36
+ ),
37
+ };
38
+
39
+ export const TextKey: Story = {
40
+ render: () => (
41
+ <span className="text-body">
42
+ Press <Kbd>Enter</Kbd> to confirm
43
+ </span>
44
+ ),
45
+ };
@@ -0,0 +1,20 @@
1
+ import { forwardRef, type HTMLAttributes } from "react";
2
+ import { cn } from "../../lib/cn";
3
+
4
+ /** Keyboard key hint, e.g. <Kbd>⌘K</Kbd>. */
5
+ export const Kbd = forwardRef<HTMLElement, HTMLAttributes<HTMLElement>>(function Kbd(
6
+ { className, ...props },
7
+ ref,
8
+ ) {
9
+ return (
10
+ <kbd
11
+ translate="no"
12
+ ref={ref}
13
+ className={cn(
14
+ "inline-flex h-5 min-w-5 select-none items-center justify-center gap-1 rounded border bg-muted px-1.5 font-mono text-[0.7rem] font-medium text-muted-foreground",
15
+ className,
16
+ )}
17
+ {...props}
18
+ />
19
+ );
20
+ });
@@ -0,0 +1 @@
1
+ export { KeyValueEditor, type KeyValueEditorProps, type KeyValueRow } from "./key-value-editor";
@@ -0,0 +1,130 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { expect } from "storybook/test";
3
+ import { useState } from "react";
4
+ import { KeyValueEditor, type KeyValueRow } from "./key-value-editor";
5
+
6
+ const meta = {
7
+ title: "Forms/KeyValueEditor",
8
+ component: KeyValueEditor,
9
+ tags: ["autodocs"],
10
+ args: {
11
+ keyPlaceholder: "KEY…",
12
+ valuePlaceholder: "value…",
13
+ },
14
+ argTypes: {
15
+ value: {
16
+ description: "Controlled, ordered rows.",
17
+ control: false,
18
+ table: { category: "State" },
19
+ },
20
+ defaultValue: {
21
+ description: "Uncontrolled initial rows.",
22
+ control: false,
23
+ table: { category: "State" },
24
+ },
25
+ keyPlaceholder: {
26
+ description: "Placeholder for each row's key field.",
27
+ control: "text",
28
+ table: { category: "Content" },
29
+ },
30
+ valuePlaceholder: {
31
+ description: "Placeholder for each row's value field.",
32
+ control: "text",
33
+ table: { category: "Content" },
34
+ },
35
+ addLabel: {
36
+ description: 'Label for the "add row" control.',
37
+ control: "text",
38
+ table: { category: "Content" },
39
+ },
40
+ disabled: {
41
+ description: "Disables the whole editor.",
42
+ control: "boolean",
43
+ table: { category: "State" },
44
+ },
45
+ onValueChange: {
46
+ description: "Called when the rows change.",
47
+ control: false,
48
+ table: { category: "Behavior" },
49
+ },
50
+ className: {
51
+ description: "Additional CSS classes applied to the root element.",
52
+ control: "text",
53
+ table: { category: "Appearance" },
54
+ },
55
+ },
56
+ } satisfies Meta<typeof KeyValueEditor>;
57
+ export default meta;
58
+ type Story = StoryObj<typeof meta>;
59
+
60
+ export const Default: Story = {
61
+ args: {
62
+ defaultValue: [
63
+ { key: "REGION", value: "us-east-1" },
64
+ { key: "LOG_LEVEL", value: "debug" },
65
+ ],
66
+ },
67
+ };
68
+
69
+ /** A secret row renders masked with a reveal toggle; its value is never exposed while masked. */
70
+ export const WithSecret: Story = {
71
+ args: {
72
+ defaultValue: [
73
+ { key: "REGION", value: "us-east-1" },
74
+ { key: "API_KEY", value: "sk-live-51H8x9K", secret: true },
75
+ ],
76
+ },
77
+ play: async ({ canvas, userEvent }) => {
78
+ const valueInput = canvas.getByLabelText("Value 2");
79
+ await expect(valueInput).toHaveAttribute("type", "password");
80
+
81
+ const revealBtn = canvas.getByRole("button", { name: /reveal value 2/i });
82
+ await userEvent.click(revealBtn);
83
+ await expect(valueInput).toHaveAttribute("type", "text");
84
+ await expect(valueInput).toHaveValue("sk-live-51H8x9K");
85
+
86
+ await userEvent.click(canvas.getByRole("button", { name: /hide value 2/i }));
87
+ await expect(valueInput).toHaveAttribute("type", "password");
88
+ },
89
+ };
90
+
91
+ export const Controlled: Story = {
92
+ render: () => {
93
+ // eslint-disable-next-line react-hooks/rules-of-hooks
94
+ const [rows, setRows] = useState<KeyValueRow[]>([{ key: "TOKEN", value: "", secret: true }]);
95
+ return (
96
+ <div className="flex flex-col gap-2 max-w-md">
97
+ <KeyValueEditor value={rows} onValueChange={setRows} />
98
+ <p className="text-body text-muted-foreground">{rows.length} row(s)</p>
99
+ </div>
100
+ );
101
+ },
102
+ };
103
+
104
+ /** Add a row, edit its key/value, and remove it — all via the composed primitives. */
105
+ export const AddEditRemove: Story = {
106
+ args: { defaultValue: [] },
107
+ play: async ({ canvas, userEvent }) => {
108
+ await userEvent.click(canvas.getByRole("button", { name: /add row/i }));
109
+ const keyInput = canvas.getByLabelText("Key 1");
110
+ const valueInput = canvas.getByLabelText("Value 1");
111
+ await userEvent.type(keyInput, "REGION");
112
+ await userEvent.type(valueInput, "eu-west-1");
113
+ await expect(keyInput).toHaveValue("REGION");
114
+ await expect(valueInput).toHaveValue("eu-west-1");
115
+
116
+ await userEvent.click(canvas.getByRole("button", { name: /remove row 1/i }));
117
+ await expect(canvas.queryByLabelText("Key 1")).not.toBeInTheDocument();
118
+ },
119
+ };
120
+
121
+ export const Disabled: Story = {
122
+ args: { defaultValue: [{ key: "LOCKED", value: "true" }], disabled: true },
123
+ };
124
+
125
+ export const Empty: Story = {
126
+ args: { defaultValue: [] },
127
+ play: async ({ canvas }) => {
128
+ await expect(canvas.getByText("No entries yet.")).toBeInTheDocument();
129
+ },
130
+ };