146 lines
4 KiB
JavaScript
146 lines
4 KiB
JavaScript
export function flattenData(raw) {
|
|
const result = {};
|
|
for (const [date, repos] of Object.entries(raw)) {
|
|
result[date] = Object.values(repos).reduce((sum, n) => sum + n, 0);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
export function constructWeeks() {
|
|
const today = new Date();
|
|
today.setHours(0, 0, 0, 0);
|
|
|
|
const start = new Date(today);
|
|
start.setDate(today.getDate() - 52 * 7);
|
|
start.setDate(start.getDate() - start.getDay());
|
|
|
|
const weeks = [];
|
|
const cur = new Date(start);
|
|
while (cur <= today) {
|
|
const week = [];
|
|
for (let d = 0; d < 7; d++) {
|
|
const day = new Date(cur);
|
|
week.push(day <= today ? day : null);
|
|
cur.setDate(cur.getDate() + 1);
|
|
}
|
|
weeks.push(week);
|
|
}
|
|
return weeks;
|
|
}
|
|
|
|
function getColor(count) {
|
|
if (count === 0) return "var(--color-empty)";
|
|
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) {
|
|
const CELL = 13;
|
|
const GAP = 2;
|
|
const SHIFT = CELL + GAP;
|
|
|
|
const svgNS = "http://www.w3.org/2000/svg";
|
|
const svg = document.createElementNS(svgNS, "svg");
|
|
|
|
const svgWidth = weeks.length * SHIFT + 30;
|
|
const svgHeight = SHIFT * 7;
|
|
svg.setAttribute("viewBox", `0 0 ${svgWidth} ${svgHeight}`);
|
|
svg.setAttribute("width", "100%");
|
|
|
|
// Place the labels first
|
|
["Mon", "Wed", "Fri"].forEach((label, i) => {
|
|
const dayLabel = document.createElementNS(svgNS, "text");
|
|
dayLabel.setAttribute("x", 0);
|
|
dayLabel.setAttribute("y", (2 * i + 1) * SHIFT + 0.75 * CELL);
|
|
dayLabel.setAttribute("font-size", 12);
|
|
|
|
const text = document.createTextNode(label);
|
|
dayLabel.appendChild(text);
|
|
svg.appendChild(dayLabel);
|
|
});
|
|
|
|
weeks.forEach((week, col) => {
|
|
week.forEach((day, row) => {
|
|
if (day === null) {
|
|
return;
|
|
}
|
|
const key = day.toISOString().slice(0, 10); // Get the key in yyyy-mm-dd format
|
|
|
|
const count = counts[key] || 0;
|
|
|
|
const rect = document.createElementNS(svgNS, "rect");
|
|
rect.setAttribute("x", col * SHIFT + 30);
|
|
rect.setAttribute("y", row * SHIFT);
|
|
rect.setAttribute("height", CELL);
|
|
rect.setAttribute("width", CELL);
|
|
rect.setAttribute("fill", getColor(count));
|
|
rect.setAttribute("rx", 2);
|
|
rect.setAttribute("data-date", key);
|
|
rect.setAttribute("data-count", count);
|
|
|
|
svg.appendChild(rect);
|
|
});
|
|
});
|
|
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);
|
|
|
|
svg.addEventListener("mouseover", (e) => {
|
|
if (e.target.tagName != "rect") return;
|
|
const date = e.target.dataset.date;
|
|
const count = e.target.dataset.count;
|
|
|
|
tooltip.textContent = `${date} - ${count} contribution${count == 1 ? "" : "s"}`;
|
|
tooltip.classList.remove("hidden");
|
|
});
|
|
|
|
svg.addEventListener("mousemove", (e) => {
|
|
tooltip.style.left = e.clientX + 12 + "px";
|
|
tooltip.style.top = e.clientY - 28 + "px";
|
|
});
|
|
|
|
svg.addEventListener("mouseout", (e) => {
|
|
if (e.target.tagName === "rect") tooltip.classList.add("hidden");
|
|
});
|
|
}
|