diff --git a/src/app.js b/src/app.js index 5b0dc20..fbc4480 100644 --- a/src/app.js +++ b/src/app.js @@ -927,6 +927,27 @@ render(); } + // Which metrics the daily-counts chart shows. Device-global, toggled via the + // checkboxes under the chart. At least one is always kept on so the chart is + // never empty; an invalid/empty stored value falls back to all three. + const COUNTS_METRICS_KEY = "puppy-tracker:counts-metrics:v1"; + const COUNTS_METRIC_KEYS = ["pees", "poos", "meals"]; + function countsMetrics() { + let stored; + try { stored = JSON.parse(localStorage.getItem(COUNTS_METRICS_KEY)); } catch { /* ignore */ } + const on = Array.isArray(stored) + ? COUNTS_METRIC_KEYS.filter(k => stored.includes(k)) + : []; + return on.length ? on : COUNTS_METRIC_KEYS.slice(); + } + function setCountsMetrics(keys) { + // Never let the user hide everything โ€” keep at least one metric visible. + const on = COUNTS_METRIC_KEYS.filter(k => keys.includes(k)); + if (!on.length) { renderChartWindow(); return; } // restore the checkbox we just rejected + try { localStorage.setItem(COUNTS_METRICS_KEY, JSON.stringify(on)); } catch { /* ignore */ } + render(); + } + // Sync every "(last N days)" header and the picker's active button. function renderChartWindow() { const n = chartDays(); @@ -938,6 +959,10 @@ document.querySelectorAll(".chart-days-picker button").forEach(b => { b.classList.toggle("active", Number(b.dataset.days) === n); }); + const on = countsMetrics(); + document.querySelectorAll("#counts-metrics input[data-metric]").forEach(cb => { + cb.checked = on.includes(cb.dataset.metric); + }); } // ---------- daily charts ---------- @@ -1094,13 +1119,19 @@ const innerW = W - ML - MR; const innerH = H - MT - MB; - const rawMax = Math.max(...days.flatMap(d => [d.pees, d.poos, d.meals])); + const series = [ + { key: "pees", label: "Pees", cls: "bar-pee" }, + { key: "poos", label: "Poos", cls: "bar-poo" }, + { key: "meals", label: "Meals", cls: "bar-eat" }, + ].filter(s => countsMetrics().includes(s.key)); + + const rawMax = Math.max(0, ...days.flatMap(d => series.map(s => d[s.key]))); const { yMax, steps: ySteps } = niceAxis(rawMax); const groupGap = days.length > 14 ? 2 : 4; const innerBarGap = days.length > 14 ? 0.5 : 1.5; const groupW = (innerW - (days.length - 1) * groupGap) / days.length; - const barW = (groupW - 2 * innerBarGap) / 3; + const barW = (groupW - (series.length - 1) * innerBarGap) / series.length; const parts = []; for (let i = 0; i <= ySteps; i++) { @@ -1110,12 +1141,6 @@ parts.push(`${v}`); } - const series = [ - { key: "pees", label: "Pees", cls: "bar-pee" }, - { key: "poos", label: "Poos", cls: "bar-poo" }, - { key: "meals", label: "Meals", cls: "bar-eat" }, - ]; - const selYmd = ymd(selectedDay()); days.forEach((d, i) => { const isToday = i === days.length - 1; @@ -3296,6 +3321,14 @@ b.addEventListener("click", () => setChartDays(Number(b.dataset.days))); }); + document.querySelectorAll("#counts-metrics input[data-metric]").forEach(cb => { + cb.addEventListener("change", () => { + const on = [...document.querySelectorAll("#counts-metrics input[data-metric]:checked")] + .map(el => el.dataset.metric); + setCountsMetrics(on); + }); + }); + // 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. { diff --git a/src/changelog.json b/src/changelog.json index 5aa63b9..a109b2f 100644 --- a/src/changelog.json +++ b/src/changelog.json @@ -1,4 +1,5 @@ [ + { "date": "2026-08-02", "text": "The Daily counts chart now has Pees / Poos / Meals checkboxes so you can focus on just the metrics you care about โ€” untick the rest to see, say, only poos; your choice is remembered" }, { "date": "2026-08-01", "text": "Logging a pee or poo now sets off ๐Ÿ’ง/๐Ÿ’ฉ fireworks that shoot up from the bottom of the screen โ€” a little celebration you can switch off in Settings (and it honours a reduced-motion preference)" }, { "date": "2026-08-01", "text": "Tidied the header on long names and ages โ€” the name now truncates instead of shoving the buttons, and the age reads as a compact \"16 wk ยท 3 mo 3 wk\"; weight-log rows are a single line again (\"Aug 1 ยท 16 wk\")" }, { "date": "2026-07-26", "text": "Added a fan-chart view of the pedigree (toggle it in the header): your dog at the centre with each generation fanning outward as a ring, so many generations fit at once without the tree sprawling sideways โ€” tap a wedge for that dog, and repeated ancestors keep their colour" }, diff --git a/src/index.html b/src/index.html index 005f2ce..d57e449 100644 --- a/src/index.html +++ b/src/index.html @@ -203,10 +203,10 @@
Daily counts
-
- Pees - Poos - Meals +
+ + +