File size: 2,034 Bytes
243dc36
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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>
  )
}