Update heatmap with legend and extra color level

This commit is contained in:
Alex Selimov 2026-05-13 07:18:04 -04:00
parent 53084c4e6f
commit d9784bdbd4
2 changed files with 75 additions and 11 deletions

View file

@ -30,9 +30,10 @@ export function constructWeeks() {
function getColor(count) {
if (count === 0) return "var(--color-empty)";
if (count <= 3) return "var(--color-low)";
if (count <= 10) return "var(--color-mid)";
return "var(--color-high)";
if (count <= 2) return "var(--color-l1)";
if (count <= 5) return "var(--color-l2)";
if (count <= 10) return "var(--color-l3)";
return "var(--color-l4)";
}
export function render(weeks, counts) {
@ -85,6 +86,42 @@ export function render(weeks, counts) {
return svg;
}
export function totalCount(counts) {
return Object.values(counts).reduce((s, v) => s + v, 0);
}
export function renderLegend() {
const CELL = 11;
const GAP = 3;
const SHIFT = CELL + GAP;
const svgNS = "http://www.w3.org/2000/svg";
const svg = document.createElementNS(svgNS, "svg");
svg.setAttribute("width", 5 * SHIFT - GAP);
svg.setAttribute("height", CELL);
svg.style.display = "inline-block";
svg.style.verticalAlign = "middle";
const colors = [
"var(--color-empty)",
"var(--color-l1)",
"var(--color-l2)",
"var(--color-l3)",
"var(--color-l4)",
];
colors.forEach((color, i) => {
const rect = document.createElementNS(svgNS, "rect");
rect.setAttribute("x", i * SHIFT);
rect.setAttribute("y", 0);
rect.setAttribute("width", CELL);
rect.setAttribute("height", CELL);
rect.setAttribute("fill", color);
rect.setAttribute("rx", 2);
svg.appendChild(rect);
});
return svg;
}
export function setupTooltips(svg) {
const tooltip = document.getElementById("tooltip");
document.body.appendChild(tooltip);
@ -94,7 +131,7 @@ export function setupTooltips(svg) {
const date = e.target.dataset.date;
const count = e.target.dataset.count;
tooltip.textContent = `${date} - ${count} commit${count == 1 ? "" : "s"}`;
tooltip.textContent = `${date} - ${count} contribution${count == 1 ? "" : "s"}`;
tooltip.classList.remove("hidden");
});

View file

@ -4,18 +4,31 @@
<style>
:root {
--color-empty: {{ $p.color_empty_dark }};
--color-low: {{ $p.color_low }};
--color-mid: {{ $p.color_mid }};
--color-high: {{ $p.color_high }};
--color-l1: {{ $p.color_max }};
--color-l2: {{ $p.color_high }};
--color-l3: {{ $p.color_mid }};
--color-l4: {{ $p.color_low }};
}
@media (prefers-color-scheme: light) {
:root {
--color-empty: {{ $p.color_empty_light }};
}
:root {
--color-empty: {{ $p.color_empty_light }};
--color-l1: {{ $p.color_low }};
--color-l2: {{ $p.color_mid }};
--color-l3: {{ $p.color_high }};
--color-l4: {{ $p.color_max }};
}
}
#heatmap { overflow-x: auto; }
#heatmap text { font-family: inherit; fill: currentColor; }
#heatmap-caption {font-size: 0.8em; text-align: center; margin-top: 0px;}
#heatmap-footer {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.75em;
margin-top: 4px;
}
#heatmap-legend { display: flex; align-items: center; gap: 4px; }
#tooltip {
position: fixed;
background: #333;
@ -30,7 +43,7 @@
<div id="heatmap"></div>
<script type="module">
import {flattenData, constructWeeks, render, setupTooltips} from "{{ $js.RelPermalink }}"
import {flattenData, constructWeeks, render, setupTooltips, totalCount, renderLegend} from "{{ $js.RelPermalink }}"
const data = await fetch("/activity.json").then((r) => r.json());
const counts = flattenData(data);
@ -38,7 +51,21 @@
const svg = render(weeks, counts);
document.getElementById("heatmap").appendChild(svg);
setupTooltips(svg);
document.getElementById("contribution-count").textContent =
`${totalCount(counts)} contributions in the last year`;
const legendEl = document.getElementById("heatmap-legend");
legendEl.appendChild(document.createTextNode("Less"));
legendEl.appendChild(renderLegend());
legendEl.appendChild(document.createTextNode("More"));
</script>
<div id="heatmap-footer">
<span id="contribution-count"></span>
<span id="heatmap-legend"></span>
</div>
{{ with $p.caption }}
<p id="heatmap-caption"> {{ . | safeHTML }} </p>
{{ end }}