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