@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,802 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as RechartsPrimitive from "recharts";
|
|
3
|
+
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
|
|
6
|
+
// Format: { THEME_NAME: CSS_SELECTOR }
|
|
7
|
+
const THEMES = { light: "", dark: ".dark" } as const;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Configuration object for chart data series
|
|
11
|
+
*
|
|
12
|
+
* Defines labels, colors, and icons for chart data series with support for
|
|
13
|
+
* both static colors and theme-aware color definitions. Each series can have
|
|
14
|
+
* either a static color or theme-specific colors for light/dark modes.
|
|
15
|
+
* This configuration drives both visual styling and accessibility features
|
|
16
|
+
* throughout the chart ecosystem.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```tsx
|
|
20
|
+
* // Static color configuration with icon
|
|
21
|
+
* const chartConfig = {
|
|
22
|
+
* revenue: {
|
|
23
|
+
* label: "Revenue",
|
|
24
|
+
* color: "hsl(var(--chart-1))",
|
|
25
|
+
* icon: DollarSignIcon,
|
|
26
|
+
* },
|
|
27
|
+
* } satisfies ChartConfig
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```tsx
|
|
32
|
+
* // Theme-aware color configuration for dark/light modes
|
|
33
|
+
* const chartConfig = {
|
|
34
|
+
* users: {
|
|
35
|
+
* label: "Active Users",
|
|
36
|
+
* theme: {
|
|
37
|
+
* light: "#0ea5e9",
|
|
38
|
+
* dark: "#0284c7",
|
|
39
|
+
* },
|
|
40
|
+
* },
|
|
41
|
+
* } satisfies ChartConfig
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```tsx
|
|
46
|
+
* // Complex configuration with multiple series
|
|
47
|
+
* const chartConfig = {
|
|
48
|
+
* desktop: {
|
|
49
|
+
* label: "Desktop Users",
|
|
50
|
+
* color: "var(--chart-1)",
|
|
51
|
+
* icon: MonitorIcon,
|
|
52
|
+
* },
|
|
53
|
+
* mobile: {
|
|
54
|
+
* label: "Mobile Users",
|
|
55
|
+
* color: "var(--chart-2)",
|
|
56
|
+
* icon: SmartphoneIcon,
|
|
57
|
+
* },
|
|
58
|
+
* revenue: {
|
|
59
|
+
* label: "Revenue ($)",
|
|
60
|
+
* theme: {
|
|
61
|
+
* light: "#059669",
|
|
62
|
+
* dark: "#10b981",
|
|
63
|
+
* },
|
|
64
|
+
* },
|
|
65
|
+
* } satisfies ChartConfig
|
|
66
|
+
* ```
|
|
67
|
+
*
|
|
68
|
+
* @since 1.0.0
|
|
69
|
+
*/
|
|
70
|
+
export type ChartConfig = {
|
|
71
|
+
[k in string]: {
|
|
72
|
+
label?: React.ReactNode;
|
|
73
|
+
icon?: React.ComponentType;
|
|
74
|
+
} & (
|
|
75
|
+
| { color?: string; theme?: never }
|
|
76
|
+
| { color?: never; theme: Record<keyof typeof THEMES, string> }
|
|
77
|
+
);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
type ChartContextProps = {
|
|
81
|
+
config: ChartConfig;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const ChartContext = React.createContext<ChartContextProps | null>(null);
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Hook to access chart configuration context
|
|
88
|
+
*
|
|
89
|
+
* Provides access to the chart configuration within chart components.
|
|
90
|
+
* Must be used within a ChartContainer component to access the chart config.
|
|
91
|
+
*
|
|
92
|
+
* @returns Chart configuration context containing config object
|
|
93
|
+
* @throws Error when used outside of ChartContainer
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```tsx
|
|
97
|
+
* function CustomTooltip() {
|
|
98
|
+
* const { config } = useChart()
|
|
99
|
+
*
|
|
100
|
+
* return (
|
|
101
|
+
* <div>
|
|
102
|
+
* {Object.entries(config).map(([key, series]) => (
|
|
103
|
+
* <div key={key} style={{ color: series.color }}>
|
|
104
|
+
* {series.label}
|
|
105
|
+
* </div>
|
|
106
|
+
* ))}
|
|
107
|
+
* </div>
|
|
108
|
+
* )
|
|
109
|
+
* }
|
|
110
|
+
* ```
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```tsx
|
|
114
|
+
* function CustomChartComponent() {
|
|
115
|
+
* const { config } = useChart()
|
|
116
|
+
* const seriesConfig = config.revenue
|
|
117
|
+
*
|
|
118
|
+
* return (
|
|
119
|
+
* <div>
|
|
120
|
+
* <seriesConfig.icon />
|
|
121
|
+
* <span>{seriesConfig.label}</span>
|
|
122
|
+
* </div>
|
|
123
|
+
* )
|
|
124
|
+
* }
|
|
125
|
+
* ```
|
|
126
|
+
*
|
|
127
|
+
* @since 1.0.0
|
|
128
|
+
*/
|
|
129
|
+
function useChart() {
|
|
130
|
+
const context = React.useContext(ChartContext);
|
|
131
|
+
|
|
132
|
+
if (!context) {
|
|
133
|
+
throw new Error("useChart must be used within a <ChartContainer />");
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return context;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Container component for Recharts charts with theming and configuration
|
|
141
|
+
*
|
|
142
|
+
* The ChartContainer provides a responsive wrapper for Recharts components with
|
|
143
|
+
* automatic theming, color management, and configuration. It generates CSS custom
|
|
144
|
+
* properties for chart colors, handles dark mode support, and applies consistent
|
|
145
|
+
* styling across all chart types. Built on the shadcn/ui design system with
|
|
146
|
+
* Recharts as the underlying charting library.
|
|
147
|
+
*
|
|
148
|
+
* @component
|
|
149
|
+
* @example
|
|
150
|
+
* ```tsx
|
|
151
|
+
* // Basic line chart with accessibility and tooltips
|
|
152
|
+
* const chartData = [
|
|
153
|
+
* { month: "Jan", revenue: 2000, profit: 400 },
|
|
154
|
+
* { month: "Feb", revenue: 2400, profit: 600 },
|
|
155
|
+
* { month: "Mar", revenue: 3200, profit: 900 },
|
|
156
|
+
* ]
|
|
157
|
+
*
|
|
158
|
+
* const chartConfig = {
|
|
159
|
+
* revenue: {
|
|
160
|
+
* label: "Revenue",
|
|
161
|
+
* color: "hsl(var(--chart-1))",
|
|
162
|
+
* },
|
|
163
|
+
* profit: {
|
|
164
|
+
* label: "Profit",
|
|
165
|
+
* color: "hsl(var(--chart-2))",
|
|
166
|
+
* },
|
|
167
|
+
* } satisfies ChartConfig
|
|
168
|
+
*
|
|
169
|
+
* <ChartContainer config={chartConfig} className="min-h-[300px]">
|
|
170
|
+
* <LineChart data={chartData} accessibilityLayer>
|
|
171
|
+
* <CartesianGrid strokeDasharray="3 3" />
|
|
172
|
+
* <XAxis dataKey="month" />
|
|
173
|
+
* <YAxis />
|
|
174
|
+
* <ChartTooltip content={<ChartTooltipContent />} />
|
|
175
|
+
* <Line
|
|
176
|
+
* type="monotone"
|
|
177
|
+
* dataKey="revenue"
|
|
178
|
+
* stroke="var(--color-revenue)"
|
|
179
|
+
* strokeWidth={2}
|
|
180
|
+
* />
|
|
181
|
+
* <Line
|
|
182
|
+
* type="monotone"
|
|
183
|
+
* dataKey="profit"
|
|
184
|
+
* stroke="var(--color-profit)"
|
|
185
|
+
* strokeWidth={2}
|
|
186
|
+
* />
|
|
187
|
+
* </LineChart>
|
|
188
|
+
* </ChartContainer>
|
|
189
|
+
* ```
|
|
190
|
+
*
|
|
191
|
+
* @example
|
|
192
|
+
* ```tsx
|
|
193
|
+
* // Bar chart with theme-aware colors and custom formatting
|
|
194
|
+
* <ChartContainer
|
|
195
|
+
* config={{
|
|
196
|
+
* sales: {
|
|
197
|
+
* label: "Sales",
|
|
198
|
+
* theme: {
|
|
199
|
+
* light: "#0ea5e9",
|
|
200
|
+
* dark: "#0284c7",
|
|
201
|
+
* },
|
|
202
|
+
* },
|
|
203
|
+
* }}
|
|
204
|
+
* className="h-[400px]"
|
|
205
|
+
* >
|
|
206
|
+
* <BarChart data={data} accessibilityLayer>
|
|
207
|
+
* <XAxis dataKey="month" />
|
|
208
|
+
* <YAxis tickFormatter={(value) => `$${value.toLocaleString()}`} />
|
|
209
|
+
* <ChartTooltip content={<ChartTooltipContent />} />
|
|
210
|
+
* <Bar dataKey="sales" fill="var(--color-sales)" radius={4} />
|
|
211
|
+
* </BarChart>
|
|
212
|
+
* </ChartContainer>
|
|
213
|
+
* ```
|
|
214
|
+
*
|
|
215
|
+
* @example
|
|
216
|
+
* ```tsx
|
|
217
|
+
* // Pie chart with legend and icons for device breakdown
|
|
218
|
+
* <ChartContainer config={{
|
|
219
|
+
* desktop: {
|
|
220
|
+
* label: "Desktop",
|
|
221
|
+
* color: "var(--chart-1)",
|
|
222
|
+
* icon: MonitorIcon,
|
|
223
|
+
* },
|
|
224
|
+
* mobile: {
|
|
225
|
+
* label: "Mobile",
|
|
226
|
+
* color: "var(--chart-2)",
|
|
227
|
+
* icon: SmartphoneIcon,
|
|
228
|
+
* },
|
|
229
|
+
* }}>
|
|
230
|
+
* <PieChart>
|
|
231
|
+
* <Pie data={data} dataKey="value" nameKey="device" />
|
|
232
|
+
* <ChartTooltip content={<ChartTooltipContent />} />
|
|
233
|
+
* <ChartLegend content={<ChartLegendContent />} />
|
|
234
|
+
* </PieChart>
|
|
235
|
+
* </ChartContainer>
|
|
236
|
+
* ```
|
|
237
|
+
*
|
|
238
|
+
* @accessibility
|
|
239
|
+
* **Core Accessibility Features:**
|
|
240
|
+
* - Responsive container adapts to screen size automatically
|
|
241
|
+
* - Theme-aware color system designed for WCAG contrast compliance
|
|
242
|
+
* - Supports Recharts' `accessibilityLayer` prop for keyboard navigation
|
|
243
|
+
* - High contrast colors available in both light and dark themes
|
|
244
|
+
* - Semantic HTML structure for screen reader compatibility
|
|
245
|
+
*
|
|
246
|
+
* **Important Accessibility Notes:**
|
|
247
|
+
* - Always include `accessibilityLayer` prop on Recharts components for keyboard access
|
|
248
|
+
* - Consider adding `aria-label` or `title` attributes for chart context
|
|
249
|
+
* - Ensure color is not the only way to distinguish data (use patterns, labels, or icons)
|
|
250
|
+
* - Test with screen readers as automatic data announcement may be limited
|
|
251
|
+
* - Provide alternative data representations (tables, text summaries) for complex charts
|
|
252
|
+
*
|
|
253
|
+
* **Keyboard Navigation:**
|
|
254
|
+
* - Arrow keys navigate between data points when `accessibilityLayer` is enabled
|
|
255
|
+
* - Tab key moves focus to interactive chart elements
|
|
256
|
+
* - Screen reader users should be provided with data summaries or alternative formats
|
|
257
|
+
*
|
|
258
|
+
* @performance
|
|
259
|
+
* - Use appropriate chart types for data size (avoid complex charts with large datasets)
|
|
260
|
+
* - Consider virtualization for charts with many data points
|
|
261
|
+
* - CSS variables are generated only for configured colors to minimize style overhead
|
|
262
|
+
*
|
|
263
|
+
* @see {@link https://ui.shadcn.com/docs/components/chart} shadcn/ui Chart documentation
|
|
264
|
+
* @see {@link https://recharts.org/en-US/api} Recharts API documentation
|
|
265
|
+
* @see {@link ChartTooltip} Tooltip component for interactive data display
|
|
266
|
+
* @see {@link ChartLegend} Legend component for series identification
|
|
267
|
+
* @see {@link useChart} Hook to access chart configuration
|
|
268
|
+
* @since 1.0.0
|
|
269
|
+
*/
|
|
270
|
+
function ChartContainer({
|
|
271
|
+
id,
|
|
272
|
+
className,
|
|
273
|
+
children,
|
|
274
|
+
config,
|
|
275
|
+
...props
|
|
276
|
+
}: React.ComponentProps<"div"> & {
|
|
277
|
+
/**
|
|
278
|
+
* Chart configuration defining series labels and colors
|
|
279
|
+
*/
|
|
280
|
+
config: ChartConfig;
|
|
281
|
+
/**
|
|
282
|
+
* Recharts component(s) to render
|
|
283
|
+
*/
|
|
284
|
+
children: React.ComponentProps<
|
|
285
|
+
typeof RechartsPrimitive.ResponsiveContainer
|
|
286
|
+
>["children"];
|
|
287
|
+
}) {
|
|
288
|
+
const uniqueId = React.useId();
|
|
289
|
+
const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`;
|
|
290
|
+
|
|
291
|
+
return (
|
|
292
|
+
<ChartContext.Provider value={{ config }}>
|
|
293
|
+
<div
|
|
294
|
+
data-slot="chart"
|
|
295
|
+
data-chart={chartId}
|
|
296
|
+
className={cn(
|
|
297
|
+
"[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border flex aspect-video justify-center text-xs [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden",
|
|
298
|
+
className,
|
|
299
|
+
)}
|
|
300
|
+
{...props}
|
|
301
|
+
>
|
|
302
|
+
<ChartStyle id={chartId} config={config} />
|
|
303
|
+
<RechartsPrimitive.ResponsiveContainer>
|
|
304
|
+
{children}
|
|
305
|
+
</RechartsPrimitive.ResponsiveContainer>
|
|
306
|
+
</div>
|
|
307
|
+
</ChartContext.Provider>
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Internal component that generates CSS custom properties for chart colors
|
|
313
|
+
*
|
|
314
|
+
* Creates CSS variables based on the chart configuration to enable theme-aware
|
|
315
|
+
* coloring. Generates variables for both light and dark themes when theme
|
|
316
|
+
* configurations are provided, or uses static colors when available.
|
|
317
|
+
*
|
|
318
|
+
* @component
|
|
319
|
+
* @internal
|
|
320
|
+
* @param id - Unique identifier for the chart instance
|
|
321
|
+
* @param config - Chart configuration with color definitions
|
|
322
|
+
* @since 1.0.0
|
|
323
|
+
*/
|
|
324
|
+
function ChartStyle({ id, config }: { id: string; config: ChartConfig }) {
|
|
325
|
+
const colorConfig = Object.entries(config).filter(
|
|
326
|
+
([, config]) => config.theme || config.color,
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
if (!colorConfig.length) {
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return (
|
|
334
|
+
<style
|
|
335
|
+
dangerouslySetInnerHTML={{
|
|
336
|
+
__html: Object.entries(THEMES)
|
|
337
|
+
.map(
|
|
338
|
+
([theme, prefix]) => `
|
|
339
|
+
${prefix} [data-chart=${id}] {
|
|
340
|
+
${colorConfig
|
|
341
|
+
.map(([key, itemConfig]) => {
|
|
342
|
+
const color =
|
|
343
|
+
itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||
|
|
344
|
+
itemConfig.color;
|
|
345
|
+
return color ? ` --color-${key}: ${color};` : null;
|
|
346
|
+
})
|
|
347
|
+
.join("\n")}
|
|
348
|
+
}
|
|
349
|
+
`,
|
|
350
|
+
)
|
|
351
|
+
.join("\n"),
|
|
352
|
+
}}
|
|
353
|
+
/>
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Tooltip component for charts
|
|
359
|
+
*
|
|
360
|
+
* Re-export of Recharts Tooltip component with support for custom content.
|
|
361
|
+
* Use with ChartTooltipContent for consistent styling and theming integration.
|
|
362
|
+
*
|
|
363
|
+
* @component
|
|
364
|
+
* @example
|
|
365
|
+
* ```tsx
|
|
366
|
+
* // Basic tooltip
|
|
367
|
+
* <ChartTooltip content={<ChartTooltipContent />} />
|
|
368
|
+
* ```
|
|
369
|
+
*
|
|
370
|
+
* @example
|
|
371
|
+
* ```tsx
|
|
372
|
+
* // Tooltip with custom positioning
|
|
373
|
+
* <ChartTooltip
|
|
374
|
+
* content={<ChartTooltipContent />}
|
|
375
|
+
* cursor={false}
|
|
376
|
+
* position={{ x: 10, y: 10 }}
|
|
377
|
+
* />
|
|
378
|
+
* ```
|
|
379
|
+
*
|
|
380
|
+
* @see {@link ChartTooltipContent} Custom tooltip content component
|
|
381
|
+
* @since 1.0.0
|
|
382
|
+
*/
|
|
383
|
+
const ChartTooltip = RechartsPrimitive.Tooltip;
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Custom tooltip content component for charts
|
|
387
|
+
*
|
|
388
|
+
* Provides a styled tooltip that displays chart data with proper formatting,
|
|
389
|
+
* colors, and labels from the chart configuration. Supports multiple indicator
|
|
390
|
+
* styles, custom formatters, and automatic theming integration.
|
|
391
|
+
*
|
|
392
|
+
* @component
|
|
393
|
+
* @example
|
|
394
|
+
* ```tsx
|
|
395
|
+
* // Basic tooltip with default styling
|
|
396
|
+
* <ChartTooltip content={<ChartTooltipContent />} />
|
|
397
|
+
* ```
|
|
398
|
+
*
|
|
399
|
+
* @example
|
|
400
|
+
* ```tsx
|
|
401
|
+
* // Tooltip with custom value formatter
|
|
402
|
+
* <ChartTooltip
|
|
403
|
+
* content={
|
|
404
|
+
* <ChartTooltipContent
|
|
405
|
+
* formatter={(value, name) => [
|
|
406
|
+
* `$${value.toLocaleString()}`,
|
|
407
|
+
* name.toUpperCase()
|
|
408
|
+
* ]}
|
|
409
|
+
* />
|
|
410
|
+
* }
|
|
411
|
+
* />
|
|
412
|
+
* ```
|
|
413
|
+
*
|
|
414
|
+
* @example
|
|
415
|
+
* ```tsx
|
|
416
|
+
* // Tooltip with line indicator and custom label
|
|
417
|
+
* <ChartTooltip
|
|
418
|
+
* content={
|
|
419
|
+
* <ChartTooltipContent
|
|
420
|
+
* indicator="line"
|
|
421
|
+
* labelFormatter={(label) => `Week of ${label}`}
|
|
422
|
+
* hideLabel={false}
|
|
423
|
+
* />
|
|
424
|
+
* }
|
|
425
|
+
* />
|
|
426
|
+
* ```
|
|
427
|
+
*
|
|
428
|
+
* @example
|
|
429
|
+
* ```tsx
|
|
430
|
+
* // Tooltip with dashed indicator and no icons
|
|
431
|
+
* <ChartTooltip
|
|
432
|
+
* content={
|
|
433
|
+
* <ChartTooltipContent
|
|
434
|
+
* indicator="dashed"
|
|
435
|
+
* hideIndicator={false}
|
|
436
|
+
* nameKey="category"
|
|
437
|
+
* />
|
|
438
|
+
* }
|
|
439
|
+
* />
|
|
440
|
+
* ```
|
|
441
|
+
*
|
|
442
|
+
* @accessibility
|
|
443
|
+
* - High contrast colors for visibility in both themes
|
|
444
|
+
* - Proper semantic markup for screen readers
|
|
445
|
+
* - Formatted numbers with locale-aware display
|
|
446
|
+
* - Clear visual indicators for data series
|
|
447
|
+
*
|
|
448
|
+
* @since 1.0.0
|
|
449
|
+
*/
|
|
450
|
+
function ChartTooltipContent({
|
|
451
|
+
active,
|
|
452
|
+
payload,
|
|
453
|
+
className,
|
|
454
|
+
indicator = "dot",
|
|
455
|
+
hideLabel = false,
|
|
456
|
+
hideIndicator = false,
|
|
457
|
+
label,
|
|
458
|
+
labelFormatter,
|
|
459
|
+
labelClassName,
|
|
460
|
+
formatter,
|
|
461
|
+
color,
|
|
462
|
+
nameKey,
|
|
463
|
+
labelKey,
|
|
464
|
+
}: React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
|
|
465
|
+
React.ComponentProps<"div"> & {
|
|
466
|
+
/**
|
|
467
|
+
* Whether to hide the label
|
|
468
|
+
* @default false
|
|
469
|
+
*/
|
|
470
|
+
hideLabel?: boolean;
|
|
471
|
+
/**
|
|
472
|
+
* Whether to hide the color indicator
|
|
473
|
+
* @default false
|
|
474
|
+
*/
|
|
475
|
+
hideIndicator?: boolean;
|
|
476
|
+
/**
|
|
477
|
+
* Style of the color indicator
|
|
478
|
+
* @default "dot"
|
|
479
|
+
*/
|
|
480
|
+
indicator?: "line" | "dot" | "dashed";
|
|
481
|
+
/**
|
|
482
|
+
* Key to use for the name in payload
|
|
483
|
+
*/
|
|
484
|
+
nameKey?: string;
|
|
485
|
+
/**
|
|
486
|
+
* Key to use for the label in payload
|
|
487
|
+
*/
|
|
488
|
+
labelKey?: string;
|
|
489
|
+
}) {
|
|
490
|
+
const { config } = useChart();
|
|
491
|
+
|
|
492
|
+
const getTooltipLabel = () => {
|
|
493
|
+
if (hideLabel || !payload?.length) {
|
|
494
|
+
return null;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
const [item] = payload;
|
|
498
|
+
const key = `${labelKey || item?.dataKey || item?.name || "value"}`;
|
|
499
|
+
const itemConfig = getPayloadConfigFromPayload(config, item, key);
|
|
500
|
+
const value =
|
|
501
|
+
!labelKey && typeof label === "string"
|
|
502
|
+
? config[label as keyof typeof config]?.label || label
|
|
503
|
+
: itemConfig?.label;
|
|
504
|
+
|
|
505
|
+
if (labelFormatter) {
|
|
506
|
+
return (
|
|
507
|
+
<div className={cn("font-medium", labelClassName)}>
|
|
508
|
+
{labelFormatter(value, payload)}
|
|
509
|
+
</div>
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
if (!value) {
|
|
514
|
+
return null;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
return <div className={cn("font-medium", labelClassName)}>{value}</div>;
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
const tooltipLabel = getTooltipLabel();
|
|
521
|
+
|
|
522
|
+
if (!active || !payload?.length) {
|
|
523
|
+
return null;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
const nestLabel = payload.length === 1 && indicator !== "dot";
|
|
527
|
+
|
|
528
|
+
return (
|
|
529
|
+
<div
|
|
530
|
+
className={cn(
|
|
531
|
+
"border-border/50 bg-background grid min-w-[8rem] items-start gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs shadow-xl",
|
|
532
|
+
className,
|
|
533
|
+
)}
|
|
534
|
+
>
|
|
535
|
+
{!nestLabel ? tooltipLabel : null}
|
|
536
|
+
<div className="grid gap-1.5">
|
|
537
|
+
{payload.map((item, index) => {
|
|
538
|
+
const key = `${nameKey || item.name || item.dataKey || "value"}`;
|
|
539
|
+
const itemConfig = getPayloadConfigFromPayload(config, item, key);
|
|
540
|
+
const indicatorColor = color || item.payload.fill || item.color;
|
|
541
|
+
|
|
542
|
+
return (
|
|
543
|
+
<div
|
|
544
|
+
key={item.dataKey}
|
|
545
|
+
className={cn(
|
|
546
|
+
"[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5",
|
|
547
|
+
indicator === "dot" && "items-center",
|
|
548
|
+
)}
|
|
549
|
+
>
|
|
550
|
+
{formatter && item?.value !== undefined && item.name ? (
|
|
551
|
+
formatter(item.value, item.name, item, index, item.payload)
|
|
552
|
+
) : (
|
|
553
|
+
<>
|
|
554
|
+
{itemConfig?.icon ? (
|
|
555
|
+
<itemConfig.icon />
|
|
556
|
+
) : (
|
|
557
|
+
!hideIndicator && (
|
|
558
|
+
<div
|
|
559
|
+
className={cn(
|
|
560
|
+
"shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)",
|
|
561
|
+
{
|
|
562
|
+
"h-2.5 w-2.5": indicator === "dot",
|
|
563
|
+
"w-1": indicator === "line",
|
|
564
|
+
"w-0 border-[1.5px] border-dashed bg-transparent":
|
|
565
|
+
indicator === "dashed",
|
|
566
|
+
"my-0.5": nestLabel && indicator === "dashed",
|
|
567
|
+
},
|
|
568
|
+
)}
|
|
569
|
+
style={
|
|
570
|
+
{
|
|
571
|
+
"--color-bg": indicatorColor,
|
|
572
|
+
"--color-border": indicatorColor,
|
|
573
|
+
} as React.CSSProperties
|
|
574
|
+
}
|
|
575
|
+
/>
|
|
576
|
+
)
|
|
577
|
+
)}
|
|
578
|
+
<div
|
|
579
|
+
className={cn(
|
|
580
|
+
"flex flex-1 justify-between leading-none",
|
|
581
|
+
nestLabel ? "items-end" : "items-center",
|
|
582
|
+
)}
|
|
583
|
+
>
|
|
584
|
+
<div className="grid gap-1.5">
|
|
585
|
+
{nestLabel ? tooltipLabel : null}
|
|
586
|
+
<span className="text-muted-foreground">
|
|
587
|
+
{itemConfig?.label || item.name}
|
|
588
|
+
</span>
|
|
589
|
+
</div>
|
|
590
|
+
{item.value && (
|
|
591
|
+
<span className="text-foreground font-mono font-medium tabular-nums">
|
|
592
|
+
{item.value.toLocaleString()}
|
|
593
|
+
</span>
|
|
594
|
+
)}
|
|
595
|
+
</div>
|
|
596
|
+
</>
|
|
597
|
+
)}
|
|
598
|
+
</div>
|
|
599
|
+
);
|
|
600
|
+
})}
|
|
601
|
+
</div>
|
|
602
|
+
</div>
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Legend component for charts
|
|
608
|
+
*
|
|
609
|
+
* Re-export of Recharts Legend component with support for custom content.
|
|
610
|
+
* Use with ChartLegendContent for consistent styling and theming integration.
|
|
611
|
+
*
|
|
612
|
+
* @component
|
|
613
|
+
* @example
|
|
614
|
+
* ```tsx
|
|
615
|
+
* // Basic legend
|
|
616
|
+
* <ChartLegend content={<ChartLegendContent />} />
|
|
617
|
+
* ```
|
|
618
|
+
*
|
|
619
|
+
* @example
|
|
620
|
+
* ```tsx
|
|
621
|
+
* // Legend positioned at top
|
|
622
|
+
* <ChartLegend
|
|
623
|
+
* content={<ChartLegendContent />}
|
|
624
|
+
* verticalAlign="top"
|
|
625
|
+
* height={36}
|
|
626
|
+
* />
|
|
627
|
+
* ```
|
|
628
|
+
*
|
|
629
|
+
* @see {@link ChartLegendContent} Custom legend content component
|
|
630
|
+
* @since 1.0.0
|
|
631
|
+
*/
|
|
632
|
+
const ChartLegend = RechartsPrimitive.Legend;
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Custom legend content component for charts
|
|
636
|
+
*
|
|
637
|
+
* Provides a styled legend that displays chart series with colors and labels
|
|
638
|
+
* from the chart configuration. Supports icon display, custom positioning,
|
|
639
|
+
* and automatic theming integration.
|
|
640
|
+
*
|
|
641
|
+
* @component
|
|
642
|
+
* @example
|
|
643
|
+
* ```tsx
|
|
644
|
+
* // Basic legend with default settings
|
|
645
|
+
* <ChartLegend content={<ChartLegendContent />} />
|
|
646
|
+
* ```
|
|
647
|
+
*
|
|
648
|
+
* @example
|
|
649
|
+
* ```tsx
|
|
650
|
+
* // Legend positioned at top without icons
|
|
651
|
+
* <ChartLegend
|
|
652
|
+
* content={
|
|
653
|
+
* <ChartLegendContent
|
|
654
|
+
* verticalAlign="top"
|
|
655
|
+
* hideIcon={true}
|
|
656
|
+
* />
|
|
657
|
+
* }
|
|
658
|
+
* />
|
|
659
|
+
* ```
|
|
660
|
+
*
|
|
661
|
+
* @example
|
|
662
|
+
* ```tsx
|
|
663
|
+
* // Legend with custom name key
|
|
664
|
+
* <ChartLegend
|
|
665
|
+
* content={
|
|
666
|
+
* <ChartLegendContent
|
|
667
|
+
* nameKey="category"
|
|
668
|
+
* className="justify-start"
|
|
669
|
+
* />
|
|
670
|
+
* }
|
|
671
|
+
* />
|
|
672
|
+
* ```
|
|
673
|
+
*
|
|
674
|
+
* @accessibility
|
|
675
|
+
* - Semantic markup for screen readers
|
|
676
|
+
* - Color indicators with clear labels
|
|
677
|
+
* - Proper contrast ratios for visibility
|
|
678
|
+
* - Logical tab order for keyboard navigation
|
|
679
|
+
*
|
|
680
|
+
* @since 1.0.0
|
|
681
|
+
*/
|
|
682
|
+
function ChartLegendContent({
|
|
683
|
+
className,
|
|
684
|
+
hideIcon = false,
|
|
685
|
+
payload,
|
|
686
|
+
verticalAlign = "bottom",
|
|
687
|
+
nameKey,
|
|
688
|
+
}: React.ComponentProps<"div"> &
|
|
689
|
+
Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
|
|
690
|
+
/**
|
|
691
|
+
* Whether to hide series icons
|
|
692
|
+
* @default false
|
|
693
|
+
*/
|
|
694
|
+
hideIcon?: boolean;
|
|
695
|
+
/**
|
|
696
|
+
* Key to use for the name in payload
|
|
697
|
+
*/
|
|
698
|
+
nameKey?: string;
|
|
699
|
+
}) {
|
|
700
|
+
const { config } = useChart();
|
|
701
|
+
|
|
702
|
+
if (!payload?.length) {
|
|
703
|
+
return null;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
return (
|
|
707
|
+
<div
|
|
708
|
+
className={cn(
|
|
709
|
+
"flex items-center justify-center gap-4",
|
|
710
|
+
verticalAlign === "top" ? "pb-3" : "pt-3",
|
|
711
|
+
className,
|
|
712
|
+
)}
|
|
713
|
+
>
|
|
714
|
+
{payload.map((item) => {
|
|
715
|
+
const key = `${nameKey || item.dataKey || "value"}`;
|
|
716
|
+
const itemConfig = getPayloadConfigFromPayload(config, item, key);
|
|
717
|
+
|
|
718
|
+
return (
|
|
719
|
+
<div
|
|
720
|
+
key={item.value}
|
|
721
|
+
className={cn(
|
|
722
|
+
"[&>svg]:text-muted-foreground flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3",
|
|
723
|
+
)}
|
|
724
|
+
>
|
|
725
|
+
{itemConfig?.icon && !hideIcon ? (
|
|
726
|
+
<itemConfig.icon />
|
|
727
|
+
) : (
|
|
728
|
+
<div
|
|
729
|
+
className="h-2 w-2 shrink-0 rounded-[2px]"
|
|
730
|
+
style={{
|
|
731
|
+
backgroundColor: item.color,
|
|
732
|
+
}}
|
|
733
|
+
/>
|
|
734
|
+
)}
|
|
735
|
+
{itemConfig?.label}
|
|
736
|
+
</div>
|
|
737
|
+
);
|
|
738
|
+
})}
|
|
739
|
+
</div>
|
|
740
|
+
);
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
/**
|
|
744
|
+
* Internal helper to extract chart configuration from payload data
|
|
745
|
+
*
|
|
746
|
+
* Retrieves the appropriate configuration for a data series from the chart
|
|
747
|
+
* config based on the payload data structure. Handles nested payload objects
|
|
748
|
+
* and fallback key resolution.
|
|
749
|
+
*
|
|
750
|
+
* @internal
|
|
751
|
+
* @param config - The chart configuration object
|
|
752
|
+
* @param payload - The data payload from Recharts
|
|
753
|
+
* @param key - The key to look up in the configuration
|
|
754
|
+
* @returns The configuration for the data series, or undefined if not found
|
|
755
|
+
* @since 1.0.0
|
|
756
|
+
*/
|
|
757
|
+
function getPayloadConfigFromPayload(
|
|
758
|
+
config: ChartConfig,
|
|
759
|
+
payload: unknown,
|
|
760
|
+
key: string,
|
|
761
|
+
) {
|
|
762
|
+
if (typeof payload !== "object" || payload === null) {
|
|
763
|
+
return undefined;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
const payloadPayload =
|
|
767
|
+
"payload" in payload &&
|
|
768
|
+
typeof payload.payload === "object" &&
|
|
769
|
+
payload.payload !== null
|
|
770
|
+
? payload.payload
|
|
771
|
+
: undefined;
|
|
772
|
+
|
|
773
|
+
let configLabelKey: string = key;
|
|
774
|
+
|
|
775
|
+
if (
|
|
776
|
+
key in payload &&
|
|
777
|
+
typeof payload[key as keyof typeof payload] === "string"
|
|
778
|
+
) {
|
|
779
|
+
configLabelKey = payload[key as keyof typeof payload] as string;
|
|
780
|
+
} else if (
|
|
781
|
+
payloadPayload &&
|
|
782
|
+
key in payloadPayload &&
|
|
783
|
+
typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
|
|
784
|
+
) {
|
|
785
|
+
configLabelKey = payloadPayload[
|
|
786
|
+
key as keyof typeof payloadPayload
|
|
787
|
+
] as string;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
return configLabelKey in config
|
|
791
|
+
? config[configLabelKey]
|
|
792
|
+
: config[key as keyof typeof config];
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
export {
|
|
796
|
+
ChartContainer,
|
|
797
|
+
ChartTooltip,
|
|
798
|
+
ChartTooltipContent,
|
|
799
|
+
ChartLegend,
|
|
800
|
+
ChartLegendContent,
|
|
801
|
+
ChartStyle,
|
|
802
|
+
};
|