Let the weigh-in list fold on its own

The Weight panel holds three things, and only one of them grows: the latest
figure and the curve stay one screen forever, while the row list gains a line
every weigh-in until it pushes everything else off. Folding the panel to get rid
of it also takes away the two parts worth keeping.

So the fold mechanism now works on any keyed element inside main, not only on a
section, and the list moves into a block that folds by its own h3 while the
panel keeps folding by its h2. The two nest without special handling — separate
keys, and a folded section hides the block along with the rest, so the block's
own state is simply there again when the section reopens.

The CSS generalises from section.collapsible to a class, but scoped to main:
the pedigree tree uses .collapsed for its own branches, and an unscoped
selector would have folded every card in it.
This commit is contained in:
Alexander Heldt
2026-08-31 22:11:27 +00:00
parent 8f9e8783fa
commit 50d0d8a726
4 changed files with 51 additions and 21 deletions
+19 -14
View File
@@ -4193,10 +4193,15 @@
});
// ---------- collapsible panels ----------
// Each main section carrying a data-panel key can be folded by clicking its
// Anything in main carrying a data-panel key can be folded by clicking its
// heading; collapsed keys are remembered across reloads. State is device-global
// (like the theme), so a single non-namespaced key is fine — it's not per-user
// data. Only collapsed panels are stored, so newly added panels default open.
//
// A whole section heads with an h2, a foldable block inside one with an h3.
// The two nest without special handling: each has its own key, and a folded
// section hides the block along with everything else it holds, which leaves
// the block's own state to reappear as it was when the section reopens.
const PANELS_KEY = "puppy-tracker:panels:v1";
function loadPanelState() {
try {
@@ -4209,25 +4214,25 @@
}
function initPanels() {
const state = loadPanelState();
document.querySelectorAll("main section[data-panel]").forEach(section => {
const key = section.dataset.panel;
const h2 = section.querySelector(":scope > h2");
if (!h2) return;
section.classList.add("collapsible");
document.querySelectorAll("main [data-panel]").forEach(panel => {
const key = panel.dataset.panel;
const head = panel.querySelector(":scope > h2, :scope > h3");
if (!head) return;
panel.classList.add("collapsible");
const collapsed = !!state[key];
section.classList.toggle("collapsed", collapsed);
h2.setAttribute("role", "button");
h2.setAttribute("tabindex", "0");
h2.setAttribute("aria-expanded", String(!collapsed));
panel.classList.toggle("collapsed", collapsed);
head.setAttribute("role", "button");
head.setAttribute("tabindex", "0");
head.setAttribute("aria-expanded", String(!collapsed));
const toggle = () => {
const nowCollapsed = section.classList.toggle("collapsed");
h2.setAttribute("aria-expanded", String(!nowCollapsed));
const nowCollapsed = panel.classList.toggle("collapsed");
head.setAttribute("aria-expanded", String(!nowCollapsed));
const s = loadPanelState();
if (nowCollapsed) s[key] = true; else delete s[key];
savePanelState(s);
};
h2.addEventListener("click", toggle);
h2.addEventListener("keydown", (e) => {
head.addEventListener("click", toggle);
head.addEventListener("keydown", (e) => {
if (e.key === "Enter" || e.key === " ") { e.preventDefault(); toggle(); }
});
});
+1
View File
@@ -1,4 +1,5 @@
[
{ "date": "2026-08-31", "text": "The list of weigh-ins under the Weight chart folds away on its own now — tap “History” inside the panel to collapse just the rows and keep the latest figure and the curve on screen. It remembers the choice like the panels do, and folding the whole Weight panel still takes everything with it" },
{ "date": "2026-08-31", "text": "Walks now get the same two pattern views sleep has. “When … walks” is a day-per-row grid shaded where a walk was on, so you can see at a glance whether the routine is actually regular or drifts around. “Walk trend” draws the minutes walked so far at each point of the day against yesterday and the average over the picked window, with the age-based goal as a line and a ✓ once the day clears it — the line climbs only while a walk is on, so every step is one walk. Both appear under the Walks panel as soon as you have logged a walk, and stay out of the way until then" },
{ "date": "2026-08-31", "text": "The 7d / 14d / 30d buttons now set the window for the Timing panel too. Until now the typical, shortest and longest gaps between pees, poos and meals were always measured over the last 7 days whatever you picked; switch to 30d and they are measured over 30, which settles down the typical gap once there is a month of history to draw on" },
{ "date": "2026-08-31", "text": "The charts are grouped by subject instead of all sharing one “Last 7 days” panel. Sleep hours per day is now its own Sleep panel, sitting just above the “When … sleeps” timeline with the rest of the sleep views. Daily counts and Food have joined the by-hour chart in one “Pees, poos & meals” panel, so how many a day, how much food went with them and what hours they fall in read together. Minutes walked per day has moved into the Walks panel. The 7d / 14d / 30d picker now sits in the Sleep panel and still sets the window for every one of these charts" },
+8 -2
View File
@@ -342,8 +342,14 @@
<svg id="chart-weight" class="chart-svg" viewBox="0 0 320 180" role="img" aria-label="Weight in kilograms over time"></svg>
<p id="weight-point-info" class="muted-note weight-point-info"></p>
</div>
<ul id="weight-list" class="wake-list"></ul>
<p id="weight-empty" class="empty">No weigh-ins logged yet.</p>
<!-- Folds on its own h3, independently of the Weight panel around it:
the row list grows by one every weigh-in, and the summary and the
curve above it are what you usually want left on screen. -->
<div class="subpanel" data-panel="weight-history">
<h3>History</h3>
<ul id="weight-list" class="wake-list"></ul>
<p id="weight-empty" class="empty">No weigh-ins logged yet.</p>
</div>
</section>
<section class="notes-log" data-panel="notes">
+23 -5
View File
@@ -1207,14 +1207,19 @@ button.ex-edit { padding: 6px 12px; flex-shrink: 0; }
.walk-chart { margin-top: 16px; }
/* ---------- collapsible panels ---------- */
section.collapsible > h2 {
/* A panel folds by its own heading: h2 for a whole section, h3 for a block
inside one. Scoped to main on purpose — the pedigree tree uses .collapsed
for its own branches, and a bare selector here would fold those too. */
main .collapsible > h2,
main .collapsible > h3 {
cursor: pointer;
position: relative;
padding-right: 20px;
-webkit-user-select: none;
user-select: none;
}
section.collapsible > h2::after {
main .collapsible > h2::after,
main .collapsible > h3::after {
content: "▾";
position: absolute;
right: 0;
@@ -1224,9 +1229,22 @@ section.collapsible > h2::after {
color: var(--muted);
font-size: 0.85em;
}
section.collapsed > h2::after { transform: translateY(-50%) rotate(-90deg); }
section.collapsed > h2 { margin-bottom: 0; }
section.collapsed > :not(h2) { display: none; }
main .collapsed > h2::after,
main .collapsed > h3::after { transform: translateY(-50%) rotate(-90deg); }
main .collapsed > h2,
main .collapsed > h3 { margin-bottom: 0; }
main .collapsed > :not(h2):not(h3) { display: none; }
/* Sub-heading for a foldable block inside a panel: the section h2's look, a
size down, so it reads as subordinate to it rather than as a second panel. */
.subpanel > h3 {
font-size: 0.8rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--muted);
margin: 0 0 10px;
}
/* ---------- pedigree lookup ---------- */
.pedigree-screen {