@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,185 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Monitor, Moon, Sun } from "lucide-react";
|
|
4
|
+
import { Button } from "@/components/ui/button";
|
|
5
|
+
import {
|
|
6
|
+
DropdownMenu,
|
|
7
|
+
DropdownMenuContent,
|
|
8
|
+
DropdownMenuItem,
|
|
9
|
+
DropdownMenuTrigger,
|
|
10
|
+
} from "@/components/ui/dropdown-menu";
|
|
11
|
+
import { useTheme } from "@/hooks/use-theme";
|
|
12
|
+
import { cn } from "@/lib/utils";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Props for the ThemeToggle component
|
|
16
|
+
*/
|
|
17
|
+
export type ThemeToggleProps = {
|
|
18
|
+
/** Visual style variant for the toggle button */
|
|
19
|
+
variant?:
|
|
20
|
+
| "default"
|
|
21
|
+
| "destructive"
|
|
22
|
+
| "outline"
|
|
23
|
+
| "secondary"
|
|
24
|
+
| "ghost"
|
|
25
|
+
| "link";
|
|
26
|
+
|
|
27
|
+
/** Size of the toggle button */
|
|
28
|
+
size?: "default" | "sm" | "lg" | "icon";
|
|
29
|
+
|
|
30
|
+
/** Additional CSS classes */
|
|
31
|
+
className?: string;
|
|
32
|
+
|
|
33
|
+
/** Whether to show text labels (only applies when size is not "icon") */
|
|
34
|
+
showLabel?: boolean;
|
|
35
|
+
|
|
36
|
+
/** Alignment of the dropdown menu */
|
|
37
|
+
align?: "start" | "center" | "end";
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* ThemeToggle - A zero-config theme switcher with system, light, and dark modes
|
|
42
|
+
*
|
|
43
|
+
* A self-contained theme toggle component that works without any provider setup.
|
|
44
|
+
* Provides an intuitive dropdown interface for switching between system preference,
|
|
45
|
+
* light mode, and dark mode. The component handles theme persistence using cookies
|
|
46
|
+
* for SSR compatibility and automatically detects system theme changes.
|
|
47
|
+
*
|
|
48
|
+
* Built as a custom Neynar component that extends the standard shadcn/ui patterns
|
|
49
|
+
* with enhanced theme management capabilities.
|
|
50
|
+
*
|
|
51
|
+
* @component
|
|
52
|
+
* @example Basic usage (icon button)
|
|
53
|
+
* ```tsx
|
|
54
|
+
* // Most common usage - just drop it in!
|
|
55
|
+
* <ThemeToggle />
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* @example With text labels
|
|
59
|
+
* ```tsx
|
|
60
|
+
* // Show current theme name next to icon
|
|
61
|
+
* <ThemeToggle size="default" showLabel />
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
64
|
+
* @example Custom styling variants
|
|
65
|
+
* ```tsx
|
|
66
|
+
* // Ghost button for minimalist UI
|
|
67
|
+
* <ThemeToggle variant="ghost" />
|
|
68
|
+
*
|
|
69
|
+
* // Secondary style for subtlety
|
|
70
|
+
* <ThemeToggle variant="secondary" size="sm" />
|
|
71
|
+
*
|
|
72
|
+
* // Custom positioning
|
|
73
|
+
* <ThemeToggle align="start" className="border-2" />
|
|
74
|
+
* ```
|
|
75
|
+
*
|
|
76
|
+
* @example In navigation components
|
|
77
|
+
* ```tsx
|
|
78
|
+
* // Header navigation
|
|
79
|
+
* <header className="flex items-center justify-between p-4">
|
|
80
|
+
* <Logo />
|
|
81
|
+
* <div className="flex items-center gap-2">
|
|
82
|
+
* <Button variant="ghost">Settings</Button>
|
|
83
|
+
* <ThemeToggle />
|
|
84
|
+
* </div>
|
|
85
|
+
* </header>
|
|
86
|
+
*
|
|
87
|
+
* // Sidebar footer
|
|
88
|
+
* <SidebarFooter>
|
|
89
|
+
* <div className="flex items-center justify-between">
|
|
90
|
+
* <UserProfile />
|
|
91
|
+
* <ThemeToggle variant="ghost" size="sm" />
|
|
92
|
+
* </div>
|
|
93
|
+
* </SidebarFooter>
|
|
94
|
+
* ```
|
|
95
|
+
*
|
|
96
|
+
* @param variant - Button style variant (inherits from Button component)
|
|
97
|
+
* @param size - Button size, defaults to "icon" for compact display
|
|
98
|
+
* @param className - Additional CSS classes for custom styling
|
|
99
|
+
* @param showLabel - Whether to show theme name text (auto-enabled for non-icon sizes)
|
|
100
|
+
* @param align - Dropdown menu alignment relative to trigger button
|
|
101
|
+
*
|
|
102
|
+
* @features
|
|
103
|
+
* - **Zero Configuration**: Works immediately without providers or setup
|
|
104
|
+
* - **System Detection**: Automatically follows OS dark/light preference
|
|
105
|
+
* - **SSR Compatible**: Uses cookies for theme persistence across server/client
|
|
106
|
+
* - **Smooth Transitions**: CSS-based theme switching with no flash
|
|
107
|
+
* - **Accessibility**: Full keyboard navigation and screen reader support
|
|
108
|
+
* - **Responsive**: Touch-friendly on mobile devices
|
|
109
|
+
*
|
|
110
|
+
* @accessibility
|
|
111
|
+
* - Screen reader label: "Toggle theme" for assistive technology
|
|
112
|
+
* - Keyboard navigation: Enter, Space, and Arrow keys for dropdown
|
|
113
|
+
* - Current selection indicated with checkmark and visual styling
|
|
114
|
+
* - High contrast support across all theme modes
|
|
115
|
+
* - Focus visible indicators for keyboard users
|
|
116
|
+
*
|
|
117
|
+
* @remarks
|
|
118
|
+
* This component uses the {@link useTheme} hook internally to manage theme state.
|
|
119
|
+
* Theme changes are persisted automatically and synchronized across browser tabs.
|
|
120
|
+
* The component is fully self-contained and doesn't interfere with existing
|
|
121
|
+
* theme systems in your application.
|
|
122
|
+
*
|
|
123
|
+
* @see {@link useTheme} - The underlying theme management hook
|
|
124
|
+
* @see {@link Button} - Base button component for styling options
|
|
125
|
+
* @see {@link DropdownMenu} - Menu component used for theme selection
|
|
126
|
+
* @since 1.0.0
|
|
127
|
+
*/
|
|
128
|
+
export function ThemeToggle({
|
|
129
|
+
variant = "outline",
|
|
130
|
+
size = "icon",
|
|
131
|
+
className,
|
|
132
|
+
showLabel = size !== "icon",
|
|
133
|
+
align = "end",
|
|
134
|
+
}: ThemeToggleProps) {
|
|
135
|
+
const { preference, setPreference } = useTheme();
|
|
136
|
+
|
|
137
|
+
// Simple icons and labels - no configuration needed
|
|
138
|
+
const items = [
|
|
139
|
+
{
|
|
140
|
+
mode: "system" as const,
|
|
141
|
+
icon: <Monitor className="h-4 w-4" />,
|
|
142
|
+
label: "System",
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
mode: "light" as const,
|
|
146
|
+
icon: <Sun className="h-4 w-4" />,
|
|
147
|
+
label: "Light",
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
mode: "dark" as const,
|
|
151
|
+
icon: <Moon className="h-4 w-4" />,
|
|
152
|
+
label: "Dark",
|
|
153
|
+
},
|
|
154
|
+
];
|
|
155
|
+
|
|
156
|
+
// Get current item based on preference, not resolved mode
|
|
157
|
+
const currentItem = items.find((item) => item.mode === preference) || items[0];
|
|
158
|
+
|
|
159
|
+
return (
|
|
160
|
+
<DropdownMenu>
|
|
161
|
+
<DropdownMenuTrigger asChild>
|
|
162
|
+
<Button variant={variant} size={size} className={cn(className)}>
|
|
163
|
+
{currentItem?.icon}
|
|
164
|
+
{showLabel && <span className="ml-2">{currentItem?.label}</span>}
|
|
165
|
+
<span className="sr-only">Toggle theme</span>
|
|
166
|
+
</Button>
|
|
167
|
+
</DropdownMenuTrigger>
|
|
168
|
+
<DropdownMenuContent align={align}>
|
|
169
|
+
{items.map((item) => (
|
|
170
|
+
<DropdownMenuItem
|
|
171
|
+
key={item.mode}
|
|
172
|
+
onClick={() => setPreference(item.mode)}
|
|
173
|
+
className="flex items-center gap-2"
|
|
174
|
+
>
|
|
175
|
+
{item.icon}
|
|
176
|
+
<span>{item.label}</span>
|
|
177
|
+
{preference === item.mode && (
|
|
178
|
+
<span className="ml-auto text-xs opacity-60">✓</span>
|
|
179
|
+
)}
|
|
180
|
+
</DropdownMenuItem>
|
|
181
|
+
))}
|
|
182
|
+
</DropdownMenuContent>
|
|
183
|
+
</DropdownMenu>
|
|
184
|
+
);
|
|
185
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme - Prevents flash of unstyled content (FOUC) during theme initialization
|
|
3
|
+
*
|
|
4
|
+
* A critical component that prevents the flash of incorrect theme styling by applying
|
|
5
|
+
* the user's preferred theme before the page renders. This component should be placed
|
|
6
|
+
* once at the root of your application, ideally in the document head or at the very
|
|
7
|
+
* beginning of the body.
|
|
8
|
+
*
|
|
9
|
+
* The component works by injecting a small inline script that:
|
|
10
|
+
* 1. Reads the stored theme preference from cookies
|
|
11
|
+
* 2. Detects system theme preference if set to "system"
|
|
12
|
+
* 3. Applies the appropriate theme class and color scheme immediately
|
|
13
|
+
* 4. Executes synchronously before first paint to prevent FOUC
|
|
14
|
+
*
|
|
15
|
+
* @component
|
|
16
|
+
* @example Basic usage in Next.js App Router
|
|
17
|
+
* ```tsx
|
|
18
|
+
* import { Theme } from "@neynar/ui";
|
|
19
|
+
*
|
|
20
|
+
* export default function RootLayout({ children }) {
|
|
21
|
+
* return (
|
|
22
|
+
* <html lang="en" suppressHydrationWarning>
|
|
23
|
+
* <head>
|
|
24
|
+
* <Theme />
|
|
25
|
+
* </head>
|
|
26
|
+
* <body>{children}</body>
|
|
27
|
+
* </html>
|
|
28
|
+
* );
|
|
29
|
+
* }
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @example Usage in Vite/React
|
|
33
|
+
* ```tsx
|
|
34
|
+
* import { Theme } from "@neynar/ui";
|
|
35
|
+
*
|
|
36
|
+
* function App() {
|
|
37
|
+
* return (
|
|
38
|
+
* <>
|
|
39
|
+
* <Theme />
|
|
40
|
+
* <div className="app">
|
|
41
|
+
* App content here
|
|
42
|
+
* </div>
|
|
43
|
+
* </>
|
|
44
|
+
* );
|
|
45
|
+
* }
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* @example Usage with Remix
|
|
49
|
+
* ```tsx
|
|
50
|
+
* import { Theme } from "@neynar/ui";
|
|
51
|
+
*
|
|
52
|
+
* export default function Root() {
|
|
53
|
+
* return (
|
|
54
|
+
* <html lang="en">
|
|
55
|
+
* <head>
|
|
56
|
+
* <Theme />
|
|
57
|
+
* <Meta />
|
|
58
|
+
* <Links />
|
|
59
|
+
* </head>
|
|
60
|
+
* <body>
|
|
61
|
+
* <Outlet />
|
|
62
|
+
* <ScrollRestoration />
|
|
63
|
+
* <Scripts />
|
|
64
|
+
* </body>
|
|
65
|
+
* </html>
|
|
66
|
+
* );
|
|
67
|
+
* }
|
|
68
|
+
* ```
|
|
69
|
+
*
|
|
70
|
+
* @features
|
|
71
|
+
* - **FOUC Prevention**: Applies theme before first paint
|
|
72
|
+
* - **Framework Agnostic**: Works with Next.js, Vite, Remix, and other React frameworks
|
|
73
|
+
* - **SSR Compatible**: Handles server-side rendering correctly
|
|
74
|
+
* - **System Detection**: Automatically follows OS dark/light preference
|
|
75
|
+
* - **Cookie Persistence**: Maintains theme across browser sessions
|
|
76
|
+
* - **Minimal Overhead**: Tiny inline script with no external dependencies
|
|
77
|
+
*
|
|
78
|
+
* @accessibility
|
|
79
|
+
* - Respects system preference for reduced motion
|
|
80
|
+
* - Maintains proper color contrast ratios
|
|
81
|
+
* - Works with screen readers and assistive technology
|
|
82
|
+
* - Follows WCAG guidelines for theme switching
|
|
83
|
+
*
|
|
84
|
+
* @performance
|
|
85
|
+
* - Executes before React hydration
|
|
86
|
+
* - No network requests or external dependencies
|
|
87
|
+
* - Minified inline script for optimal performance
|
|
88
|
+
* - Zero runtime overhead after initial execution
|
|
89
|
+
*
|
|
90
|
+
* @security
|
|
91
|
+
* - Uses dangerouslySetInnerHTML with known safe content
|
|
92
|
+
* - No external script sources or eval usage
|
|
93
|
+
* - Cookie reading with proper error handling
|
|
94
|
+
* - Graceful fallback on script execution errors
|
|
95
|
+
*
|
|
96
|
+
* @remarks
|
|
97
|
+
* This component must be used alongside the {@link useTheme} hook and
|
|
98
|
+
* {@link ThemeToggle} component for a complete theming solution. The script
|
|
99
|
+
* handles initial theme application, while the hook manages runtime theme changes.
|
|
100
|
+
*
|
|
101
|
+
* The component is designed to work with Tailwind CSS's dark mode strategy,
|
|
102
|
+
* specifically the 'dark' class approach. It applies the 'dark' class to the
|
|
103
|
+
* document root and sets the appropriate color-scheme CSS property.
|
|
104
|
+
*
|
|
105
|
+
* @see {@link useTheme} - Hook for managing theme state and preferences
|
|
106
|
+
* @see {@link ThemeToggle} - UI component for theme switching
|
|
107
|
+
* @since 1.0.0
|
|
108
|
+
*/
|
|
109
|
+
export function Theme() {
|
|
110
|
+
return (
|
|
111
|
+
<script
|
|
112
|
+
dangerouslySetInnerHTML={{
|
|
113
|
+
__html: `
|
|
114
|
+
(function() {
|
|
115
|
+
try {
|
|
116
|
+
// Read theme preference from cookie
|
|
117
|
+
var cookie = document.cookie.match(/theme=([^;]*)/);
|
|
118
|
+
var stored = cookie ? JSON.parse(decodeURIComponent(cookie[1])) : null;
|
|
119
|
+
var preference = stored ? stored.preference : 'system';
|
|
120
|
+
var mode;
|
|
121
|
+
|
|
122
|
+
// Determine the active theme mode
|
|
123
|
+
if (preference === 'system') {
|
|
124
|
+
mode = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
125
|
+
} else {
|
|
126
|
+
mode = preference;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Apply theme to document root
|
|
130
|
+
if (mode === 'dark') {
|
|
131
|
+
document.documentElement.classList.add('dark');
|
|
132
|
+
} else {
|
|
133
|
+
document.documentElement.classList.remove('dark');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Set color-scheme for native elements
|
|
137
|
+
document.documentElement.style.colorScheme = mode;
|
|
138
|
+
} catch (e) {
|
|
139
|
+
// Graceful fallback - default to light theme
|
|
140
|
+
document.documentElement.classList.remove('dark');
|
|
141
|
+
document.documentElement.style.colorScheme = 'light';
|
|
142
|
+
}
|
|
143
|
+
})();
|
|
144
|
+
`,
|
|
145
|
+
}}
|
|
146
|
+
/>
|
|
147
|
+
);
|
|
148
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group";
|
|
3
|
+
import { type VariantProps } from "class-variance-authority";
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
import { toggleVariants } from "@/components/ui/toggle";
|
|
7
|
+
|
|
8
|
+
const ToggleGroupContext = React.createContext<
|
|
9
|
+
VariantProps<typeof toggleVariants>
|
|
10
|
+
>({
|
|
11
|
+
size: "default",
|
|
12
|
+
variant: "default",
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Toggle Group - A group of toggle buttons with single or multiple selection
|
|
17
|
+
*
|
|
18
|
+
* ToggleGroup provides a way to group related toggle buttons together, allowing users
|
|
19
|
+
* to select from a set of mutually exclusive options (single selection) or independent
|
|
20
|
+
* options (multiple selection). Built on Radix UI Toggle Group primitive with support
|
|
21
|
+
* for keyboard navigation and accessibility features.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```tsx
|
|
25
|
+
* // Single selection (like radio buttons)
|
|
26
|
+
* <ToggleGroup type="single" defaultValue="center">
|
|
27
|
+
* <ToggleGroupItem value="left" aria-label="Align left">
|
|
28
|
+
* <AlignLeft className="h-4 w-4" />
|
|
29
|
+
* </ToggleGroupItem>
|
|
30
|
+
* <ToggleGroupItem value="center" aria-label="Align center">
|
|
31
|
+
* <AlignCenter className="h-4 w-4" />
|
|
32
|
+
* </ToggleGroupItem>
|
|
33
|
+
* <ToggleGroupItem value="right" aria-label="Align right">
|
|
34
|
+
* <AlignRight className="h-4 w-4" />
|
|
35
|
+
* </ToggleGroupItem>
|
|
36
|
+
* </ToggleGroup>
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```tsx
|
|
41
|
+
* // Multiple selection (like checkboxes)
|
|
42
|
+
* <ToggleGroup type="multiple" defaultValue={["bold", "italic"]}>
|
|
43
|
+
* <ToggleGroupItem value="bold" aria-label="Toggle bold">
|
|
44
|
+
* <Bold className="h-4 w-4" />
|
|
45
|
+
* </ToggleGroupItem>
|
|
46
|
+
* <ToggleGroupItem value="italic" aria-label="Toggle italic">
|
|
47
|
+
* <Italic className="h-4 w-4" />
|
|
48
|
+
* </ToggleGroupItem>
|
|
49
|
+
* <ToggleGroupItem value="underline" aria-label="Toggle underline">
|
|
50
|
+
* <Underline className="h-4 w-4" />
|
|
51
|
+
* </ToggleGroupItem>
|
|
52
|
+
* </ToggleGroup>
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```tsx
|
|
57
|
+
* // Text-based options with variants
|
|
58
|
+
* <ToggleGroup type="single" variant="outline" size="sm">
|
|
59
|
+
* <ToggleGroupItem value="draft">Draft</ToggleGroupItem>
|
|
60
|
+
* <ToggleGroupItem value="published">Published</ToggleGroupItem>
|
|
61
|
+
* <ToggleGroupItem value="archived">Archived</ToggleGroupItem>
|
|
62
|
+
* </ToggleGroup>
|
|
63
|
+
* ```
|
|
64
|
+
*
|
|
65
|
+
* @param type - Selection mode: "single" for radio-like behavior, "multiple" for checkbox-like
|
|
66
|
+
* @param variant - Visual style: "default" (subtle) or "outline" (bordered)
|
|
67
|
+
* @param size - Size variant: "sm", "default", or "lg"
|
|
68
|
+
* @param disabled - Whether the entire group is disabled
|
|
69
|
+
* @param defaultValue - Initial selected value(s) - string for single, array for multiple
|
|
70
|
+
* @param value - Controlled selected value(s) - string for single, array for multiple
|
|
71
|
+
* @param onValueChange - Callback when selection changes
|
|
72
|
+
*
|
|
73
|
+
* @accessibility
|
|
74
|
+
* - Uses role="group" with appropriate ARIA attributes
|
|
75
|
+
* - Arrow keys navigate between items with roving tabindex
|
|
76
|
+
* - Space or Enter key toggles item selection
|
|
77
|
+
* - Home/End keys jump to first/last items
|
|
78
|
+
* - Supports disabled state for individual items or entire group
|
|
79
|
+
* - Screen readers announce selection state changes
|
|
80
|
+
*
|
|
81
|
+
* @see {@link ToggleGroupItem} - Individual toggle items within the group
|
|
82
|
+
* @see {@link Toggle} - Standalone toggle button component
|
|
83
|
+
* @see {@link https://ui.shadcn.com/docs/components/toggle-group} - Design system documentation
|
|
84
|
+
* @since 1.0.0
|
|
85
|
+
*/
|
|
86
|
+
function ToggleGroup({
|
|
87
|
+
className,
|
|
88
|
+
variant,
|
|
89
|
+
size,
|
|
90
|
+
children,
|
|
91
|
+
...props
|
|
92
|
+
}: React.ComponentProps<typeof ToggleGroupPrimitive.Root> &
|
|
93
|
+
VariantProps<typeof toggleVariants>) {
|
|
94
|
+
return (
|
|
95
|
+
<ToggleGroupPrimitive.Root
|
|
96
|
+
data-slot="toggle-group"
|
|
97
|
+
data-variant={variant}
|
|
98
|
+
data-size={size}
|
|
99
|
+
className={cn(
|
|
100
|
+
"group/toggle-group flex w-fit items-center rounded-md data-[variant=outline]:shadow-xs",
|
|
101
|
+
className,
|
|
102
|
+
)}
|
|
103
|
+
{...props}
|
|
104
|
+
>
|
|
105
|
+
<ToggleGroupContext.Provider value={{ variant, size }}>
|
|
106
|
+
{children}
|
|
107
|
+
</ToggleGroupContext.Provider>
|
|
108
|
+
</ToggleGroupPrimitive.Root>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Toggle Group Item - Individual selectable item within a ToggleGroup
|
|
114
|
+
*
|
|
115
|
+
* Represents a single option within a ToggleGroup component. Automatically inherits
|
|
116
|
+
* variant and size styling from the parent ToggleGroup context, but these can be
|
|
117
|
+
* overridden for specific items if needed. Supports icons, text, or mixed content.
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```tsx
|
|
121
|
+
* // Text-based item
|
|
122
|
+
* <ToggleGroupItem value="draft">Draft</ToggleGroupItem>
|
|
123
|
+
* ```
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```tsx
|
|
127
|
+
* // Icon-only item (requires aria-label)
|
|
128
|
+
* <ToggleGroupItem value="bold" aria-label="Toggle bold formatting">
|
|
129
|
+
* <Bold className="h-4 w-4" />
|
|
130
|
+
* </ToggleGroupItem>
|
|
131
|
+
* ```
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* ```tsx
|
|
135
|
+
* // Mixed content with icon and text
|
|
136
|
+
* <ToggleGroupItem value="bold">
|
|
137
|
+
* <Bold className="h-4 w-4" />
|
|
138
|
+
* Bold
|
|
139
|
+
* </ToggleGroupItem>
|
|
140
|
+
* ```
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* ```tsx
|
|
144
|
+
* // Override inherited styling
|
|
145
|
+
* <ToggleGroupItem value="special" variant="outline" size="lg">
|
|
146
|
+
* Special Option
|
|
147
|
+
* </ToggleGroupItem>
|
|
148
|
+
* ```
|
|
149
|
+
*
|
|
150
|
+
* @param value - Unique identifier for this item within the group (required)
|
|
151
|
+
* @param disabled - Whether this specific item is disabled
|
|
152
|
+
* @param variant - Override variant from parent ToggleGroup
|
|
153
|
+
* @param size - Override size from parent ToggleGroup
|
|
154
|
+
* @param children - Content to display (icons, text, or both)
|
|
155
|
+
*
|
|
156
|
+
* @accessibility
|
|
157
|
+
* - Participates in parent ToggleGroup's keyboard navigation
|
|
158
|
+
* - Uses aria-pressed to indicate selection state
|
|
159
|
+
* - Supports focus management with visible focus indicators
|
|
160
|
+
* - For icon-only items, always provide aria-label for screen readers
|
|
161
|
+
* - Disabled items are excluded from keyboard navigation
|
|
162
|
+
*
|
|
163
|
+
* @see {@link ToggleGroup} - Parent container component
|
|
164
|
+
* @see {@link Toggle} - Standalone toggle button
|
|
165
|
+
*/
|
|
166
|
+
function ToggleGroupItem({
|
|
167
|
+
className,
|
|
168
|
+
children,
|
|
169
|
+
variant,
|
|
170
|
+
size,
|
|
171
|
+
...props
|
|
172
|
+
}: React.ComponentProps<typeof ToggleGroupPrimitive.Item> &
|
|
173
|
+
VariantProps<typeof toggleVariants>) {
|
|
174
|
+
const context = React.useContext(ToggleGroupContext);
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<ToggleGroupPrimitive.Item
|
|
178
|
+
data-slot="toggle-group-item"
|
|
179
|
+
data-variant={context.variant || variant}
|
|
180
|
+
data-size={context.size || size}
|
|
181
|
+
className={cn(
|
|
182
|
+
toggleVariants({
|
|
183
|
+
variant: context.variant || variant,
|
|
184
|
+
size: context.size || size,
|
|
185
|
+
}),
|
|
186
|
+
"min-w-0 flex-1 shrink-0 rounded-none shadow-none first:rounded-l-md last:rounded-r-md focus:z-10 focus-visible:z-10 data-[variant=outline]:border-l-0 data-[variant=outline]:first:border-l",
|
|
187
|
+
className,
|
|
188
|
+
)}
|
|
189
|
+
{...props}
|
|
190
|
+
>
|
|
191
|
+
{children}
|
|
192
|
+
</ToggleGroupPrimitive.Item>
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export { ToggleGroup, ToggleGroupItem };
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as TogglePrimitive from "@radix-ui/react-toggle";
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
const toggleVariants = cva(
|
|
8
|
+
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none transition-[color,box-shadow] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default: "bg-transparent",
|
|
13
|
+
outline:
|
|
14
|
+
"border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground",
|
|
15
|
+
},
|
|
16
|
+
size: {
|
|
17
|
+
default: "h-9 px-2 min-w-9",
|
|
18
|
+
sm: "h-8 px-1.5 min-w-8",
|
|
19
|
+
lg: "h-10 px-2.5 min-w-10",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
defaultVariants: {
|
|
23
|
+
variant: "default",
|
|
24
|
+
size: "default",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* A two-state button that can be either on or off
|
|
31
|
+
*
|
|
32
|
+
* The Toggle component provides an accessible button with on/off state functionality.
|
|
33
|
+
* Built on Radix UI Toggle primitives with customizable variants and sizes.
|
|
34
|
+
* Ideal for text formatting controls, settings toggles, and any binary state controls.
|
|
35
|
+
*
|
|
36
|
+
* @example Basic usage with icon
|
|
37
|
+
* ```tsx
|
|
38
|
+
* import { Bold } from "lucide-react"
|
|
39
|
+
*
|
|
40
|
+
* <Toggle aria-label="Toggle bold">
|
|
41
|
+
* <Bold className="h-4 w-4" />
|
|
42
|
+
* </Toggle>
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @example Controlled toggle with state management
|
|
46
|
+
* ```tsx
|
|
47
|
+
* const [isBold, setIsBold] = useState(false)
|
|
48
|
+
*
|
|
49
|
+
* <Toggle
|
|
50
|
+
* pressed={isBold}
|
|
51
|
+
* onPressedChange={setIsBold}
|
|
52
|
+
* aria-label="Toggle bold formatting"
|
|
53
|
+
* >
|
|
54
|
+
* <Bold className="h-4 w-4" />
|
|
55
|
+
* </Toggle>
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* @example With text and outline variant
|
|
59
|
+
* ```tsx
|
|
60
|
+
* <Toggle variant="outline" size="lg">
|
|
61
|
+
* <Italic className="h-4 w-4" />
|
|
62
|
+
* Italic
|
|
63
|
+
* </Toggle>
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* @example Text formatting toolbar
|
|
67
|
+
* ```tsx
|
|
68
|
+
* <div className="flex gap-1">
|
|
69
|
+
* <Toggle pressed={bold} onPressedChange={setBold}>
|
|
70
|
+
* <Bold className="h-4 w-4" />
|
|
71
|
+
* </Toggle>
|
|
72
|
+
* <Toggle pressed={italic} onPressedChange={setItalic}>
|
|
73
|
+
* <Italic className="h-4 w-4" />
|
|
74
|
+
* </Toggle>
|
|
75
|
+
* </div>
|
|
76
|
+
* ```
|
|
77
|
+
*
|
|
78
|
+
* @accessibility
|
|
79
|
+
* - Supports keyboard navigation (Space/Enter to toggle)
|
|
80
|
+
* - Automatically manages aria-pressed attribute
|
|
81
|
+
* - Requires aria-label for icon-only toggles
|
|
82
|
+
* - Focus management with visible focus indicators
|
|
83
|
+
* - Disabled state properly communicated to assistive technology
|
|
84
|
+
*
|
|
85
|
+
* @see {@link https://ui.shadcn.com/docs/components/toggle} shadcn/ui Toggle documentation
|
|
86
|
+
* @since 1.0.0
|
|
87
|
+
* @see {@link ToggleGroup} For mutually exclusive toggle groups
|
|
88
|
+
*/
|
|
89
|
+
/**
|
|
90
|
+
* Toggle component props
|
|
91
|
+
*
|
|
92
|
+
* @param className - Additional CSS classes to apply
|
|
93
|
+
* @param variant - Visual style variant ("default" | "outline")
|
|
94
|
+
* @param size - Size variant ("sm" | "default" | "lg")
|
|
95
|
+
* @param pressed - Controlled pressed state
|
|
96
|
+
* @param defaultPressed - Default pressed state for uncontrolled usage
|
|
97
|
+
* @param onPressedChange - Callback fired when pressed state changes
|
|
98
|
+
* @param disabled - Whether the toggle is disabled
|
|
99
|
+
* @param children - Toggle content (typically icons or text)
|
|
100
|
+
* @param ...props - Additional props passed to Radix Toggle primitive
|
|
101
|
+
*/
|
|
102
|
+
type ToggleProps = React.ComponentProps<typeof TogglePrimitive.Root> &
|
|
103
|
+
VariantProps<typeof toggleVariants>;
|
|
104
|
+
|
|
105
|
+
function Toggle({ className, variant, size, ...props }: ToggleProps) {
|
|
106
|
+
return (
|
|
107
|
+
<TogglePrimitive.Root
|
|
108
|
+
data-slot="toggle"
|
|
109
|
+
className={cn(toggleVariants({ variant, size, className }))}
|
|
110
|
+
{...props}
|
|
111
|
+
/>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export { Toggle, toggleVariants };
|