chart-demo.tsx
prompt.md
Collapse
Reset
Copy Code
Download
Run
1
"use client";
2
3
import React, { useState, useEffect } from 'react';
4
import { createPortal } from 'react-dom';
5
import { cn } from "@/lib/utils";
6
import {
7
BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip as RechartsTooltip, ResponsiveContainer,
8
LineChart, Line,
9
AreaChart, Area,
10
PieChart, Pie, Cell,
11
Legend
12
} from 'recharts';
13
14
const data = [
15
{ name: 'Jan', Desktop: 180, Mobile: 80 },
16
{ name: 'Feb', Desktop: 300, Mobile: 200 },
17
{ name: 'Mar', Desktop: 240, Mobile: 120 },
18
{ name: 'Apr', Desktop: 270, Mobile: 190 },
19
{ name: 'May', Desktop: 210, Mobile: 130 },
20
{ name: 'Jun', Desktop: 310, Mobile: 240 },
21
];
22
23
const pieData = [
24
{ name: 'Desktop', value: 400 },
25
{ name: 'Mobile', value: 300 },
26
{ name: 'Other', value: 200 },
27
{ name: 'Tablet', value: 100 },
28
];
29
30
const COLORS = {
31
desktop: '#3b82f6',
32
mobile: '#60a5fa',
33
other: '#2563eb',
34
tablet: '#93c5fd'
35
};
36
37
const PIE_COLORS = [COLORS.desktop, COLORS.mobile, COLORS.other, COLORS.tablet];
38
39
export function ChartDemo({ className }: { className?: string }) {
40
const [chartType, setChartType] = useState<'bar' | 'pie' | 'area' | 'line'>('bar');
41
const [mounted, setMounted] = useState(false);
42
43
useEffect(() => {
44
setMounted(true);
45
}, []);
46
47
const CustomTooltip = ({ active, payload, label }: any) => {
48
if (active && payload && payload.length) {
49
return (
50
<div className="bg-white dark:bg-[#1C1C1C] text-zinc-900 dark:text-white text-[12px] p-3 rounded-lg shadow-lg border border-black/5 dark:border-white/10">
51
<p className="font-medium mb-2">{label}</p>
52
<div className="flex flex-col gap-1.5">
53
{payload.map((entry: any, index: number) => (
54
<div key={index} className="flex items-center gap-2">
55
<div className="w-2 h-2 rounded-full" style={{ backgroundColor: entry.color }} />
56
<span className="text-zinc-500 dark:text-zinc-400">{entry.name}:</span>
57
<span className="font-mono font-medium">{entry.value}</span>
58
</div>
59
))}
60
</div>
61
</div>
62
);
63
}
64
return null;
65
};
66
67
const renderChart = () => {
68
switch (chartType) {
69
case 'bar':
70
return (
71
<ResponsiveContainer width="100%" height={300}>
72
<BarChart data={data} margin={{ top: 20, right: 0, left: -20, bottom: 0 }}>
73
<CartesianGrid strokeDasharray="4 4" vertical={false} stroke="var(--border)" />
74
<XAxis dataKey="name" axisLine={false} tickLine={false} tick={{ fill: '#888', fontSize: 11 }} dy={10} />
75
<YAxis axisLine={false} tickLine={false} tick={{ fill: '#888', fontSize: 11 }} />
76
<RechartsTooltip content={<CustomTooltip />} cursor={{ fill: 'var(--border)' }} />
77
<Legend verticalAlign="top" height={40} iconType="square" iconSize={8} wrapperStyle={{ fontSize: '11px', fontWeight: 500 }} />
78
<Bar dataKey="Desktop" fill={COLORS.desktop} radius={[4, 4, 0, 0]} animationDuration={1000} />
79
<Bar dataKey="Mobile" fill={COLORS.mobile} radius={[4, 4, 0, 0]} animationDuration={1000} />
80
</BarChart>
81
</ResponsiveContainer>
82
);
83
case 'line':
84
return (
85
<ResponsiveContainer width="100%" height={300}>
86
<LineChart data={data} margin={{ top: 20, right: 0, left: -20, bottom: 0 }}>
87
<CartesianGrid strokeDasharray="4 4" vertical={false} stroke="var(--border)" />
88
<XAxis dataKey="name" axisLine={false} tickLine={false} tick={{ fill: '#888', fontSize: 11 }} dy={10} />
89
<YAxis axisLine={false} tickLine={false} tick={{ fill: '#888', fontSize: 11 }} />
90
<RechartsTooltip content={<CustomTooltip />} />
91
<Legend verticalAlign="top" height={40} iconType="square" iconSize={8} wrapperStyle={{ fontSize: '11px', fontWeight: 500 }} />
92
<Line type="monotone" dataKey="Desktop" stroke={COLORS.desktop} strokeWidth={2} dot={false} animationDuration={1500} />
93
<Line type="monotone" dataKey="Mobile" stroke={COLORS.mobile} strokeWidth={2} strokeDasharray="5 5" dot={false} animationDuration={1500} />
94
</LineChart>
95
</ResponsiveContainer>
96
);
97
case 'area':
98
return (
99
<ResponsiveContainer width="100%" height={300}>
100
<AreaChart data={data} margin={{ top: 20, right: 0, left: -20, bottom: 0 }}>
101
<defs>
102
<linearGradient id="colorDesktop" x1="0" y1="0" x2="0" y2="1">
103
<stop offset="5%" stopColor={COLORS.desktop} stopOpacity={0.3}/>
104
<stop offset="95%" stopColor={COLORS.desktop} stopOpacity={0}/>
105
</linearGradient>
106
<linearGradient id="colorMobile" x1="0" y1="0" x2="0" y2="1">
107
<stop offset="5%" stopColor={COLORS.mobile} stopOpacity={0.3}/>
108
<stop offset="95%" stopColor={COLORS.mobile} stopOpacity={0}/>
109
</linearGradient>
110
</defs>
111
<CartesianGrid strokeDasharray="4 4" vertical={false} stroke="var(--border)" />
112
<XAxis dataKey="name" axisLine={false} tickLine={false} tick={{ fill: '#888', fontSize: 11 }} dy={10} />
113
<YAxis axisLine={false} tickLine={false} tick={{ fill: '#888', fontSize: 11 }} />
114
<RechartsTooltip content={<CustomTooltip />} />
115
<Legend verticalAlign="top" height={40} iconType="square" iconSize={8} wrapperStyle={{ fontSize: '11px', fontWeight: 500 }} />
116
<Area type="monotone" dataKey="Desktop" stroke={COLORS.desktop} strokeWidth={2} fillOpacity={1} fill="url(#colorDesktop)" animationDuration={1500} />
117
<Area type="monotone" dataKey="Mobile" stroke={COLORS.mobile} strokeWidth={2} strokeDasharray="5 5" fillOpacity={1} fill="url(#colorMobile)" animationDuration={1500} />
118
</AreaChart>
119
</ResponsiveContainer>
120
);
121
case 'pie':
122
return (
123
<ResponsiveContainer width="100%" height={300}>
124
<PieChart>
125
<RechartsTooltip content={<CustomTooltip />} />
126
<Legend verticalAlign="bottom" height={40} iconType="square" iconSize={8} wrapperStyle={{ fontSize: '11px', fontWeight: 500 }} />
127
<Pie
128
data={pieData}
129
cx="50%"
130
cy="45%"
131
innerRadius={65}
132
outerRadius={100}
133
paddingAngle={2}
134
dataKey="value"
135
animationDuration={1000}
136
stroke="var(--background)"
137
strokeWidth={2}
138
>
139
{pieData.map((entry, index) => (
140
<Cell key={`cell-${index}`} fill={PIE_COLORS[index % PIE_COLORS.length]} />
141
))}
142
</Pie>
143
</PieChart>
144
</ResponsiveContainer>
145
);
146
}
147
};
148
149
const portalTarget = mounted && typeof document !== "undefined" ? document.getElementById('custom-controls-portal') : null;
150
151
return (
152
<div className={cn("w-full max-w-[650px] h-[400px] p-4 sm:p-6 flex flex-col items-center justify-center relative", className)}>
153
{/* Mobile Floating Pill to change graph - Top of screen only in mobile view */}
154
<div className="md:hidden absolute top-2 left-1/2 -translate-x-1/2 z-20 flex items-center p-1 rounded-full bg-white/95 dark:bg-[#1E1E20]/95 backdrop-blur-md border border-black/10 dark:border-white/10 shadow-[0_4px_20px_rgba(0,0,0,0.08)] dark:shadow-[0_4px_20px_rgba(0,0,0,0.4)]">
155
{(['bar', 'line', 'area', 'pie'] as const).map((type) => {
156
const isActive = chartType === type;
157
return (
158
<button
159
key={type}
160
onClick={() => setChartType(type)}
161
className={cn(
162
"px-3 py-1 rounded-full text-[12px] font-medium capitalize transition-all font-matter",
163
isActive
164
? "bg-zinc-900 text-white dark:bg-white dark:text-zinc-900 shadow-sm"
165
: "text-zinc-500 hover:text-zinc-900 dark:text-[#86868B] dark:hover:text-[#F5F5F7]"
166
)}
167
>
168
{type}
169
</button>
170
);
171
})}
172
</div>
173
174
{portalTarget && createPortal(
175
<div className="flex flex-col gap-3 animate-in fade-in duration-500">
176
<div className="flex items-center gap-2 text-[13px] font-semibold text-[#1F1F1F] dark:text-[#D4D4D8]">
177
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="text-[#9FE870]"><path d="M2 12h20"/><path d="M12 2v20"/><circle cx="12" cy="12" r="3"/></svg>
178
Preview
179
</div>
180
<div className="relative">
181
<select
182
value={chartType}
183
onChange={(e) => setChartType(e.target.value as any)}
184
className="w-full appearance-none bg-[#F9F9F9] dark:bg-[#18181B] border border-black/5 dark:border-white/5 rounded-xl px-4 py-3 text-[12.5px] font-medium text-[#1F1F1F] dark:text-[#E4E4E7] shadow-sm outline-none focus:ring-2 focus:ring-black/10 dark:focus:ring-white/10 transition-shadow cursor-pointer"
185
>
186
<option value="bar">Bar chart</option>
187
<option value="pie">Pie chart</option>
188
<option value="area">Area chart</option>
189
<option value="line">Line chart</option>
190
</select>
191
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="absolute right-4 top-1/2 -translate-y-1/2 text-[#555555] dark:text-[#71717A] pointer-events-none">
192
<path d="m6 9 6 6 6-6"/>
193
</svg>
194
</div>
195
</div>,
196
portalTarget
197
)}
198
199
{/* Chart wrapper */}
200
<div className="w-full h-full flex items-center justify-center font-sans pt-10 md:pt-0">
201
{renderChart()}
202
</div>
203
</div>
204
);
205
}

