// Chart interactivo: línea + área con glow, crosshair en hover, tooltip, // botones de rango, etiquetas adaptativas. function Chart({ symbol, accent, intensity }) { const ranges = ['1H', '4H', '1D', '1S', '1M', '3M', '1A']; const [range, setRange] = React.useState('1D'); const [hover, setHover] = React.useState(null); // {x, idx} const wrapRef = React.useRef(null); const POINTS = range === '1H' || range === '4H' ? 60 : range === '1D' ? 78 : range === '1S' ? 60 : range === '1M' ? 64 : range === '3M' ? 72 : 96; const W = 900, H = 320; const padL = 8, padR = 56, padT = 14, padB = 28; const plotW = W - padL - padR; const plotH = H - padT - padB; const series = React.useMemo(() => window.genSeries(symbol, range, POINTS), [symbol, range, POINTS]); const labels = React.useMemo(() => window.timeLabels(range, POINTS), [range, POINTS]); const min = Math.min(...series); const max = Math.max(...series); const pad = (max - min) * 0.15 || max * 0.005; const yMin = min - pad; const yMax = max + pad; const yRange = yMax - yMin; const xFor = (i) => padL + (i / (POINTS - 1)) * plotW; const yFor = (v) => padT + (1 - (v - yMin) / yRange) * plotH; // Si serie sube o baja en el rango const isUp = series[series.length - 1] >= series[0]; const lineColor = isUp ? accent : 'var(--neg)'; const glowOpacity = intensity === 'atrevida' ? 0.55 : intensity === 'media' ? 0.35 : 0.2; const areaOpacity = intensity === 'atrevida' ? 0.30 : intensity === 'media' ? 0.18 : 0.10; const linePath = series.map((v, i) => `${i === 0 ? 'M' : 'L'} ${xFor(i).toFixed(2)} ${yFor(v).toFixed(2)}`).join(' '); const areaPath = linePath + ` L ${xFor(POINTS - 1).toFixed(2)} ${padT + plotH} L ${padL} ${padT + plotH} Z`; // Eje Y: 4 ticks const yTicks = []; for (let i = 0; i <= 4; i++) { const v = yMin + (yRange * i) / 4; yTicks.push({ v, y: padT + plotH - (i / 4) * plotH }); } // Eje X: 5 etiquetas espaciadas const xLabelCount = 5; const xTicks = []; for (let i = 0; i < xLabelCount; i++) { const idx = Math.floor((i / (xLabelCount - 1)) * (POINTS - 1)); xTicks.push({ idx, label: labels[idx] }); } const onMouseMove = (e) => { const rect = wrapRef.current.getBoundingClientRect(); const px = ((e.clientX - rect.left) / rect.width) * W; const i = Math.round(((px - padL) / plotW) * (POINTS - 1)); if (i < 0 || i > POINTS - 1) return setHover(null); setHover({ idx: i, x: xFor(i), y: yFor(series[i]), value: series[i] }); }; const onMouseLeave = () => setHover(null); const lastV = series[POINTS - 1]; const lastY = yFor(lastV); return (
Precio · {{ '1H': '1 hora', '4H': '4 horas', '1D': '1 día', '1S': '1 semana', '1M': '1 mes', '3M': '3 meses', '1A': '1 año', }[range]} {hover && ( ${series[hover.idx].toFixed(2)} {labels[hover.idx]} )}
{ranges.map((r) => ( ))}
{/* Cuadrícula horizontal */} {yTicks.map((t, i) => ( ))} {/* Etiquetas Y (derecha) */} {yTicks.map((t, i) => ( {t.v.toFixed(2)} ))} {/* Línea del precio actual */} {/* Área */} {/* Línea con glow */} {/* Brillo extra (highlight) */} {intensity === 'atrevida' && ( )} {/* Punto actual */} {/* Crosshair hover */} {hover && ( <> )} {/* Etiqueta de precio actual a la derecha */} {lastV.toFixed(2)} {/* Etiquetas X */} {xTicks.map((t, i) => ( {t.label} ))} {/* Tooltip flotante */} {hover && (
{labels[hover.idx]}
${series[hover.idx].toFixed(2)}
= series[0] ? lineColor : 'var(--neg)' }}> {series[hover.idx] >= series[0] ? '+' : ''} {(((series[hover.idx] - series[0]) / series[0]) * 100).toFixed(2)}%
)}
); } window.Chart = Chart;