growth-activities / src /components /MiniLineChart.tsx
3v324v23's picture
init: growth-activities for hf spaces
243dc36
Raw
History Blame Contribute Delete
2.03 kB
import { cn } from "@/lib/utils"
type Point = { x: number; y: number }
function toPath(points: Point[]) {
if (!points.length) return ""
return points
.map((p, i) => `${i === 0 ? "M" : "L"} ${p.x.toFixed(2)} ${p.y.toFixed(2)}`)
.join(" ")
}
export default function MiniLineChart(props: { values: number[]; className?: string }) {
const w = 420
const h = 140
const padX = 10
const padY = 14
const max = Math.max(1, ...props.values)
const min = Math.min(0, ...props.values)
const span = Math.max(1, max - min)
const points: Point[] = props.values.map((v, idx) => {
const t = props.values.length <= 1 ? 0 : idx / (props.values.length - 1)
const x = padX + t * (w - padX * 2)
const y = padY + (1 - (v - min) / span) * (h - padY * 2)
return { x, y }
})
const areaPath =
points.length > 1
? `${toPath(points)} L ${points[points.length - 1].x} ${h - padY} L ${points[0].x} ${h - padY} Z`
: ""
return (
<svg viewBox={`0 0 ${w} ${h}`} className={cn("h-36 w-full", props.className)} role="img" aria-label="trend">
<defs>
<linearGradient id="trendFill" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="var(--accent)" stopOpacity="0.25" />
<stop offset="100%" stopColor="var(--accent)" stopOpacity="0" />
</linearGradient>
<linearGradient id="trendStroke" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stopColor="var(--accent)" stopOpacity="0.5" />
<stop offset="60%" stopColor="var(--accent)" stopOpacity="1" />
<stop offset="100%" stopColor="var(--accent)" stopOpacity="0.6" />
</linearGradient>
</defs>
<path d={areaPath} fill="url(#trendFill)" />
<path d={toPath(points)} fill="none" stroke="url(#trendStroke)" strokeWidth="2.6" strokeLinejoin="round" strokeLinecap="round" />
{points.length ? (
<circle cx={points[points.length - 1].x} cy={points[points.length - 1].y} r="4.4" fill="var(--accent)" />
) : null}
</svg>
)
}