~bigbes/lethe

ref: 2d9d2b8ec08ee09cc64c5d925ab85716b1d7d1fb lethe/web/src/primitives/StackedBars.tsx -rw-r--r-- 1.9 KiB
2d9d2b8e — Eugene Blikh search: add /api/v1/search API and opencode collector parser 24 days ago
                                                                                
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import type React from 'react'

interface StackedBarsProps {
  days: { tools: Record<string, number> }[]
  toolColor: (tool: string) => string
  w?: number
  h?: number
}

export function StackedBars({ days, toolColor, w = 600, h = 130 }: StackedBarsProps): React.JSX.Element {
  const n = days.length
  if (n === 0) {
    return (
      <svg width={w} height={h} viewBox={`0 0 ${w} ${h}`} style={{ display: 'block', width: '100%' }} />
    )
  }

  // Find the maximum total across all days for scaling.
  const totals = days.map(d => Object.values(d.tools).reduce((a, b) => a + b, 0))
  const maxTotal = Math.max(...totals, 1)

  const barW = Math.max(1, (w / n) - 2)

  return (
    <svg
      viewBox={`0 0 ${w} ${h}`}
      style={{ display: 'block', width: '100%', height: h }}
      preserveAspectRatio="none"
    >
      {/* Y axis grid lines */}
      {[0, 0.25, 0.5, 0.75, 1].map(frac => {
        const y = h - frac * h
        return (
          <line
            key={frac}
            x1={0}
            x2={w}
            y1={y}
            y2={y}
            stroke="var(--rule-2)"
            strokeWidth={0.5}
          />
        )
      })}
      {days.map((day, i) => {
        const x = i * (w / n) + 1
        const tools = Object.entries(day.tools).sort((a, b) => a[0].localeCompare(b[0]))
        let yBottom = h
        return (
          <g key={i}>
            {tools.map(([tool, count]) => {
              if (count <= 0) return null
              const barH = (count / maxTotal) * h
              yBottom -= barH
              return (
                <rect
                  key={tool}
                  x={x}
                  y={yBottom}
                  width={barW}
                  height={barH}
                  fill={toolColor(tool)}
                  opacity={0.85}
                />
              )
            })}
          </g>
        )
      })}
    </svg>
  )
}