@neynar/ui 0.1.1
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 +195 -0
- package/dist/components/ui/accordion.d.ts +229 -0
- package/dist/components/ui/accordion.d.ts.map +1 -0
- package/dist/components/ui/alert-dialog.d.ts +247 -0
- package/dist/components/ui/alert-dialog.d.ts.map +1 -0
- package/dist/components/ui/alert.d.ts +187 -0
- package/dist/components/ui/alert.d.ts.map +1 -0
- package/dist/components/ui/aspect-ratio.d.ts +94 -0
- package/dist/components/ui/aspect-ratio.d.ts.map +1 -0
- package/dist/components/ui/avatar.d.ts +244 -0
- package/dist/components/ui/avatar.d.ts.map +1 -0
- package/dist/components/ui/badge.d.ts +163 -0
- package/dist/components/ui/badge.d.ts.map +1 -0
- package/dist/components/ui/breadcrumb.d.ts +281 -0
- package/dist/components/ui/breadcrumb.d.ts.map +1 -0
- package/dist/components/ui/button.d.ts +129 -0
- package/dist/components/ui/button.d.ts.map +1 -0
- package/dist/components/ui/calendar.d.ts +169 -0
- package/dist/components/ui/calendar.d.ts.map +1 -0
- package/dist/components/ui/card.d.ts +365 -0
- package/dist/components/ui/card.d.ts.map +1 -0
- package/dist/components/ui/carousel.d.ts +369 -0
- package/dist/components/ui/carousel.d.ts.map +1 -0
- package/dist/components/ui/chart.d.ts +442 -0
- package/dist/components/ui/chart.d.ts.map +1 -0
- package/dist/components/ui/checkbox.d.ts +88 -0
- package/dist/components/ui/checkbox.d.ts.map +1 -0
- package/dist/components/ui/collapsible.d.ts +182 -0
- package/dist/components/ui/collapsible.d.ts.map +1 -0
- package/dist/components/ui/combobox.d.ts +270 -0
- package/dist/components/ui/combobox.d.ts.map +1 -0
- package/dist/components/ui/command.d.ts +355 -0
- package/dist/components/ui/command.d.ts.map +1 -0
- package/dist/components/ui/container.d.ts +102 -0
- package/dist/components/ui/container.d.ts.map +1 -0
- package/dist/components/ui/context-menu.d.ts +339 -0
- package/dist/components/ui/context-menu.d.ts.map +1 -0
- package/dist/components/ui/date-picker.d.ts +145 -0
- package/dist/components/ui/date-picker.d.ts.map +1 -0
- package/dist/components/ui/dialog.d.ts +322 -0
- package/dist/components/ui/dialog.d.ts.map +1 -0
- package/dist/components/ui/drawer.d.ts +154 -0
- package/dist/components/ui/drawer.d.ts.map +1 -0
- package/dist/components/ui/dropdown-menu.d.ts +349 -0
- package/dist/components/ui/dropdown-menu.d.ts.map +1 -0
- package/dist/components/ui/empty-state.d.ts +133 -0
- package/dist/components/ui/empty-state.d.ts.map +1 -0
- package/dist/components/ui/hover-card.d.ts +109 -0
- package/dist/components/ui/hover-card.d.ts.map +1 -0
- package/dist/components/ui/input.d.ts +89 -0
- package/dist/components/ui/input.d.ts.map +1 -0
- package/dist/components/ui/label.d.ts +93 -0
- package/dist/components/ui/label.d.ts.map +1 -0
- package/dist/components/ui/menubar.d.ts +306 -0
- package/dist/components/ui/menubar.d.ts.map +1 -0
- package/dist/components/ui/navigation-menu.d.ts +318 -0
- package/dist/components/ui/navigation-menu.d.ts.map +1 -0
- package/dist/components/ui/pagination.d.ts +343 -0
- package/dist/components/ui/pagination.d.ts.map +1 -0
- package/dist/components/ui/popover.d.ts +178 -0
- package/dist/components/ui/popover.d.ts.map +1 -0
- package/dist/components/ui/progress.d.ts +64 -0
- package/dist/components/ui/progress.d.ts.map +1 -0
- package/dist/components/ui/radio-group.d.ts +144 -0
- package/dist/components/ui/radio-group.d.ts.map +1 -0
- package/dist/components/ui/resizable.d.ts +164 -0
- package/dist/components/ui/resizable.d.ts.map +1 -0
- package/dist/components/ui/scroll-area.d.ts +82 -0
- package/dist/components/ui/scroll-area.d.ts.map +1 -0
- package/dist/components/ui/select.d.ts +316 -0
- package/dist/components/ui/select.d.ts.map +1 -0
- package/dist/components/ui/separator.d.ts +80 -0
- package/dist/components/ui/separator.d.ts.map +1 -0
- package/dist/components/ui/sheet.d.ts +346 -0
- package/dist/components/ui/sheet.d.ts.map +1 -0
- package/dist/components/ui/sidebar.d.ts +1561 -0
- package/dist/components/ui/sidebar.d.ts.map +1 -0
- package/dist/components/ui/skeleton.d.ts +66 -0
- package/dist/components/ui/skeleton.d.ts.map +1 -0
- package/dist/components/ui/slider.d.ts +95 -0
- package/dist/components/ui/slider.d.ts.map +1 -0
- package/dist/components/ui/sonner.d.ts +101 -0
- package/dist/components/ui/sonner.d.ts.map +1 -0
- package/dist/components/ui/stack.d.ts +192 -0
- package/dist/components/ui/stack.d.ts.map +1 -0
- package/dist/components/ui/stories/accordion.stories.d.ts +71 -0
- package/dist/components/ui/stories/accordion.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/alert-dialog.stories.d.ts +39 -0
- package/dist/components/ui/stories/alert-dialog.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/alert.stories.d.ts +48 -0
- package/dist/components/ui/stories/alert.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/aspect-ratio.stories.d.ts +53 -0
- package/dist/components/ui/stories/aspect-ratio.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/avatar.stories.d.ts +49 -0
- package/dist/components/ui/stories/avatar.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/badge.stories.d.ts +64 -0
- package/dist/components/ui/stories/badge.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/breadcrumb.stories.d.ts +27 -0
- package/dist/components/ui/stories/breadcrumb.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/button.stories.d.ts +92 -0
- package/dist/components/ui/stories/button.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/calendar.stories.d.ts +94 -0
- package/dist/components/ui/stories/calendar.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/card.stories.d.ts +29 -0
- package/dist/components/ui/stories/card.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/carousel.stories.d.ts +42 -0
- package/dist/components/ui/stories/carousel.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/chart.stories.d.ts +51 -0
- package/dist/components/ui/stories/chart.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/checkbox.stories.d.ts +72 -0
- package/dist/components/ui/stories/checkbox.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/cn.stories.d.ts +19 -0
- package/dist/components/ui/stories/cn.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/collapsible.stories.d.ts +51 -0
- package/dist/components/ui/stories/collapsible.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/colors.stories.d.ts +31 -0
- package/dist/components/ui/stories/colors.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/combobox.stories.d.ts +89 -0
- package/dist/components/ui/stories/combobox.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/command.stories.d.ts +69 -0
- package/dist/components/ui/stories/command.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/container.stories.d.ts +42 -0
- package/dist/components/ui/stories/container.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/context-menu.stories.d.ts +32 -0
- package/dist/components/ui/stories/context-menu.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/date-picker.stories.d.ts +67 -0
- package/dist/components/ui/stories/date-picker.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/dialog.stories.d.ts +48 -0
- package/dist/components/ui/stories/dialog.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/drawer.stories.d.ts +33 -0
- package/dist/components/ui/stories/drawer.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/dropdown-menu.stories.d.ts +31 -0
- package/dist/components/ui/stories/dropdown-menu.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/empty-state.stories.d.ts +74 -0
- package/dist/components/ui/stories/empty-state.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/hover-card.stories.d.ts +35 -0
- package/dist/components/ui/stories/hover-card.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/input.stories.d.ts +69 -0
- package/dist/components/ui/stories/input.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/label.stories.d.ts +47 -0
- package/dist/components/ui/stories/label.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/menubar.stories.d.ts +39 -0
- package/dist/components/ui/stories/menubar.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/navigation-menu.stories.d.ts +44 -0
- package/dist/components/ui/stories/navigation-menu.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/pagination.stories.d.ts +33 -0
- package/dist/components/ui/stories/pagination.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/popover.stories.d.ts +36 -0
- package/dist/components/ui/stories/popover.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/progress.stories.d.ts +38 -0
- package/dist/components/ui/stories/progress.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/radio-group.stories.d.ts +76 -0
- package/dist/components/ui/stories/radio-group.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/resizable.stories.d.ts +49 -0
- package/dist/components/ui/stories/resizable.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/scroll-area.stories.d.ts +35 -0
- package/dist/components/ui/stories/scroll-area.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/select.stories.d.ts +51 -0
- package/dist/components/ui/stories/select.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/separator.stories.d.ts +58 -0
- package/dist/components/ui/stories/separator.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/sheet.stories.d.ts +43 -0
- package/dist/components/ui/stories/sheet.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/sidebar.stories.d.ts +60 -0
- package/dist/components/ui/stories/sidebar.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/skeleton.stories.d.ts +42 -0
- package/dist/components/ui/stories/skeleton.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/slider.stories.d.ts +99 -0
- package/dist/components/ui/stories/slider.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/sonner.stories.d.ts +9 -0
- package/dist/components/ui/stories/sonner.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/stack.stories.d.ts +39 -0
- package/dist/components/ui/stories/stack.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/switch.stories.d.ts +71 -0
- package/dist/components/ui/stories/switch.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/table.stories.d.ts +40 -0
- package/dist/components/ui/stories/table.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/tabs.stories.d.ts +62 -0
- package/dist/components/ui/stories/tabs.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/text-field.stories.d.ts +78 -0
- package/dist/components/ui/stories/text-field.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/textarea.stories.d.ts +57 -0
- package/dist/components/ui/stories/textarea.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/theme-toggle.stories.d.ts +71 -0
- package/dist/components/ui/stories/theme-toggle.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/theme.stories.d.ts +51 -0
- package/dist/components/ui/stories/theme.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/toggle-group.stories.d.ts +71 -0
- package/dist/components/ui/stories/toggle-group.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/toggle.stories.d.ts +78 -0
- package/dist/components/ui/stories/toggle.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/tooltip.stories.d.ts +37 -0
- package/dist/components/ui/stories/tooltip.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/typography.stories.d.ts +137 -0
- package/dist/components/ui/stories/typography.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/use-mobile.stories.d.ts +20 -0
- package/dist/components/ui/stories/use-mobile.stories.d.ts.map +1 -0
- package/dist/components/ui/stories/use-theme.stories.d.ts +23 -0
- package/dist/components/ui/stories/use-theme.stories.d.ts.map +1 -0
- package/dist/components/ui/switch.d.ts +84 -0
- package/dist/components/ui/switch.d.ts.map +1 -0
- package/dist/components/ui/table.d.ts +321 -0
- package/dist/components/ui/table.d.ts.map +1 -0
- package/dist/components/ui/tabs.d.ts +260 -0
- package/dist/components/ui/tabs.d.ts.map +1 -0
- package/dist/components/ui/text-field.d.ts +157 -0
- package/dist/components/ui/text-field.d.ts.map +1 -0
- package/dist/components/ui/textarea.d.ts +84 -0
- package/dist/components/ui/textarea.d.ts.map +1 -0
- package/dist/components/ui/theme-toggle.d.ts +105 -0
- package/dist/components/ui/theme-toggle.d.ts.map +1 -0
- package/dist/components/ui/theme.d.ts +110 -0
- package/dist/components/ui/theme.d.ts.map +1 -0
- package/dist/components/ui/toggle-group.d.ts +133 -0
- package/dist/components/ui/toggle-group.d.ts.map +1 -0
- package/dist/components/ui/toggle.d.ts +84 -0
- package/dist/components/ui/toggle.d.ts.map +1 -0
- package/dist/components/ui/tooltip.d.ts +202 -0
- package/dist/components/ui/tooltip.d.ts.map +1 -0
- package/dist/components/ui/typography.d.ts +287 -0
- package/dist/components/ui/typography.d.ts.map +1 -0
- package/dist/hooks/use-mobile.d.ts +74 -0
- package/dist/hooks/use-mobile.d.ts.map +1 -0
- package/dist/hooks/use-theme.d.ts +142 -0
- package/dist/hooks/use-theme.d.ts.map +1 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27498 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/utils.d.ts +43 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/docs/llm/colors.md +273 -0
- package/docs/llm/components/buttons.md +68 -0
- package/docs/llm/components/cards.md +53 -0
- package/docs/llm/components/display.md +134 -0
- package/docs/llm/components/feedback.md +96 -0
- package/docs/llm/components/forms.md +90 -0
- package/docs/llm/components/layout.md +59 -0
- package/docs/llm/components/menus.md +70 -0
- package/docs/llm/components/navigation.md +80 -0
- package/docs/llm/components/overlays.md +83 -0
- package/docs/llm/components/tables.md +73 -0
- package/docs/llm/components/typography.md +199 -0
- package/docs/llm/components/utilities.md +114 -0
- package/docs/llm/guide.md +165 -0
- package/llms.txt +122 -0
- package/package.json +104 -0
- package/src/components/ui/accordion.tsx +285 -0
- package/src/components/ui/alert-dialog.tsx +387 -0
- package/src/components/ui/alert.tsx +243 -0
- package/src/components/ui/aspect-ratio.tsx +99 -0
- package/src/components/ui/avatar.tsx +288 -0
- package/src/components/ui/badge.tsx +205 -0
- package/src/components/ui/breadcrumb.tsx +378 -0
- package/src/components/ui/button.tsx +195 -0
- package/src/components/ui/calendar.tsx +371 -0
- package/src/components/ui/card.tsx +447 -0
- package/src/components/ui/carousel.tsx +624 -0
- package/src/components/ui/chart.tsx +802 -0
- package/src/components/ui/checkbox.tsx +113 -0
- package/src/components/ui/collapsible.tsx +207 -0
- package/src/components/ui/combobox.tsx +373 -0
- package/src/components/ui/command.tsx +518 -0
- package/src/components/ui/container.tsx +114 -0
- package/src/components/ui/context-menu.tsx +563 -0
- package/src/components/ui/date-picker.tsx +213 -0
- package/src/components/ui/dialog.tsx +447 -0
- package/src/components/ui/drawer.tsx +273 -0
- package/src/components/ui/dropdown-menu.tsx +578 -0
- package/src/components/ui/empty-state.tsx +145 -0
- package/src/components/ui/hover-card.tsx +144 -0
- package/src/components/ui/input.tsx +106 -0
- package/src/components/ui/label.tsx +110 -0
- package/src/components/ui/menubar.tsx +553 -0
- package/src/components/ui/navigation-menu.tsx +471 -0
- package/src/components/ui/pagination.tsx +456 -0
- package/src/components/ui/popover.tsx +216 -0
- package/src/components/ui/progress.tsx +88 -0
- package/src/components/ui/radio-group.tsx +183 -0
- package/src/components/ui/resizable.tsx +209 -0
- package/src/components/ui/scroll-area.tsx +132 -0
- package/src/components/ui/select.tsx +485 -0
- package/src/components/ui/separator.tsx +101 -0
- package/src/components/ui/sheet.tsx +495 -0
- package/src/components/ui/sidebar.tsx +2211 -0
- package/src/components/ui/skeleton.tsx +76 -0
- package/src/components/ui/slider.tsx +147 -0
- package/src/components/ui/sonner.tsx +120 -0
- package/src/components/ui/stack.tsx +180 -0
- package/src/components/ui/stories/accordion.stories.tsx +429 -0
- package/src/components/ui/stories/alert-dialog.stories.tsx +519 -0
- package/src/components/ui/stories/alert.stories.tsx +228 -0
- package/src/components/ui/stories/aspect-ratio.stories.tsx +200 -0
- package/src/components/ui/stories/avatar.stories.tsx +317 -0
- package/src/components/ui/stories/badge.stories.tsx +260 -0
- package/src/components/ui/stories/breadcrumb.stories.tsx +482 -0
- package/src/components/ui/stories/button.stories.tsx +266 -0
- package/src/components/ui/stories/calendar.stories.tsx +375 -0
- package/src/components/ui/stories/card.stories.tsx +308 -0
- package/src/components/ui/stories/carousel.stories.tsx +328 -0
- package/src/components/ui/stories/chart.stories.tsx +430 -0
- package/src/components/ui/stories/checkbox.stories.tsx +297 -0
- package/src/components/ui/stories/cn.stories.tsx +433 -0
- package/src/components/ui/stories/collapsible.stories.tsx +256 -0
- package/src/components/ui/stories/colors.stories.tsx +502 -0
- package/src/components/ui/stories/combobox.stories.tsx +301 -0
- package/src/components/ui/stories/command.stories.tsx +632 -0
- package/src/components/ui/stories/container.stories.tsx +250 -0
- package/src/components/ui/stories/context-menu.stories.tsx +446 -0
- package/src/components/ui/stories/date-picker.stories.tsx +378 -0
- package/src/components/ui/stories/dialog.stories.tsx +535 -0
- package/src/components/ui/stories/drawer.stories.tsx +364 -0
- package/src/components/ui/stories/dropdown-menu.stories.tsx +374 -0
- package/src/components/ui/stories/empty-state.stories.tsx +244 -0
- package/src/components/ui/stories/hover-card.stories.tsx +355 -0
- package/src/components/ui/stories/input.stories.tsx +289 -0
- package/src/components/ui/stories/label.stories.tsx +294 -0
- package/src/components/ui/stories/menubar.stories.tsx +764 -0
- package/src/components/ui/stories/navigation-menu.stories.tsx +539 -0
- package/src/components/ui/stories/pagination.stories.tsx +604 -0
- package/src/components/ui/stories/popover.stories.tsx +392 -0
- package/src/components/ui/stories/progress.stories.tsx +218 -0
- package/src/components/ui/stories/radio-group.stories.tsx +400 -0
- package/src/components/ui/stories/resizable.stories.tsx +417 -0
- package/src/components/ui/stories/scroll-area.stories.tsx +180 -0
- package/src/components/ui/stories/select.stories.tsx +389 -0
- package/src/components/ui/stories/separator.stories.tsx +192 -0
- package/src/components/ui/stories/sheet.stories.tsx +468 -0
- package/src/components/ui/stories/sidebar.stories.tsx +731 -0
- package/src/components/ui/stories/skeleton.stories.tsx +216 -0
- package/src/components/ui/stories/slider.stories.tsx +321 -0
- package/src/components/ui/stories/sonner.stories.tsx +373 -0
- package/src/components/ui/stories/stack.stories.tsx +222 -0
- package/src/components/ui/stories/switch.stories.tsx +202 -0
- package/src/components/ui/stories/table.stories.tsx +541 -0
- package/src/components/ui/stories/tabs.stories.tsx +544 -0
- package/src/components/ui/stories/text-field.stories.tsx +280 -0
- package/src/components/ui/stories/textarea.stories.tsx +245 -0
- package/src/components/ui/stories/theme-toggle.stories.tsx +275 -0
- package/src/components/ui/stories/theme.stories.tsx +412 -0
- package/src/components/ui/stories/toggle-group.stories.tsx +337 -0
- package/src/components/ui/stories/toggle.stories.tsx +325 -0
- package/src/components/ui/stories/tooltip.stories.tsx +444 -0
- package/src/components/ui/stories/typography.stories.tsx +1586 -0
- package/src/components/ui/stories/use-mobile.stories.tsx +420 -0
- package/src/components/ui/stories/use-theme.stories.tsx +531 -0
- package/src/components/ui/switch.tsx +106 -0
- package/src/components/ui/table.tsx +424 -0
- package/src/components/ui/tabs.tsx +316 -0
- package/src/components/ui/text-field.tsx +206 -0
- package/src/components/ui/textarea.tsx +98 -0
- package/src/components/ui/theme-toggle.tsx +185 -0
- package/src/components/ui/theme.tsx +148 -0
- package/src/components/ui/toggle-group.tsx +196 -0
- package/src/components/ui/toggle.tsx +115 -0
- package/src/components/ui/tooltip.tsx +253 -0
- package/src/components/ui/typography.tsx +468 -0
- package/src/hooks/use-mobile.ts +91 -0
- package/src/hooks/use-theme.ts +319 -0
- package/src/index.ts +77 -0
- package/src/lib/utils.ts +57 -0
- package/src/styles/globals.css +160 -0
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { useState, useEffect } from "react";
|
|
3
|
+
import { useTheme } from "../../../hooks/use-theme";
|
|
4
|
+
import { Theme } from "../theme";
|
|
5
|
+
import { ThemeToggle } from "../theme-toggle";
|
|
6
|
+
import { Button } from "../button";
|
|
7
|
+
import {
|
|
8
|
+
Card,
|
|
9
|
+
CardContent,
|
|
10
|
+
CardDescription,
|
|
11
|
+
CardHeader,
|
|
12
|
+
CardTitle,
|
|
13
|
+
} from "../card";
|
|
14
|
+
import { Badge } from "../badge";
|
|
15
|
+
import { Moon, Sun, Monitor } from "lucide-react";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The useTheme hook provides a simple, powerful way to manage theme state in React applications.
|
|
19
|
+
* It handles system preference detection, cookie persistence, and synchronization across multiple instances.
|
|
20
|
+
*/
|
|
21
|
+
const meta: Meta = {
|
|
22
|
+
title: "Theme/useTheme",
|
|
23
|
+
parameters: {
|
|
24
|
+
layout: "padded",
|
|
25
|
+
docs: {
|
|
26
|
+
description: {
|
|
27
|
+
component: `
|
|
28
|
+
The **useTheme** hook provides complete theme management functionality with automatic system preference detection, cookie persistence, and perfect synchronization across multiple instances.
|
|
29
|
+
|
|
30
|
+
**Key Features:**
|
|
31
|
+
- 🌓 **Automatic theme switching** - Follows system light/dark preference when set to "system"
|
|
32
|
+
- 🍪 **Cookie persistence** - Maintains theme choice across browser sessions
|
|
33
|
+
- 🔄 **Perfect synchronization** - Multiple hook instances stay in sync via custom events
|
|
34
|
+
- ⚡ **Event-driven updates** - Responds to system theme changes in real-time
|
|
35
|
+
- 🚀 **Zero configuration** - Works out of the box without providers
|
|
36
|
+
- 🎯 **Type safe** - Full TypeScript support
|
|
37
|
+
|
|
38
|
+
**Usage:**
|
|
39
|
+
\`\`\`tsx
|
|
40
|
+
import { useTheme } from "@neynar/ui";
|
|
41
|
+
|
|
42
|
+
function ThemeToggle() {
|
|
43
|
+
const { preference, mode, setPreference } = useTheme();
|
|
44
|
+
|
|
45
|
+
const toggleTheme = () => {
|
|
46
|
+
setPreference(mode === "dark" ? "light" : "dark");
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<button onClick={toggleTheme}>
|
|
51
|
+
Current: {mode} (preference: {preference})
|
|
52
|
+
</button>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
\`\`\`
|
|
56
|
+
`,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
tags: ["autodocs"],
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export default meta;
|
|
64
|
+
type Story = StoryObj;
|
|
65
|
+
|
|
66
|
+
// Demo component to show hook usage
|
|
67
|
+
function ThemeDemo() {
|
|
68
|
+
const { preference, mode, setPreference } = useTheme();
|
|
69
|
+
const [systemTheme, setSystemTheme] = useState<"light" | "dark">("light");
|
|
70
|
+
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
73
|
+
setSystemTheme(mediaQuery.matches ? "dark" : "light");
|
|
74
|
+
|
|
75
|
+
const handleChange = (e: MediaQueryListEvent) => {
|
|
76
|
+
setSystemTheme(e.matches ? "dark" : "light");
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
mediaQuery.addEventListener("change", handleChange);
|
|
80
|
+
return () => mediaQuery.removeEventListener("change", handleChange);
|
|
81
|
+
}, []);
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<div className="space-y-6">
|
|
85
|
+
<Theme />
|
|
86
|
+
|
|
87
|
+
{/* Status Display */}
|
|
88
|
+
<Card>
|
|
89
|
+
<CardHeader>
|
|
90
|
+
<CardTitle className="text-sm font-medium">Hook State</CardTitle>
|
|
91
|
+
<CardDescription>
|
|
92
|
+
Current values returned by the useTheme hook
|
|
93
|
+
</CardDescription>
|
|
94
|
+
</CardHeader>
|
|
95
|
+
<CardContent className="space-y-4">
|
|
96
|
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
97
|
+
<div className="text-center p-4 border rounded-lg">
|
|
98
|
+
<div className="text-sm text-muted-foreground mb-2">
|
|
99
|
+
User Preference
|
|
100
|
+
</div>
|
|
101
|
+
<div className="flex items-center justify-center gap-2">
|
|
102
|
+
{preference === "system" && <Monitor className="h-4 w-4" />}
|
|
103
|
+
{preference === "light" && <Sun className="h-4 w-4" />}
|
|
104
|
+
{preference === "dark" && <Moon className="h-4 w-4" />}
|
|
105
|
+
<Badge variant="outline" className="text-sm">
|
|
106
|
+
{preference}
|
|
107
|
+
</Badge>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
<div className="text-center p-4 border rounded-lg">
|
|
112
|
+
<div className="text-sm text-muted-foreground mb-2">
|
|
113
|
+
Active Mode
|
|
114
|
+
</div>
|
|
115
|
+
<div className="flex items-center justify-center gap-2">
|
|
116
|
+
{mode === "light" && <Sun className="h-4 w-4" />}
|
|
117
|
+
{mode === "dark" && <Moon className="h-4 w-4" />}
|
|
118
|
+
<Badge variant="secondary" className="text-sm">
|
|
119
|
+
{mode}
|
|
120
|
+
</Badge>
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
|
|
124
|
+
<div className="text-center p-4 border rounded-lg">
|
|
125
|
+
<div className="text-sm text-muted-foreground mb-2">
|
|
126
|
+
System Preference
|
|
127
|
+
</div>
|
|
128
|
+
<div className="flex items-center justify-center gap-2">
|
|
129
|
+
{systemTheme === "light" && <Sun className="h-4 w-4" />}
|
|
130
|
+
{systemTheme === "dark" && <Moon className="h-4 w-4" />}
|
|
131
|
+
<Badge variant="outline" className="text-sm">
|
|
132
|
+
{systemTheme}
|
|
133
|
+
</Badge>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
|
|
138
|
+
<div className="text-xs text-muted-foreground bg-muted/50 p-3 rounded">
|
|
139
|
+
<strong>Explanation:</strong> When preference is "system",
|
|
140
|
+
the active mode follows your OS setting. When set to
|
|
141
|
+
"light" or "dark", the mode is fixed regardless
|
|
142
|
+
of system preference.
|
|
143
|
+
</div>
|
|
144
|
+
</CardContent>
|
|
145
|
+
</Card>
|
|
146
|
+
|
|
147
|
+
{/* Theme Controls */}
|
|
148
|
+
<Card>
|
|
149
|
+
<CardHeader>
|
|
150
|
+
<CardTitle className="text-sm font-medium">Theme Controls</CardTitle>
|
|
151
|
+
<CardDescription>
|
|
152
|
+
Use setPreference() to change the theme
|
|
153
|
+
</CardDescription>
|
|
154
|
+
</CardHeader>
|
|
155
|
+
<CardContent>
|
|
156
|
+
<div className="flex flex-wrap gap-3">
|
|
157
|
+
<Button
|
|
158
|
+
variant={preference === "system" ? "default" : "outline"}
|
|
159
|
+
size="sm"
|
|
160
|
+
onClick={() => setPreference("system")}
|
|
161
|
+
className="flex items-center gap-2"
|
|
162
|
+
>
|
|
163
|
+
<Monitor className="h-4 w-4" />
|
|
164
|
+
System
|
|
165
|
+
</Button>
|
|
166
|
+
|
|
167
|
+
<Button
|
|
168
|
+
variant={preference === "light" ? "default" : "outline"}
|
|
169
|
+
size="sm"
|
|
170
|
+
onClick={() => setPreference("light")}
|
|
171
|
+
className="flex items-center gap-2"
|
|
172
|
+
>
|
|
173
|
+
<Sun className="h-4 w-4" />
|
|
174
|
+
Light
|
|
175
|
+
</Button>
|
|
176
|
+
|
|
177
|
+
<Button
|
|
178
|
+
variant={preference === "dark" ? "default" : "outline"}
|
|
179
|
+
size="sm"
|
|
180
|
+
onClick={() => setPreference("dark")}
|
|
181
|
+
className="flex items-center gap-2"
|
|
182
|
+
>
|
|
183
|
+
<Moon className="h-4 w-4" />
|
|
184
|
+
Dark
|
|
185
|
+
</Button>
|
|
186
|
+
</div>
|
|
187
|
+
</CardContent>
|
|
188
|
+
</Card>
|
|
189
|
+
|
|
190
|
+
{/* Code Example */}
|
|
191
|
+
<Card>
|
|
192
|
+
<CardHeader>
|
|
193
|
+
<CardTitle className="text-sm font-medium">Implementation</CardTitle>
|
|
194
|
+
<CardDescription>How the buttons above work</CardDescription>
|
|
195
|
+
</CardHeader>
|
|
196
|
+
<CardContent>
|
|
197
|
+
<pre className="text-xs bg-muted p-4 rounded overflow-x-auto">
|
|
198
|
+
{`const { preference, mode, setPreference } = useTheme();
|
|
199
|
+
|
|
200
|
+
// Set to follow system preference
|
|
201
|
+
<Button onClick={() => setPreference('system')}>
|
|
202
|
+
System ({mode})
|
|
203
|
+
</Button>
|
|
204
|
+
|
|
205
|
+
// Set to light mode
|
|
206
|
+
<Button onClick={() => setPreference('light')}>
|
|
207
|
+
Light
|
|
208
|
+
</Button>
|
|
209
|
+
|
|
210
|
+
// Set to dark mode
|
|
211
|
+
<Button onClick={() => setPreference('dark')}>
|
|
212
|
+
Dark
|
|
213
|
+
</Button>
|
|
214
|
+
|
|
215
|
+
// Current state:
|
|
216
|
+
// preference: "${preference}"
|
|
217
|
+
// mode: "${mode}"`}
|
|
218
|
+
</pre>
|
|
219
|
+
</CardContent>
|
|
220
|
+
</Card>
|
|
221
|
+
</div>
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Interactive demonstration of the useTheme hook showing all return values and methods.
|
|
227
|
+
* Change the theme preference and see how the hook state updates in real-time.
|
|
228
|
+
*/
|
|
229
|
+
export const Interactive: Story = {
|
|
230
|
+
render: () => <ThemeDemo />,
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Multiple synchronized theme controls showing how the hook maintains consistency across instances.
|
|
235
|
+
* All controls share the same state through cookie persistence and custom event synchronization.
|
|
236
|
+
*/
|
|
237
|
+
export const MultipleSynchronizedInstances: Story = {
|
|
238
|
+
render: () => (
|
|
239
|
+
<div className="space-y-6">
|
|
240
|
+
<Theme />
|
|
241
|
+
|
|
242
|
+
<Card>
|
|
243
|
+
<CardHeader>
|
|
244
|
+
<CardTitle>Perfect Synchronization</CardTitle>
|
|
245
|
+
<CardDescription>
|
|
246
|
+
Multiple useTheme hook instances automatically stay in sync. Change
|
|
247
|
+
one and watch them all update.
|
|
248
|
+
</CardDescription>
|
|
249
|
+
</CardHeader>
|
|
250
|
+
<CardContent className="space-y-6">
|
|
251
|
+
{/* Instance 1 - Button Group */}
|
|
252
|
+
<div className="border rounded-lg p-4">
|
|
253
|
+
<h3 className="text-sm font-medium mb-3">
|
|
254
|
+
Instance 1: Button Group
|
|
255
|
+
</h3>
|
|
256
|
+
<ThemeControlButtons />
|
|
257
|
+
</div>
|
|
258
|
+
|
|
259
|
+
{/* Instance 2 - Theme Toggle Component */}
|
|
260
|
+
<div className="border rounded-lg p-4">
|
|
261
|
+
<h3 className="text-sm font-medium mb-3">
|
|
262
|
+
Instance 2: ThemeToggle Component
|
|
263
|
+
</h3>
|
|
264
|
+
<div className="flex items-center justify-between">
|
|
265
|
+
<span className="text-sm text-muted-foreground">
|
|
266
|
+
Built-in component using useTheme:
|
|
267
|
+
</span>
|
|
268
|
+
<ThemeToggle showLabel />
|
|
269
|
+
</div>
|
|
270
|
+
</div>
|
|
271
|
+
|
|
272
|
+
{/* Instance 3 - Status Display */}
|
|
273
|
+
<div className="border rounded-lg p-4">
|
|
274
|
+
<h3 className="text-sm font-medium mb-3">
|
|
275
|
+
Instance 3: Live Status Display
|
|
276
|
+
</h3>
|
|
277
|
+
<ThemeStatusDisplay />
|
|
278
|
+
</div>
|
|
279
|
+
|
|
280
|
+
<div className="text-xs text-muted-foreground bg-accent/50 p-3 rounded">
|
|
281
|
+
<strong>Technical Details:</strong> Each useTheme instance
|
|
282
|
+
dispatches custom events when the theme changes. All other instances
|
|
283
|
+
listen for these events and update their state accordingly, ensuring
|
|
284
|
+
perfect synchronization without the need for React context or
|
|
285
|
+
external state management.
|
|
286
|
+
</div>
|
|
287
|
+
</CardContent>
|
|
288
|
+
</Card>
|
|
289
|
+
</div>
|
|
290
|
+
),
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Real-world usage examples showing common implementation patterns.
|
|
295
|
+
*/
|
|
296
|
+
export const UsagePatterns: Story = {
|
|
297
|
+
render: () => (
|
|
298
|
+
<div className="space-y-6">
|
|
299
|
+
<Theme />
|
|
300
|
+
|
|
301
|
+
<div className="grid gap-6 md:grid-cols-2">
|
|
302
|
+
{/* Pattern 1: Simple Toggle */}
|
|
303
|
+
<Card>
|
|
304
|
+
<CardHeader>
|
|
305
|
+
<CardTitle className="text-sm font-medium">Simple Toggle</CardTitle>
|
|
306
|
+
<CardDescription>Basic light/dark mode toggle</CardDescription>
|
|
307
|
+
</CardHeader>
|
|
308
|
+
<CardContent>
|
|
309
|
+
<SimpleToggleExample />
|
|
310
|
+
<pre className="text-xs bg-muted p-3 rounded mt-4 overflow-x-auto">
|
|
311
|
+
{`function SimpleToggle() {
|
|
312
|
+
const { mode, setPreference } = useTheme();
|
|
313
|
+
|
|
314
|
+
const toggle = () => {
|
|
315
|
+
setPreference(mode === 'dark' ? 'light' : 'dark');
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
return (
|
|
319
|
+
<Button onClick={toggle}>
|
|
320
|
+
{mode === 'dark' ? <Sun /> : <Moon />}
|
|
321
|
+
{mode} Mode
|
|
322
|
+
</Button>
|
|
323
|
+
);
|
|
324
|
+
}`}
|
|
325
|
+
</pre>
|
|
326
|
+
</CardContent>
|
|
327
|
+
</Card>
|
|
328
|
+
|
|
329
|
+
{/* Pattern 2: Conditional Rendering */}
|
|
330
|
+
<Card>
|
|
331
|
+
<CardHeader>
|
|
332
|
+
<CardTitle className="text-sm font-medium">
|
|
333
|
+
Conditional Content
|
|
334
|
+
</CardTitle>
|
|
335
|
+
<CardDescription>
|
|
336
|
+
Render different content based on theme
|
|
337
|
+
</CardDescription>
|
|
338
|
+
</CardHeader>
|
|
339
|
+
<CardContent>
|
|
340
|
+
<ConditionalContentExample />
|
|
341
|
+
<pre className="text-xs bg-muted p-3 rounded mt-4 overflow-x-auto">
|
|
342
|
+
{`function ThemedContent() {
|
|
343
|
+
const { mode } = useTheme();
|
|
344
|
+
|
|
345
|
+
return (
|
|
346
|
+
<div>
|
|
347
|
+
{mode === 'dark' ? (
|
|
348
|
+
<div>🌙 Dark mode content</div>
|
|
349
|
+
) : (
|
|
350
|
+
<div>☀️ Light mode content</div>
|
|
351
|
+
)}
|
|
352
|
+
</div>
|
|
353
|
+
);
|
|
354
|
+
}`}
|
|
355
|
+
</pre>
|
|
356
|
+
</CardContent>
|
|
357
|
+
</Card>
|
|
358
|
+
|
|
359
|
+
{/* Pattern 3: System Preference Handler */}
|
|
360
|
+
<Card>
|
|
361
|
+
<CardHeader>
|
|
362
|
+
<CardTitle className="text-sm font-medium">
|
|
363
|
+
System Integration
|
|
364
|
+
</CardTitle>
|
|
365
|
+
<CardDescription>
|
|
366
|
+
Respect user's system preference
|
|
367
|
+
</CardDescription>
|
|
368
|
+
</CardHeader>
|
|
369
|
+
<CardContent>
|
|
370
|
+
<SystemIntegrationExample />
|
|
371
|
+
<pre className="text-xs bg-muted p-3 rounded mt-4 overflow-x-auto">
|
|
372
|
+
{`function SystemThemeInfo() {
|
|
373
|
+
const { preference, mode } = useTheme();
|
|
374
|
+
|
|
375
|
+
return (
|
|
376
|
+
<div>
|
|
377
|
+
<p>Preference: {preference}</p>
|
|
378
|
+
<p>Active: {mode}</p>
|
|
379
|
+
{preference === 'system' && (
|
|
380
|
+
<p>Following OS setting</p>
|
|
381
|
+
)}
|
|
382
|
+
</div>
|
|
383
|
+
);
|
|
384
|
+
}`}
|
|
385
|
+
</pre>
|
|
386
|
+
</CardContent>
|
|
387
|
+
</Card>
|
|
388
|
+
|
|
389
|
+
{/* Pattern 4: Theme Status Badge */}
|
|
390
|
+
<Card>
|
|
391
|
+
<CardHeader>
|
|
392
|
+
<CardTitle className="text-sm font-medium">
|
|
393
|
+
Status Indicator
|
|
394
|
+
</CardTitle>
|
|
395
|
+
<CardDescription>Show current theme in UI</CardDescription>
|
|
396
|
+
</CardHeader>
|
|
397
|
+
<CardContent>
|
|
398
|
+
<ThemeStatusBadgeExample />
|
|
399
|
+
<pre className="text-xs bg-muted p-3 rounded mt-4 overflow-x-auto">
|
|
400
|
+
{`function ThemeStatusBadge() {
|
|
401
|
+
const { preference, mode } = useTheme();
|
|
402
|
+
|
|
403
|
+
return (
|
|
404
|
+
<Badge variant="outline">
|
|
405
|
+
{preference === 'system'
|
|
406
|
+
? \`System (\${mode})\`
|
|
407
|
+
: mode
|
|
408
|
+
}
|
|
409
|
+
</Badge>
|
|
410
|
+
);
|
|
411
|
+
}`}
|
|
412
|
+
</pre>
|
|
413
|
+
</CardContent>
|
|
414
|
+
</Card>
|
|
415
|
+
</div>
|
|
416
|
+
</div>
|
|
417
|
+
),
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
// Helper components for the stories
|
|
421
|
+
function ThemeControlButtons() {
|
|
422
|
+
const { preference, setPreference } = useTheme();
|
|
423
|
+
|
|
424
|
+
return (
|
|
425
|
+
<div className="flex gap-2">
|
|
426
|
+
{(["system", "light", "dark"] as const).map((theme) => (
|
|
427
|
+
<Button
|
|
428
|
+
key={theme}
|
|
429
|
+
variant={preference === theme ? "default" : "outline"}
|
|
430
|
+
size="sm"
|
|
431
|
+
onClick={() => setPreference(theme)}
|
|
432
|
+
className="flex items-center gap-2"
|
|
433
|
+
>
|
|
434
|
+
{theme === "system" && <Monitor className="h-3 w-3" />}
|
|
435
|
+
{theme === "light" && <Sun className="h-3 w-3" />}
|
|
436
|
+
{theme === "dark" && <Moon className="h-3 w-3" />}
|
|
437
|
+
{theme.charAt(0).toUpperCase() + theme.slice(1)}
|
|
438
|
+
</Button>
|
|
439
|
+
))}
|
|
440
|
+
</div>
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function ThemeStatusDisplay() {
|
|
445
|
+
const { preference, mode } = useTheme();
|
|
446
|
+
|
|
447
|
+
return (
|
|
448
|
+
<div className="flex items-center gap-4 text-sm">
|
|
449
|
+
<div>
|
|
450
|
+
<span className="text-muted-foreground">Preference:</span>{" "}
|
|
451
|
+
<Badge variant="outline">{preference}</Badge>
|
|
452
|
+
</div>
|
|
453
|
+
<div>
|
|
454
|
+
<span className="text-muted-foreground">Active:</span>{" "}
|
|
455
|
+
<Badge variant="secondary">{mode}</Badge>
|
|
456
|
+
</div>
|
|
457
|
+
</div>
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function SimpleToggleExample() {
|
|
462
|
+
const { mode, setPreference } = useTheme();
|
|
463
|
+
|
|
464
|
+
const toggle = () => {
|
|
465
|
+
setPreference(mode === "dark" ? "light" : "dark");
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
return (
|
|
469
|
+
<Button onClick={toggle} variant="outline" size="sm">
|
|
470
|
+
{mode === "dark" ? (
|
|
471
|
+
<Sun className="mr-2 h-4 w-4" />
|
|
472
|
+
) : (
|
|
473
|
+
<Moon className="mr-2 h-4 w-4" />
|
|
474
|
+
)}
|
|
475
|
+
{mode === "dark" ? "Light" : "Dark"} Mode
|
|
476
|
+
</Button>
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function ConditionalContentExample() {
|
|
481
|
+
const { mode } = useTheme();
|
|
482
|
+
|
|
483
|
+
return (
|
|
484
|
+
<div className="p-3 border rounded text-center">
|
|
485
|
+
{mode === "dark" ? (
|
|
486
|
+
<div className="flex items-center justify-center gap-2">
|
|
487
|
+
<Moon className="h-4 w-4" />
|
|
488
|
+
<span>🌙 Dark mode active</span>
|
|
489
|
+
</div>
|
|
490
|
+
) : (
|
|
491
|
+
<div className="flex items-center justify-center gap-2">
|
|
492
|
+
<Sun className="h-4 w-4" />
|
|
493
|
+
<span>☀️ Light mode active</span>
|
|
494
|
+
</div>
|
|
495
|
+
)}
|
|
496
|
+
</div>
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function SystemIntegrationExample() {
|
|
501
|
+
const { preference, mode } = useTheme();
|
|
502
|
+
|
|
503
|
+
return (
|
|
504
|
+
<div className="space-y-2 text-sm">
|
|
505
|
+
<div>
|
|
506
|
+
Preference: <Badge variant="outline">{preference}</Badge>
|
|
507
|
+
</div>
|
|
508
|
+
<div>
|
|
509
|
+
Active Mode: <Badge variant="secondary">{mode}</Badge>
|
|
510
|
+
</div>
|
|
511
|
+
{preference === "system" && (
|
|
512
|
+
<div className="text-muted-foreground">✓ Following system setting</div>
|
|
513
|
+
)}
|
|
514
|
+
</div>
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
function ThemeStatusBadgeExample() {
|
|
519
|
+
const { preference, mode } = useTheme();
|
|
520
|
+
|
|
521
|
+
return (
|
|
522
|
+
<div className="flex justify-center">
|
|
523
|
+
<Badge variant="outline" className="flex items-center gap-2">
|
|
524
|
+
{preference === "system" && <Monitor className="h-3 w-3" />}
|
|
525
|
+
{preference === "light" && <Sun className="h-3 w-3" />}
|
|
526
|
+
{preference === "dark" && <Moon className="h-3 w-3" />}
|
|
527
|
+
{preference === "system" ? `System (${mode})` : mode}
|
|
528
|
+
</Badge>
|
|
529
|
+
</div>
|
|
530
|
+
);
|
|
531
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as SwitchPrimitive from "@radix-ui/react-switch";
|
|
3
|
+
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Switch component for binary toggle controls
|
|
8
|
+
*
|
|
9
|
+
* A versatile toggle switch component built on Radix UI primitives that provides
|
|
10
|
+
* an accessible way to toggle between on/off states. Commonly used for settings,
|
|
11
|
+
* preferences, and feature toggles that have immediate effects.
|
|
12
|
+
*
|
|
13
|
+
* Built with Radix UI Switch primitive and styled with Tailwind CSS to ensure
|
|
14
|
+
* consistent visual appearance across light and dark themes.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* // Basic usage with label
|
|
19
|
+
* <div className="flex items-center space-x-2">
|
|
20
|
+
* <Switch id="airplane-mode" />
|
|
21
|
+
* <Label htmlFor="airplane-mode">Airplane Mode</Label>
|
|
22
|
+
* </div>
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```tsx
|
|
27
|
+
* // Controlled switch with state
|
|
28
|
+
* const [enabled, setEnabled] = useState(false);
|
|
29
|
+
*
|
|
30
|
+
* <Switch
|
|
31
|
+
* id="notifications"
|
|
32
|
+
* checked={enabled}
|
|
33
|
+
* onCheckedChange={setEnabled}
|
|
34
|
+
* />
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```tsx
|
|
39
|
+
* // Switch with custom handler
|
|
40
|
+
* <Switch
|
|
41
|
+
* checked={isDarkMode}
|
|
42
|
+
* onCheckedChange={(checked) => {
|
|
43
|
+
* setIsDarkMode(checked);
|
|
44
|
+
* document.documentElement.classList.toggle("dark", checked);
|
|
45
|
+
* }}
|
|
46
|
+
* />
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```tsx
|
|
51
|
+
* // Disabled switch
|
|
52
|
+
* <Switch disabled />
|
|
53
|
+
* <Switch disabled defaultChecked />
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* @accessibility
|
|
57
|
+
* - Fully keyboard accessible with Space and Enter key support
|
|
58
|
+
* - ARIA attributes automatically managed by Radix UI
|
|
59
|
+
* - Screen reader announcements for state changes
|
|
60
|
+
* - Focus indicator with visible ring
|
|
61
|
+
* - Associates with labels via htmlFor/id pairing
|
|
62
|
+
* - Disabled state properly communicated to assistive technology
|
|
63
|
+
* - Follows WAI-ARIA Switch pattern
|
|
64
|
+
*
|
|
65
|
+
* @see {@link https://ui.shadcn.com/docs/components/switch} shadcn/ui Switch documentation
|
|
66
|
+
* @since 1.0.0
|
|
67
|
+
* @see {@link https://www.radix-ui.com/primitives/docs/components/switch} Radix UI Switch primitive
|
|
68
|
+
*/
|
|
69
|
+
/**
|
|
70
|
+
* Switch component props
|
|
71
|
+
*
|
|
72
|
+
* Extends all props from Radix UI Switch Root primitive, including:
|
|
73
|
+
* - `checked` - The controlled checked state of the switch
|
|
74
|
+
* - `defaultChecked` - The default checked state when uncontrolled
|
|
75
|
+
* - `onCheckedChange` - Event handler called when the checked state changes
|
|
76
|
+
* - `disabled` - Whether the switch is disabled
|
|
77
|
+
* - `required` - Whether the switch is required in forms
|
|
78
|
+
* - `name` - Name attribute for form submission
|
|
79
|
+
* - `value` - Value attribute for form submission
|
|
80
|
+
*
|
|
81
|
+
* @param className - Additional CSS classes to apply to the switch
|
|
82
|
+
* @param props - All other props from Radix UI Switch Root
|
|
83
|
+
*/
|
|
84
|
+
type SwitchProps = React.ComponentProps<typeof SwitchPrimitive.Root>;
|
|
85
|
+
|
|
86
|
+
function Switch({ className, ...props }: SwitchProps) {
|
|
87
|
+
return (
|
|
88
|
+
<SwitchPrimitive.Root
|
|
89
|
+
data-slot="switch"
|
|
90
|
+
className={cn(
|
|
91
|
+
"peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
|
92
|
+
className,
|
|
93
|
+
)}
|
|
94
|
+
{...props}
|
|
95
|
+
>
|
|
96
|
+
<SwitchPrimitive.Thumb
|
|
97
|
+
data-slot="switch-thumb"
|
|
98
|
+
className={cn(
|
|
99
|
+
"bg-background dark:data-[state=unchecked]:bg-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block size-4 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0",
|
|
100
|
+
)}
|
|
101
|
+
/>
|
|
102
|
+
</SwitchPrimitive.Root>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export { Switch };
|