{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "sparkline-glass", "type": "registry:component", "title": "Sparkline Glass", "description": "Sparkline Glass component with glass effects", "dependencies": [ "class-variance-authority", "react", "recharts" ], "registryDependencies": [ "cn", "variants" ], "files": [ { "path": "components/glass/specialized/sparkline-glass.tsx", "type": "registry:component", "content": "// ========================================\n// SPARKLINE GLASS COMPONENT\n// Recharts-based sparkline following shadcn/ui Charts pattern\n// ========================================\n\nimport { forwardRef, useMemo } from 'react';\nimport { Bar, BarChart, Cell, ResponsiveContainer, Tooltip } from 'recharts';\nimport { type VariantProps } from 'class-variance-authority';\nimport { cn } from '@/lib/utils';\nimport { sparklineContainerVariants } from '@/lib/variants/sparkline-glass-variants';\nimport { type ChartConfig } from '@/components/ui/chart';\nimport '@/glass-theme.css';\n\n// ========================================\n// SPARKLINE CONFIG TYPE\n// ========================================\n\nexport type SparklineConfig = ChartConfig;\n\n// ========================================\n// DEFAULT CONFIG\n// ========================================\n\nconst defaultSparklineConfig = {\n value: {\n label: 'Value',\n // Use existing chart-1 color with fallback to primary accent\n color: 'var(--sparkline-bar-fill, hsl(var(--chart-1-base)))',\n },\n max: {\n label: 'Maximum',\n // Use existing success color with fallback\n color: 'var(--sparkline-bar-max, var(--alert-success-text))',\n },\n} satisfies SparklineConfig;\n\n// ========================================\n// SPARKLINE PROPS\n// ========================================\n\nexport interface SparklineGlassProps\n extends\n Omit, 'style'>,\n VariantProps {\n /** Array of numeric values to display */\n readonly data: readonly number[];\n /** Labels for each bar (shown in tooltip) */\n readonly labels?: readonly string[];\n /** Show labels below bars */\n readonly showLabels?: boolean;\n /** Highlight the maximum value with different color */\n readonly highlightMax?: boolean;\n /** Color for regular bars (CSS variable or color) */\n readonly barColor?: string;\n /** Color for maximum bar */\n readonly maxBarColor?: string;\n /** Tooltips for each bar */\n readonly tooltips?: readonly string[];\n /** Enable animation */\n readonly animated?: boolean;\n /** Minimum bar height as percentage */\n readonly minBarHeightPercent?: number;\n /** Chart configuration (shadcn/ui pattern) */\n readonly config?: SparklineConfig;\n /** Value formatter for tooltip */\n readonly valueFormatter?: (value: number, index: number) => string;\n /** Callback when bar is clicked */\n readonly onBarClick?: (value: number, index: number) => void;\n /** Show tooltip on hover */\n readonly showTooltip?: boolean;\n}\n\n// ========================================\n// HEIGHT MAP\n// ========================================\n\nconst heightMap = {\n sm: 16,\n md: 24,\n lg: 32,\n} as const;\n\n// ========================================\n// CUSTOM TOOLTIP\n// ========================================\n\ninterface SparklineTooltipProps {\n active?: boolean;\n payload?: Array<{\n value: number;\n payload: { index: number; label?: string; value: number };\n }>;\n valueFormatter?: (value: number, index: number) => string;\n}\n\nconst SparklineTooltip = ({ active, payload, valueFormatter }: SparklineTooltipProps) => {\n if (!active || !payload?.length) {\n return null;\n }\n\n const data = payload[0];\n const value = data.value;\n const index = data.payload.index;\n const label = data.payload.label;\n\n const displayValue = valueFormatter ? valueFormatter(value, index) : value.toLocaleString();\n\n return (\n
\n {label &&
{label}
}\n
{displayValue}
\n
\n );\n};\n\n// ========================================\n// SPARKLINE COMPONENT\n// ========================================\n\nexport const SparklineGlass = forwardRef(\n (\n {\n data,\n labels,\n showLabels = false,\n highlightMax = false,\n barColor,\n maxBarColor,\n tooltips,\n height = 'md',\n gap = 'sm',\n animated = false,\n minBarHeightPercent = 4,\n config = defaultSparklineConfig,\n valueFormatter,\n onBarClick,\n showTooltip = true,\n className,\n ...props\n },\n ref\n ) => {\n // Transform data for Recharts\n const chartData = useMemo(() => {\n const maxValue = Math.max(...data, 0);\n const maxIndex = data.indexOf(maxValue);\n\n return data.map((value, index) => ({\n index,\n value,\n label: labels?.[index] || tooltips?.[index],\n isMax: highlightMax && index === maxIndex && value > 0,\n // Ensure minimum bar height\n displayValue:\n maxValue > 0\n ? Math.max(value, maxValue * (minBarHeightPercent / 100))\n : minBarHeightPercent,\n }));\n }, [data, labels, tooltips, highlightMax, minBarHeightPercent]);\n\n const { maxValue, maxIndex } = useMemo(() => {\n const max = Math.max(...data, 0);\n return { maxValue: max, maxIndex: data.indexOf(max) };\n }, [data]);\n\n const ariaLabel = useMemo(() => {\n if (data.length === 0) return 'Empty sparkline chart';\n return `Sparkline chart with ${data.length} data points, maximum value ${maxValue}${\n labels ? ` at ${labels[maxIndex] || `position ${maxIndex + 1}`}` : ''\n }`;\n }, [data.length, maxValue, maxIndex, labels]);\n\n // Resolve colors\n const resolvedBarColor = barColor || config.value?.color || 'var(--sparkline-bar-fill)';\n const resolvedMaxColor = maxBarColor || config.max?.color || 'var(--sparkline-bar-max)';\n\n // Gap map for bar spacing\n const gapMap = { none: 0, sm: 1, md: 2 };\n const barGap = gapMap[gap || 'sm'];\n\n const chartHeight = heightMap[height || 'md'];\n\n const handleClick = (dataPoint: (typeof chartData)[0]) => {\n if (onBarClick) {\n onBarClick(dataPoint.value, dataPoint.index);\n }\n };\n\n return (\n \n {/* Chart Container - following shadcn/ui pattern */}\n \n \n \n {showTooltip && (\n }\n cursor={false}\n isAnimationActive={false}\n />\n )}\n handleClick(chartData[index])}\n style={{ cursor: onBarClick ? 'pointer' : 'default' }}\n >\n {chartData.map((entry, index) => (\n \n ))}\n \n \n \n \n\n {/* Labels */}\n {showLabels && labels && labels.length > 0 && (\n
\n {labels.map((label, index) => (\n \n {label}\n \n ))}\n
\n )}\n \n );\n }\n);\n\nSparklineGlass.displayName = 'SparklineGlass';\n" } ], "categories": [ "specialized" ] }