@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,466 @@
1
+ /**
2
+ * Object Detail Hub — the master-detail screen the library was missing.
3
+ *
4
+ * The single most common enterprise screen: a list of objects on the left drives
5
+ * a rich DETAIL HUB on the right. It demonstrates the structural choices the
6
+ * enterprise rulebook insists on, so an end-user copies the right anatomy instead
7
+ * of dumping everything on one page or stacking detail *below* the list:
8
+ *
9
+ * • SplitPanel master-detail (list drives detail — never detail below list).
10
+ * • A detail hub = sticky SectionHeader (identity + status) + a ButtonGroup
11
+ * toolbar + Tabs for the object's sections (Overview / Activity / Settings).
12
+ * • Descriptions for the object's attributes; a related list for its children.
13
+ * • A high-value task (Run) promoted to its own focused Dialog surface.
14
+ * • A real empty state (StatePanel kind="empty") when nothing is selected.
15
+ *
16
+ * Compose-only from @elabs-ai/components-* primitives; semantic tokens only; reads in
17
+ * light AND dark. Verify with globals=theme:<slug>.
18
+ */
19
+ import type { Meta, StoryObj } from "@storybook/react-vite";
20
+ import { useState } from "react";
21
+ import { Pencil, Play, Search } from "lucide-react";
22
+ import {
23
+ Badge,
24
+ Button,
25
+ ButtonGroup,
26
+ Descriptions,
27
+ DescriptionsItem,
28
+ Dialog,
29
+ DialogClose,
30
+ DialogContent,
31
+ DialogDescription,
32
+ DialogFooter,
33
+ DialogHeader,
34
+ DialogTitle,
35
+ DialogTrigger,
36
+ Input,
37
+ Label,
38
+ MetricCard,
39
+ SectionHeader,
40
+ Separator,
41
+ SplitPanel,
42
+ StatePanel,
43
+ StatusBadge,
44
+ Tabs,
45
+ TabsContent,
46
+ TabsList,
47
+ TabsTrigger,
48
+ Toaster,
49
+ toast,
50
+ type Status,
51
+ } from "./index";
52
+
53
+ /* -------------------------------------------------------------------------- */
54
+ /* Object model — a "Pipeline" with attributes, runs (children) and settings */
55
+ /* -------------------------------------------------------------------------- */
56
+
57
+ type Health = "active" | "paused" | "error";
58
+
59
+ interface Run {
60
+ id: string;
61
+ status: Status;
62
+ trigger: string;
63
+ rows: number;
64
+ when: string;
65
+ }
66
+
67
+ interface Pipeline {
68
+ id: string;
69
+ name: string;
70
+ source: string;
71
+ destination: string;
72
+ health: Health;
73
+ schedule: string;
74
+ owner: string;
75
+ lastRun: string;
76
+ rows24h: string;
77
+ successRate: string;
78
+ avgDuration: string;
79
+ runs: Run[];
80
+ }
81
+
82
+ const HEALTH_VARIANT: Record<Health, "success" | "secondary" | "destructive"> = {
83
+ active: "success",
84
+ paused: "secondary",
85
+ error: "destructive",
86
+ };
87
+
88
+ const PIPELINES: Pipeline[] = [
89
+ {
90
+ id: "rev",
91
+ name: "Revenue rollup",
92
+ source: "Postgres — prod",
93
+ destination: "Snowflake · analytics.revenue",
94
+ health: "active",
95
+ schedule: "Every 15 min",
96
+ owner: "Avery Rao",
97
+ lastRun: "2 min ago",
98
+ rows24h: "1.2M",
99
+ successRate: "99.8%",
100
+ avgDuration: "42s",
101
+ runs: [
102
+ { id: "r1", status: "complete", trigger: "Schedule", rows: 84_120, when: "2 min ago" },
103
+ { id: "r2", status: "complete", trigger: "Schedule", rows: 83_902, when: "17 min ago" },
104
+ { id: "r3", status: "complete", trigger: "Manual", rows: 84_001, when: "32 min ago" },
105
+ ],
106
+ },
107
+ {
108
+ id: "cust",
109
+ name: "Customer 360",
110
+ source: "Salesforce",
111
+ destination: "Snowflake · analytics.customer",
112
+ health: "active",
113
+ schedule: "Hourly",
114
+ owner: "Jordan Lee",
115
+ lastRun: "24 min ago",
116
+ rows24h: "410K",
117
+ successRate: "99.1%",
118
+ avgDuration: "1m 12s",
119
+ runs: [
120
+ { id: "r1", status: "running", trigger: "Schedule", rows: 0, when: "now" },
121
+ { id: "r2", status: "complete", trigger: "Schedule", rows: 17_420, when: "1 h ago" },
122
+ ],
123
+ },
124
+ {
125
+ id: "feed",
126
+ name: "Partner feed import",
127
+ source: "SFTP · partner-drop",
128
+ destination: "Snowflake · staging.partner",
129
+ health: "error",
130
+ schedule: "Daily 02:00",
131
+ owner: "Sam Cole",
132
+ lastRun: "1 d ago",
133
+ rows24h: "0",
134
+ successRate: "71%",
135
+ avgDuration: "3m 04s",
136
+ runs: [
137
+ { id: "r1", status: "failed", trigger: "Schedule", rows: 0, when: "1 d ago" },
138
+ { id: "r2", status: "complete", trigger: "Schedule", rows: 9_210, when: "2 d ago" },
139
+ ],
140
+ },
141
+ {
142
+ id: "ga",
143
+ name: "Marketing analytics",
144
+ source: "Google Analytics",
145
+ destination: "Snowflake · analytics.web",
146
+ health: "paused",
147
+ schedule: "Paused",
148
+ owner: "Avery Rao",
149
+ lastRun: "3 d ago",
150
+ rows24h: "0",
151
+ successRate: "98.4%",
152
+ avgDuration: "55s",
153
+ runs: [{ id: "r1", status: "skipped", trigger: "Schedule", rows: 0, when: "3 d ago" }],
154
+ },
155
+ ];
156
+
157
+ /* -------------------------------------------------------------------------- */
158
+ /* Master — searchable list of objects (the "which object" rail) */
159
+ /* -------------------------------------------------------------------------- */
160
+
161
+ function PipelineList({
162
+ selectedId,
163
+ onSelect,
164
+ }: {
165
+ selectedId: string | null;
166
+ onSelect: (id: string) => void;
167
+ }) {
168
+ const [q, setQ] = useState("");
169
+ const results = PIPELINES.filter((p) => p.name.toLowerCase().includes(q.toLowerCase()));
170
+
171
+ return (
172
+ <div className="flex h-full flex-col">
173
+ <div className="border-b p-3">
174
+ <div className="relative">
175
+ <Search
176
+ className="pointer-events-none absolute start-2.5 top-1/2 size-4 -translate-y-1/2 text-muted-foreground"
177
+ aria-hidden="true"
178
+ />
179
+ <Input
180
+ type="search"
181
+ value={q}
182
+ onChange={(e) => setQ(e.target.value)}
183
+ placeholder="Search pipelines…"
184
+ aria-label="Search pipelines"
185
+ className="ps-8"
186
+ />
187
+ </div>
188
+ <p className="mt-2 text-meta text-muted-foreground">
189
+ {results.length} of {PIPELINES.length} pipelines
190
+ </p>
191
+ </div>
192
+
193
+ {results.length === 0 ? (
194
+ <StatePanel
195
+ kind="empty"
196
+ className="m-3 border-0 bg-transparent py-10"
197
+ title="No matches"
198
+ description="No pipelines match that search."
199
+ />
200
+ ) : (
201
+ <ul className="min-h-0 flex-1 overflow-y-auto p-2">
202
+ {results.map((p) => {
203
+ const selected = p.id === selectedId;
204
+ return (
205
+ <li key={p.id}>
206
+ <button
207
+ type="button"
208
+ aria-current={selected ? "true" : undefined}
209
+ onClick={() => onSelect(p.id)}
210
+ className={[
211
+ "flex w-full items-center gap-2 rounded-md px-2.5 py-2 text-start transition-colors",
212
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
213
+ selected ? "bg-primary/10 text-foreground" : "hover:bg-surface-muted",
214
+ ].join(" ")}
215
+ >
216
+ <span className="min-w-0 flex-1">
217
+ <span className="block truncate text-body font-medium">{p.name}</span>
218
+ <span className="block truncate text-meta text-muted-foreground">
219
+ {p.source}
220
+ </span>
221
+ </span>
222
+ <Badge variant={HEALTH_VARIANT[p.health]}>{p.health}</Badge>
223
+ </button>
224
+ </li>
225
+ );
226
+ })}
227
+ </ul>
228
+ )}
229
+ </div>
230
+ );
231
+ }
232
+
233
+ /* -------------------------------------------------------------------------- */
234
+ /* The "Run" task — a high-value action promoted to its own Dialog surface */
235
+ /* -------------------------------------------------------------------------- */
236
+
237
+ function RunPipelineDialog({ pipeline }: { pipeline: Pipeline }) {
238
+ return (
239
+ <Dialog>
240
+ <DialogTrigger asChild>
241
+ <Button size="sm">
242
+ <Play className="size-4" aria-hidden="true" />
243
+ Run
244
+ </Button>
245
+ </DialogTrigger>
246
+ <DialogContent className="max-w-md">
247
+ <DialogHeader>
248
+ <DialogTitle>Run “{pipeline.name}”</DialogTitle>
249
+ <DialogDescription>
250
+ Starts a manual run now. The schedule ({pipeline.schedule}) is unaffected.
251
+ </DialogDescription>
252
+ </DialogHeader>
253
+
254
+ <div className="space-y-3">
255
+ <div className="space-y-1.5">
256
+ <Label htmlFor="run-window">Backfill window (optional)</Label>
257
+ <Input id="run-window" placeholder="e.g. last 24 hours" autoComplete="off" />
258
+ </div>
259
+ <p className="text-meta text-muted-foreground">
260
+ Writes to <span className="font-mono">{pipeline.destination}</span>.
261
+ </p>
262
+ </div>
263
+
264
+ <DialogFooter>
265
+ <DialogClose asChild>
266
+ <Button variant="outline" size="sm">
267
+ Cancel
268
+ </Button>
269
+ </DialogClose>
270
+ <DialogClose asChild>
271
+ <Button size="sm" onClick={() => toast.success(`Run started for ${pipeline.name}`)}>
272
+ Start run
273
+ </Button>
274
+ </DialogClose>
275
+ </DialogFooter>
276
+ </DialogContent>
277
+ </Dialog>
278
+ );
279
+ }
280
+
281
+ /* -------------------------------------------------------------------------- */
282
+ /* Detail hub — header + toolbar + tabbed sections for ONE object */
283
+ /* -------------------------------------------------------------------------- */
284
+
285
+ function PipelineHub({ pipeline }: { pipeline: Pipeline }) {
286
+ return (
287
+ <div className="flex h-full flex-col">
288
+ {/* Sticky identity + toolbar */}
289
+ <div className="sticky top-0 z-10 border-b bg-card px-6 pb-3 pt-5">
290
+ <SectionHeader
291
+ eyebrow="Pipeline"
292
+ title={
293
+ <span className="flex items-center gap-2">
294
+ {pipeline.name}
295
+ <Badge variant={HEALTH_VARIANT[pipeline.health]}>{pipeline.health}</Badge>
296
+ </span>
297
+ }
298
+ description={`${pipeline.source} → ${pipeline.destination}`}
299
+ actions={
300
+ <div className="flex items-center gap-2">
301
+ <RunPipelineDialog pipeline={pipeline} />
302
+ <ButtonGroup>
303
+ <Button variant="outline" size="sm">
304
+ <Pencil className="size-4" aria-hidden="true" />
305
+ Edit
306
+ </Button>
307
+ <Button variant="outline" size="sm">
308
+ Duplicate
309
+ </Button>
310
+ </ButtonGroup>
311
+ </div>
312
+ }
313
+ />
314
+ </div>
315
+
316
+ {/* Tabbed sections — the object's sub-views, switched in place */}
317
+ <div className="min-h-0 flex-1 overflow-y-auto p-6">
318
+ <Tabs defaultValue="overview" className="gap-6">
319
+ <TabsList>
320
+ <TabsTrigger value="overview">Overview</TabsTrigger>
321
+ <TabsTrigger value="activity">Activity</TabsTrigger>
322
+ <TabsTrigger value="settings">Settings</TabsTrigger>
323
+ </TabsList>
324
+
325
+ {/* Overview — summary first: KPIs, then attributes, then children */}
326
+ <TabsContent value="overview" className="space-y-6">
327
+ <div className="grid grid-cols-1 gap-4 sm:grid-cols-3">
328
+ <MetricCard
329
+ label="Rows (24h)"
330
+ value={pipeline.rows24h}
331
+ description="written to destination"
332
+ />
333
+ <MetricCard
334
+ label="Success rate"
335
+ value={pipeline.successRate}
336
+ description="last 7 days"
337
+ />
338
+ <MetricCard label="Avg duration" value={pipeline.avgDuration} description="per run" />
339
+ </div>
340
+
341
+ <section aria-label="Attributes">
342
+ <h3 className="mb-2 text-body font-semibold text-foreground">Configuration</h3>
343
+ <Descriptions columns={2} layout="vertical">
344
+ <DescriptionsItem label="Source">{pipeline.source}</DescriptionsItem>
345
+ <DescriptionsItem label="Destination">{pipeline.destination}</DescriptionsItem>
346
+ <DescriptionsItem label="Schedule">{pipeline.schedule}</DescriptionsItem>
347
+ <DescriptionsItem label="Owner">{pipeline.owner}</DescriptionsItem>
348
+ <DescriptionsItem label="Last run">{pipeline.lastRun}</DescriptionsItem>
349
+ <DescriptionsItem label="Pipeline ID">{pipeline.id}</DescriptionsItem>
350
+ </Descriptions>
351
+ </section>
352
+
353
+ <section aria-label="Recent runs">
354
+ <h3 className="mb-2 text-body font-semibold text-foreground">Recent runs</h3>
355
+ <RunList runs={pipeline.runs} />
356
+ </section>
357
+ </TabsContent>
358
+
359
+ {/* Activity — the related child list in full */}
360
+ <TabsContent value="activity" className="space-y-4">
361
+ <RunList runs={pipeline.runs} />
362
+ </TabsContent>
363
+
364
+ {/* Settings — a couple of editable fields */}
365
+ <TabsContent value="settings" className="max-w-lg space-y-5">
366
+ <div className="space-y-1.5">
367
+ <Label htmlFor="hub-name">Pipeline name</Label>
368
+ <Input id="hub-name" defaultValue={pipeline.name} autoComplete="off" />
369
+ </div>
370
+ <div className="space-y-1.5">
371
+ <Label htmlFor="hub-schedule">Schedule</Label>
372
+ <Input id="hub-schedule" defaultValue={pipeline.schedule} autoComplete="off" />
373
+ </div>
374
+ <Separator />
375
+ <div className="flex justify-end">
376
+ <Button size="sm" onClick={() => toast.success("Pipeline settings saved")}>
377
+ Save changes
378
+ </Button>
379
+ </div>
380
+ </TabsContent>
381
+ </Tabs>
382
+ </div>
383
+ </div>
384
+ );
385
+ }
386
+
387
+ function RunList({ runs }: { runs: Run[] }) {
388
+ return (
389
+ <ul className="overflow-hidden rounded-lg border bg-card">
390
+ {runs.map((r, i) => (
391
+ <li
392
+ key={r.id}
393
+ className={["flex items-center gap-3 px-4 py-2.5", i > 0 ? "border-t" : ""].join(" ")}
394
+ >
395
+ <StatusBadge status={r.status} />
396
+ <span className="min-w-0 flex-1 truncate text-body text-foreground">{r.trigger} run</span>
397
+ <span className="hidden tabular-nums text-meta text-muted-foreground sm:inline">
398
+ {r.rows.toLocaleString()} rows
399
+ </span>
400
+ <span className="shrink-0 text-meta text-muted-foreground">{r.when}</span>
401
+ </li>
402
+ ))}
403
+ </ul>
404
+ );
405
+ }
406
+
407
+ /* -------------------------------------------------------------------------- */
408
+ /* The screen */
409
+ /* -------------------------------------------------------------------------- */
410
+
411
+ function ObjectDetailHub({ startSelected = "rev" }: { startSelected?: string | null }) {
412
+ const [selectedId, setSelectedId] = useState<string | null>(startSelected);
413
+ const selected = PIPELINES.find((p) => p.id === selectedId) ?? null;
414
+
415
+ return (
416
+ <div className="h-dvh w-full bg-background text-foreground">
417
+ <SplitPanel
418
+ startSize="300px"
419
+ className="h-full"
420
+ start={<PipelineList selectedId={selectedId} onSelect={setSelectedId} />}
421
+ end={
422
+ selected ? (
423
+ <PipelineHub pipeline={selected} />
424
+ ) : (
425
+ <div className="grid h-full place-items-center p-6">
426
+ <StatePanel
427
+ kind="empty"
428
+ title="Select a pipeline"
429
+ description="Choose a pipeline from the list to see its configuration, runs and settings."
430
+ />
431
+ </div>
432
+ )
433
+ }
434
+ />
435
+ <Toaster />
436
+ </div>
437
+ );
438
+ }
439
+
440
+ /* -------------------------------------------------------------------------- */
441
+ /* Stories */
442
+ /* -------------------------------------------------------------------------- */
443
+
444
+ const meta = {
445
+ title: "Patterns/Templates/Object Detail Hub",
446
+ component: ObjectDetailHub,
447
+ parameters: {
448
+ layout: "fullscreen",
449
+ docs: {
450
+ description: {
451
+ component:
452
+ "The canonical master-detail enterprise screen: a searchable object list (SplitPanel `start`) drives a detail HUB (`end`) with a sticky SectionHeader identity + status, a ButtonGroup toolbar, Tabs for the object's sections (Overview / Activity / Settings), Descriptions for attributes, a related-runs list, and a high-value Run task promoted to its own Dialog. Selecting nothing shows a real StatePanel empty state. Compose-only from @elabs-ai/components-* primitives; semantic tokens only; reads in all three themes.",
453
+ },
454
+ },
455
+ },
456
+ tags: ["autodocs"],
457
+ } satisfies Meta<typeof ObjectDetailHub>;
458
+
459
+ export default meta;
460
+ type Story = StoryObj<typeof meta>;
461
+
462
+ /** Master-detail with the first object selected (the common entry state). */
463
+ export const Default: Story = {};
464
+
465
+ /** The unselected state — the detail pane shows a real empty state. */
466
+ export const NoSelection: Story = { args: { startSelected: null } };