Spaces:
Running
Running
File size: 3,490 Bytes
c2ea5ed |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
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>
);
}
|