Spaces:
Running
Running
| import React from "react"; | |
| import { LineChart, Line, ResponsiveContainer } from "recharts"; | |
| interface TrendDataPoint { | |
| time: string; | |
| value: number; | |
| value2?: number; // Optional second value for dual-line charts | |
| } | |
| interface TinyTrendChartProps { | |
| data: TrendDataPoint[]; | |
| color: string; | |
| color2?: string; // Optional second color for dual-line | |
| className?: string; | |
| isDual?: boolean; // Flag to enable dual-line mode | |
| } | |
| export function TinyTrendChart({ | |
| data, | |
| color, | |
| color2, | |
| className, | |
| isDual = false, | |
| }: TinyTrendChartProps) { | |
| // Generate unique IDs for animation elements | |
| const chartId = React.useId(); | |
| // Add subtle flowing line animations | |
| React.useEffect(() => { | |
| const style = document.createElement("style"); | |
| style.textContent = ` | |
| @keyframes subtleWave { | |
| 0% { | |
| opacity: 0.7; | |
| stroke-width: 1; | |
| stroke-dasharray: 100 0; | |
| stroke-dashoffset: 0; | |
| } | |
| 25% { | |
| opacity: 0.9; | |
| stroke-width: 1.1; | |
| stroke-dasharray: 80 20; | |
| stroke-dashoffset: -10; | |
| } | |
| 50% { | |
| opacity: 1; | |
| stroke-width: 1.2; | |
| stroke-dasharray: 60 40; | |
| stroke-dashoffset: -20; | |
| } | |
| 75% { | |
| opacity: 0.9; | |
| stroke-width: 1.1; | |
| stroke-dasharray: 80 20; | |
| stroke-dashoffset: -30; | |
| } | |
| 100% { | |
| opacity: 0.7; | |
| stroke-width: 1; | |
| stroke-dasharray: 100 0; | |
| stroke-dashoffset: -40; | |
| } | |
| } | |
| .animated-line { | |
| animation: subtleWave 4s ease-in-out infinite; | |
| transition: all 0.3s ease; | |
| } | |
| `; | |
| document.head.appendChild(style); | |
| return () => { | |
| document.head.removeChild(style); | |
| }; | |
| }, []); | |
| // Use actual data - no fallbacks | |
| const chartData = data; | |
| // Show empty state if no data | |
| if (!chartData || chartData.length === 0) { | |
| return ( | |
| <div | |
| className={`h-8 w-full relative ${className} flex items-center justify-center`} | |
| > | |
| <div className="text-xs text-muted-foreground/50">No data</div> | |
| </div> | |
| ); | |
| } | |
| return ( | |
| <div | |
| className={`h-8 w-full relative ${className}`} | |
| id={`trend-chart-${chartId}`} | |
| > | |
| {/* Glass overlay effect */} | |
| <div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/5 to-transparent rounded-lg" /> | |
| <ResponsiveContainer width="100%" height="100%"> | |
| <LineChart | |
| data={chartData} | |
| margin={{ top: 2, right: 2, left: 2, bottom: 2 }} | |
| > | |
| <Line | |
| type="monotone" | |
| dataKey="value" | |
| stroke={color} | |
| strokeWidth={1.5} | |
| dot={false} | |
| activeDot={false} | |
| connectNulls={true} | |
| className="animated-line" | |
| style={{ | |
| filter: `drop-shadow(0 0 4px ${color}60)`, | |
| }} | |
| /> | |
| {isDual && color2 && ( | |
| <Line | |
| type="monotone" | |
| dataKey="value2" | |
| stroke={color2} | |
| strokeWidth={1.5} | |
| dot={false} | |
| activeDot={false} | |
| connectNulls={true} | |
| className="animated-line" | |
| style={{ | |
| filter: `drop-shadow(0 0 4px ${color2}60)`, | |
| animationDelay: "1s", | |
| }} | |
| /> | |
| )} | |
| </LineChart> | |
| </ResponsiveContainer> | |
| </div> | |
| ); | |
| } | |