@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.
- package/LICENSE +21 -0
- package/README.md +78 -0
- package/dist/index.d.ts +4497 -0
- package/dist/index.js +15192 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/cn.d.ts +10 -0
- package/dist/lib/cn.js +20 -0
- package/dist/lib/cn.js.map +1 -0
- package/package.json +106 -0
- package/src/blocks/sidebar-02/app-sidebar.tsx +160 -0
- package/src/blocks/sidebar-02/logo.tsx +18 -0
- package/src/blocks/sidebar-02/nav-main.tsx +13 -0
- package/src/blocks/sidebar-02/nav-notifications.tsx +7 -0
- package/src/blocks/sidebar-02/sidebar-02.stories.tsx +23 -0
- package/src/blocks/sidebar-02/team-switcher.tsx +7 -0
- package/src/blocks/sidebar-04/app-sidebar.tsx +236 -0
- package/src/blocks/sidebar-04/mail-context.tsx +29 -0
- package/src/blocks/sidebar-04/nav-user.tsx +6 -0
- package/src/blocks/sidebar-04/sidebar-04.stories.tsx +150 -0
- package/src/blocks/sidebar-05/app-sidebar.tsx +375 -0
- package/src/blocks/sidebar-05/sidebar-05.stories.tsx +18 -0
- package/src/blocks/sidebar-05/team-switcher.tsx +6 -0
- package/src/blocks-comparison-table.stories.tsx +165 -0
- package/src/components/accordion/accordion.stories.tsx +71 -0
- package/src/components/accordion/accordion.test.tsx +18 -0
- package/src/components/accordion/accordion.tsx +49 -0
- package/src/components/accordion/index.ts +1 -0
- package/src/components/advanced-group/advanced-group.stories.tsx +143 -0
- package/src/components/advanced-group/advanced-group.test.tsx +81 -0
- package/src/components/advanced-group/advanced-group.tsx +111 -0
- package/src/components/advanced-group/index.ts +2 -0
- package/src/components/alert/alert.stories.tsx +156 -0
- package/src/components/alert/alert.test.tsx +57 -0
- package/src/components/alert/alert.tsx +91 -0
- package/src/components/alert/index.ts +8 -0
- package/src/components/alert-dialog/alert-dialog.stories.tsx +73 -0
- package/src/components/alert-dialog/alert-dialog.tsx +104 -0
- package/src/components/alert-dialog/index.ts +12 -0
- package/src/components/app-shell/app-shell.stories.tsx +61 -0
- package/src/components/app-shell/app-shell.tsx +31 -0
- package/src/components/app-shell/index.ts +1 -0
- package/src/components/app-sidebar/app-sidebar.stories.tsx +121 -0
- package/src/components/app-sidebar/app-sidebar.test.tsx +55 -0
- package/src/components/app-sidebar/app-sidebar.tsx +53 -0
- package/src/components/app-sidebar/index.ts +1 -0
- package/src/components/aspect-ratio/aspect-ratio.stories.tsx +46 -0
- package/src/components/aspect-ratio/aspect-ratio.tsx +3 -0
- package/src/components/aspect-ratio/index.ts +1 -0
- package/src/components/attribution-panel/attribution-panel.stories.tsx +83 -0
- package/src/components/attribution-panel/attribution-panel.test.tsx +123 -0
- package/src/components/attribution-panel/attribution-panel.tsx +191 -0
- package/src/components/attribution-panel/attribution-types.ts +44 -0
- package/src/components/attribution-panel/attributions.generated.ts +1422 -0
- package/src/components/attribution-panel/index.ts +3 -0
- package/src/components/avatar/avatar.stories.tsx +45 -0
- package/src/components/avatar/avatar.test.tsx +13 -0
- package/src/components/avatar/avatar.tsx +46 -0
- package/src/components/avatar/index.ts +1 -0
- package/src/components/badge/badge.stories.tsx +44 -0
- package/src/components/badge/badge.test.tsx +10 -0
- package/src/components/badge/badge.tsx +28 -0
- package/src/components/badge/index.ts +1 -0
- package/src/components/bento-grid/bento-grid.stories.tsx +165 -0
- package/src/components/bento-grid/bento-grid.test.tsx +119 -0
- package/src/components/bento-grid/bento-grid.tsx +339 -0
- package/src/components/bento-grid/index.ts +9 -0
- package/src/components/bounded-number/bounded-number.stories.tsx +118 -0
- package/src/components/bounded-number/bounded-number.test.tsx +73 -0
- package/src/components/bounded-number/bounded-number.tsx +110 -0
- package/src/components/bounded-number/index.ts +1 -0
- package/src/components/breadcrumb/breadcrumb.stories.tsx +47 -0
- package/src/components/breadcrumb/breadcrumb.test.tsx +17 -0
- package/src/components/breadcrumb/breadcrumb.tsx +86 -0
- package/src/components/breadcrumb/index.ts +9 -0
- package/src/components/button/button.stories.tsx +70 -0
- package/src/components/button/button.test.tsx +50 -0
- package/src/components/button/button.tsx +63 -0
- package/src/components/button/index.ts +1 -0
- package/src/components/button-group/button-group.stories.tsx +45 -0
- package/src/components/button-group/button-group.tsx +70 -0
- package/src/components/button-group/index.ts +6 -0
- package/src/components/calendar/calendar.stories.tsx +95 -0
- package/src/components/calendar/calendar.test.tsx +140 -0
- package/src/components/calendar/calendar.tsx +70 -0
- package/src/components/calendar/index.ts +1 -0
- package/src/components/card/card.stories.tsx +438 -0
- package/src/components/card/card.test.tsx +153 -0
- package/src/components/card/card.tsx +332 -0
- package/src/components/card/index.ts +13 -0
- package/src/components/carousel/carousel.stories.tsx +102 -0
- package/src/components/carousel/carousel.tsx +225 -0
- package/src/components/carousel/index.ts +8 -0
- package/src/components/change-review/change-review.stories.tsx +288 -0
- package/src/components/change-review/change-review.test.tsx +261 -0
- package/src/components/change-review/change-review.tsx +637 -0
- package/src/components/change-review/index.ts +23 -0
- package/src/components/checkbox/checkbox.stories.tsx +119 -0
- package/src/components/checkbox/checkbox.test.tsx +40 -0
- package/src/components/checkbox/checkbox.tsx +34 -0
- package/src/components/checkbox/index.ts +1 -0
- package/src/components/collapsible/collapsible.stories.tsx +62 -0
- package/src/components/collapsible/collapsible.tsx +22 -0
- package/src/components/collapsible/index.ts +1 -0
- package/src/components/collapsible-panel/index.ts +8 -0
- package/src/components/collapsible-panel/use-collapsible-panel.test.tsx +93 -0
- package/src/components/collapsible-panel/use-collapsible-panel.ts +159 -0
- package/src/components/color-picker/color-picker.stories.tsx +93 -0
- package/src/components/color-picker/color-picker.test.tsx +79 -0
- package/src/components/color-picker/color-picker.tsx +341 -0
- package/src/components/color-picker/index.ts +1 -0
- package/src/components/combobox/combobox.stories.tsx +154 -0
- package/src/components/combobox/combobox.test.tsx +246 -0
- package/src/components/combobox/combobox.tsx +210 -0
- package/src/components/combobox/index.ts +1 -0
- package/src/components/command/command.stories.tsx +371 -0
- package/src/components/command/command.test.tsx +280 -0
- package/src/components/command/command.tsx +301 -0
- package/src/components/command/index.ts +13 -0
- package/src/components/confirm-dialog/confirm-dialog.stories.tsx +183 -0
- package/src/components/confirm-dialog/confirm-dialog.test.tsx +147 -0
- package/src/components/confirm-dialog/confirm-dialog.tsx +146 -0
- package/src/components/confirm-dialog/index.ts +2 -0
- package/src/components/context-menu/context-menu.stories.tsx +70 -0
- package/src/components/context-menu/context-menu.tsx +172 -0
- package/src/components/context-menu/index.ts +16 -0
- package/src/components/copyable-value/copyable-value.stories.tsx +80 -0
- package/src/components/copyable-value/copyable-value.test.tsx +128 -0
- package/src/components/copyable-value/copyable-value.tsx +101 -0
- package/src/components/copyable-value/index.ts +1 -0
- package/src/components/date-picker/date-picker.stories.tsx +42 -0
- package/src/components/date-picker/date-picker.tsx +49 -0
- package/src/components/date-picker/index.ts +1 -0
- package/src/components/date-range-picker/date-range-picker.stories.tsx +151 -0
- package/src/components/date-range-picker/date-range-picker.test.tsx +62 -0
- package/src/components/date-range-picker/date-range-picker.tsx +126 -0
- package/src/components/date-range-picker/index.ts +6 -0
- package/src/components/descriptions/descriptions.stories.tsx +43 -0
- package/src/components/descriptions/descriptions.test.tsx +41 -0
- package/src/components/descriptions/descriptions.tsx +95 -0
- package/src/components/descriptions/index.ts +6 -0
- package/src/components/dialog/dialog.stories.tsx +728 -0
- package/src/components/dialog/dialog.test.tsx +285 -0
- package/src/components/dialog/dialog.tsx +340 -0
- package/src/components/dialog/index.ts +18 -0
- package/src/components/dialog/use-dialog-dismiss-guard.test.tsx +80 -0
- package/src/components/dialog/use-dialog-dismiss-guard.ts +109 -0
- package/src/components/drawer/drawer.stories.tsx +76 -0
- package/src/components/drawer/drawer.tsx +85 -0
- package/src/components/drawer/index.ts +11 -0
- package/src/components/dropdown-menu/dropdown-menu.stories.tsx +176 -0
- package/src/components/dropdown-menu/dropdown-menu.test.tsx +109 -0
- package/src/components/dropdown-menu/dropdown-menu.tsx +179 -0
- package/src/components/dropdown-menu/index.ts +17 -0
- package/src/components/empty-state/empty-state.stories.tsx +54 -0
- package/src/components/empty-state/empty-state.test.tsx +11 -0
- package/src/components/empty-state/empty-state.tsx +30 -0
- package/src/components/empty-state/index.ts +1 -0
- package/src/components/error-state/error-state.stories.tsx +51 -0
- package/src/components/error-state/error-state.tsx +27 -0
- package/src/components/error-state/index.ts +1 -0
- package/src/components/expand-dialog/expand-dialog.stories.tsx +246 -0
- package/src/components/expand-dialog/expand-dialog.test.tsx +185 -0
- package/src/components/expand-dialog/expand-dialog.tsx +387 -0
- package/src/components/expand-dialog/index.ts +11 -0
- package/src/components/field-row/field-row.stories.tsx +114 -0
- package/src/components/field-row/field-row.test.tsx +188 -0
- package/src/components/field-row/field-row.tsx +115 -0
- package/src/components/field-row/index.ts +1 -0
- package/src/components/file-upload/file-upload.stories.tsx +183 -0
- package/src/components/file-upload/file-upload.test.tsx +169 -0
- package/src/components/file-upload/file-upload.tsx +459 -0
- package/src/components/file-upload/index.ts +13 -0
- package/src/components/form/form.stories.tsx +71 -0
- package/src/components/form/form.tsx +141 -0
- package/src/components/form/index.ts +10 -0
- package/src/components/hover-card/hover-card.stories.tsx +57 -0
- package/src/components/hover-card/hover-card.tsx +25 -0
- package/src/components/hover-card/index.ts +1 -0
- package/src/components/icon-button/icon-button.stories.tsx +108 -0
- package/src/components/icon-button/icon-button.test.tsx +93 -0
- package/src/components/icon-button/icon-button.tsx +139 -0
- package/src/components/icon-button/index.ts +1 -0
- package/src/components/input/index.ts +1 -0
- package/src/components/input/input.stories.tsx +44 -0
- package/src/components/input/input.test.tsx +19 -0
- package/src/components/input/input.tsx +26 -0
- package/src/components/input-group/index.ts +11 -0
- package/src/components/input-group/input-group.stories.tsx +87 -0
- package/src/components/input-group/input-group.test.tsx +48 -0
- package/src/components/input-group/input-group.tsx +170 -0
- package/src/components/input-otp/index.ts +1 -0
- package/src/components/input-otp/input-otp.stories.tsx +66 -0
- package/src/components/input-otp/input-otp.tsx +68 -0
- package/src/components/kbd/index.ts +1 -0
- package/src/components/kbd/kbd.stories.tsx +45 -0
- package/src/components/kbd/kbd.tsx +20 -0
- package/src/components/key-value-editor/index.ts +1 -0
- package/src/components/key-value-editor/key-value-editor.stories.tsx +130 -0
- package/src/components/key-value-editor/key-value-editor.test.tsx +231 -0
- package/src/components/key-value-editor/key-value-editor.tsx +263 -0
- package/src/components/label/index.ts +1 -0
- package/src/components/label/label.stories.tsx +35 -0
- package/src/components/label/label.tsx +19 -0
- package/src/components/link-preview/index.ts +2 -0
- package/src/components/link-preview/link-preview.stories.tsx +178 -0
- package/src/components/link-preview/link-preview.test.tsx +123 -0
- package/src/components/link-preview/link-preview.tsx +194 -0
- package/src/components/list-editor/index.ts +1 -0
- package/src/components/list-editor/list-editor.stories.tsx +119 -0
- package/src/components/list-editor/list-editor.test.tsx +96 -0
- package/src/components/list-editor/list-editor.tsx +192 -0
- package/src/components/loading-state/index.ts +1 -0
- package/src/components/loading-state/loading-state.stories.tsx +38 -0
- package/src/components/loading-state/loading-state.tsx +26 -0
- package/src/components/locale-provider/index.ts +8 -0
- package/src/components/locale-provider/locale-provider.stories.tsx +83 -0
- package/src/components/locale-provider/locale-provider.test.tsx +238 -0
- package/src/components/locale-provider/locale-provider.tsx +187 -0
- package/src/components/locale-provider/messages.ts +300 -0
- package/src/components/match-highlight/index.ts +7 -0
- package/src/components/match-highlight/match-highlight.stories.tsx +146 -0
- package/src/components/match-highlight/match-highlight.test.tsx +202 -0
- package/src/components/match-highlight/match-highlight.tsx +145 -0
- package/src/components/mention-input/index.ts +28 -0
- package/src/components/mention-input/mention-input-mirror.stories.tsx +171 -0
- package/src/components/mention-input/mention-input.stories.tsx +463 -0
- package/src/components/mention-input/mention-input.test.tsx +617 -0
- package/src/components/mention-input/mention-input.tsx +1102 -0
- package/src/components/mention-input/mention-value.test.ts +253 -0
- package/src/components/mention-input/mention-value.ts +193 -0
- package/src/components/menubar/index.ts +17 -0
- package/src/components/menubar/menubar.stories.tsx +67 -0
- package/src/components/menubar/menubar.tsx +198 -0
- package/src/components/metric-card/index.ts +1 -0
- package/src/components/metric-card/metric-card.stories.tsx +224 -0
- package/src/components/metric-card/metric-card.test.tsx +198 -0
- package/src/components/metric-card/metric-card.tsx +229 -0
- package/src/components/model-picker/index.ts +11 -0
- package/src/components/model-picker/model-picker-state.ts +85 -0
- package/src/components/model-picker/model-picker.stories.tsx +128 -0
- package/src/components/model-picker/model-picker.test.tsx +241 -0
- package/src/components/model-picker/model-picker.tsx +270 -0
- package/src/components/nav-main/index.ts +1 -0
- package/src/components/nav-main/nav-main.tsx +147 -0
- package/src/components/nav-notifications/index.ts +5 -0
- package/src/components/nav-notifications/nav-notifications.test.tsx +15 -0
- package/src/components/nav-notifications/nav-notifications.tsx +70 -0
- package/src/components/nav-user/index.ts +1 -0
- package/src/components/nav-user/nav-user.test.tsx +29 -0
- package/src/components/nav-user/nav-user.tsx +114 -0
- package/src/components/navigation-menu/index.ts +10 -0
- package/src/components/navigation-menu/navigation-menu.stories.tsx +92 -0
- package/src/components/navigation-menu/navigation-menu.tsx +99 -0
- package/src/components/number-input/index.ts +1 -0
- package/src/components/number-input/number-input.stories.tsx +120 -0
- package/src/components/number-input/number-input.test.tsx +96 -0
- package/src/components/number-input/number-input.tsx +219 -0
- package/src/components/page-shell/index.ts +1 -0
- package/src/components/page-shell/page-shell.stories.tsx +105 -0
- package/src/components/page-shell/page-shell.test.tsx +57 -0
- package/src/components/page-shell/page-shell.tsx +62 -0
- package/src/components/pagination/index.ts +9 -0
- package/src/components/pagination/pagination.stories.tsx +57 -0
- package/src/components/pagination/pagination.tsx +77 -0
- package/src/components/popover/index.ts +1 -0
- package/src/components/popover/popover.stories.tsx +28 -0
- package/src/components/popover/popover.test.tsx +16 -0
- package/src/components/popover/popover.tsx +29 -0
- package/src/components/progress/index.ts +1 -0
- package/src/components/progress/progress.stories.tsx +80 -0
- package/src/components/progress/progress.test.tsx +35 -0
- package/src/components/progress/progress.tsx +50 -0
- package/src/components/radio-group/index.ts +1 -0
- package/src/components/radio-group/radio-group.stories.tsx +75 -0
- package/src/components/radio-group/radio-group.test.tsx +16 -0
- package/src/components/radio-group/radio-group.tsx +33 -0
- package/src/components/rating/index.ts +1 -0
- package/src/components/rating/rating.stories.tsx +111 -0
- package/src/components/rating/rating.test.tsx +41 -0
- package/src/components/rating/rating.tsx +226 -0
- package/src/components/resizable/index.ts +1 -0
- package/src/components/resizable/resizable.stories.tsx +25 -0
- package/src/components/resizable/resizable.tsx +40 -0
- package/src/components/reveal/index.ts +7 -0
- package/src/components/reveal/reveal.stories.tsx +90 -0
- package/src/components/reveal/reveal.test.tsx +43 -0
- package/src/components/reveal/reveal.tsx +95 -0
- package/src/components/revision-timeline/index.ts +10 -0
- package/src/components/revision-timeline/revision-timeline.stories.tsx +785 -0
- package/src/components/revision-timeline/revision-timeline.test.tsx +509 -0
- package/src/components/revision-timeline/revision-timeline.tsx +1107 -0
- package/src/components/scroll-area/index.ts +1 -0
- package/src/components/scroll-area/scroll-area.stories.tsx +45 -0
- package/src/components/scroll-area/scroll-area.tsx +43 -0
- package/src/components/section-header/index.ts +1 -0
- package/src/components/section-header/section-header.stories.tsx +20 -0
- package/src/components/section-header/section-header.tsx +36 -0
- package/src/components/segmented-field/index.ts +5 -0
- package/src/components/segmented-field/segmented-field.stories.tsx +121 -0
- package/src/components/segmented-field/segmented-field.test.tsx +163 -0
- package/src/components/segmented-field/segmented-field.tsx +235 -0
- package/src/components/select/index.ts +10 -0
- package/src/components/select/select.stories.tsx +106 -0
- package/src/components/select/select.test.tsx +98 -0
- package/src/components/select/select.tsx +230 -0
- package/src/components/separator/index.ts +2 -0
- package/src/components/separator/separator.stories.tsx +87 -0
- package/src/components/separator/separator.tsx +51 -0
- package/src/components/sheet/index.ts +12 -0
- package/src/components/sheet/sheet.stories.tsx +65 -0
- package/src/components/sheet/sheet.tsx +109 -0
- package/src/components/sidebar/index.ts +27 -0
- package/src/components/sidebar/sidebar.stories.tsx +69 -0
- package/src/components/sidebar/sidebar.test.tsx +43 -0
- package/src/components/sidebar/sidebar.tsx +688 -0
- package/src/components/skeleton/index.ts +1 -0
- package/src/components/skeleton/skeleton.stories.tsx +28 -0
- package/src/components/skeleton/skeleton.tsx +15 -0
- package/src/components/slider/index.ts +1 -0
- package/src/components/slider/slider.stories.tsx +211 -0
- package/src/components/slider/slider.test.tsx +168 -0
- package/src/components/slider/slider.tsx +136 -0
- package/src/components/slider-number/index.ts +1 -0
- package/src/components/slider-number/slider-number.stories.tsx +123 -0
- package/src/components/slider-number/slider-number.test.tsx +165 -0
- package/src/components/slider-number/slider-number.tsx +191 -0
- package/src/components/sonner/index.ts +1 -0
- package/src/components/sonner/sonner.stories.tsx +124 -0
- package/src/components/sonner/sonner.test.tsx +76 -0
- package/src/components/sonner/sonner.tsx +101 -0
- package/src/components/spinner/index.ts +1 -0
- package/src/components/spinner/spinner.stories.tsx +27 -0
- package/src/components/spinner/spinner.tsx +19 -0
- package/src/components/split-panel/index.ts +1 -0
- package/src/components/split-panel/split-panel.stories.tsx +60 -0
- package/src/components/split-panel/split-panel.test.tsx +38 -0
- package/src/components/split-panel/split-panel.tsx +95 -0
- package/src/components/state-panel/index.ts +1 -0
- package/src/components/state-panel/state-panel.stories.tsx +80 -0
- package/src/components/state-panel/state-panel.test.tsx +64 -0
- package/src/components/state-panel/state-panel.tsx +167 -0
- package/src/components/status-badge/index.ts +18 -0
- package/src/components/status-badge/status-badge.stories.tsx +132 -0
- package/src/components/status-badge/status-badge.test.tsx +189 -0
- package/src/components/status-badge/status-badge.tsx +330 -0
- package/src/components/switch/index.ts +1 -0
- package/src/components/switch/switch.stories.tsx +58 -0
- package/src/components/switch/switch.test.tsx +12 -0
- package/src/components/switch/switch.tsx +29 -0
- package/src/components/table/index.ts +10 -0
- package/src/components/table/table.stories.tsx +53 -0
- package/src/components/table/table.test.tsx +17 -0
- package/src/components/table/table.tsx +106 -0
- package/src/components/tabs/index.ts +1 -0
- package/src/components/tabs/tabs.stories.tsx +267 -0
- package/src/components/tabs/tabs.test.tsx +94 -0
- package/src/components/tabs/tabs.tsx +170 -0
- package/src/components/tag-input/index.ts +1 -0
- package/src/components/tag-input/tag-input.stories.tsx +120 -0
- package/src/components/tag-input/tag-input.test.tsx +97 -0
- package/src/components/tag-input/tag-input.tsx +253 -0
- package/src/components/team-switcher/index.ts +1 -0
- package/src/components/team-switcher/team-switcher.test.tsx +39 -0
- package/src/components/team-switcher/team-switcher.tsx +100 -0
- package/src/components/textarea/index.ts +1 -0
- package/src/components/textarea/textarea.stories.tsx +42 -0
- package/src/components/textarea/textarea.tsx +24 -0
- package/src/components/theme-switcher/index.ts +2 -0
- package/src/components/theme-switcher/theme-switcher.stories.tsx +146 -0
- package/src/components/theme-switcher/theme-switcher.test.tsx +253 -0
- package/src/components/theme-switcher/theme-switcher.tsx +280 -0
- package/src/components/theme-switcher/use-theme-transition.ts +62 -0
- package/src/components/timeline/index.ts +13 -0
- package/src/components/timeline/timeline.stories.tsx +99 -0
- package/src/components/timeline/timeline.test.tsx +196 -0
- package/src/components/timeline/timeline.tsx +188 -0
- package/src/components/toggle/index.ts +1 -0
- package/src/components/toggle/toggle.stories.tsx +116 -0
- package/src/components/toggle/toggle.test.tsx +119 -0
- package/src/components/toggle/toggle.tsx +64 -0
- package/src/components/toggle-group/index.ts +1 -0
- package/src/components/toggle-group/toggle-group.stories.tsx +110 -0
- package/src/components/toggle-group/toggle-group.tsx +59 -0
- package/src/components/toolbar/index.ts +14 -0
- package/src/components/toolbar/toolbar.stories.tsx +126 -0
- package/src/components/toolbar/toolbar.test.tsx +73 -0
- package/src/components/toolbar/toolbar.tsx +177 -0
- package/src/components/tooltip/index.ts +1 -0
- package/src/components/tooltip/tooltip.stories.tsx +67 -0
- package/src/components/tooltip/tooltip.tsx +27 -0
- package/src/components/top-nav/index.ts +1 -0
- package/src/components/top-nav/top-nav.stories.tsx +18 -0
- package/src/components/top-nav/top-nav.tsx +34 -0
- package/src/components/transfer/index.ts +2 -0
- package/src/components/transfer/transfer.stories.tsx +162 -0
- package/src/components/transfer/transfer.test.tsx +177 -0
- package/src/components/transfer/transfer.tsx +481 -0
- package/src/components/tree/index.ts +6 -0
- package/src/components/tree/tree.stories.tsx +618 -0
- package/src/components/tree/tree.test.tsx +699 -0
- package/src/components/tree/tree.tsx +1149 -0
- package/src/components/tree/use-tree-keyboard.ts +314 -0
- package/src/components/tree-select/index.ts +1 -0
- package/src/components/tree-select/tree-select.stories.tsx +127 -0
- package/src/components/tree-select/tree-select.test.tsx +40 -0
- package/src/components/tree-select/tree-select.tsx +137 -0
- package/src/components/typography/index.ts +31 -0
- package/src/components/typography/prose.test.tsx +48 -0
- package/src/components/typography/prose.tsx +194 -0
- package/src/components/typography/typography.stories.tsx +244 -0
- package/src/components/typography/typography.test.tsx +140 -0
- package/src/components/typography/typography.tsx +126 -0
- package/src/components/view-toolbar/index.ts +10 -0
- package/src/components/view-toolbar/view-toolbar.stories.tsx +342 -0
- package/src/components/view-toolbar/view-toolbar.test.tsx +262 -0
- package/src/components/view-toolbar/view-toolbar.tsx +320 -0
- package/src/components/virtual-select/index.ts +2 -0
- package/src/components/virtual-select/use-virtual-listbox.ts +132 -0
- package/src/components/virtual-select/virtual-select.stories.tsx +203 -0
- package/src/components/virtual-select/virtual-select.test.tsx +106 -0
- package/src/components/virtual-select/virtual-select.tsx +361 -0
- package/src/components/wizard/index.ts +12 -0
- package/src/components/wizard/wizard.stories.tsx +148 -0
- package/src/components/wizard/wizard.test.tsx +67 -0
- package/src/components/wizard/wizard.tsx +354 -0
- package/src/decoration.stories.tsx +236 -0
- package/src/index.ts +174 -0
- package/src/lib/cn.test.ts +39 -0
- package/src/lib/cn.ts +31 -0
- package/src/lib/compact-number.ts +61 -0
- package/src/lib/document-address.test.ts +75 -0
- package/src/lib/document-address.ts +265 -0
- package/src/lib/download.ts +93 -0
- package/src/lib/file-icon.test.ts +45 -0
- package/src/lib/file-icon.ts +58 -0
- package/src/lib/file-kind.test.ts +98 -0
- package/src/lib/file-kind.ts +303 -0
- package/src/lib/file-source.test.ts +217 -0
- package/src/lib/file-source.ts +269 -0
- package/src/lib/merge-refs.test.ts +37 -0
- package/src/lib/merge-refs.ts +17 -0
- package/src/lib/streamdown-translations.ts +99 -0
- package/src/lib/use-copy-to-clipboard.ts +75 -0
- package/src/lib/use-mobile.ts +16 -0
- package/src/motion.stories.tsx +266 -0
- package/src/taste-profile.test.tsx +252 -0
- package/src/templates-object-detail-hub.stories.tsx +466 -0
- package/src/templates-screen-states.stories.tsx +306 -0
- 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
|
+
};
|