@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,306 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Screen States — the full state grid the templates omit.
|
|
3
|
+
*
|
|
4
|
+
* `design-first` requires that empty, loading, error and first-run states are
|
|
5
|
+
* designed WITH the happy path, never retrofitted — yet every existing
|
|
6
|
+
* `Patterns/Templates/*` ships only a populated "Ready" variant. This scenario
|
|
7
|
+
* fixes that: ONE representative data screen (Customers), rendered across the
|
|
8
|
+
* complete state grid as separate stories, with the SAME chrome and the real
|
|
9
|
+
* state primitives wired in:
|
|
10
|
+
*
|
|
11
|
+
* • Ready — populated Table (the happy path).
|
|
12
|
+
* • Loading — Skeleton rows (never a bare spinner on a data screen).
|
|
13
|
+
* • Empty — StatePanel kind="empty" after a filter returns nothing.
|
|
14
|
+
* • Error — StatePanel kind="error" with a retry action (role="alert").
|
|
15
|
+
* • FirstRun — onboarding empty: illustration slot + one sentence + one CTA.
|
|
16
|
+
*
|
|
17
|
+
* Compose-only from @elabs-ai/components-* primitives; semantic tokens only; reads in all
|
|
18
|
+
* three themes. Verify with globals=theme:<slug>.
|
|
19
|
+
*/
|
|
20
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
21
|
+
import { Inbox, Plus, RotateCcw, Search, UploadCloud, UserPlus } from "lucide-react";
|
|
22
|
+
import {
|
|
23
|
+
Badge,
|
|
24
|
+
Button,
|
|
25
|
+
Input,
|
|
26
|
+
PageShell,
|
|
27
|
+
SectionHeader,
|
|
28
|
+
Skeleton,
|
|
29
|
+
StatePanel,
|
|
30
|
+
Table,
|
|
31
|
+
TableBody,
|
|
32
|
+
TableCell,
|
|
33
|
+
TableHead,
|
|
34
|
+
TableHeader,
|
|
35
|
+
TableRow,
|
|
36
|
+
Toaster,
|
|
37
|
+
toast,
|
|
38
|
+
} from "./index";
|
|
39
|
+
|
|
40
|
+
type ScreenState = "ready" | "loading" | "empty" | "error" | "first-run";
|
|
41
|
+
|
|
42
|
+
type CustomerStatus = "active" | "trialing" | "past_due";
|
|
43
|
+
|
|
44
|
+
interface Customer {
|
|
45
|
+
id: string;
|
|
46
|
+
name: string;
|
|
47
|
+
plan: string;
|
|
48
|
+
status: CustomerStatus;
|
|
49
|
+
mrr: string;
|
|
50
|
+
joined: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const STATUS_VARIANT: Record<CustomerStatus, "success" | "secondary" | "destructive"> = {
|
|
54
|
+
active: "success",
|
|
55
|
+
trialing: "secondary",
|
|
56
|
+
past_due: "destructive",
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const STATUS_LABEL: Record<CustomerStatus, string> = {
|
|
60
|
+
active: "Active",
|
|
61
|
+
trialing: "Trialing",
|
|
62
|
+
past_due: "Past due",
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const CUSTOMERS: Customer[] = [
|
|
66
|
+
{
|
|
67
|
+
id: "1",
|
|
68
|
+
name: "Northwind Traders",
|
|
69
|
+
plan: "Enterprise",
|
|
70
|
+
status: "active",
|
|
71
|
+
mrr: "$4,200",
|
|
72
|
+
joined: "Jan 2024",
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: "2",
|
|
76
|
+
name: "Globex Corp",
|
|
77
|
+
plan: "Business",
|
|
78
|
+
status: "active",
|
|
79
|
+
mrr: "$1,800",
|
|
80
|
+
joined: "Mar 2024",
|
|
81
|
+
},
|
|
82
|
+
{ id: "3", name: "Initech", plan: "Business", status: "trialing", mrr: "$0", joined: "Jun 2026" },
|
|
83
|
+
{
|
|
84
|
+
id: "4",
|
|
85
|
+
name: "Hooli",
|
|
86
|
+
plan: "Enterprise",
|
|
87
|
+
status: "past_due",
|
|
88
|
+
mrr: "$6,500",
|
|
89
|
+
joined: "Sep 2023",
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: "5",
|
|
93
|
+
name: "Soylent Inc",
|
|
94
|
+
plan: "Starter",
|
|
95
|
+
status: "active",
|
|
96
|
+
mrr: "$240",
|
|
97
|
+
joined: "Feb 2025",
|
|
98
|
+
},
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
/* -------------------------------------------------------------------------- */
|
|
102
|
+
/* Chrome shared by every state */
|
|
103
|
+
/* -------------------------------------------------------------------------- */
|
|
104
|
+
|
|
105
|
+
function Toolbar({ count }: { count: number }) {
|
|
106
|
+
return (
|
|
107
|
+
<div className="flex items-center justify-between gap-3">
|
|
108
|
+
<div className="relative w-full max-w-xs">
|
|
109
|
+
<Search
|
|
110
|
+
className="pointer-events-none absolute start-2.5 top-1/2 size-4 -translate-y-1/2 text-muted-foreground"
|
|
111
|
+
aria-hidden="true"
|
|
112
|
+
/>
|
|
113
|
+
<Input
|
|
114
|
+
type="search"
|
|
115
|
+
placeholder="Search customers…"
|
|
116
|
+
aria-label="Search customers"
|
|
117
|
+
className="ps-8"
|
|
118
|
+
/>
|
|
119
|
+
</div>
|
|
120
|
+
<span className="shrink-0 text-meta tabular-nums text-muted-foreground">
|
|
121
|
+
{count} customers
|
|
122
|
+
</span>
|
|
123
|
+
</div>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function CustomersTable() {
|
|
128
|
+
return (
|
|
129
|
+
<div className="overflow-hidden rounded-xl border bg-card">
|
|
130
|
+
<Table>
|
|
131
|
+
<TableHeader>
|
|
132
|
+
<TableRow>
|
|
133
|
+
<TableHead>Customer</TableHead>
|
|
134
|
+
<TableHead>Plan</TableHead>
|
|
135
|
+
<TableHead>Status</TableHead>
|
|
136
|
+
<TableHead className="text-end">MRR</TableHead>
|
|
137
|
+
<TableHead>Joined</TableHead>
|
|
138
|
+
</TableRow>
|
|
139
|
+
</TableHeader>
|
|
140
|
+
<TableBody>
|
|
141
|
+
{CUSTOMERS.map((c) => (
|
|
142
|
+
<TableRow key={c.id}>
|
|
143
|
+
<TableCell className="font-medium">{c.name}</TableCell>
|
|
144
|
+
<TableCell className="text-muted-foreground">{c.plan}</TableCell>
|
|
145
|
+
<TableCell>
|
|
146
|
+
<Badge variant={STATUS_VARIANT[c.status]}>{STATUS_LABEL[c.status]}</Badge>
|
|
147
|
+
</TableCell>
|
|
148
|
+
<TableCell className="text-end tabular-nums">{c.mrr}</TableCell>
|
|
149
|
+
<TableCell className="text-muted-foreground">{c.joined}</TableCell>
|
|
150
|
+
</TableRow>
|
|
151
|
+
))}
|
|
152
|
+
</TableBody>
|
|
153
|
+
</Table>
|
|
154
|
+
</div>
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function LoadingTable() {
|
|
159
|
+
return (
|
|
160
|
+
<div className="overflow-hidden rounded-xl border bg-card" aria-busy="true">
|
|
161
|
+
<div className="border-b px-4 py-3">
|
|
162
|
+
<Skeleton className="h-4 w-40" />
|
|
163
|
+
</div>
|
|
164
|
+
<ul>
|
|
165
|
+
{Array.from({ length: 5 }).map((_, i) => (
|
|
166
|
+
<li key={i} className="flex items-center gap-4 px-4 py-3 [&:not(:first-child)]:border-t">
|
|
167
|
+
<Skeleton className="h-4 flex-1" />
|
|
168
|
+
<Skeleton className="h-4 w-20" />
|
|
169
|
+
<Skeleton className="h-5 w-16 rounded-full" />
|
|
170
|
+
<Skeleton className="h-4 w-14" />
|
|
171
|
+
</li>
|
|
172
|
+
))}
|
|
173
|
+
</ul>
|
|
174
|
+
</div>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* -------------------------------------------------------------------------- */
|
|
179
|
+
/* The screen — one body per state */
|
|
180
|
+
/* -------------------------------------------------------------------------- */
|
|
181
|
+
|
|
182
|
+
function CustomersScreen({ state = "ready" }: { state?: ScreenState }) {
|
|
183
|
+
return (
|
|
184
|
+
<div className="min-h-dvh bg-background text-foreground">
|
|
185
|
+
<PageShell
|
|
186
|
+
header={
|
|
187
|
+
<SectionHeader
|
|
188
|
+
eyebrow="Billing"
|
|
189
|
+
title="Customers"
|
|
190
|
+
description="Everyone with an active or trialing subscription."
|
|
191
|
+
actions={
|
|
192
|
+
state === "first-run" ? undefined : (
|
|
193
|
+
<Button size="sm" onClick={() => toast.success("New-customer form opened")}>
|
|
194
|
+
<Plus className="size-4" aria-hidden="true" />
|
|
195
|
+
New customer
|
|
196
|
+
</Button>
|
|
197
|
+
)
|
|
198
|
+
}
|
|
199
|
+
/>
|
|
200
|
+
}
|
|
201
|
+
>
|
|
202
|
+
{(state === "ready" || state === "empty") && (
|
|
203
|
+
<Toolbar count={state === "ready" ? CUSTOMERS.length : 0} />
|
|
204
|
+
)}
|
|
205
|
+
|
|
206
|
+
{state === "ready" && <CustomersTable />}
|
|
207
|
+
|
|
208
|
+
{state === "loading" && <LoadingTable />}
|
|
209
|
+
|
|
210
|
+
{state === "error" && (
|
|
211
|
+
<StatePanel
|
|
212
|
+
kind="error"
|
|
213
|
+
title="Couldn't load customers"
|
|
214
|
+
description="The billing service didn't respond. Your data is safe — this is a display error."
|
|
215
|
+
actions={
|
|
216
|
+
<Button variant="outline" size="sm" onClick={() => toast.success("Retrying…")}>
|
|
217
|
+
<RotateCcw className="size-4" aria-hidden="true" />
|
|
218
|
+
Try again
|
|
219
|
+
</Button>
|
|
220
|
+
}
|
|
221
|
+
/>
|
|
222
|
+
)}
|
|
223
|
+
|
|
224
|
+
{state === "empty" && (
|
|
225
|
+
<StatePanel
|
|
226
|
+
kind="empty"
|
|
227
|
+
icon={<Inbox aria-hidden="true" />}
|
|
228
|
+
title="No customers match"
|
|
229
|
+
description="No customers match your current search or filters."
|
|
230
|
+
actions={
|
|
231
|
+
<Button variant="outline" size="sm">
|
|
232
|
+
Clear filters
|
|
233
|
+
</Button>
|
|
234
|
+
}
|
|
235
|
+
/>
|
|
236
|
+
)}
|
|
237
|
+
|
|
238
|
+
{state === "first-run" && (
|
|
239
|
+
<StatePanel
|
|
240
|
+
kind="empty"
|
|
241
|
+
icon={<UserPlus aria-hidden="true" />}
|
|
242
|
+
title="Add your first customer"
|
|
243
|
+
description="Customers appear here once they start a subscription. Add one manually or import an existing book of business."
|
|
244
|
+
actions={
|
|
245
|
+
<>
|
|
246
|
+
<Button size="sm" onClick={() => toast.success("New-customer form opened")}>
|
|
247
|
+
<Plus className="size-4" aria-hidden="true" />
|
|
248
|
+
New customer
|
|
249
|
+
</Button>
|
|
250
|
+
<Button variant="outline" size="sm">
|
|
251
|
+
<UploadCloud className="size-4" aria-hidden="true" />
|
|
252
|
+
Import CSV
|
|
253
|
+
</Button>
|
|
254
|
+
</>
|
|
255
|
+
}
|
|
256
|
+
/>
|
|
257
|
+
)}
|
|
258
|
+
</PageShell>
|
|
259
|
+
<Toaster />
|
|
260
|
+
</div>
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/* -------------------------------------------------------------------------- */
|
|
265
|
+
/* Stories — one per state */
|
|
266
|
+
/* -------------------------------------------------------------------------- */
|
|
267
|
+
|
|
268
|
+
const meta = {
|
|
269
|
+
title: "Patterns/Templates/Screen States",
|
|
270
|
+
component: CustomersScreen,
|
|
271
|
+
parameters: {
|
|
272
|
+
layout: "fullscreen",
|
|
273
|
+
docs: {
|
|
274
|
+
description: {
|
|
275
|
+
component:
|
|
276
|
+
'One representative data screen (Customers) rendered across the FULL state grid — Ready, Loading, Empty, Error, FirstRun — with the same chrome and the real state primitives (Table, Skeleton, StatePanel kind="empty"|"error"). This is the design-first state grid that the Patterns/Templates examples omit. Semantic tokens only; reads in all three themes.',
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
tags: ["autodocs"],
|
|
281
|
+
argTypes: {
|
|
282
|
+
state: {
|
|
283
|
+
control: "inline-radio",
|
|
284
|
+
options: ["ready", "loading", "empty", "error", "first-run"],
|
|
285
|
+
description: "Which screen state to render.",
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
} satisfies Meta<typeof CustomersScreen>;
|
|
289
|
+
|
|
290
|
+
export default meta;
|
|
291
|
+
type Story = StoryObj<typeof meta>;
|
|
292
|
+
|
|
293
|
+
/** Populated happy path. */
|
|
294
|
+
export const Ready: Story = { args: { state: "ready" } };
|
|
295
|
+
|
|
296
|
+
/** In-flight load — Skeleton rows, not a bare spinner. */
|
|
297
|
+
export const Loading: Story = { args: { state: "loading" } };
|
|
298
|
+
|
|
299
|
+
/** A search/filter returned nothing — a real empty state with a way out. */
|
|
300
|
+
export const Empty: Story = { args: { state: "empty" } };
|
|
301
|
+
|
|
302
|
+
/** Recoverable failure — StatePanel kind="error" (role="alert") + retry. */
|
|
303
|
+
export const Errored: Story = { name: "Error", args: { state: "error" } };
|
|
304
|
+
|
|
305
|
+
/** Never-used screen — onboarding empty with one primary action. */
|
|
306
|
+
export const FirstRun: Story = { args: { state: "first-run" } };
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings template — the canonical full-screen settings composition
|
|
3
|
+
* (app-shell + Descriptions + Wizard). This story is the single source of
|
|
4
|
+
* truth: `pnpm gen:templates` derives the consumer template source
|
|
5
|
+
* (`docs/playbooks/templates/settings.tsx`) from it.
|
|
6
|
+
* Verify across all three themes with globals=theme:<slug>.
|
|
7
|
+
*/
|
|
8
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
9
|
+
import { useState } from "react";
|
|
10
|
+
import {
|
|
11
|
+
Button,
|
|
12
|
+
Card,
|
|
13
|
+
CardContent,
|
|
14
|
+
CardDescription,
|
|
15
|
+
CardHeader,
|
|
16
|
+
CardTitle,
|
|
17
|
+
Descriptions,
|
|
18
|
+
DescriptionsItem,
|
|
19
|
+
Input,
|
|
20
|
+
Label,
|
|
21
|
+
Separator,
|
|
22
|
+
Sidebar,
|
|
23
|
+
SidebarContent,
|
|
24
|
+
SidebarGroup,
|
|
25
|
+
SidebarGroupContent,
|
|
26
|
+
SidebarHeader,
|
|
27
|
+
SidebarInset,
|
|
28
|
+
SidebarMenu,
|
|
29
|
+
SidebarMenuButton,
|
|
30
|
+
SidebarMenuItem,
|
|
31
|
+
SidebarProvider,
|
|
32
|
+
SidebarTrigger,
|
|
33
|
+
Wizard,
|
|
34
|
+
WizardNav,
|
|
35
|
+
WizardStep,
|
|
36
|
+
WizardSteps,
|
|
37
|
+
type WizardStepMeta,
|
|
38
|
+
} from "./index";
|
|
39
|
+
import { Bell, Home, Shield, User } from "lucide-react";
|
|
40
|
+
|
|
41
|
+
const WIZARD_STEPS: WizardStepMeta[] = [
|
|
42
|
+
{ id: "profile", title: "Profile", description: "Your details" },
|
|
43
|
+
{ id: "notifications", title: "Notifications", description: "Alert prefs" },
|
|
44
|
+
{ id: "confirm", title: "Confirm", description: "Review & finish" },
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
const nav = [
|
|
48
|
+
{ id: "profile", label: "Profile", icon: User },
|
|
49
|
+
{ id: "notifications", label: "Notifications", icon: Bell },
|
|
50
|
+
{ id: "security", label: "Security", icon: Shield },
|
|
51
|
+
{ id: "home", label: "Home", icon: Home },
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
function ProfileView() {
|
|
55
|
+
return (
|
|
56
|
+
<div className="flex flex-col gap-6">
|
|
57
|
+
<Card>
|
|
58
|
+
<CardHeader>
|
|
59
|
+
<CardTitle>Your profile</CardTitle>
|
|
60
|
+
<CardDescription>Visible to other members of your workspace.</CardDescription>
|
|
61
|
+
</CardHeader>
|
|
62
|
+
<CardContent>
|
|
63
|
+
<Descriptions columns={2} layout="vertical">
|
|
64
|
+
<DescriptionsItem label="Full name">Alex Johnson</DescriptionsItem>
|
|
65
|
+
<DescriptionsItem label="Email">alex@example.com</DescriptionsItem>
|
|
66
|
+
<DescriptionsItem label="Role">Admin</DescriptionsItem>
|
|
67
|
+
<DescriptionsItem label="Member since">Jan 2024</DescriptionsItem>
|
|
68
|
+
</Descriptions>
|
|
69
|
+
</CardContent>
|
|
70
|
+
</Card>
|
|
71
|
+
<Separator />
|
|
72
|
+
<Card>
|
|
73
|
+
<CardHeader>
|
|
74
|
+
<CardTitle>Setup wizard</CardTitle>
|
|
75
|
+
<CardDescription>
|
|
76
|
+
Complete the steps below to finish configuring your account.
|
|
77
|
+
</CardDescription>
|
|
78
|
+
</CardHeader>
|
|
79
|
+
<CardContent>
|
|
80
|
+
<Wizard steps={WIZARD_STEPS} defaultStep={0} className="flex flex-col gap-6">
|
|
81
|
+
<WizardSteps />
|
|
82
|
+
<WizardStep step={0}>
|
|
83
|
+
<div className="flex flex-col gap-4">
|
|
84
|
+
<div className="flex flex-col gap-1.5">
|
|
85
|
+
<Label htmlFor="display-name">Display name</Label>
|
|
86
|
+
<Input id="display-name" defaultValue="Alex Johnson" autoComplete="name" />
|
|
87
|
+
</div>
|
|
88
|
+
<div className="flex flex-col gap-1.5">
|
|
89
|
+
<Label htmlFor="job-title">Job title</Label>
|
|
90
|
+
<Input
|
|
91
|
+
id="job-title"
|
|
92
|
+
placeholder="e.g. Data Engineer"
|
|
93
|
+
autoComplete="organization-title"
|
|
94
|
+
/>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
</WizardStep>
|
|
98
|
+
<WizardStep step={1}>
|
|
99
|
+
<div className="flex flex-col gap-4">
|
|
100
|
+
<p className="text-body text-muted-foreground">
|
|
101
|
+
Choose which notifications you'd like to receive.
|
|
102
|
+
</p>
|
|
103
|
+
<div className="flex flex-col gap-2 text-body">
|
|
104
|
+
{["Alerts & incidents", "Weekly digests", "Product updates"].map((item) => (
|
|
105
|
+
<label key={item} className="flex cursor-pointer items-center gap-2">
|
|
106
|
+
<input type="checkbox" defaultChecked className="accent-primary" />
|
|
107
|
+
<span>{item}</span>
|
|
108
|
+
</label>
|
|
109
|
+
))}
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
</WizardStep>
|
|
113
|
+
<WizardStep step={2}>
|
|
114
|
+
<div className="flex flex-col gap-3">
|
|
115
|
+
<p className="text-body text-muted-foreground">
|
|
116
|
+
Your account is ready. Review your settings and click Finish.
|
|
117
|
+
</p>
|
|
118
|
+
<Descriptions layout="vertical">
|
|
119
|
+
<DescriptionsItem label="Display name">Alex Johnson</DescriptionsItem>
|
|
120
|
+
<DescriptionsItem label="Notifications">Alerts, Digests</DescriptionsItem>
|
|
121
|
+
</Descriptions>
|
|
122
|
+
</div>
|
|
123
|
+
</WizardStep>
|
|
124
|
+
<WizardNav finishLabel="Finish setup" />
|
|
125
|
+
</Wizard>
|
|
126
|
+
</CardContent>
|
|
127
|
+
</Card>
|
|
128
|
+
</div>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function NotificationsView() {
|
|
133
|
+
return (
|
|
134
|
+
<Card>
|
|
135
|
+
<CardHeader>
|
|
136
|
+
<CardTitle>Notification preferences</CardTitle>
|
|
137
|
+
<CardDescription>Control how and when you receive alerts.</CardDescription>
|
|
138
|
+
</CardHeader>
|
|
139
|
+
<CardContent className="flex flex-col gap-4 text-body">
|
|
140
|
+
{[
|
|
141
|
+
{ label: "Alerts & incidents", checked: true },
|
|
142
|
+
{ label: "Weekly digest", checked: true },
|
|
143
|
+
{ label: "Product updates", checked: false },
|
|
144
|
+
{ label: "Marketing emails", checked: false },
|
|
145
|
+
].map((item) => (
|
|
146
|
+
<label key={item.label} className="flex cursor-pointer items-center gap-2">
|
|
147
|
+
<input type="checkbox" defaultChecked={item.checked} className="accent-primary" />
|
|
148
|
+
<span>{item.label}</span>
|
|
149
|
+
</label>
|
|
150
|
+
))}
|
|
151
|
+
<div className="mt-2">
|
|
152
|
+
<Button size="sm">Save preferences</Button>
|
|
153
|
+
</div>
|
|
154
|
+
</CardContent>
|
|
155
|
+
</Card>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function SecurityView() {
|
|
160
|
+
return (
|
|
161
|
+
<Card>
|
|
162
|
+
<CardHeader>
|
|
163
|
+
<CardTitle>Security</CardTitle>
|
|
164
|
+
<CardDescription>Manage your password and two-factor authentication.</CardDescription>
|
|
165
|
+
</CardHeader>
|
|
166
|
+
<CardContent>
|
|
167
|
+
<Descriptions layout="vertical">
|
|
168
|
+
<DescriptionsItem label="Password">Last changed 3 months ago</DescriptionsItem>
|
|
169
|
+
<DescriptionsItem label="Two-factor auth">Enabled (Authenticator app)</DescriptionsItem>
|
|
170
|
+
<DescriptionsItem label="Active sessions" numeric>
|
|
171
|
+
2
|
|
172
|
+
</DescriptionsItem>
|
|
173
|
+
</Descriptions>
|
|
174
|
+
<div className="mt-4 flex gap-2">
|
|
175
|
+
<Button variant="outline" size="sm">
|
|
176
|
+
Change password
|
|
177
|
+
</Button>
|
|
178
|
+
<Button variant="outline" size="sm">
|
|
179
|
+
Manage 2FA
|
|
180
|
+
</Button>
|
|
181
|
+
</div>
|
|
182
|
+
</CardContent>
|
|
183
|
+
</Card>
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function SettingsTemplate() {
|
|
188
|
+
const [active, setActive] = useState("profile");
|
|
189
|
+
|
|
190
|
+
const views: Record<string, React.ReactNode> = {
|
|
191
|
+
profile: <ProfileView />,
|
|
192
|
+
notifications: <NotificationsView />,
|
|
193
|
+
security: <SecurityView />,
|
|
194
|
+
home: (
|
|
195
|
+
<p className="text-body text-muted-foreground">
|
|
196
|
+
Navigate to a settings section using the sidebar.
|
|
197
|
+
</p>
|
|
198
|
+
),
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
return (
|
|
202
|
+
<SidebarProvider>
|
|
203
|
+
<Sidebar collapsible="icon">
|
|
204
|
+
<SidebarHeader className="px-3 py-2 font-semibold">Settings</SidebarHeader>
|
|
205
|
+
<SidebarContent>
|
|
206
|
+
<SidebarGroup>
|
|
207
|
+
<SidebarGroupContent>
|
|
208
|
+
<SidebarMenu>
|
|
209
|
+
{nav.map((n) => (
|
|
210
|
+
<SidebarMenuItem key={n.id}>
|
|
211
|
+
<SidebarMenuButton
|
|
212
|
+
isActive={active === n.id}
|
|
213
|
+
tooltip={n.label}
|
|
214
|
+
onClick={() => setActive(n.id)}
|
|
215
|
+
>
|
|
216
|
+
<n.icon aria-hidden="true" />
|
|
217
|
+
<span>{n.label}</span>
|
|
218
|
+
</SidebarMenuButton>
|
|
219
|
+
</SidebarMenuItem>
|
|
220
|
+
))}
|
|
221
|
+
</SidebarMenu>
|
|
222
|
+
</SidebarGroupContent>
|
|
223
|
+
</SidebarGroup>
|
|
224
|
+
</SidebarContent>
|
|
225
|
+
</Sidebar>
|
|
226
|
+
<SidebarInset>
|
|
227
|
+
<header className="flex h-14 items-center gap-2 border-b px-4">
|
|
228
|
+
<SidebarTrigger />
|
|
229
|
+
<h1 className="text-body font-medium capitalize">{active}</h1>
|
|
230
|
+
</header>
|
|
231
|
+
<main className="max-w-2xl p-6">{views[active]}</main>
|
|
232
|
+
</SidebarInset>
|
|
233
|
+
</SidebarProvider>
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const meta = {
|
|
238
|
+
title: "Patterns/Templates/Settings",
|
|
239
|
+
parameters: { layout: "fullscreen" },
|
|
240
|
+
tags: ["autodocs"],
|
|
241
|
+
} satisfies Meta;
|
|
242
|
+
export default meta;
|
|
243
|
+
type Story = StoryObj<typeof meta>;
|
|
244
|
+
|
|
245
|
+
export const Default: Story = { render: () => <SettingsTemplate /> };
|