feat(dashboard): move This Week stats into the library bar as a popover
Release / build-and-push (push) Successful in 3m11s

The "This Week" reading-stats strip leaves its full-width row below the
subbar and now lives inside the library bar itself, between the switcher
and the customize/refresh actions. Expanding it opens an absolutely
positioned popover that FLOATS over the collections — opening the stats
no longer shifts the page down.

- LibrarySwitcher gains an optional `center` component slot rendered
  between the left group and the actions group (nil = unused). Series
  and collections call sites pass nil and render byte-identical output.
- DashboardWeekWidget redesigned for the slot: compact inline summary
  (icon, "This Week", minutes; pages/sessions/books/streak reveal at
  md/lg/xl) and a card popover panel with the labeled grid, sync note,
  and the Full stats link moved in from the old summary row.
- Panel anchoring is responsive: below `sm` it anchors to the sticky
  bar itself (details goes static; bar-anchored left-4/right-4, i.e.
  screen-centered); at `sm`+ the details is relative again and the
  panel centers on the widget (w-[30rem], translate-x centering).
- On phones the summary collapses to the chart icon + chevron (bar row
  width is scarce); "This Week"/minutes reveal at `sm`, and the
  "Your Library" title hides below `sm` to keep the row from
  overlapping. Icon-only toggle carries an aria-label.
- web/src/dashboard.ts: click-outside and Escape close the popover,
  re-querying #week-widget per event because htmx replaces it on
  library switches. Rendering untouched (behavior-only island).
- id="week-widget" and all htmx wiring unchanged: week-refresh refetch
  + the /dashboard/week-widget fragment still drive library-scoped
  refreshes behind the switch fade; stats-off suppression now yields an
  empty slot via weekWidgetComponent instead of a hidden strip.
- web/static/style.css rebuilt for the new utilities.

Known behavior (unchanged or deliberate): the bar is sticky, so stats
stay visible while scrolling; a library switch re-renders the details
closed; scopes with no current-week reading show zeros (e.g. Manga).
This commit is contained in:
John O'Keefe committed 2026-10-27 10:22:13 -04:00
1 parent f6a73ab5aa
commit 8c97f59453
10 files changed
+299 -234

No files matched your search

+17
View File
@@ -508,6 +508,23 @@ function initDashboard() {
}
});
// The week-widget popover floats over the page content (absolute
// panel inside the library bar), so a click anywhere outside the open
// <details> closes it — same for Escape. The element is re-queried on
// every event because htmx replaces #week-widget on library switches.
document.addEventListener("click", (e: Event) => {
const widget = document.getElementById("week-widget") as HTMLDetailsElement | null;
if (widget?.open && !widget.contains(e.target as Node)) {
widget.open = false;
}
});
document.addEventListener("keydown", (e: KeyboardEvent) => {
if (e.key !== "Escape") return;
const widget = document.getElementById("week-widget") as HTMLDetailsElement | null;
if (widget?.open) widget.open = false;
});
if (!scanListenerRegistered) {
scanListenerRegistered = true;
window.addEventListener("bookhoard:scan-complete", async () => {