@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,179 @@
1
+ import { forwardRef, type ComponentPropsWithoutRef, type ElementRef } from "react";
2
+ import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
3
+ import { Check, ChevronRight, Circle } from "lucide-react";
4
+ import { cn } from "../../lib/cn";
5
+
6
+ export const DropdownMenu = DropdownMenuPrimitive.Root;
7
+ export const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
8
+ export const DropdownMenuGroup = DropdownMenuPrimitive.Group;
9
+ export const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
10
+ export const DropdownMenuSub = DropdownMenuPrimitive.Sub;
11
+ export const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
12
+
13
+ export const DropdownMenuSubTrigger = forwardRef<
14
+ ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
15
+ ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & { inset?: boolean }
16
+ >(function DropdownMenuSubTrigger({ className, inset, children, ...props }, ref) {
17
+ return (
18
+ <DropdownMenuPrimitive.SubTrigger
19
+ ref={ref}
20
+ data-slot="dropdown-menu-sub-trigger"
21
+ className={cn(
22
+ "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-body outline-none transition-colors duration-fast focus:bg-accent data-[state=open]:bg-accent",
23
+ inset && "ps-8",
24
+ className,
25
+ )}
26
+ {...props}
27
+ >
28
+ {children}
29
+ <ChevronRight className="ms-auto size-4" />
30
+ </DropdownMenuPrimitive.SubTrigger>
31
+ );
32
+ });
33
+
34
+ export const DropdownMenuSubContent = forwardRef<
35
+ ElementRef<typeof DropdownMenuPrimitive.SubContent>,
36
+ ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
37
+ >(function DropdownMenuSubContent({ className, ...props }, ref) {
38
+ return (
39
+ <DropdownMenuPrimitive.Portal>
40
+ <DropdownMenuPrimitive.SubContent
41
+ ref={ref}
42
+ data-slot="dropdown-menu-sub-content"
43
+ className={cn(
44
+ "z-50 min-w-[8rem] overflow-hidden rounded-md bg-popover p-1 text-popover-foreground shadow-ring-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2",
45
+ className,
46
+ )}
47
+ {...props}
48
+ />
49
+ </DropdownMenuPrimitive.Portal>
50
+ );
51
+ });
52
+
53
+ export const DropdownMenuRadioItem = forwardRef<
54
+ ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
55
+ ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
56
+ >(function DropdownMenuRadioItem({ className, children, ...props }, ref) {
57
+ return (
58
+ <DropdownMenuPrimitive.RadioItem
59
+ ref={ref}
60
+ data-slot="dropdown-menu-radio-item"
61
+ className={cn(
62
+ "relative flex cursor-default select-none items-center rounded-sm py-1.5 ps-8 pe-2 text-body outline-none transition-colors duration-fast focus:bg-accent",
63
+ className,
64
+ )}
65
+ {...props}
66
+ >
67
+ <span className="absolute start-2 flex size-3.5 items-center justify-center">
68
+ <DropdownMenuPrimitive.ItemIndicator>
69
+ <Circle className="size-2 fill-current" />
70
+ </DropdownMenuPrimitive.ItemIndicator>
71
+ </span>
72
+ {children}
73
+ </DropdownMenuPrimitive.RadioItem>
74
+ );
75
+ });
76
+
77
+ export const DropdownMenuCheckboxItem = forwardRef<
78
+ ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
79
+ ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
80
+ >(function DropdownMenuCheckboxItem({ className, children, checked, ...props }, ref) {
81
+ return (
82
+ <DropdownMenuPrimitive.CheckboxItem
83
+ ref={ref}
84
+ checked={checked}
85
+ data-slot="dropdown-menu-checkbox-item"
86
+ className={cn(
87
+ "relative flex cursor-default select-none items-center rounded-sm py-1.5 ps-8 pe-2 text-body outline-none transition-colors duration-fast focus:bg-accent",
88
+ className,
89
+ )}
90
+ {...props}
91
+ >
92
+ <span className="absolute start-2 flex size-3.5 items-center justify-center">
93
+ <DropdownMenuPrimitive.ItemIndicator>
94
+ <Check className="size-4" />
95
+ </DropdownMenuPrimitive.ItemIndicator>
96
+ </span>
97
+ {children}
98
+ </DropdownMenuPrimitive.CheckboxItem>
99
+ );
100
+ });
101
+
102
+ export const DropdownMenuContent = forwardRef<
103
+ ElementRef<typeof DropdownMenuPrimitive.Content>,
104
+ ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
105
+ >(function DropdownMenuContent({ className, sideOffset = 6, ...props }, ref) {
106
+ return (
107
+ <DropdownMenuPrimitive.Portal>
108
+ <DropdownMenuPrimitive.Content
109
+ ref={ref}
110
+ sideOffset={sideOffset}
111
+ className={cn(
112
+ "z-50 min-w-[10rem] overflow-hidden rounded-md bg-popover p-1 text-popover-foreground shadow-ring-md",
113
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[state=closed]:zoom-out-95",
114
+ "data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2",
115
+ className,
116
+ )}
117
+ {...props}
118
+ />
119
+ </DropdownMenuPrimitive.Portal>
120
+ );
121
+ });
122
+
123
+ export const DropdownMenuItem = forwardRef<
124
+ ElementRef<typeof DropdownMenuPrimitive.Item>,
125
+ ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & { inset?: boolean }
126
+ >(function DropdownMenuItem({ className, inset, ...props }, ref) {
127
+ return (
128
+ <DropdownMenuPrimitive.Item
129
+ ref={ref}
130
+ className={cn(
131
+ "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors duration-fast",
132
+ "focus:bg-accent focus:text-accent-foreground",
133
+ "data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
134
+ "[&_svg]:size-4 [&_svg]:shrink-0",
135
+ inset && "ps-8",
136
+ className,
137
+ )}
138
+ {...props}
139
+ />
140
+ );
141
+ });
142
+
143
+ export const DropdownMenuLabel = forwardRef<
144
+ ElementRef<typeof DropdownMenuPrimitive.Label>,
145
+ ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label>
146
+ >(function DropdownMenuLabel({ className, ...props }, ref) {
147
+ return (
148
+ <DropdownMenuPrimitive.Label
149
+ ref={ref}
150
+ className={cn("px-2 py-1.5 text-xs font-semibold text-muted-foreground", className)}
151
+ {...props}
152
+ />
153
+ );
154
+ });
155
+
156
+ export const DropdownMenuSeparator = forwardRef<
157
+ ElementRef<typeof DropdownMenuPrimitive.Separator>,
158
+ ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
159
+ >(function DropdownMenuSeparator({ className, ...props }, ref) {
160
+ return (
161
+ <DropdownMenuPrimitive.Separator
162
+ ref={ref}
163
+ className={cn("-mx-1 my-1 h-px bg-border", className)}
164
+ {...props}
165
+ />
166
+ );
167
+ });
168
+
169
+ export function DropdownMenuShortcut({
170
+ className,
171
+ ...props
172
+ }: React.HTMLAttributes<HTMLSpanElement>) {
173
+ return (
174
+ <span
175
+ className={cn("ms-auto text-xs tracking-widest text-muted-foreground", className)}
176
+ {...props}
177
+ />
178
+ );
179
+ }
@@ -0,0 +1,17 @@
1
+ export {
2
+ DropdownMenu,
3
+ DropdownMenuTrigger,
4
+ DropdownMenuGroup,
5
+ DropdownMenuPortal,
6
+ DropdownMenuSub,
7
+ DropdownMenuRadioGroup,
8
+ DropdownMenuSubTrigger,
9
+ DropdownMenuSubContent,
10
+ DropdownMenuContent,
11
+ DropdownMenuItem,
12
+ DropdownMenuCheckboxItem,
13
+ DropdownMenuRadioItem,
14
+ DropdownMenuLabel,
15
+ DropdownMenuSeparator,
16
+ DropdownMenuShortcut,
17
+ } from "./dropdown-menu";
@@ -0,0 +1,54 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { Button } from "../button";
3
+ import { EmptyState } from "./empty-state";
4
+
5
+ const meta = {
6
+ title: "States/EmptyState",
7
+ component: EmptyState,
8
+ tags: ["autodocs"],
9
+ parameters: {
10
+ docs: {
11
+ description: {
12
+ component:
13
+ '@deprecated — use `<StatePanel kind="empty" />` instead. Thin back-compat wrapper.',
14
+ },
15
+ },
16
+ },
17
+ argTypes: {
18
+ title: {
19
+ description: "Heading text shown in the empty state.",
20
+ control: "text",
21
+ table: { category: "Content" },
22
+ },
23
+ description: {
24
+ description: "Supporting description shown below the title.",
25
+ control: "text",
26
+ table: { category: "Content" },
27
+ },
28
+ icon: {
29
+ description: "Icon or illustration shown above the title.",
30
+ control: false,
31
+ table: { category: "Content" },
32
+ },
33
+ actions: {
34
+ description: "Primary or secondary action buttons.",
35
+ control: false,
36
+ table: { category: "Content" },
37
+ },
38
+ className: {
39
+ description: "Additional CSS classes applied to the container.",
40
+ control: "text",
41
+ table: { category: "Styling" },
42
+ },
43
+ },
44
+ } satisfies Meta<typeof EmptyState>;
45
+ export default meta;
46
+ type Story = StoryObj<typeof meta>;
47
+
48
+ export const Default: Story = {
49
+ args: {
50
+ title: "No projects yet",
51
+ description: "Create your first project to get started.",
52
+ actions: <Button size="sm">New project</Button>,
53
+ },
54
+ };
@@ -0,0 +1,11 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { render, screen } from "@testing-library/react";
3
+ import { EmptyState } from "./empty-state";
4
+
5
+ describe("EmptyState", () => {
6
+ it("renders title and description", () => {
7
+ render(<EmptyState title="Empty" description="Nothing here" />);
8
+ expect(screen.getByText("Empty")).toBeInTheDocument();
9
+ expect(screen.getByText("Nothing here")).toBeInTheDocument();
10
+ });
11
+ });
@@ -0,0 +1,30 @@
1
+ /**
2
+ * @deprecated Use `StatePanel kind="empty"` from `@elabs-ai/components-ui` instead.
3
+ * This wrapper is kept for back-compat and will be removed in a future release.
4
+ */
5
+ import { type ReactNode } from "react";
6
+ import { StatePanel } from "../state-panel/state-panel";
7
+
8
+ export interface EmptyStateProps {
9
+ title: ReactNode;
10
+ description?: ReactNode;
11
+ /** Optional icon/illustration shown above the title. */
12
+ icon?: ReactNode;
13
+ /** Primary/secondary actions. */
14
+ actions?: ReactNode;
15
+ className?: string;
16
+ }
17
+
18
+ /** @deprecated Use `<StatePanel kind="empty" />` instead. */
19
+ export function EmptyState({ title, description, icon, actions, className }: EmptyStateProps) {
20
+ return (
21
+ <StatePanel
22
+ kind="empty"
23
+ title={title}
24
+ description={description}
25
+ icon={icon}
26
+ actions={actions}
27
+ className={className}
28
+ />
29
+ );
30
+ }
@@ -0,0 +1 @@
1
+ export { EmptyState, type EmptyStateProps } from "./empty-state";
@@ -0,0 +1,51 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { Button } from "../button";
3
+ import { ErrorState } from "./error-state";
4
+
5
+ const meta = {
6
+ title: "States/ErrorState",
7
+ component: ErrorState,
8
+ tags: ["autodocs"],
9
+ parameters: {
10
+ docs: {
11
+ description: {
12
+ component:
13
+ '@deprecated — use `<StatePanel kind="error" />` instead. Thin back-compat wrapper.',
14
+ },
15
+ },
16
+ },
17
+ argTypes: {
18
+ title: {
19
+ description: 'Heading text. Defaults to "Something went wrong".',
20
+ control: "text",
21
+ table: { category: "Content" },
22
+ },
23
+ description: {
24
+ description: "Supporting description. Defaults to a generic error message.",
25
+ control: "text",
26
+ table: { category: "Content" },
27
+ },
28
+ actions: {
29
+ description: "Retry or support action buttons.",
30
+ control: false,
31
+ table: { category: "Content" },
32
+ },
33
+ className: {
34
+ description: "Additional CSS classes applied to the container.",
35
+ control: "text",
36
+ table: { category: "Styling" },
37
+ },
38
+ },
39
+ } satisfies Meta<typeof ErrorState>;
40
+ export default meta;
41
+ type Story = StoryObj<typeof meta>;
42
+
43
+ export const Default: Story = {
44
+ args: {
45
+ actions: (
46
+ <Button size="sm" variant="outline">
47
+ Retry
48
+ </Button>
49
+ ),
50
+ },
51
+ };
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @deprecated Use `StatePanel kind="error"` from `@elabs-ai/components-ui` instead.
3
+ * This wrapper is kept for back-compat and will be removed in a future release.
4
+ */
5
+ import { type ReactNode } from "react";
6
+ import { StatePanel } from "../state-panel/state-panel";
7
+
8
+ export interface ErrorStateProps {
9
+ title?: ReactNode;
10
+ description?: ReactNode;
11
+ /** Optional retry / support actions. */
12
+ actions?: ReactNode;
13
+ className?: string;
14
+ }
15
+
16
+ /** @deprecated Use `<StatePanel kind="error" />` instead. */
17
+ export function ErrorState({ title, description, actions, className }: ErrorStateProps) {
18
+ return (
19
+ <StatePanel
20
+ kind="error"
21
+ title={title}
22
+ description={description}
23
+ actions={actions}
24
+ className={className}
25
+ />
26
+ );
27
+ }
@@ -0,0 +1 @@
1
+ export { ErrorState, type ErrorStateProps } from "./error-state";
@@ -0,0 +1,246 @@
1
+ import { useState, type ReactNode } from "react";
2
+ import type { Meta, StoryObj } from "@storybook/react-vite";
3
+ import { expect, waitFor, within } from "storybook/test";
4
+ import { Button } from "../button";
5
+ import { Dialog, DialogTrigger } from "../dialog";
6
+ import { Heading, Text } from "../typography";
7
+ import { ExpandDialog } from "./expand-dialog";
8
+
9
+ /**
10
+ * The one "make this bigger" surface: enlarged content on the inline-start, its
11
+ * context on the inline-end. Every expand affordance in the system — a chart, a
12
+ * table, an image, a document — opens this, so the gesture means the same thing
13
+ * everywhere.
14
+ */
15
+ const meta = {
16
+ title: "Overlays/ExpandDialog",
17
+ component: ExpandDialog,
18
+ tags: ["autodocs"],
19
+ } satisfies Meta<typeof ExpandDialog>;
20
+ export default meta;
21
+ type Story = StoryObj<typeof meta>;
22
+
23
+ function SampleView({ label = "Enlarged content" }: { label?: string }) {
24
+ return (
25
+ <div className="grid h-full min-h-64 place-items-center rounded-md bg-surface-muted">
26
+ <Text tone="muted">{label}</Text>
27
+ </div>
28
+ );
29
+ }
30
+
31
+ function SampleDetail() {
32
+ return (
33
+ <div className="flex flex-col gap-3 p-4">
34
+ <Heading level={3} size="subtitle">
35
+ Summary
36
+ </Heading>
37
+ <Text variant="caption" tone="muted">
38
+ Context that explains what is on the left — a data summary, capture metadata, the provenance
39
+ of a document.
40
+ </Text>
41
+ </div>
42
+ );
43
+ }
44
+
45
+ /*
46
+ * Opens from a trigger you own; state stays in your component. The trigger is
47
+ * `outline` rather than the default primary purely so these stories exercise
48
+ * ExpandDialog and not the known light primary-fill contrast exemption
49
+ * (#180) — the component itself has no opinion about the trigger.
50
+ */
51
+ function Frame({ children, label = "Expand" }: { children: ReactNode; label?: string }) {
52
+ return (
53
+ <Dialog>
54
+ <DialogTrigger asChild>
55
+ <Button variant="outline">{label}</Button>
56
+ </DialogTrigger>
57
+ {children}
58
+ </Dialog>
59
+ );
60
+ }
61
+
62
+ export const Default: Story = {
63
+ render: () => (
64
+ <Frame>
65
+ <ExpandDialog title="Revenue by region" detail={<SampleDetail />}>
66
+ <SampleView />
67
+ </ExpandDialog>
68
+ </Frame>
69
+ ),
70
+ play: async ({ canvas, canvasElement, userEvent }) => {
71
+ await userEvent.click(canvas.getByRole("button", { name: /expand/i }));
72
+ const body = within(canvasElement.ownerDocument.body);
73
+ const dialog = await body.findByRole("dialog");
74
+ await waitFor(() => expect(within(dialog).getByText("Revenue by region")).toBeVisible());
75
+ // The context pane is a named region, so AT can jump straight to it.
76
+ await expect(within(dialog).getByRole("region", { name: "Details" })).toBeInTheDocument();
77
+ },
78
+ };
79
+
80
+ /** A description gives the dialog its `aria-describedby`; without one it opts out. */
81
+ export const WithDescription: Story = {
82
+ render: () => (
83
+ <Frame>
84
+ <ExpandDialog
85
+ title="Revenue by region"
86
+ description="Trailing twelve months, in reporting currency."
87
+ detail={<SampleDetail />}
88
+ >
89
+ <SampleView />
90
+ </ExpandDialog>
91
+ </Frame>
92
+ ),
93
+ };
94
+
95
+ /** Header actions sit on the title row — never in a second toolbar strip above it. */
96
+ export const WithActions: Story = {
97
+ render: () => (
98
+ <Frame>
99
+ <ExpandDialog
100
+ title="Revenue by region"
101
+ actions={
102
+ <Button size="sm" variant="outline">
103
+ Download
104
+ </Button>
105
+ }
106
+ detail={<SampleDetail />}
107
+ >
108
+ <SampleView />
109
+ </ExpandDialog>
110
+ </Frame>
111
+ ),
112
+ };
113
+
114
+ /**
115
+ * No `detail` → one full-width track and no region wrapper at all. The optional
116
+ * pane costs nothing when it is not asked for.
117
+ */
118
+ export const NoDetail: Story = {
119
+ render: () => (
120
+ <Frame>
121
+ <ExpandDialog title="Q3 architecture diagram">
122
+ <SampleView label="Single pane" />
123
+ </ExpandDialog>
124
+ </Frame>
125
+ ),
126
+ };
127
+
128
+ /** Context below rather than beside — for wide content that needs the full width. */
129
+ export const DetailBottom: Story = {
130
+ render: () => (
131
+ <Frame>
132
+ <ExpandDialog
133
+ title="Deployment timeline"
134
+ detailPlacement="bottom"
135
+ detailSize="minmax(0, 12rem)"
136
+ detail={<SampleDetail />}
137
+ >
138
+ <SampleView />
139
+ </ExpandDialog>
140
+ </Frame>
141
+ ),
142
+ };
143
+
144
+ /**
145
+ * `stackBelow` re-runs the placement decision at a breakpoint: a 2.5fr/1fr split
146
+ * on a phone leaves the context pane about 107px wide — present, unreadable. All
147
+ * three breakpoints render here; resize the preview to see them swap.
148
+ */
149
+ export const StackBelowSm: Story = {
150
+ render: () => (
151
+ <Frame>
152
+ <ExpandDialog title="Stacks below sm" stackBelow="sm" detail={<SampleDetail />}>
153
+ <SampleView />
154
+ </ExpandDialog>
155
+ </Frame>
156
+ ),
157
+ };
158
+
159
+ export const StackBelowMd: Story = {
160
+ render: () => (
161
+ <Frame>
162
+ <ExpandDialog title="Stacks below md" stackBelow="md" detail={<SampleDetail />}>
163
+ <SampleView />
164
+ </ExpandDialog>
165
+ </Frame>
166
+ ),
167
+ };
168
+
169
+ export const StackBelowLg: Story = {
170
+ render: () => (
171
+ <Frame>
172
+ <ExpandDialog title="Stacks below lg" stackBelow="lg" detail={<SampleDetail />}>
173
+ <SampleView />
174
+ </ExpandDialog>
175
+ </Frame>
176
+ ),
177
+ };
178
+
179
+ /** Explicit `never` — the default, spelled out. */
180
+ export const StackNever: Story = {
181
+ render: () => (
182
+ <Frame>
183
+ <ExpandDialog title="Never stacks" stackBelow="never" detail={<SampleDetail />}>
184
+ <SampleView />
185
+ </ExpandDialog>
186
+ </Frame>
187
+ ),
188
+ };
189
+
190
+ /** `detailPlacement="side"` spelled out, for the same reason. */
191
+ export const DetailSide: Story = {
192
+ render: () => (
193
+ <Frame>
194
+ <ExpandDialog title="Detail beside" detailPlacement="side" detail={<SampleDetail />}>
195
+ <SampleView />
196
+ </ExpandDialog>
197
+ </Frame>
198
+ ),
199
+ };
200
+
201
+ /**
202
+ * Both panes overflow. Each is its own scroll owner and its own tab stop — a
203
+ * scroll container with no focusable child is unreachable by keyboard (WCAG
204
+ * 2.1.1; axe `scrollable-region-focusable`).
205
+ */
206
+ export const LongContent: Story = {
207
+ render: () => (
208
+ <Frame>
209
+ <ExpandDialog
210
+ title="A long report"
211
+ detail={
212
+ <div className="flex flex-col gap-2 p-4">
213
+ {Array.from({ length: 40 }, (_, i) => (
214
+ <Text key={i} variant="caption" tone="muted">
215
+ Note {i + 1}
216
+ </Text>
217
+ ))}
218
+ </div>
219
+ }
220
+ >
221
+ <div className="flex flex-col gap-4">
222
+ {Array.from({ length: 40 }, (_, i) => (
223
+ <Text key={i}>Paragraph {i + 1}</Text>
224
+ ))}
225
+ </div>
226
+ </ExpandDialog>
227
+ </Frame>
228
+ ),
229
+ };
230
+
231
+ /** Controlled open state, for an app that drives it from a menu or a route. */
232
+ export const Controlled: Story = {
233
+ render: function ControlledStory() {
234
+ const [open, setOpen] = useState(false);
235
+ return (
236
+ <Dialog open={open} onOpenChange={setOpen}>
237
+ <Button variant="outline" onClick={() => setOpen(true)}>
238
+ Open from outside
239
+ </Button>
240
+ <ExpandDialog title="Controlled" detail={<SampleDetail />}>
241
+ <SampleView />
242
+ </ExpandDialog>
243
+ </Dialog>
244
+ );
245
+ },
246
+ };