@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,229 @@
1
+ "use client";
2
+
3
+ import { type HTMLAttributes, type ReactNode, forwardRef } from "react";
4
+ import { cva, type VariantProps } from "class-variance-authority";
5
+ import { Card, CardContent } from "../card";
6
+ import { CopyableValue } from "../copyable-value";
7
+ import { Skeleton } from "../skeleton";
8
+ import { useLocale } from "../locale-provider";
9
+ import { cn } from "../../lib/cn";
10
+ import { type NumberFormatKind, numberFormatOptions } from "../../lib/compact-number";
11
+
12
+ /*
13
+ * `emphasis` — size/weight/space ONLY, no new hue (research 11 §B.5 KPI-1/5):
14
+ * `headline` is the answer-leading rung (`text-kpi`, ~32px); `default` stays
15
+ * calm on the intermediate 1.5rem reading rung (the pre-#188 step between
16
+ * `title` and `kpi` — no app role exists there, same precedent as the prose
17
+ * h1/h3 rungs; baselined in the text-scale ratchet).
18
+ */
19
+ const metricValueVariants = cva("tabular-nums text-foreground", {
20
+ variants: {
21
+ emphasis: {
22
+ default: "text-2xl font-semibold tracking-tight",
23
+ headline: "text-kpi",
24
+ },
25
+ },
26
+ defaultVariants: { emphasis: "default" },
27
+ });
28
+
29
+ const metricContentVariants = cva("space-y-2", {
30
+ variants: {
31
+ emphasis: {
32
+ default: "p-5",
33
+ headline: "p-6",
34
+ },
35
+ },
36
+ defaultVariants: { emphasis: "default" },
37
+ });
38
+
39
+ export type MetricCardEmphasis = NonNullable<VariantProps<typeof metricValueVariants>["emphasis"]>;
40
+
41
+ /**
42
+ * How a NUMERIC `value` is rendered. A function takes over completely.
43
+ *
44
+ * `"compact"` is the default because a KPI tile is a small box and
45
+ * `50012102.632741` does not fit in one — the exact figure stays one click
46
+ * away via `CopyableValue`. Anything that is not a `number` (a string, an
47
+ * element) is passed through untouched, so every existing call site is
48
+ * unaffected.
49
+ */
50
+ export type MetricCardValueFormat = NumberFormatKind | ((value: number) => ReactNode);
51
+
52
+ export interface MetricCardProps
53
+ extends HTMLAttributes<HTMLDivElement>, VariantProps<typeof metricValueVariants> {
54
+ label: ReactNode;
55
+ value: ReactNode;
56
+ /**
57
+ * How to render a NUMERIC `value`. Default: `"compact"`. Ignored for every
58
+ * other `ReactNode` — a string you already formatted is rendered verbatim.
59
+ */
60
+ valueFormat?: MetricCardValueFormat;
61
+ /** ISO 4217 code for `valueFormat: "currency"`. Default: `"USD"`. */
62
+ currency?: string;
63
+ /**
64
+ * Offer the exact figure on click when a numeric `value` was shortened.
65
+ * Default: `true`. Turning it off leaves a compacted number with no way back
66
+ * to its digits, so only do that when the tile is decorative.
67
+ */
68
+ copyExactValue?: boolean;
69
+ /** Secondary line under the value — sublabel / context. */
70
+ description?: ReactNode;
71
+ /** Signed change, e.g. "+12.4%". Direction colors it. */
72
+ delta?: string;
73
+ deltaDirection?: "up" | "down" | "neutral";
74
+ /** Whether "up" is good (green) — flip for metrics where down is good. */
75
+ positiveIsGood?: boolean;
76
+ icon?: ReactNode;
77
+ /** Optional inline visual (e.g. a sparkline) shown under the value/description. */
78
+ visual?: ReactNode;
79
+ /**
80
+ * Optional grounding footer (research 11 §B.5 KPI-3) — connects a cited
81
+ * figure to its source (e.g. an `EvidenceChip` from `@elabs-ai/components-ai`). Rendered
82
+ * subdued (`text-meta text-muted-foreground`) under the tile body.
83
+ */
84
+ evidence?: ReactNode;
85
+ /**
86
+ * Loading vs ready — renders a layout-shaped skeleton (matching the real
87
+ * box height) instead of the value/label/delta text. Default: `false`.
88
+ */
89
+ loading?: boolean;
90
+ /**
91
+ * Whether this tile announces its own `loading` state via a live region.
92
+ * Set to `false` when several tiles are composed inside a container that
93
+ * already announces once for the whole region (e.g. a loading
94
+ * `MetricGrid`) — see loading-states.md §a11y ("one live region per
95
+ * region, not per box"). Default: `true`.
96
+ */
97
+ announceLoading?: boolean;
98
+ }
99
+
100
+ /** Compact KPI tile for dashboards. */
101
+ export const MetricCard = forwardRef<HTMLDivElement, MetricCardProps>(function MetricCard(
102
+ {
103
+ label,
104
+ value,
105
+ valueFormat = "compact",
106
+ currency,
107
+ copyExactValue = true,
108
+ description,
109
+ delta,
110
+ deltaDirection = "neutral",
111
+ positiveIsGood = true,
112
+ icon,
113
+ visual,
114
+ evidence,
115
+ emphasis,
116
+ loading = false,
117
+ announceLoading = true,
118
+ className,
119
+ ...props
120
+ },
121
+ ref,
122
+ ) {
123
+ const { t, formatNumber } = useLocale();
124
+
125
+ // Formatting applies to NUMBERS only. Every shipped call site passes a string
126
+ // it formatted itself, so this branch is inert for them — that is what makes
127
+ // a new default safe.
128
+ const isNumeric = typeof value === "number";
129
+ const formattedValue: ReactNode = !isNumeric
130
+ ? value
131
+ : typeof valueFormat === "function"
132
+ ? valueFormat(value)
133
+ : formatNumber(value, numberFormatOptions(valueFormat, value, currency));
134
+ // Only offer the exact figure when the display actually differs from it —
135
+ // a button around a value that was never shortened is noise.
136
+ const showsExact = isNumeric && String(formattedValue) === String(value);
137
+ const canCopyExact =
138
+ isNumeric && copyExactValue && typeof valueFormat !== "function" && !showsExact;
139
+
140
+ const good =
141
+ deltaDirection === "up" ? positiveIsGood : deltaDirection === "down" ? !positiveIsGood : null;
142
+ const deltaColor =
143
+ good === null ? "text-muted-foreground" : good ? "text-success-text" : "text-destructive-text";
144
+ // Stable, theme-agnostic polarity hook. High decoration reveals a non-color
145
+ // polarity glyph from this attribute (decoration.css); chromatic themes ignore it
146
+ // and use color. Issue #162.
147
+ const polarity = good === null ? "neutral" : good ? "good" : "bad";
148
+ const arrow = deltaDirection === "up" ? "↑" : deltaDirection === "down" ? "↓" : "";
149
+ // Direction is conveyed visually by the arrow glyph + color; give AT a single
150
+ // unambiguous name ("up 12.4%, favorable") since neither the glyph nor color is a
151
+ // reliable signal (high decoration / high-contrast collapse the hue) — and good/bad
152
+ // polarity is otherwise invisible to AT. See accessibility.md, #162.
153
+ const directionLabel = deltaDirection === "up" ? "up" : deltaDirection === "down" ? "down" : "";
154
+ const polarityLabel = good === null ? "" : good ? ", favorable" : ", unfavorable";
155
+
156
+ return (
157
+ <Card ref={ref} className={cn("overflow-hidden", className)} {...props}>
158
+ <CardContent
159
+ className={metricContentVariants({ emphasis })}
160
+ {...(loading && announceLoading ? { role: "status", "aria-live": "polite" as const } : {})}
161
+ >
162
+ {loading && announceLoading ? (
163
+ <span className="sr-only">{t("ui.metricCard.loading")}</span>
164
+ ) : null}
165
+ <div className="flex items-center justify-between">
166
+ {loading ? (
167
+ <Skeleton className="h-4 w-24" />
168
+ ) : (
169
+ <span className="text-body font-medium text-muted-foreground">{label}</span>
170
+ )}
171
+ {icon ? <span className="text-muted-foreground [&_svg]:size-4">{icon}</span> : null}
172
+ </div>
173
+ <div className="flex items-baseline gap-2">
174
+ {loading ? (
175
+ <Skeleton className={emphasis === "headline" ? "h-9 w-28" : "h-8 w-24"} />
176
+ ) : canCopyExact ? (
177
+ <CopyableValue
178
+ className={metricValueVariants({ emphasis })}
179
+ // Two tiles in one grid can compact to the same display ("$1.2M"),
180
+ // and the visible label is a sibling node the button's name does
181
+ // not include — so without this a screen-reader user tabbing the
182
+ // grid hears the identical name twice. A non-string label has no
183
+ // text to borrow; the default hint stands.
184
+ hint={
185
+ typeof label === "string" ? `${label}, ${t("ui.copyableValue.hint")}` : undefined
186
+ }
187
+ value={String(value)}
188
+ >
189
+ {formattedValue}
190
+ </CopyableValue>
191
+ ) : (
192
+ <span className={metricValueVariants({ emphasis })}>{formattedValue}</span>
193
+ )}
194
+ {delta ? (
195
+ loading ? (
196
+ <Skeleton className="h-4 w-14" />
197
+ ) : (
198
+ <span
199
+ data-polarity={polarity}
200
+ className={cn("text-meta tabular-nums", deltaColor)}
201
+ aria-label={
202
+ directionLabel ? `${directionLabel} ${delta}${polarityLabel}` : undefined
203
+ }
204
+ >
205
+ {arrow ? <span aria-hidden="true">{arrow} </span> : null}
206
+ {delta}
207
+ </span>
208
+ )
209
+ ) : null}
210
+ </div>
211
+ {description ? (
212
+ loading ? (
213
+ <Skeleton className="h-3 w-32" />
214
+ ) : (
215
+ <p className="text-meta font-normal text-muted-foreground">{description}</p>
216
+ )
217
+ ) : null}
218
+ {visual ? (
219
+ <div className="pt-1">{loading ? <Skeleton className="h-8 w-full" /> : visual}</div>
220
+ ) : null}
221
+ {evidence ? (
222
+ <div className="pt-1 text-meta font-normal text-muted-foreground">
223
+ {loading ? <Skeleton className="h-3 w-24" /> : evidence}
224
+ </div>
225
+ ) : null}
226
+ </CardContent>
227
+ </Card>
228
+ );
229
+ });
@@ -0,0 +1,11 @@
1
+ export { ModelPicker } from "./model-picker";
2
+ export type { ModelPickerProps } from "./model-picker";
3
+ export {
4
+ countItems,
5
+ modelPickerBody,
6
+ showsInlineError,
7
+ type ModelPickerBody,
8
+ type ModelPickerGroup,
9
+ type ModelPickerItem,
10
+ type ModelPickerStatus,
11
+ } from "./model-picker-state";
@@ -0,0 +1,85 @@
1
+ /**
2
+ * `ModelPicker`'s two state decisions, kept OUT of the component on purpose.
3
+ *
4
+ * Both were inline ternaries in the consuming app this component came from, and
5
+ * both survived every mutation thrown at them — because no test could reach an
6
+ * expression buried in JSX. As exported pure functions they are directly
7
+ * testable, which is the whole reason they live in their own module.
8
+ */
9
+
10
+ /** What the picker knows about the list it was handed. */
11
+ export type ModelPickerStatus = "idle" | "loading" | "ready" | "stale" | "error" | "empty";
12
+
13
+ /** Which of the four mutually-exclusive bodies the popover renders. */
14
+ export type ModelPickerBody = "loading" | "list" | "error" | "empty";
15
+
16
+ /**
17
+ * Pick the body.
18
+ *
19
+ * **A non-empty list always wins.** A failed refresh over usable stale data is an
20
+ * inline strip above a working list (see `showsInlineError`), never a takeover —
21
+ * collapsing "stale list + error" into "error panel" throws away a list the user
22
+ * could still have picked from.
23
+ */
24
+ export function modelPickerBody(status: ModelPickerStatus, itemCount: number): ModelPickerBody {
25
+ if (itemCount > 0) return "list";
26
+ if (status === "loading" || status === "idle") return "loading";
27
+ if (status === "error" || status === "stale") return "error";
28
+ return "empty";
29
+ }
30
+
31
+ /**
32
+ * Whether to show the error line ABOVE a list that still works — i.e. the
33
+ * refresh failed but there is stale data worth keeping on screen.
34
+ */
35
+ export function showsInlineError(status: ModelPickerStatus, itemCount: number): boolean {
36
+ return itemCount > 0 && (status === "stale" || status === "error");
37
+ }
38
+
39
+ /** One selectable row. */
40
+ export interface ModelPickerItem {
41
+ id: string;
42
+ label: string;
43
+ /** Muted second line. */
44
+ description?: string;
45
+ /**
46
+ * Extra strings cmdk should MATCH on without displaying — ids, a space or
47
+ * workspace name. This is what lets a search for "northwind" find a target
48
+ * whose visible label never says it.
49
+ */
50
+ keywords?: string[];
51
+ /**
52
+ * Leading glyph. A ReactNode, never a URL: a renderer with no remote origins
53
+ * cannot fetch a logo, so the caller supplies a bundled node.
54
+ */
55
+ icon?: React.ReactNode;
56
+ /**
57
+ * Trailing muted badges ("not provisioned", "no modes").
58
+ *
59
+ * Deliberately a caller-supplied ARRAY rather than derived from flags. A
60
+ * three-valued flag where absent means "undecided" renders as "not enabled"
61
+ * the moment a component computes the badge from `!flag` — which mislabels
62
+ * every row on a deployment that simply never probed. Keeping this a list the
63
+ * caller builds means the library cannot get that wrong on their behalf.
64
+ */
65
+ meta?: string[];
66
+ /** Disable this row without removing it. */
67
+ disabled?: boolean;
68
+ }
69
+
70
+ /** A titled group of rows. */
71
+ export interface ModelPickerGroup {
72
+ key: string;
73
+ /**
74
+ * Rendered verbatim as the group heading. The CALLER derives it (`Apps ·
75
+ * northwind`, `Assistants · personal`) — the library never invents grouping or
76
+ * heading text from the data.
77
+ */
78
+ label: string;
79
+ items: ModelPickerItem[];
80
+ }
81
+
82
+ /** Total rows across all groups. */
83
+ export function countItems(groups: readonly ModelPickerGroup[]): number {
84
+ return groups.reduce((n, g) => n + g.items.length, 0);
85
+ }
@@ -0,0 +1,128 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { AppWindow, Bot, Sparkles } from "lucide-react";
3
+ import { useState } from "react";
4
+ import { ModelPicker } from "./model-picker";
5
+ import type { ModelPickerGroup } from "./model-picker-state";
6
+
7
+ const groups: ModelPickerGroup[] = [
8
+ {
9
+ key: "auto",
10
+ label: "Auto",
11
+ items: [
12
+ {
13
+ id: "auto",
14
+ label: "Choose for me",
15
+ description: "Picks the best target for each question",
16
+ icon: <Sparkles className="size-4 shrink-0" aria-hidden="true" />,
17
+ },
18
+ ],
19
+ },
20
+ {
21
+ key: "apps-northwind",
22
+ label: "Apps · northwind",
23
+ items: [
24
+ {
25
+ id: "app-sales",
26
+ label: "Sales overview",
27
+ description: "Updated 2 days ago",
28
+ // "northwind" appears in no visible label on this row — typing it still
29
+ // finds the row, which is the most-used behaviour of this component.
30
+ keywords: ["northwind", "app-sales"],
31
+ icon: <AppWindow className="size-4 shrink-0" aria-hidden="true" />,
32
+ },
33
+ {
34
+ id: "app-stock",
35
+ label: "Stock levels",
36
+ keywords: ["northwind", "app-stock"],
37
+ icon: <AppWindow className="size-4 shrink-0" aria-hidden="true" />,
38
+ meta: ["not provisioned"],
39
+ },
40
+ ],
41
+ },
42
+ {
43
+ key: "assistants-personal",
44
+ label: "Assistants · personal",
45
+ items: [
46
+ {
47
+ id: "asst-research",
48
+ label: "Research helper",
49
+ keywords: ["personal", "asst-research"],
50
+ icon: <Bot className="size-4 shrink-0" aria-hidden="true" />,
51
+ },
52
+ ],
53
+ },
54
+ ];
55
+
56
+ const meta = {
57
+ title: "Core/ModelPicker",
58
+ component: ModelPicker,
59
+ tags: ["autodocs"],
60
+ parameters: {
61
+ docs: {
62
+ description: {
63
+ component:
64
+ "A compact pill that opens a grouped, searchable target list anchored under itself — " +
65
+ "sized for a composer footer. The inline sibling of `@elabs-ai/components-ai`'s " +
66
+ "`ModelSelector`, which is the same `Command` internals in a modal `Dialog`.\n\n" +
67
+ "**`Command` lives in a `Popover`, never a `DropdownMenu`.** `DropdownMenuContent` owns " +
68
+ "roving tabindex and its own typeahead; both fight a real `<input>` in its subtree, so " +
69
+ "arrow keys move the menu's focus instead of cmdk's highlight and typing never reaches " +
70
+ "the field. `Popover` claims no keyboard.\n\n" +
71
+ "Search matches `keywords` as well as the label — try typing `northwind`.",
72
+ },
73
+ },
74
+ },
75
+ args: { groups, triggerLabel: "Sales overview", value: "app-sales" },
76
+ } satisfies Meta<typeof ModelPicker>;
77
+
78
+ export default meta;
79
+ type Story = StoryObj<typeof meta>;
80
+
81
+ export const Default: Story = {};
82
+
83
+ /** Skeleton rows while the first load is in flight. */
84
+ export const Loading: Story = {
85
+ args: { groups: [], status: "loading", triggerLabel: "Loading…", value: undefined },
86
+ };
87
+
88
+ /**
89
+ * A failed refresh over usable stale data: an inline strip ABOVE a list that
90
+ * still works. Collapsing this into the error panel would throw away a list the
91
+ * user could still pick from.
92
+ */
93
+ export const StaleWithInlineError: Story = {
94
+ args: { status: "stale", onRetry: () => {} },
95
+ };
96
+
97
+ /** Nothing to show AND the load failed — the panel takes over, with a retry. */
98
+ export const ErrorState: Story = {
99
+ args: {
100
+ groups: [],
101
+ status: "error",
102
+ triggerLabel: "Unavailable",
103
+ value: undefined,
104
+ onRetry: () => {},
105
+ },
106
+ };
107
+
108
+ /** Nothing to show, but nothing went wrong — a different message, deliberately. */
109
+ export const Empty: Story = {
110
+ args: { groups: [], status: "empty", triggerLabel: "No targets", value: undefined },
111
+ };
112
+
113
+ export const Disabled: Story = { args: { disabled: true } };
114
+
115
+ /**
116
+ * Programmatic open via `open`/`onOpenChange` — e.g. re-opening the picker when
117
+ * a previously pinned target becomes unavailable, without the consumer having
118
+ * to reach into internal state.
119
+ */
120
+ export const Controlled: Story = {
121
+ render: (args) => {
122
+ function ControlledModelPicker() {
123
+ const [open, setOpen] = useState(true);
124
+ return <ModelPicker {...args} open={open} onOpenChange={setOpen} />;
125
+ }
126
+ return <ControlledModelPicker />;
127
+ },
128
+ };