@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,132 @@
1
+ // a2ui.exposed: yes
2
+ "use client";
3
+
4
+ import { useCallback, useEffect, useState } from "react";
5
+ import type { Virtualizer } from "@tanstack/react-virtual";
6
+
7
+ export interface VirtualSelectOption {
8
+ label: string;
9
+ value: string;
10
+ disabled?: boolean;
11
+ }
12
+
13
+ interface UseVirtualListboxOptions {
14
+ count: number;
15
+ filtered: VirtualSelectOption[];
16
+ /** Reserved for future use (e.g. multi-select keyboard shortcuts). */
17
+ multiple?: boolean;
18
+ isOpen: boolean;
19
+ onSelect: (value: string) => void;
20
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
+ virtualizer: Virtualizer<any, any> | null;
22
+ }
23
+
24
+ interface UseVirtualListboxReturn {
25
+ activeIndex: number;
26
+ setActiveIndex: (index: number) => void;
27
+ onInputKeyDown: (e: React.KeyboardEvent<HTMLInputElement>) => void;
28
+ }
29
+
30
+ /** First non-disabled option index at or after `from`; -1 if none exists. */
31
+ function findNext(filtered: VirtualSelectOption[], from: number): number {
32
+ for (let i = Math.max(0, from); i < filtered.length; i++) {
33
+ if (!filtered[i]?.disabled) return i;
34
+ }
35
+ return -1;
36
+ }
37
+
38
+ /** Last non-disabled option index at or before `from`; -1 if none exists. */
39
+ function findPrev(filtered: VirtualSelectOption[], from: number): number {
40
+ for (let i = Math.min(from, filtered.length - 1); i >= 0; i--) {
41
+ if (!filtered[i]?.disabled) return i;
42
+ }
43
+ return -1;
44
+ }
45
+
46
+ /**
47
+ * Keyboard navigation logic for the VirtualSelect listbox.
48
+ * Encapsulates ArrowDown/Up/Home/End/Enter/Escape handling.
49
+ * Focus stays on the search input; `aria-activedescendant` tracks the active row.
50
+ */
51
+ export function useVirtualListbox({
52
+ count,
53
+ filtered,
54
+ isOpen,
55
+ onSelect,
56
+ virtualizer,
57
+ }: UseVirtualListboxOptions): UseVirtualListboxReturn {
58
+ const [activeIndex, setActiveIndexRaw] = useState(0);
59
+
60
+ // When the filtered list changes, reset to the first enabled option.
61
+ useEffect(() => {
62
+ const first = findNext(filtered, 0);
63
+ setActiveIndexRaw(first >= 0 ? first : 0);
64
+ }, [filtered]);
65
+
66
+ // When the popover closes, reset to 0.
67
+ useEffect(() => {
68
+ if (!isOpen) setActiveIndexRaw(0);
69
+ }, [isOpen]);
70
+
71
+ const setActiveIndex = useCallback(
72
+ (index: number) => {
73
+ const clamped = Math.max(0, Math.min(index, count - 1));
74
+ setActiveIndexRaw(clamped);
75
+ virtualizer?.scrollToIndex(clamped, { align: "auto" });
76
+ },
77
+ [count, virtualizer],
78
+ );
79
+
80
+ const onInputKeyDown = useCallback(
81
+ (e: React.KeyboardEvent<HTMLInputElement>) => {
82
+ if (!isOpen) return;
83
+
84
+ switch (e.key) {
85
+ case "ArrowDown": {
86
+ e.preventDefault();
87
+ if (count === 0) return;
88
+ const next = findNext(filtered, activeIndex + 1);
89
+ if (next >= 0) setActiveIndex(next);
90
+ break;
91
+ }
92
+ case "ArrowUp": {
93
+ e.preventDefault();
94
+ if (count === 0) return;
95
+ const prev = findPrev(filtered, activeIndex - 1);
96
+ if (prev >= 0) setActiveIndex(prev);
97
+ break;
98
+ }
99
+ case "Home": {
100
+ e.preventDefault();
101
+ if (count === 0) return;
102
+ const first = findNext(filtered, 0);
103
+ setActiveIndex(first >= 0 ? first : 0);
104
+ break;
105
+ }
106
+ case "End": {
107
+ e.preventDefault();
108
+ if (count === 0) return;
109
+ const last = findPrev(filtered, filtered.length - 1);
110
+ setActiveIndex(last >= 0 ? last : 0);
111
+ break;
112
+ }
113
+ case "Enter": {
114
+ e.preventDefault();
115
+ if (count === 0) return;
116
+ const opt = filtered[activeIndex];
117
+ if (opt && !opt.disabled) {
118
+ onSelect(opt.value);
119
+ }
120
+ break;
121
+ }
122
+ // Escape is handled by Radix Popover (it closes the portal and
123
+ // returns focus to the trigger). No additional handling required here.
124
+ default:
125
+ break;
126
+ }
127
+ },
128
+ [isOpen, count, filtered, activeIndex, setActiveIndex, onSelect],
129
+ );
130
+
131
+ return { activeIndex, setActiveIndex, onInputKeyDown };
132
+ }
@@ -0,0 +1,203 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { useState } from "react";
3
+ import { expect, userEvent, within } from "storybook/test";
4
+ import { VirtualSelect } from "./virtual-select";
5
+ import type { VirtualSelectOption } from "./virtual-select";
6
+
7
+ // ---------------------------------------------------------------------------
8
+ // Shared option sets
9
+ // ---------------------------------------------------------------------------
10
+
11
+ const SMALL_OPTIONS: VirtualSelectOption[] = [
12
+ { label: "Design", value: "design" },
13
+ { label: "Engineering", value: "engineering" },
14
+ { label: "Product", value: "product" },
15
+ { label: "Marketing", value: "marketing" },
16
+ { label: "Sales", value: "sales" },
17
+ { label: "Finance", value: "finance" },
18
+ { label: "Legal", value: "legal" },
19
+ { label: "HR", value: "hr" },
20
+ { label: "Operations", value: "operations" },
21
+ { label: "Support", value: "support" },
22
+ { label: "Analytics", value: "analytics" },
23
+ { label: "Infrastructure", value: "infrastructure" },
24
+ { label: "Security", value: "security" },
25
+ { label: "Data Science", value: "data-science" },
26
+ { label: "Research", value: "research" },
27
+ { label: "QA", value: "qa" },
28
+ { label: "DevRel", value: "devrel" },
29
+ { label: "Growth", value: "growth" },
30
+ { label: "Platform", value: "platform" },
31
+ { label: "Core Services", value: "core-services" },
32
+ ];
33
+
34
+ /** Generate 10,000 options — the perf acceptance story. */
35
+ const LARGE_OPTIONS: VirtualSelectOption[] = Array.from({ length: 10_000 }, (_, i) => ({
36
+ label: `Option ${i + 1} — ${["Alpha", "Beta", "Gamma", "Delta", "Epsilon"][i % 5]} Group`,
37
+ value: `opt-${i + 1}`,
38
+ }));
39
+
40
+ const OPTIONS_WITH_DISABLED: VirtualSelectOption[] = SMALL_OPTIONS.map((o, i) => ({
41
+ ...o,
42
+ disabled: i === 2 || i === 7, // "Product" and "HR" are disabled
43
+ }));
44
+
45
+ // ---------------------------------------------------------------------------
46
+ // Meta
47
+ // ---------------------------------------------------------------------------
48
+
49
+ const meta = {
50
+ title: "Forms/VirtualSelect",
51
+ component: VirtualSelect,
52
+ tags: ["autodocs"],
53
+ parameters: {
54
+ docs: {
55
+ description: {
56
+ component:
57
+ "A combobox backed by `@tanstack/react-virtual` that renders only the visible window of options. Handles 10,000+ items without DOM bloat. Single or multiple selection; keyboard-navigable via `aria-activedescendant` while focus stays on the search input.",
58
+ },
59
+ },
60
+ },
61
+ argTypes: {
62
+ options: {
63
+ description:
64
+ "Full option list `{ label, value, disabled? }[]`. Only visible rows are in the DOM.",
65
+ control: false,
66
+ table: { category: "Content" },
67
+ },
68
+ multiple: {
69
+ description: "Allow selecting multiple values.",
70
+ control: "boolean",
71
+ table: { category: "Behavior" },
72
+ },
73
+ value: {
74
+ description: "Controlled value — `string` (single) or `string[]` (multiple).",
75
+ control: false,
76
+ table: { category: "State" },
77
+ },
78
+ defaultValue: {
79
+ description: "Uncontrolled initial value.",
80
+ control: false,
81
+ table: { category: "State" },
82
+ },
83
+ placeholder: {
84
+ description: "Trigger placeholder when nothing is selected.",
85
+ control: "text",
86
+ table: { category: "Content" },
87
+ },
88
+ searchPlaceholder: {
89
+ description: "Search-input placeholder.",
90
+ control: "text",
91
+ table: { category: "Content" },
92
+ },
93
+ emptyText: {
94
+ description: "Message shown when no options match the query.",
95
+ control: "text",
96
+ table: { category: "Content" },
97
+ },
98
+ estimateOptionHeight: {
99
+ description: "Estimated row height in px used by the virtualizer (default 32).",
100
+ control: "number",
101
+ table: { category: "Behavior" },
102
+ },
103
+ maxListHeight: {
104
+ description: "CSS max-height for the scrollable listbox (default `18rem`).",
105
+ control: "text",
106
+ table: { category: "Appearance" },
107
+ },
108
+ onValueChange: {
109
+ description: "Called when the selection changes.",
110
+ control: false,
111
+ table: { category: "Behavior" },
112
+ },
113
+ className: {
114
+ description: "Additional CSS classes applied to the trigger button.",
115
+ control: "text",
116
+ table: { category: "Appearance" },
117
+ },
118
+ },
119
+ } satisfies Meta<typeof VirtualSelect>;
120
+
121
+ export default meta;
122
+ type Story = StoryObj<typeof meta>;
123
+
124
+ // ---------------------------------------------------------------------------
125
+ // Stories
126
+ // ---------------------------------------------------------------------------
127
+
128
+ /** Basic single-select with ~20 options. */
129
+ export const Default: Story = {
130
+ args: {
131
+ options: SMALL_OPTIONS,
132
+ placeholder: "Select a department…",
133
+ },
134
+ // Regression lock: the virtualizer must measure once the Popover mounts the
135
+ // listbox, so opening the trigger renders options (not an empty listbox).
136
+ // Guards the state-backed-scroll-element fix; jsdom unit tests can't open the
137
+ // Radix portal, so this runs in the browser via test-storybook.
138
+ play: async ({ canvasElement }) => {
139
+ await userEvent.click(within(canvasElement).getByRole("combobox"));
140
+ const options = await within(document.body).findAllByRole("option");
141
+ await expect(options.length).toBeGreaterThan(0);
142
+ },
143
+ };
144
+
145
+ /**
146
+ * 10,000 generated options — the performance acceptance story.
147
+ * Acceptance criterion: the list opens instantly, scrolling is smooth (no jank),
148
+ * and the trigger + search filter still respond in < 16 ms.
149
+ * (Smooth scroll is not measurable in jsdom; verify visually in the browser.)
150
+ */
151
+ export const LargeDataset: Story = {
152
+ args: {
153
+ options: LARGE_OPTIONS,
154
+ placeholder: "Select from 10,000 options…",
155
+ searchPlaceholder: "Filter 10,000 options…",
156
+ },
157
+ };
158
+
159
+ /** Multiple selection — controlled with chips and overflow badge. */
160
+ export const Multiple: Story = {
161
+ render: () => {
162
+ // eslint-disable-next-line react-hooks/rules-of-hooks
163
+ const [value, setValue] = useState<string[]>(["design", "engineering"]);
164
+ return (
165
+ <VirtualSelect
166
+ options={SMALL_OPTIONS}
167
+ multiple
168
+ value={value}
169
+ onValueChange={(v) => setValue(v as string[])}
170
+ placeholder="Select departments…"
171
+ />
172
+ );
173
+ },
174
+ };
175
+
176
+ /** Single-select with a pre-selected value via `defaultValue`. */
177
+ export const Preselected: Story = {
178
+ args: {
179
+ options: SMALL_OPTIONS,
180
+ defaultValue: "product",
181
+ placeholder: "Select a department…",
182
+ },
183
+ };
184
+
185
+ /** Two options are disabled; they can be seen but not selected or focused. */
186
+ export const DisabledOptions: Story = {
187
+ args: {
188
+ options: OPTIONS_WITH_DISABLED,
189
+ placeholder: "Select a department…",
190
+ },
191
+ };
192
+
193
+ /**
194
+ * Filtering to an empty result — the empty state renders `emptyText`.
195
+ * Type something like "zzz" in the search box to trigger it.
196
+ */
197
+ export const Empty: Story = {
198
+ args: {
199
+ options: SMALL_OPTIONS,
200
+ placeholder: "Select a department…",
201
+ emptyText: "No departments match your search.",
202
+ },
203
+ };
@@ -0,0 +1,106 @@
1
+ /**
2
+ * VirtualSelect — unit tests.
3
+ *
4
+ * Scope: things reachable WITHOUT opening the Radix Popover portal in jsdom
5
+ * (Radix portals render outside the test container and are unreliable in jsdom).
6
+ *
7
+ * What is NOT tested here (covered by Storybook interaction tests + orchestrator
8
+ * a11y / visual review on the real rendered surface):
9
+ * - Opening / closing the popover
10
+ * - Search filtering while open
11
+ * - Keyboard navigation (ArrowDown/Up/Enter/Escape) driving aria-activedescendant
12
+ * - Virtualizer scroll behavior with 10,000 options
13
+ * - Six-theme visual correctness
14
+ */
15
+ import { describe, expect, it } from "vitest";
16
+ import { render, screen } from "@testing-library/react";
17
+ import { VirtualSelect } from "./virtual-select";
18
+ import type { VirtualSelectOption } from "./virtual-select";
19
+
20
+ const OPTIONS: VirtualSelectOption[] = [
21
+ { label: "Alpha", value: "alpha" },
22
+ { label: "Beta", value: "beta" },
23
+ { label: "Gamma", value: "gamma" },
24
+ { label: "Delta", value: "delta" },
25
+ ];
26
+
27
+ // ---------------------------------------------------------------------------
28
+ // Trigger ARIA
29
+ // ---------------------------------------------------------------------------
30
+
31
+ describe("VirtualSelect trigger", () => {
32
+ it("renders with role=combobox and aria-expanded=false when closed", () => {
33
+ render(<VirtualSelect options={OPTIONS} />);
34
+ const trigger = screen.getByRole("combobox");
35
+ expect(trigger).toBeInTheDocument();
36
+ expect(trigger).toHaveAttribute("aria-expanded", "false");
37
+ expect(trigger).toHaveAttribute("aria-haspopup", "listbox");
38
+ });
39
+
40
+ it("shows placeholder when no value is selected", () => {
41
+ render(<VirtualSelect options={OPTIONS} placeholder="Pick one" />);
42
+ expect(screen.getByText("Pick one")).toBeInTheDocument();
43
+ });
44
+ });
45
+
46
+ // ---------------------------------------------------------------------------
47
+ // Single — controlled value
48
+ // ---------------------------------------------------------------------------
49
+
50
+ describe("VirtualSelect single mode", () => {
51
+ it("shows the selected option label on the trigger for a controlled value", () => {
52
+ render(<VirtualSelect options={OPTIONS} value="beta" />);
53
+ expect(screen.getByText("Beta")).toBeInTheDocument();
54
+ });
55
+
56
+ it("falls back to placeholder when controlled value is empty string", () => {
57
+ render(<VirtualSelect options={OPTIONS} value="" placeholder="Choose…" />);
58
+ expect(screen.getByText("Choose…")).toBeInTheDocument();
59
+ });
60
+ });
61
+
62
+ // ---------------------------------------------------------------------------
63
+ // Multiple — chips + overflow
64
+ // ---------------------------------------------------------------------------
65
+
66
+ describe("VirtualSelect multiple mode", () => {
67
+ it("renders a Badge chip for each selected value (up to 2)", () => {
68
+ render(<VirtualSelect options={OPTIONS} multiple value={["alpha", "beta"]} />);
69
+ // Both values fit within the 2-chip window
70
+ expect(screen.getByText("Alpha")).toBeInTheDocument();
71
+ expect(screen.getByText("Beta")).toBeInTheDocument();
72
+ });
73
+
74
+ it("collapses overflow into a +N badge when more than 2 values are selected", () => {
75
+ render(<VirtualSelect options={OPTIONS} multiple value={["alpha", "beta", "gamma"]} />);
76
+ // First two chips visible
77
+ expect(screen.getByText("Alpha")).toBeInTheDocument();
78
+ expect(screen.getByText("Beta")).toBeInTheDocument();
79
+ // Third is collapsed
80
+ expect(screen.queryByText("Gamma")).not.toBeInTheDocument();
81
+ expect(screen.getByText("+1")).toBeInTheDocument();
82
+ });
83
+
84
+ it("shows +N = 2 when 4 values are selected", () => {
85
+ render(
86
+ <VirtualSelect options={OPTIONS} multiple value={["alpha", "beta", "gamma", "delta"]} />,
87
+ );
88
+ expect(screen.getByText("+2")).toBeInTheDocument();
89
+ });
90
+
91
+ it("shows placeholder when multiple=true and value=[]", () => {
92
+ render(<VirtualSelect options={OPTIONS} multiple value={[]} placeholder="Select items…" />);
93
+ expect(screen.getByText("Select items…")).toBeInTheDocument();
94
+ });
95
+ });
96
+
97
+ // ---------------------------------------------------------------------------
98
+ // Uncontrolled defaultValue
99
+ // ---------------------------------------------------------------------------
100
+
101
+ describe("VirtualSelect defaultValue (uncontrolled)", () => {
102
+ it("shows the pre-selected label on mount", () => {
103
+ render(<VirtualSelect options={OPTIONS} defaultValue="gamma" />);
104
+ expect(screen.getByText("Gamma")).toBeInTheDocument();
105
+ });
106
+ });