Spaces:
Running
Running
| import React from 'react' | |
| import { | |
| BarChart, | |
| Bar, | |
| XAxis, | |
| YAxis, | |
| CartesianGrid, | |
| Tooltip, | |
| Cell, | |
| ResponsiveContainer, | |
| } from 'recharts' | |
| import { OHLCVPoint } from '../api/stockApi' | |
| interface Props { | |
| data: OHLCVPoint[] | |
| } | |
| function formatVolume(v: number): string { | |
| if (v >= 1_000_000) return `${(v / 1_000_000).toFixed(1)}M` | |
| if (v >= 1_000) return `${(v / 1_000).toFixed(0)}K` | |
| return String(v) | |
| } | |
| const CustomTooltip = ({ active, payload, label }: any) => { | |
| if (!active || !payload?.length) return null | |
| const vol: number = payload[0]?.value | |
| return ( | |
| <div className="bg-slate-800 border border-slate-600 rounded p-2 text-xs shadow-lg"> | |
| <p className="text-slate-300">{label}</p> | |
| <p className="text-white font-semibold">Vol: {formatVolume(vol)}</p> | |
| </div> | |
| ) | |
| } | |
| export const VolumeChart: React.FC<Props> = ({ data }) => { | |
| const len = data.length | |
| return ( | |
| <ResponsiveContainer width="100%" height={160}> | |
| <BarChart data={data} margin={{ top: 4, right: 16, left: 0, bottom: 0 }}> | |
| <CartesianGrid strokeDasharray="3 3" stroke="#1e293b" /> | |
| <XAxis | |
| dataKey="date" | |
| tick={{ fill: '#94a3b8', fontSize: 10 }} | |
| tickLine={false} | |
| tickFormatter={(val, idx) => { | |
| const step = Math.max(1, Math.floor(len / 10)) | |
| return idx % step === 0 ? val : '' | |
| }} | |
| interval={0} | |
| /> | |
| <YAxis | |
| tick={{ fill: '#94a3b8', fontSize: 10 }} | |
| tickLine={false} | |
| tickFormatter={formatVolume} | |
| width={48} | |
| /> | |
| <Tooltip content={<CustomTooltip />} /> | |
| <Bar dataKey="volume" isAnimationActive={false} maxBarSize={6}> | |
| {data.map((d, i) => ( | |
| <Cell | |
| key={i} | |
| fill={d.close >= d.open ? '#22c55e' : '#ef4444'} | |
| fillOpacity={0.8} | |
| /> | |
| ))} | |
| </Bar> | |
| </BarChart> | |
| </ResponsiveContainer> | |
| ) | |
| } | |