Session 28: Parlay builder, line movement tracker, book comparison — 3 features, zero credits (1623 tests)
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
'use client';
|
||||
|
||||
/**
|
||||
* LineMovementChart (Session 28).
|
||||
*
|
||||
* Dependency-free SVG sparkline of a prop's line through the day. Green
|
||||
* when the line rose, red when it dropped. Renders open + current labels.
|
||||
* Degrades to a flat midline for <2 points.
|
||||
*/
|
||||
|
||||
export interface Snapshot {
|
||||
time?: number;
|
||||
line: number;
|
||||
}
|
||||
|
||||
export interface LineMovementChartProps {
|
||||
snapshots: Snapshot[];
|
||||
width?: number;
|
||||
height?: number;
|
||||
}
|
||||
|
||||
export default function LineMovementChart({ snapshots, width = 120, height = 32 }: LineMovementChartProps) {
|
||||
const pts = (snapshots || []).map((s) => Number(s.line)).filter((n) => Number.isFinite(n));
|
||||
if (pts.length === 0) return null;
|
||||
|
||||
const opening = pts[0];
|
||||
const current = pts[pts.length - 1];
|
||||
const delta = current - opening;
|
||||
const stroke = Math.abs(delta) < 0.5 ? 'var(--text-tertiary, #6B6B7B)' : delta > 0 ? '#00D4A0' : '#FF4D4D';
|
||||
|
||||
const min = Math.min(...pts);
|
||||
const max = Math.max(...pts);
|
||||
const range = max - min || 1;
|
||||
const pad = 4;
|
||||
const innerH = height - pad * 2;
|
||||
const stepX = pts.length > 1 ? width / (pts.length - 1) : 0;
|
||||
const yScale = (v: number) => pad + innerH - ((v - min) / range) * innerH;
|
||||
|
||||
const polyline =
|
||||
pts.length > 1
|
||||
? pts.map((v, i) => `${(i * stepX).toFixed(1)},${yScale(v).toFixed(1)}`).join(' ')
|
||||
: `0,${(height / 2).toFixed(1)} ${width},${(height / 2).toFixed(1)}`;
|
||||
|
||||
return (
|
||||
<svg
|
||||
viewBox={`0 0 ${width} ${height}`}
|
||||
width={width}
|
||||
height={height}
|
||||
role="img"
|
||||
aria-label={`Line moved from ${opening} to ${current}`}
|
||||
style={{ display: 'block' }}
|
||||
>
|
||||
<polyline points={polyline} fill="none" stroke={stroke} strokeWidth={2} strokeLinejoin="round" strokeLinecap="round" />
|
||||
{pts.length > 1 && <circle cx={width} cy={yScale(current)} r={2.5} fill={stroke} />}
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user