Bracket sleep and walk pairs in the history log
A pair reads as two unrelated rows in the day's log: "Walk start 08:00" and "Walk end 08:30" sit apart from each other and, with a pee logged in between, apart from anything that happened while the puppy was out. A dotted rail down the margin ties the pair together, in that pair's colour. It runs the full span rather than only touching the two boundary rows, so whatever was logged in between falls inside the bracket — a pee taken on a walk now reads as having happened during it, which is the more useful claim and comes for free once the rail spans rows at all. Which rows a span covers comes from the same sleepWindowsForDay / walkWindowsForDay the Sleep and Walks panels draw, so a pair crossing midnight is treated identically in both places. Where a span's boundary isn't a row in this day — an ongoing walk, or one that ran over from yesterday — that end of the rail is left open and runs off the edge of the list, rather than anchoring on a row that didn't start or end anything. A boundary with no partner in the day draws nothing: a rail needs two rows to tie. The roles are named for the rendered list, which runs newest-first, so a span's last event is its top row and the rail is built downward from there. It costs no layout. The rail is drawn in the padding the section already has, left of the cards, so no row indents or moves; the anchored end stops level with the row's dot and the open ends overshoot the list gap so consecutive segments join into one line.
This commit is contained in:
+42
-1
@@ -1060,8 +1060,47 @@
|
||||
goalEl.hidden = !goal;
|
||||
}
|
||||
|
||||
// Which history rows sit inside a sleep or walk window, so the list can run a
|
||||
// dotted rail down the margin from a start row to its end row. It reuses the
|
||||
// windows the Sleep and Walks panels already draw, so a pair that crosses
|
||||
// midnight is treated the same way here as there. Rows logged in between — a
|
||||
// pee taken on a walk — fall inside the bracket, which is the point: the rail
|
||||
// says "this happened during that", not merely "these two are a pair".
|
||||
//
|
||||
// Roles are named for where the row sits in the *rendered* list, which runs
|
||||
// newest-first, so a span's chronologically last event is its top row. A span
|
||||
// whose boundary isn't itself a row here (an ongoing walk, or one that runs
|
||||
// past midnight) leaves that end open, and the rail runs off the list edge
|
||||
// rather than stopping at a row that didn't end anything.
|
||||
function historyRails(events, dayEvents, day) {
|
||||
const rails = new Map();
|
||||
const spans = [
|
||||
...sleepWindowsForDay(events, day).map(w => ({ ...w, kind: "sleep" })),
|
||||
...walkWindowsForDay(events, day).map(w => ({ ...w, kind: "walk" })),
|
||||
];
|
||||
for (const span of spans) {
|
||||
const inside = dayEvents.filter(e => e.at >= span.start && e.at <= span.end);
|
||||
if (inside.length < 2) continue; // nothing to tie to
|
||||
const closedTop = inside[inside.length - 1].at >= span.end;
|
||||
const closedBottom = inside[0].at <= span.start;
|
||||
inside.forEach((e, i) => {
|
||||
const role =
|
||||
i === inside.length - 1 ? (closedTop ? "top" : "mid")
|
||||
: i === 0 ? (closedBottom ? "bottom" : "mid")
|
||||
: "mid";
|
||||
// Sleep is added first, so the (in practice impossible) overlap of a
|
||||
// walk and a sleep paints as the walk.
|
||||
rails.set(e.id, { kind: span.kind, role });
|
||||
});
|
||||
}
|
||||
return rails;
|
||||
}
|
||||
|
||||
function renderHistory(events) {
|
||||
const dayEvents = eventsForDay(events, selectedDay()).reverse();
|
||||
const day = selectedDay();
|
||||
const chronological = eventsForDay(events, day);
|
||||
const rails = historyRails(events, chronological, day);
|
||||
const dayEvents = [...chronological].reverse();
|
||||
const exNames = exerciseNames();
|
||||
eventList.innerHTML = "";
|
||||
if (dayEvents.length === 0) {
|
||||
@@ -1078,6 +1117,8 @@
|
||||
li.className = "event";
|
||||
li.dataset.type = ev.type;
|
||||
li.dataset.id = ev.id;
|
||||
const rail = rails.get(ev.id);
|
||||
if (rail) li.classList.add("rail", `rail-${rail.kind}`, `rail-${rail.role}`);
|
||||
li.innerHTML = `
|
||||
<span class="dot"></span>
|
||||
<span class="time">${formatTime(ev.at)}</span>
|
||||
|
||||
Reference in New Issue
Block a user