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:
+49
-6
@@ -673,29 +673,59 @@
|
|||||||
let bigClockState = null; // "asleep" | "awake" | null
|
let bigClockState = null; // "asleep" | "awake" | null
|
||||||
let bigClockSince = 0;
|
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 pill = document.getElementById("bar-clock");
|
||||||
const icon = document.getElementById("bar-clock-icon");
|
if (!bigClockState || pill.hidden) return;
|
||||||
const time = document.getElementById("bar-clock-time");
|
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);
|
const { state, since: ts } = currentSleepState(events);
|
||||||
bigClockState = state;
|
bigClockState = state;
|
||||||
bigClockSince = ts;
|
bigClockSince = ts;
|
||||||
if (!state) {
|
if (!state) {
|
||||||
|
card.hidden = true;
|
||||||
pill.hidden = true;
|
pill.hidden = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
card.hidden = false;
|
||||||
pill.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("asleep", state === "asleep");
|
||||||
pill.classList.toggle("awake", state === "awake");
|
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" ? "😴" : "☀️";
|
icon.textContent = state === "asleep" ? "😴" : "☀️";
|
||||||
pill.title = `${state === "asleep" ? "Asleep" : "Awake"} since ${formatTime(ts)}`;
|
pill.title = `${state === "asleep" ? "Asleep" : "Awake"} since ${formatTime(ts)}`;
|
||||||
time.textContent = formatCounter(Date.now() - ts);
|
updateBarClockMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
function tickBigClock() {
|
function tickBigClock() {
|
||||||
if (!bigClockState) return;
|
if (!bigClockState) return;
|
||||||
const time = document.getElementById("bar-clock-time");
|
const counter = formatCounter(Date.now() - bigClockSince);
|
||||||
if (time) time.textContent = 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) {
|
function renderWindowList(listId, emptyId, windows, ongoingLabel, extraClass) {
|
||||||
@@ -2309,6 +2339,19 @@
|
|||||||
b.addEventListener("click", () => setChartDays(Number(b.dataset.days)));
|
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.value = ymd(new Date());
|
||||||
dayPicker.addEventListener("change", render);
|
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": "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": "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" },
|
{ "date": "2026-07-13", "text": "Much finer y-axis on the food chart" },
|
||||||
|
|||||||
+9
-2
@@ -78,8 +78,9 @@
|
|||||||
|
|
||||||
<main>
|
<main>
|
||||||
<section class="day-bar">
|
<section class="day-bar">
|
||||||
<!-- Compact asleep/awake counter (the old big clock); hidden until a
|
<!-- Compact twin of the big timer below: invisible (but keeping its
|
||||||
sleep event exists. Hover/long-press shows "since" via title. -->
|
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>
|
<div id="bar-clock" class="bar-clock" hidden>
|
||||||
<span id="bar-clock-icon" aria-hidden="true"></span>
|
<span id="bar-clock-icon" aria-hidden="true"></span>
|
||||||
<span id="bar-clock-time"></span>
|
<span id="bar-clock-time"></span>
|
||||||
@@ -90,6 +91,12 @@
|
|||||||
<button type="button" id="day-today" class="ghost">Today</button>
|
<button type="button" id="day-today" class="ghost">Today</button>
|
||||||
</section>
|
</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">
|
<section class="quick-actions">
|
||||||
<h2>Log event</h2>
|
<h2>Log event</h2>
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
|
|||||||
@@ -179,9 +179,40 @@ body::before {
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
.bar-clock[hidden] { display: none; }
|
.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.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); }
|
.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 {
|
.grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
||||||
|
|||||||
Reference in New Issue
Block a user