Make the Sleep trend chart follow the selected day
The curves were always computed relative to today regardless of the day picker. Anchor them to selectedDay() instead: a past day shows its full 24h curve against the day before it and the average of the days leading up to it, with no now-cap and no projection. Legend and tooltip labels show the actual dates when viewing a past day.
This commit is contained in:
+23
-12
@@ -1278,9 +1278,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ---------- sleep trend ----------
|
// ---------- sleep trend ----------
|
||||||
// Cumulative hours slept as the day progresses: today's running total (up to
|
// Cumulative hours slept as the selected day progresses: that day's running
|
||||||
// now) against yesterday's full curve and the mean of the last N full days,
|
// total (up to now when it's today, else the full 24h) against the previous
|
||||||
// where N follows the 7/14/30 chart-days picker.
|
// day's full curve and the mean of the N full days before it, where N
|
||||||
|
// follows the 7/14/30 chart-days picker.
|
||||||
// Each curve is a list of { x: hour-of-day 0..24, y: cumulative hours }.
|
// Each curve is a list of { x: hour-of-day 0..24, y: cumulative hours }.
|
||||||
function sleepTrendCurves(events) {
|
function sleepTrendCurves(events) {
|
||||||
const HOUR = 3_600_000;
|
const HOUR = 3_600_000;
|
||||||
@@ -1294,8 +1295,10 @@
|
|||||||
}
|
}
|
||||||
return total;
|
return total;
|
||||||
};
|
};
|
||||||
|
const day = selectedDay();
|
||||||
|
const isToday = ymd(day) === ymd(new Date());
|
||||||
const dayStartTs = (daysAgo) => {
|
const dayStartTs = (daysAgo) => {
|
||||||
const d = startOfDay(new Date());
|
const d = startOfDay(day);
|
||||||
d.setDate(d.getDate() - daysAgo);
|
d.setDate(d.getDate() - daysAgo);
|
||||||
return d.getTime();
|
return d.getTime();
|
||||||
};
|
};
|
||||||
@@ -1314,7 +1317,8 @@
|
|||||||
return pts;
|
return pts;
|
||||||
};
|
};
|
||||||
|
|
||||||
const today = curveFor(dayStartTs(0), Date.now());
|
// A past day is complete, so its curve runs the full 24h uncapped.
|
||||||
|
const today = curveFor(dayStartTs(0), isToday ? Date.now() : null);
|
||||||
|
|
||||||
const yesterdayCurve = curveFor(dayStartTs(1));
|
const yesterdayCurve = curveFor(dayStartTs(1));
|
||||||
const yesterday = yesterdayCurve[24].y > 0 ? yesterdayCurve : null;
|
const yesterday = yesterdayCurve[24].y > 0 ? yesterdayCurve : null;
|
||||||
@@ -1339,9 +1343,10 @@
|
|||||||
// the average day typically adds between now and midnight. That respects
|
// the average day typically adds between now and midnight. That respects
|
||||||
// the time-of-day rhythm (night sleep, nap clusters), unlike a linear
|
// the time-of-day rhythm (night sleep, nap clusters), unlike a linear
|
||||||
// rate extrapolation, which overshoots wildly just after a long night.
|
// rate extrapolation, which overshoots wildly just after a long night.
|
||||||
// No history → no average → no projection.
|
// No history → no average → no projection. Past days are already complete,
|
||||||
|
// so there is nothing to project.
|
||||||
let projected = null;
|
let projected = null;
|
||||||
if (avg) {
|
if (avg && isToday) {
|
||||||
const nowPt = today[today.length - 1];
|
const nowPt = today[today.length - 1];
|
||||||
const avgAt = (x) => {
|
const avgAt = (x) => {
|
||||||
const lo = Math.floor(x);
|
const lo = Math.floor(x);
|
||||||
@@ -1355,7 +1360,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { today, yesterday, avg, avgDays, projected };
|
// Labels follow the selection so a past day reads as its date, not "Today".
|
||||||
|
const fmtDay = (daysAgo) =>
|
||||||
|
new Date(dayStartTs(daysAgo)).toLocaleDateString(undefined, { month: "short", day: "numeric" });
|
||||||
|
const dayLabel = isToday ? "Today" : fmtDay(0);
|
||||||
|
const prevDayLabel = isToday ? "Yesterday" : fmtDay(1);
|
||||||
|
|
||||||
|
return { today, yesterday, avg, avgDays, projected, dayLabel, prevDayLabel };
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawSleepTrendChart(curves, target) {
|
function drawSleepTrendChart(curves, target) {
|
||||||
@@ -1369,9 +1380,9 @@
|
|||||||
const series = [
|
const series = [
|
||||||
// Reference lines first so today (and its projected tail) draw on top.
|
// Reference lines first so today (and its projected tail) draw on top.
|
||||||
{ pts: curves.avg, cls: "trend-avg", label: `${curves.avgDays}-day average` },
|
{ pts: curves.avg, cls: "trend-avg", label: `${curves.avgDays}-day average` },
|
||||||
{ pts: curves.yesterday, cls: "trend-yesterday", label: "Yesterday" },
|
{ pts: curves.yesterday, cls: "trend-yesterday", label: curves.prevDayLabel },
|
||||||
{ pts: curves.projected, cls: "trend-projected", label: "Projected end of day" },
|
{ pts: curves.projected, cls: "trend-projected", label: "Projected end of day" },
|
||||||
{ pts: curves.today, cls: "trend-today", label: "Today" },
|
{ pts: curves.today, cls: "trend-today", label: curves.dayLabel },
|
||||||
].filter(s => s.pts && s.pts.length > 1);
|
].filter(s => s.pts && s.pts.length > 1);
|
||||||
|
|
||||||
// The axis must reach the goal band's top even when the data doesn't yet.
|
// The axis must reach the goal band's top even when the data doesn't yet.
|
||||||
@@ -1452,11 +1463,11 @@
|
|||||||
// write each curve's slept-hours total into its chip.
|
// write each curve's slept-hours total into its chip.
|
||||||
const chip = (id) => document.getElementById(id);
|
const chip = (id) => document.getElementById(id);
|
||||||
const hrs = (pts) => `${pts[pts.length - 1].y.toFixed(1)}h`;
|
const hrs = (pts) => `${pts[pts.length - 1].y.toFixed(1)}h`;
|
||||||
chip("legend-trend-today-text").textContent = `Today ${hrs(curves.today)}`;
|
chip("legend-trend-today-text").textContent = `${curves.dayLabel} ${hrs(curves.today)}`;
|
||||||
const yLegend = chip("legend-trend-yesterday");
|
const yLegend = chip("legend-trend-yesterday");
|
||||||
yLegend.hidden = !curves.yesterday;
|
yLegend.hidden = !curves.yesterday;
|
||||||
if (curves.yesterday) {
|
if (curves.yesterday) {
|
||||||
chip("legend-trend-yesterday-text").textContent = `Yesterday ${hrs(curves.yesterday)}`;
|
chip("legend-trend-yesterday-text").textContent = `${curves.prevDayLabel} ${hrs(curves.yesterday)}`;
|
||||||
}
|
}
|
||||||
const aLegend = chip("legend-trend-avg");
|
const aLegend = chip("legend-trend-avg");
|
||||||
aLegend.hidden = !curves.avg;
|
aLegend.hidden = !curves.avg;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
[
|
[
|
||||||
|
{ "date": "2026-07-17", "text": "The Sleep trend chart follows the selected day — pick a past day to see its full curve against the day before and the average leading up to it" },
|
||||||
{ "date": "2026-07-15", "text": "The Sleep trend y-axis is stretched above 10h, giving the hours around the sleep goal most of the chart" },
|
{ "date": "2026-07-15", "text": "The Sleep trend y-axis is stretched above 10h, giving the hours around the sleep goal most of the chart" },
|
||||||
{ "date": "2026-07-15", "text": "The Sleep trend chart shows the sleep goal for your puppy's age as a shaded band — the projection chip gets a ✓ when today is on track" },
|
{ "date": "2026-07-15", "text": "The Sleep trend chart shows the sleep goal for your puppy's age as a shaded band — the projection chip gets a ✓ when today is on track" },
|
||||||
{ "date": "2026-07-15", "text": "Logging a training session while viewing another day puts it on that day (the snackbar tells you where it went)" },
|
{ "date": "2026-07-15", "text": "Logging a training session while viewing another day puts it on that day (the snackbar tells you where it went)" },
|
||||||
|
|||||||
+1
-1
@@ -222,7 +222,7 @@
|
|||||||
|
|
||||||
<section class="patterns" data-panel="sleep-trend">
|
<section class="patterns" data-panel="sleep-trend">
|
||||||
<h2>Sleep trend</h2>
|
<h2>Sleep trend</h2>
|
||||||
<svg id="chart-sleep-trend" class="chart-svg" viewBox="0 0 320 220" role="img" aria-label="Cumulative sleep hours through the day: today, yesterday, the recent average and the projected end-of-day total, with the age-based sleep goal band"></svg>
|
<svg id="chart-sleep-trend" class="chart-svg" viewBox="0 0 320 220" role="img" aria-label="Cumulative sleep hours through the selected day, the day before it, the recent average and (for today) the projected end-of-day total, with the age-based sleep goal band"></svg>
|
||||||
<div class="legend">
|
<div class="legend">
|
||||||
<span class="lg trend-today"><span class="sw"></span><span id="legend-trend-today-text">Today</span></span>
|
<span class="lg trend-today"><span class="sw"></span><span id="legend-trend-today-text">Today</span></span>
|
||||||
<span class="lg trend-projected" id="legend-trend-projected" hidden><span class="sw"></span><span id="legend-trend-projected-text">Projected</span></span>
|
<span class="lg trend-projected" id="legend-trend-projected" hidden><span class="sw"></span><span id="legend-trend-projected-text">Projected</span></span>
|
||||||
|
|||||||
Reference in New Issue
Block a user