@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,45 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { Avatar, AvatarFallback, AvatarImage } from "./avatar";
|
|
3
|
+
const meta = {
|
|
4
|
+
title: "Display/Avatar",
|
|
5
|
+
component: Avatar,
|
|
6
|
+
tags: ["autodocs"],
|
|
7
|
+
argTypes: {
|
|
8
|
+
className: {
|
|
9
|
+
description:
|
|
10
|
+
"Extra Tailwind classes merged via cn() on the avatar root (controls size/shape).",
|
|
11
|
+
control: "text",
|
|
12
|
+
table: { category: "Appearance" },
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
} satisfies Meta<typeof Avatar>;
|
|
16
|
+
export default meta;
|
|
17
|
+
type Story = StoryObj<typeof meta>;
|
|
18
|
+
export const Default: Story = {
|
|
19
|
+
render: () => (
|
|
20
|
+
<Avatar>
|
|
21
|
+
<AvatarImage src="" alt="" />
|
|
22
|
+
<AvatarFallback>MR</AvatarFallback>
|
|
23
|
+
</Avatar>
|
|
24
|
+
),
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/** Larger avatar via className. */
|
|
28
|
+
export const Large: Story = {
|
|
29
|
+
render: () => (
|
|
30
|
+
<Avatar className="size-16">
|
|
31
|
+
<AvatarImage src="" alt="" />
|
|
32
|
+
<AvatarFallback>AB</AvatarFallback>
|
|
33
|
+
</Avatar>
|
|
34
|
+
),
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/** Image avatar — fallback only shows while image loads or if it fails. */
|
|
38
|
+
export const WithImage: Story = {
|
|
39
|
+
render: () => (
|
|
40
|
+
<Avatar>
|
|
41
|
+
<AvatarImage src="https://github.com/shadcn.png" alt="shadcn" />
|
|
42
|
+
<AvatarFallback>SC</AvatarFallback>
|
|
43
|
+
</Avatar>
|
|
44
|
+
),
|
|
45
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { render, screen } from "@testing-library/react";
|
|
3
|
+
import { Avatar, AvatarFallback } from "./avatar";
|
|
4
|
+
describe("Avatar", () => {
|
|
5
|
+
it("renders the fallback", () => {
|
|
6
|
+
render(
|
|
7
|
+
<Avatar>
|
|
8
|
+
<AvatarFallback>MR</AvatarFallback>
|
|
9
|
+
</Avatar>,
|
|
10
|
+
);
|
|
11
|
+
expect(screen.getByText("MR")).toBeInTheDocument();
|
|
12
|
+
});
|
|
13
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { forwardRef, type ComponentPropsWithoutRef, type ElementRef } from "react";
|
|
2
|
+
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
|
3
|
+
import { cn } from "../../lib/cn";
|
|
4
|
+
|
|
5
|
+
export const Avatar = forwardRef<
|
|
6
|
+
ElementRef<typeof AvatarPrimitive.Root>,
|
|
7
|
+
ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
|
|
8
|
+
>(function Avatar({ className, ...props }, ref) {
|
|
9
|
+
return (
|
|
10
|
+
<AvatarPrimitive.Root
|
|
11
|
+
ref={ref}
|
|
12
|
+
className={cn("relative flex size-9 shrink-0 overflow-hidden rounded-full", className)}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
);
|
|
16
|
+
});
|
|
17
|
+
export const AvatarImage = forwardRef<
|
|
18
|
+
ElementRef<typeof AvatarPrimitive.Image>,
|
|
19
|
+
ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
|
|
20
|
+
>(function AvatarImage({ className, ...props }, ref) {
|
|
21
|
+
return (
|
|
22
|
+
<AvatarPrimitive.Image
|
|
23
|
+
ref={ref}
|
|
24
|
+
className={cn(
|
|
25
|
+
"aspect-square size-full animate-in fade-in duration-base ease-entrance",
|
|
26
|
+
className,
|
|
27
|
+
)}
|
|
28
|
+
{...props}
|
|
29
|
+
/>
|
|
30
|
+
);
|
|
31
|
+
});
|
|
32
|
+
export const AvatarFallback = forwardRef<
|
|
33
|
+
ElementRef<typeof AvatarPrimitive.Fallback>,
|
|
34
|
+
ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
|
|
35
|
+
>(function AvatarFallback({ className, ...props }, ref) {
|
|
36
|
+
return (
|
|
37
|
+
<AvatarPrimitive.Fallback
|
|
38
|
+
ref={ref}
|
|
39
|
+
className={cn(
|
|
40
|
+
"flex size-full items-center justify-center rounded-full bg-muted text-sm font-medium text-muted-foreground",
|
|
41
|
+
className,
|
|
42
|
+
)}
|
|
43
|
+
{...props}
|
|
44
|
+
/>
|
|
45
|
+
);
|
|
46
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Avatar, AvatarImage, AvatarFallback } from "./avatar";
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { Badge } from "./badge";
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: "Core/Badge",
|
|
6
|
+
component: Badge,
|
|
7
|
+
tags: ["autodocs"],
|
|
8
|
+
args: { children: "Badge" },
|
|
9
|
+
argTypes: {
|
|
10
|
+
variant: {
|
|
11
|
+
description: "Visual style / semantic tone of the badge.",
|
|
12
|
+
control: { type: "select" },
|
|
13
|
+
options: ["default", "secondary", "outline", "success", "warning", "destructive", "info"],
|
|
14
|
+
table: { category: "Appearance" },
|
|
15
|
+
},
|
|
16
|
+
children: {
|
|
17
|
+
description: "Badge label content.",
|
|
18
|
+
control: "text",
|
|
19
|
+
table: { category: "Content" },
|
|
20
|
+
},
|
|
21
|
+
className: {
|
|
22
|
+
description: "Extra Tailwind classes merged via cn().",
|
|
23
|
+
control: "text",
|
|
24
|
+
table: { category: "Appearance" },
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
} satisfies Meta<typeof Badge>;
|
|
28
|
+
export default meta;
|
|
29
|
+
type Story = StoryObj<typeof meta>;
|
|
30
|
+
|
|
31
|
+
export const Default: Story = {};
|
|
32
|
+
export const Statuses: Story = {
|
|
33
|
+
render: () => (
|
|
34
|
+
<div className="flex flex-wrap gap-2">
|
|
35
|
+
<Badge>Default</Badge>
|
|
36
|
+
<Badge variant="secondary">Secondary</Badge>
|
|
37
|
+
<Badge variant="outline">Outline</Badge>
|
|
38
|
+
<Badge variant="success">Success</Badge>
|
|
39
|
+
<Badge variant="warning">Warning</Badge>
|
|
40
|
+
<Badge variant="destructive">Error</Badge>
|
|
41
|
+
<Badge variant="info">Info</Badge>
|
|
42
|
+
</div>
|
|
43
|
+
),
|
|
44
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { render, screen } from "@testing-library/react";
|
|
3
|
+
import { Badge } from "./badge";
|
|
4
|
+
|
|
5
|
+
describe("Badge", () => {
|
|
6
|
+
it("renders content", () => {
|
|
7
|
+
render(<Badge>New</Badge>);
|
|
8
|
+
expect(screen.getByText("New")).toBeInTheDocument();
|
|
9
|
+
});
|
|
10
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type HTMLAttributes } from "react";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
import { cn } from "../../lib/cn";
|
|
4
|
+
|
|
5
|
+
export const badgeVariants = cva(
|
|
6
|
+
"inline-flex items-center gap-1 rounded-full border px-2.5 py-0.5 text-meta font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
|
7
|
+
{
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
default: "border-transparent bg-primary text-primary-foreground",
|
|
11
|
+
secondary: "border-transparent bg-secondary text-secondary-foreground",
|
|
12
|
+
outline: "text-foreground",
|
|
13
|
+
success: "border-transparent bg-success text-success-foreground",
|
|
14
|
+
warning: "border-transparent bg-warning text-warning-foreground",
|
|
15
|
+
destructive: "border-transparent bg-destructive text-destructive-foreground",
|
|
16
|
+
info: "border-transparent bg-info text-info-foreground",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
defaultVariants: { variant: "default" },
|
|
20
|
+
},
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
export interface BadgeProps
|
|
24
|
+
extends HTMLAttributes<HTMLSpanElement>, VariantProps<typeof badgeVariants> {}
|
|
25
|
+
|
|
26
|
+
export function Badge({ className, variant, ...props }: BadgeProps) {
|
|
27
|
+
return <span className={cn(badgeVariants({ variant }), className)} {...props} />;
|
|
28
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Badge, badgeVariants, type BadgeProps } from "./badge";
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
|
|
3
|
+
import { BentoGrid, BentoGridItem } from "./bento-grid";
|
|
4
|
+
|
|
5
|
+
const meta = {
|
|
6
|
+
title: "Layout/BentoGrid",
|
|
7
|
+
component: BentoGrid,
|
|
8
|
+
tags: ["autodocs"],
|
|
9
|
+
parameters: { layout: "fullscreen" },
|
|
10
|
+
} satisfies Meta<typeof BentoGrid>;
|
|
11
|
+
|
|
12
|
+
export default meta;
|
|
13
|
+
type Story = StoryObj<typeof meta>;
|
|
14
|
+
|
|
15
|
+
function Tile({ title, body }: { title: string; body?: string }) {
|
|
16
|
+
return (
|
|
17
|
+
<div className="flex h-full min-w-0 flex-col p-5">
|
|
18
|
+
<h3 className="text-subtitle font-medium text-foreground">{title}</h3>
|
|
19
|
+
{body ? <p className="mt-1 min-w-0 text-body text-muted-foreground">{body}</p> : null}
|
|
20
|
+
</div>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* A 6-tile overview: one hero (2×2, gradient wash) + five supporting tiles.
|
|
26
|
+
*
|
|
27
|
+
* The grid rests FLAT — no tile carries an elevation at rest, so the sheet reads
|
|
28
|
+
* as one plane. Hover a tile and it alone lifts to `shadow-lg`.
|
|
29
|
+
*/
|
|
30
|
+
export const Default: Story = {
|
|
31
|
+
render: () => (
|
|
32
|
+
<div className="p-6">
|
|
33
|
+
<BentoGrid>
|
|
34
|
+
<BentoGridItem size="hero">
|
|
35
|
+
<Tile
|
|
36
|
+
title="Revenue"
|
|
37
|
+
body="Up 24% this quarter — the headline the team opens the dashboard for. Hover any tile: it lifts, the rest stay flat."
|
|
38
|
+
/>
|
|
39
|
+
</BentoGridItem>
|
|
40
|
+
<BentoGridItem size="sm">
|
|
41
|
+
<Tile title="Active users" body="12,480" />
|
|
42
|
+
</BentoGridItem>
|
|
43
|
+
<BentoGridItem size="sm">
|
|
44
|
+
<Tile title="Churn" body="1.2%" />
|
|
45
|
+
</BentoGridItem>
|
|
46
|
+
<BentoGridItem size="md">
|
|
47
|
+
<Tile title="Pipeline" body="38 deals in flight across 4 stages" />
|
|
48
|
+
</BentoGridItem>
|
|
49
|
+
<BentoGridItem size="sm">
|
|
50
|
+
<Tile title="NPS" body="62" />
|
|
51
|
+
</BentoGridItem>
|
|
52
|
+
<BentoGridItem size="sm">
|
|
53
|
+
<Tile title="Open tickets" body="7" />
|
|
54
|
+
</BentoGridItem>
|
|
55
|
+
</BentoGrid>
|
|
56
|
+
</div>
|
|
57
|
+
),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Clickable tiles via the stretched-link pattern: an inner `<a className="absolute
|
|
62
|
+
* inset-0">` makes the whole tile a single link, and `interactive` gives it a
|
|
63
|
+
* pointer cursor + a `focus-within` ring (keyboard-visible).
|
|
64
|
+
*/
|
|
65
|
+
export const Interactive: Story = {
|
|
66
|
+
render: () => (
|
|
67
|
+
<div className="p-6">
|
|
68
|
+
<BentoGrid>
|
|
69
|
+
<BentoGridItem size="hero" interactive>
|
|
70
|
+
<a href="#revenue" aria-label="Revenue" className="absolute inset-0 z-30" />
|
|
71
|
+
<Tile title="Revenue →" body="The whole tile is one link (stretched inner anchor)." />
|
|
72
|
+
</BentoGridItem>
|
|
73
|
+
<BentoGridItem interactive>
|
|
74
|
+
<a href="#users" aria-label="Users" className="absolute inset-0 z-30" />
|
|
75
|
+
<Tile title="Users →" body="12,480" />
|
|
76
|
+
</BentoGridItem>
|
|
77
|
+
<BentoGridItem interactive>
|
|
78
|
+
<a href="#churn" aria-label="Churn" className="absolute inset-0 z-30" />
|
|
79
|
+
<Tile title="Churn →" body="1.2%" />
|
|
80
|
+
</BentoGridItem>
|
|
81
|
+
<BentoGridItem size="md" interactive>
|
|
82
|
+
<a href="#pipeline" aria-label="Pipeline" className="absolute inset-0 z-30" />
|
|
83
|
+
<Tile title="Pipeline →" body="38 deals" />
|
|
84
|
+
</BentoGridItem>
|
|
85
|
+
</BentoGrid>
|
|
86
|
+
</div>
|
|
87
|
+
),
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The cursor-following spotlight is OPT-IN. Set `spotlight` on the `BentoGrid`
|
|
92
|
+
* to enable it for every tile; a tile's own `spotlight` prop still wins, so one
|
|
93
|
+
* tile can opt back out. Suppressed entirely under OS reduced-motion — the hover
|
|
94
|
+
* elevation carries the interaction on its own there.
|
|
95
|
+
*/
|
|
96
|
+
export const Spotlight: Story = {
|
|
97
|
+
render: () => (
|
|
98
|
+
<div className="p-6">
|
|
99
|
+
<BentoGrid spotlight>
|
|
100
|
+
<BentoGridItem size="hero">
|
|
101
|
+
<Tile
|
|
102
|
+
title="Revenue"
|
|
103
|
+
body="A primary-tinted glow follows the cursor across the tile, on top of the hover lift."
|
|
104
|
+
/>
|
|
105
|
+
</BentoGridItem>
|
|
106
|
+
<BentoGridItem>
|
|
107
|
+
<Tile title="Active users" body="12,480" />
|
|
108
|
+
</BentoGridItem>
|
|
109
|
+
<BentoGridItem>
|
|
110
|
+
<Tile title="Churn" body="1.2%" />
|
|
111
|
+
</BentoGridItem>
|
|
112
|
+
<BentoGridItem size="md" spotlight={false}>
|
|
113
|
+
<Tile title="Opted out" body="spotlight={false} — lifts on hover, no glow." />
|
|
114
|
+
</BentoGridItem>
|
|
115
|
+
</BentoGrid>
|
|
116
|
+
</div>
|
|
117
|
+
),
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
/** Explicit `span` overrides the size presets for bespoke layouts. */
|
|
121
|
+
export const ExplicitSpans: Story = {
|
|
122
|
+
render: () => (
|
|
123
|
+
<div className="p-6">
|
|
124
|
+
<BentoGrid>
|
|
125
|
+
<BentoGridItem span={{ col: 3, row: 1 }}>
|
|
126
|
+
<Tile title="Wide banner" body="span={{ col: 3 }}" />
|
|
127
|
+
</BentoGridItem>
|
|
128
|
+
<BentoGridItem span={{ col: 1, row: 2 }}>
|
|
129
|
+
<Tile title="Tall rail" body="span={{ row: 2 }}" />
|
|
130
|
+
</BentoGridItem>
|
|
131
|
+
<BentoGridItem>
|
|
132
|
+
<Tile title="A" />
|
|
133
|
+
</BentoGridItem>
|
|
134
|
+
<BentoGridItem>
|
|
135
|
+
<Tile title="B" />
|
|
136
|
+
</BentoGridItem>
|
|
137
|
+
<BentoGridItem>
|
|
138
|
+
<Tile title="C" />
|
|
139
|
+
</BentoGridItem>
|
|
140
|
+
</BentoGrid>
|
|
141
|
+
</div>
|
|
142
|
+
),
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
/** Edge states: a very long unbroken string (must not overflow) and a per-tile spotlight opt-in. */
|
|
146
|
+
export const EdgeStates: Story = {
|
|
147
|
+
render: () => (
|
|
148
|
+
<div className="p-6">
|
|
149
|
+
<BentoGrid>
|
|
150
|
+
<BentoGridItem size="md">
|
|
151
|
+
<Tile
|
|
152
|
+
title="Long content"
|
|
153
|
+
body="supercalifragilisticexpialidocious-antidisestablishmentarianism-pneumonoultramicroscopicsilicovolcanoconiosis"
|
|
154
|
+
/>
|
|
155
|
+
</BentoGridItem>
|
|
156
|
+
<BentoGridItem spotlight>
|
|
157
|
+
<Tile title="Spotlight" body="spotlight — on one tile only" />
|
|
158
|
+
</BentoGridItem>
|
|
159
|
+
<BentoGridItem>
|
|
160
|
+
<Tile title="Plain" />
|
|
161
|
+
</BentoGridItem>
|
|
162
|
+
</BentoGrid>
|
|
163
|
+
</div>
|
|
164
|
+
),
|
|
165
|
+
};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { render, screen } from "@testing-library/react";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
|
|
4
|
+
import { BentoGrid, BentoGridItem } from "./bento-grid";
|
|
5
|
+
|
|
6
|
+
describe("BentoGrid", () => {
|
|
7
|
+
it("renders a grid container with its children", () => {
|
|
8
|
+
render(
|
|
9
|
+
<BentoGrid data-testid="grid">
|
|
10
|
+
<BentoGridItem>A</BentoGridItem>
|
|
11
|
+
<BentoGridItem>B</BentoGridItem>
|
|
12
|
+
</BentoGrid>,
|
|
13
|
+
);
|
|
14
|
+
const grid = screen.getByTestId("grid");
|
|
15
|
+
expect(grid).toBeInTheDocument();
|
|
16
|
+
expect(grid.className).toMatch(/grid/);
|
|
17
|
+
expect(screen.getByText("A")).toBeInTheDocument();
|
|
18
|
+
expect(screen.getByText("B")).toBeInTheDocument();
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe("BentoGridItem", () => {
|
|
23
|
+
it("applies the hero size as a 2x2 span", () => {
|
|
24
|
+
render(
|
|
25
|
+
<BentoGridItem size="hero" data-testid="item">
|
|
26
|
+
hero
|
|
27
|
+
</BentoGridItem>,
|
|
28
|
+
);
|
|
29
|
+
const item = screen.getByTestId("item");
|
|
30
|
+
expect(item.style.gridColumn).toContain("span 2");
|
|
31
|
+
expect(item.style.gridRow).toContain("span 2");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("lets an explicit span override the size preset", () => {
|
|
35
|
+
render(
|
|
36
|
+
<BentoGridItem size="sm" span={{ col: 3 }} data-testid="item">
|
|
37
|
+
wide
|
|
38
|
+
</BentoGridItem>,
|
|
39
|
+
);
|
|
40
|
+
const item = screen.getByTestId("item");
|
|
41
|
+
expect(item.style.gridColumn).toContain("span 3");
|
|
42
|
+
// size sm had row 1 → no row span emitted
|
|
43
|
+
expect(item.style.gridRow).toBe("");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("omits the spotlight overlay by default and renders it when opted in", () => {
|
|
47
|
+
const { rerender } = render(<BentoGridItem data-testid="item">x</BentoGridItem>);
|
|
48
|
+
expect(screen.queryByTestId("bento-spotlight")).toBeNull();
|
|
49
|
+
|
|
50
|
+
rerender(
|
|
51
|
+
<BentoGridItem data-testid="item" spotlight>
|
|
52
|
+
x
|
|
53
|
+
</BentoGridItem>,
|
|
54
|
+
);
|
|
55
|
+
expect(screen.getByTestId("bento-spotlight")).toBeInTheDocument();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("inherits the grid's spotlight opt-in, and a tile's own prop wins", () => {
|
|
59
|
+
render(
|
|
60
|
+
<BentoGrid spotlight>
|
|
61
|
+
<BentoGridItem data-testid="inherited">a</BentoGridItem>
|
|
62
|
+
<BentoGridItem data-testid="opted-out" spotlight={false}>
|
|
63
|
+
b
|
|
64
|
+
</BentoGridItem>
|
|
65
|
+
</BentoGrid>,
|
|
66
|
+
);
|
|
67
|
+
expect(screen.getByTestId("inherited").querySelector("[data-testid=bento-spotlight]")).not.toBe(
|
|
68
|
+
null,
|
|
69
|
+
);
|
|
70
|
+
expect(
|
|
71
|
+
screen.getByTestId("opted-out").querySelector("[data-testid=bento-spotlight]"),
|
|
72
|
+
).toBeNull();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("rests flat and lifts to a visible elevation on hover", () => {
|
|
76
|
+
render(<BentoGridItem data-testid="item">x</BentoGridItem>);
|
|
77
|
+
const item = screen.getByTestId("item");
|
|
78
|
+
// No resting elevation — the Card `shadow-sm` is overridden down to none...
|
|
79
|
+
expect(item.className).toMatch(/(?:^|\s)shadow-none(?:\s|$)/);
|
|
80
|
+
expect(item.className).not.toMatch(/(?:^|\s)shadow-sm(?:\s|$)/);
|
|
81
|
+
// ...and the hover state reaches a clearly readable rung of the ONE ramp.
|
|
82
|
+
expect(item.className).toMatch(/hover:shadow-xl/);
|
|
83
|
+
// The black-ink shadow all but vanishes on a dark ground, so the hover edge is
|
|
84
|
+
// the channel that carries the lift there — it must ride the base, not the
|
|
85
|
+
// `interactive` variant (a plain, non-clickable tile lifts too). It is the
|
|
86
|
+
// soft `ring/40` tint, never the hard `border-strong` outline.
|
|
87
|
+
expect(item.className).toMatch(/hover:border-ring\/40/);
|
|
88
|
+
expect(item.className).not.toMatch(/hover:border-border-strong/);
|
|
89
|
+
// ~4px of travel, neutralized (not dropped) under reduced motion.
|
|
90
|
+
expect(item.className).toMatch(/hover:-translate-y-1/);
|
|
91
|
+
expect(item.className).toMatch(/motion-reduce:hover:translate-y-0/);
|
|
92
|
+
// Smoothness comes from the arrival curve, not from a longer duration.
|
|
93
|
+
expect(item.className).toMatch(/transition-\[translate,box-shadow,border-color\]/);
|
|
94
|
+
expect(item.className).toMatch(/ease-entrance/);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("supports the stretched-link pattern with a focus-within ring when interactive", () => {
|
|
98
|
+
render(
|
|
99
|
+
<BentoGridItem size="hero" interactive data-testid="item">
|
|
100
|
+
<a href="#go" aria-label="go" className="absolute inset-0" />
|
|
101
|
+
content
|
|
102
|
+
</BentoGridItem>,
|
|
103
|
+
);
|
|
104
|
+
// The tile stays a div; the inner anchor is the focusable element.
|
|
105
|
+
const item = screen.getByTestId("item");
|
|
106
|
+
expect(item.tagName).toBe("DIV");
|
|
107
|
+
expect(item.className).toMatch(/focus-within:ring/);
|
|
108
|
+
expect(screen.getByRole("link", { name: "go" })).toBeInTheDocument();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("hero tiles carry the gradient emphasis wash", () => {
|
|
112
|
+
render(
|
|
113
|
+
<BentoGridItem size="hero" data-testid="item">
|
|
114
|
+
hero
|
|
115
|
+
</BentoGridItem>,
|
|
116
|
+
);
|
|
117
|
+
expect(screen.getByTestId("item").className).toMatch(/from-primary/);
|
|
118
|
+
});
|
|
119
|
+
});
|