~bigbes/lethe

ref: ac2cd651f3d9d0315e1d4373743cd5434c446939 lethe/web/src/primitives/Heatmap.tsx -rw-r--r-- 1.2 KiB
ac2cd651 — Eugene Blikh docs: track tooling DX and lethe-oidc-stub follow-up (#10) a month 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
import type React from 'react'
import { EmptyState } from './EmptyState'

interface HeatmapProps {
  cells: { count: number }[]
  max: number
}

// 12 weeks × 7 days = 84 cells. Layout: columns = weeks (left→right), rows = days (top→bottom).
const WEEKS = 12
const DAYS = 7
const CELL_W = 18
const CELL_H = 9
const GAP_X = 2
const GAP_Y = 2

export function Heatmap({ cells, max }: HeatmapProps): React.JSX.Element {
  if (cells.length !== 84) {
    return <EmptyState glyph="∅" copy="no activity yet" />
  }

  const svgW = WEEKS * (CELL_W + GAP_X)
  const svgH = DAYS * (CELL_H + GAP_Y)
  const safeMax = max > 0 ? max : 1

  return (
    <svg
      viewBox={`0 0 ${svgW} ${svgH}`}
      style={{ display: 'block', width: '100%', height: svgH }}
    >
      {cells.map((cell, i) => {
        const week = Math.floor(i / DAYS)
        const day = i % DAYS
        const intensity = 0.1 + (cell.count / safeMax) * 0.85
        return (
          <rect
            key={i}
            x={week * (CELL_W + GAP_X)}
            y={day * (CELL_H + GAP_Y)}
            width={CELL_W}
            height={CELL_H}
            fill="var(--accent)"
            opacity={intensity}
            rx={1.5}
          />
        )
      })}
    </svg>
  )
}