{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "bar-chart", "title": "Bar Chart", "description": "A customizable bar chart component with multiple variants (default, horizontal, stacked, mixed, interactive).", "dependencies": [ "recharts", "lucide-react" ], "registryDependencies": [ "card", "chart" ], "files": [ { "path": "registry/new-york/bar-chart/bar-chart.tsx", "content": "\"use client\"\n\nimport { useMemo } from \"react\"\nimport {\n Bar,\n BarChart,\n CartesianGrid,\n Cell,\n LabelList,\n XAxis,\n YAxis,\n Rectangle,\n} from \"recharts\"\nimport {\n Card,\n CardContent,\n CardDescription,\n CardFooter,\n CardHeader,\n CardTitle,\n} from \"@/components/ui/card\"\nimport {\n ChartContainer,\n ChartLegend,\n ChartLegendContent,\n ChartTooltip,\n ChartTooltipContent,\n type ChartConfig,\n} from \"@/components/ui/chart\"\nimport { TrendingUp } from \"lucide-react\"\n\nexport type BarChartVariant =\n | 'default'\n | 'horizontal'\n | 'multiple'\n | 'stacked'\n | 'stacked-legend'\n | 'label'\n | 'negative'\n | 'mixed'\n | 'interactive'\n\nexport interface BarConfig {\n dataKey: string\n fill?: string\n radius?: number | [number, number, number, number]\n stackId?: string\n name?: string\n}\n\nexport interface BarChartComponentProps {\n title?: string\n description?: string\n data: Array>\n chartConfig: ChartConfig\n className?: string\n\n variant?: BarChartVariant\n\n bars?: BarConfig[]\n\n xAxisKey?: string\n xAxisFormatter?: (value: string) => string\n yAxisConfig?: {\n dataKey?: string // for horizontal layout\n domain?: [number, number] | 'auto'\n tickFormatter?: (value: number) => string\n hide?: boolean\n }\n\n // Features\n showGrid?: boolean\n showTooltip?: boolean\n showLegend?: boolean\n showLabels?: boolean\n labelPosition?: 'top' | 'center' | 'bottom' | 'inside' | 'insideTop' | 'insideBottom'\n labelFormatter?: (value: number) => string\n tooltipNameKey?: string // Key to use for tooltip name lookup (for mixed variant)\n\n // Layout\n layout?: 'vertical' | 'horizontal'\n margin?: { top?: number; right?: number; bottom?: number; left?: number }\n barRadius?: number | [number, number, number, number]\n barGap?: number\n barCategoryGap?: string | number\n\n // Negative bar colors\n positiveColor?: string\n negativeColor?: string\n\n footerContent?: {\n mainText?: string\n subText?: string\n showTrending?: boolean\n trendingIcon?: React.ReactNode\n trendingColor?: string\n }\n}\n\nconst getDefaultRadius = (variant: BarChartVariant): number | [number, number, number, number] => {\n switch (variant) {\n case 'stacked':\n case 'stacked-legend':\n return 0\n case 'horizontal':\n return [0, 4, 4, 0]\n default:\n return 8\n }\n}\n\nconst getDefaultLayout = (variant: BarChartVariant): 'vertical' | 'horizontal' | undefined => {\n // In Recharts: 'vertical' layout = horizontal bars (bars grow to the right)\n // undefined/default = vertical bars (bars grow upward) \n return variant === 'horizontal' ? 'vertical' : undefined\n}\n\nexport const BarChartComponent = ({\n title,\n description,\n data,\n chartConfig,\n className = \"\",\n variant = 'default',\n bars,\n xAxisKey = \"month\",\n xAxisFormatter = (value) => typeof value === 'string' ? value.slice(0, 3) : String(value),\n yAxisConfig,\n showGrid = true,\n showTooltip = true,\n showLegend,\n showLabels,\n labelPosition = 'top',\n labelFormatter,\n tooltipNameKey,\n layout,\n margin = { left: 12, right: 12 },\n barRadius,\n barGap,\n barCategoryGap,\n positiveColor = \"var(--chart-1)\",\n negativeColor = \"var(--chart-2)\",\n footerContent,\n}: BarChartComponentProps) => {\n\n // Determine layout based on variant\n // Note: In Recharts, layout=\"vertical\" = horizontal bars (bars grow to the right)\n const isHorizontalBars = variant === 'horizontal' || layout === 'vertical'\n const chartLayout = layout ?? getDefaultLayout(variant)\n\n // Determine radius\n const defaultRadius = barRadius ?? getDefaultRadius(variant)\n\n // Auto determine showLegend based on variant\n const shouldShowLegend = showLegend ?? (variant === 'stacked-legend' || variant === 'multiple')\n\n // Auto determine showLabels based on variant\n const shouldShowLabels = showLabels ?? (variant === 'label')\n\n // Auto-generate bars from chartConfig if not provided\n const chartBars = useMemo(() => {\n if (bars) return bars\n\n return Object.keys(chartConfig).map((key, index) => ({\n dataKey: key,\n fill: chartConfig[key].color || `var(--chart-${index + 1})`,\n radius: defaultRadius,\n stackId: (variant === 'stacked' || variant === 'stacked-legend') ? 'stack' : undefined,\n }))\n }, [bars, chartConfig, variant, defaultRadius])\n\n // Render bars based on variant\n const renderBars = () => {\n switch (variant) {\n case 'mixed':\n // Mixed: Each bar has different color from data's fill property\n return (\n \n {data.map((entry, index) => (\n \n ))}\n \n )\n\n case 'negative':\n // Negative: Different colors for positive/negative values\n return (\n \n {data.map((entry, index) => {\n const value = entry[chartBars[0]?.dataKey || 'value'] as number\n return (\n = 0 ? positiveColor : negativeColor}\n />\n )\n })}\n \n )\n\n case 'interactive':\n // Interactive: Active bar on hover\n return chartBars.map((bar) => (\n (\n \n )}\n >\n {shouldShowLabels && (\n \n )}\n \n ))\n\n case 'stacked':\n case 'stacked-legend':\n case 'multiple':\n default:\n // Default, Multiple, Stacked variants\n return chartBars.map((bar) => (\n \n {shouldShowLabels && (\n \n )}\n \n ))\n }\n }\n\n // Render X Axis\n const renderXAxis = () => {\n if (isHorizontalBars) {\n return (\n \n )\n }\n\n return (\n \n )\n }\n\n // Render Y Axis\n const renderYAxis = () => {\n if (isHorizontalBars) {\n return (\n \n )\n }\n\n if (yAxisConfig?.hide !== true && (variant === 'negative' || yAxisConfig?.tickFormatter)) {\n return (\n \n )\n }\n\n return null\n }\n\n return (\n \n {(title || description) && (\n \n {title && {title}}\n {description && {description}}\n \n )}\n\n \n
\n \n \n {showGrid && }\n\n {renderXAxis()}\n {renderYAxis()}\n\n {showTooltip && (\n \n }\n />\n )}\n\n {shouldShowLegend && } />}\n\n {renderBars()}\n \n \n
\n
\n\n {footerContent && (\n \n
\n {footerContent.mainText}\n {footerContent.showTrending && footerContent.trendingIcon}\n
\n {footerContent.subText && (\n
\n {footerContent.subText}\n
\n )}\n
\n )}\n
\n )\n}\n\n// ========================================================================================\n// Bar Chart Example\n// ========================================================================================\nexport const BarChartExample = () => {\n const monthlyData = [\n { period: \"Jan\", newSubscriptions: 1245, revenue: 18500, churnedUsers: 32 },\n { period: \"Feb\", newSubscriptions: 1289, revenue: 21200, churnedUsers: 28 },\n { period: \"Mar\", newSubscriptions: 1312, revenue: 24800, churnedUsers: 35 },\n { period: \"Apr\", newSubscriptions: 1378, revenue: 29100, churnedUsers: 41 },\n { period: \"May\", newSubscriptions: 1425, revenue: 33600, churnedUsers: 38 },\n { period: \"Jun\", newSubscriptions: 1468, revenue: 37800, churnedUsers: 45 },\n { period: \"Jul\", newSubscriptions: 1512, revenue: 42300, churnedUsers: 49 },\n { period: \"Aug\", newSubscriptions: 1498, revenue: 41200, churnedUsers: 52 },\n { period: \"Sep\", newSubscriptions: 1542, revenue: 45900, churnedUsers: 47 },\n { period: \"Oct\", newSubscriptions: 1589, revenue: 49800, churnedUsers: 51 },\n { period: \"Nov\", newSubscriptions: 1634, revenue: 54200, churnedUsers: 48 },\n { period: \"Dec\", newSubscriptions: 1701, revenue: 61500, churnedUsers: 55 },\n ]\n\n const chartConfig = {\n newSubscriptions: {\n label: \"New Subscriptions\",\n color: \"var(--chart-1)\",\n },\n revenue: {\n label: \"Revenue ($)\",\n color: \"var(--chart-2)\",\n },\n } satisfies ChartConfig\n\n return (\n {\n if (value >= 1000) return `${(value / 1000).toFixed(1)}K`\n return value.toString()\n }\n }}\n margin={{ top: 20, right: 30, bottom: 5, left: 20 }}\n footerContent={{\n mainText: \"Trending up by 12.5% this period\",\n subText: `Showing monthly performance metrics for 2024`,\n showTrending: true,\n trendingIcon: ,\n trendingColor: \"text-emerald-600\"\n }}\n />\n )\n}\n", "type": "registry:component" } ], "type": "registry:block" }