Bring back the big timer; the bar pill takes over on scroll

The big asleep/awake card returns below the frozen bar, and the bar
pill becomes its twin: both tick together, but the pill stays
visibility-hidden — slot reserved, so the bar never shifts — while the
card is on screen, appearing only once the card scrolls out of sight
(rAF-throttled scroll check against the bar's bottom edge).
This commit is contained in:
Alexander Heldt
2026-07-13 21:31:29 +00:00
parent 8981214e55
commit cdd701f0b4
4 changed files with 90 additions and 8 deletions
+49 -6
View File
@@ -673,29 +673,59 @@
let bigClockState = null; // "asleep" | "awake" | null
let bigClockSince = 0;
function renderBigClock(events) {
// The counter renders twice: the big card at the top of the page and the
// compact pill in the frozen day bar. The pill only *shows* once the big
// card is scrolled out of sight (see updateBarClockMode); while the card is
// visible the pill is invisible but keeps its slot so the bar never shifts.
function updateBarClockMode() {
const pill = document.getElementById("bar-clock");
const icon = document.getElementById("bar-clock-icon");
const time = document.getElementById("bar-clock-time");
if (!bigClockState || pill.hidden) return;
const card = document.getElementById("big-clock");
const bar = document.querySelector(".day-bar");
const cardVisible =
card.getBoundingClientRect().bottom > bar.getBoundingClientRect().bottom;
pill.classList.toggle("standby", cardVisible);
}
function renderBigClock(events) {
const card = document.getElementById("big-clock");
const label = document.getElementById("bc-label");
const time = document.getElementById("bc-time");
const since = document.getElementById("bc-since");
const pill = document.getElementById("bar-clock");
const icon = document.getElementById("bar-clock-icon");
const ptime = document.getElementById("bar-clock-time");
const { state, since: ts } = currentSleepState(events);
bigClockState = state;
bigClockSince = ts;
if (!state) {
card.hidden = true;
pill.hidden = true;
return;
}
card.hidden = false;
pill.hidden = false;
card.classList.toggle("asleep", state === "asleep");
card.classList.toggle("awake", state === "awake");
pill.classList.toggle("asleep", state === "asleep");
pill.classList.toggle("awake", state === "awake");
label.textContent = state === "asleep" ? "Asleep for" : "Awake for";
const counter = formatCounter(Date.now() - ts);
time.textContent = counter;
ptime.textContent = counter;
since.textContent = `since ${formatTime(ts)}`;
icon.textContent = state === "asleep" ? "😴" : "☀️";
pill.title = `${state === "asleep" ? "Asleep" : "Awake"} since ${formatTime(ts)}`;
time.textContent = formatCounter(Date.now() - ts);
updateBarClockMode();
}
function tickBigClock() {
if (!bigClockState) return;
const time = document.getElementById("bar-clock-time");
if (time) time.textContent = formatCounter(Date.now() - bigClockSince);
const counter = formatCounter(Date.now() - bigClockSince);
const time = document.getElementById("bc-time");
const ptime = document.getElementById("bar-clock-time");
if (time) time.textContent = counter;
if (ptime) ptime.textContent = counter;
}
function renderWindowList(listId, emptyId, windows, ongoingLabel, extraClass) {
@@ -2309,6 +2339,19 @@
b.addEventListener("click", () => setChartDays(Number(b.dataset.days)));
});
// Swap the timer pill in/out of the frozen bar as the big card scrolls
// past. rAF-throttled: scroll events fire far more often than we can paint.
{
let queued = false;
const onScroll = () => {
if (queued) return;
queued = true;
requestAnimationFrame(() => { queued = false; updateBarClockMode(); });
};
window.addEventListener("scroll", onScroll, { passive: true });
window.addEventListener("resize", onScroll, { passive: true });
}
dayPicker.value = ymd(new Date());
dayPicker.addEventListener("change", render);