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:
+47
-4
@@ -673,29 +673,59 @@
|
||||
let bigClockState = null; // "asleep" | "awake" | null
|
||||
let bigClockSince = 0;
|
||||
|
||||
// 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");
|
||||
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 time = document.getElementById("bar-clock-time");
|
||||
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);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
[
|
||||
{ "date": "2026-07-13", "text": "The big timer is back at the top — scroll past it and it hops into the frozen bar instead" },
|
||||
{ "date": "2026-07-13", "text": "The top bar fits on one row: timer, day arrows, date picker and Today" },
|
||||
{ "date": "2026-07-13", "text": "Pick how many days the charts cover — 7, 14 or 30 (default 7)" },
|
||||
{ "date": "2026-07-13", "text": "Much finer y-axis on the food chart" },
|
||||
|
||||
+9
-2
@@ -78,8 +78,9 @@
|
||||
|
||||
<main>
|
||||
<section class="day-bar">
|
||||
<!-- Compact asleep/awake counter (the old big clock); hidden until a
|
||||
sleep event exists. Hover/long-press shows "since" via title. -->
|
||||
<!-- Compact twin of the big timer below: invisible (but keeping its
|
||||
slot) while the big card is on screen, shown once it scrolls
|
||||
away. Hidden entirely until a sleep event exists. -->
|
||||
<div id="bar-clock" class="bar-clock" hidden>
|
||||
<span id="bar-clock-icon" aria-hidden="true"></span>
|
||||
<span id="bar-clock-time"></span>
|
||||
@@ -90,6 +91,12 @@
|
||||
<button type="button" id="day-today" class="ghost">Today</button>
|
||||
</section>
|
||||
|
||||
<section id="big-clock" class="big-clock" hidden>
|
||||
<div class="bc-label" id="bc-label">—</div>
|
||||
<div class="bc-time" id="bc-time">0:00</div>
|
||||
<div class="bc-since" id="bc-since"></div>
|
||||
</section>
|
||||
|
||||
<section class="quick-actions">
|
||||
<h2>Log event</h2>
|
||||
<div class="grid">
|
||||
|
||||
@@ -179,9 +179,40 @@ body::before {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.bar-clock[hidden] { display: none; }
|
||||
/* Big timer still on screen: keep the pill's slot but show nothing. */
|
||||
.bar-clock.standby { visibility: hidden; }
|
||||
.bar-clock.asleep { background: color-mix(in srgb, var(--sleep) 18%, var(--surface)); color: var(--sleep); }
|
||||
.bar-clock.awake { background: color-mix(in srgb, var(--accent) 18%, var(--surface)); color: var(--accent); }
|
||||
|
||||
.big-clock {
|
||||
text-align: center;
|
||||
padding: 24px 16px;
|
||||
}
|
||||
.big-clock .bc-label {
|
||||
font-size: 0.8rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
color: var(--muted);
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.big-clock .bc-time {
|
||||
font-size: 3.25rem;
|
||||
font-weight: 700;
|
||||
font-variant-numeric: tabular-nums;
|
||||
line-height: 1.05;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
.big-clock .bc-since {
|
||||
margin-top: 6px;
|
||||
font-size: 0.8rem;
|
||||
color: var(--muted);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.big-clock.asleep { background: linear-gradient(180deg, var(--surface), color-mix(in srgb, var(--sleep) 10%, var(--surface))); }
|
||||
.big-clock.asleep .bc-time { color: var(--sleep); }
|
||||
.big-clock.awake { background: linear-gradient(180deg, var(--surface), color-mix(in srgb, var(--accent) 10%, var(--surface))); }
|
||||
.big-clock.awake .bc-time { color: var(--accent); }
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
||||
|
||||
Reference in New Issue
Block a user