From 0df5983f65140dc58e6c2233906e576915a172db Mon Sep 17 00:00:00 2001 From: Florian Heinz Date: Thu, 21 May 2026 12:47:30 +0200 Subject: [PATCH] Make day strip draggable and fix sleep bars --- assets/css/app.css | 19 +++++++++--- assets/js/app.js | 55 +++++++++++++++++++++++++++++++---- templates/pages/dashboard.php | 10 +++++-- 3 files changed, 72 insertions(+), 12 deletions(-) diff --git a/assets/css/app.css b/assets/css/app.css index 65146d7..8eafdd1 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -467,6 +467,15 @@ body.page-dashboard .content { width: min(100%, 20rem); height: 11rem; margin-top: 0.95rem; + touch-action: pan-y; + transform: translateX(var(--day-strip-offset, 0)); + transition: transform 180ms ease; + will-change: transform; +} + +.dashboard-compare-strip.is-dragging { + transition: none; + cursor: grabbing; } .compare-day { @@ -915,7 +924,6 @@ body.page-dashboard .content { .sleep-phase-bar { position: relative; - display: flex; min-height: 2.35rem; margin-top: 1.55rem; overflow: visible; @@ -949,20 +957,23 @@ body.page-dashboard .content { } .sleep-phase-bar__segment { + box-sizing: border-box; + position: absolute; + left: var(--sleep-segment-left, 0); + top: 0; + bottom: 0; + width: var(--sleep-segment-width, auto); display: inline-flex; align-items: center; justify-content: center; gap: 0.25rem; min-width: 0; - flex: 0 0 var(--sleep-segment-width, auto); - width: var(--sleep-segment-width, auto); padding: 0.35rem 0.45rem; color: rgba(255, 255, 255, 0.94); font-size: 0.82rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - position: relative; } .sleep-phase-bar__segment.is-compact { diff --git a/assets/js/app.js b/assets/js/app.js index 68fcdf9..5a70a45 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -1013,6 +1013,7 @@ const typeSelect = document.querySelector("[data-event-type-select]"); const unitInput = document.querySelector("[data-event-unit]"); const swipeContainer = document.querySelector("[data-day-swipe]"); + const dayStrip = document.querySelector("[data-day-strip]"); const periodRail = document.querySelector(".range-period-rail"); const walkMode = document.body.dataset.walkMode || "time"; @@ -1446,27 +1447,38 @@ typeSelect.addEventListener("change", syncUnit); } - if (swipeContainer) { + if (swipeContainer && dayStrip) { let pointerStartX = 0; let pointerStartY = 0; let dragging = false; let didSwipe = false; + let activePointerId = null; + + const resetStrip = () => { + dayStrip.classList.remove("is-dragging"); + dayStrip.style.setProperty("--day-strip-offset", "0px"); + }; const handleSwipe = (deltaX, deltaY) => { if (document.body.classList.contains("is-dashboard-overlay-open") || Math.abs(deltaX) < 70 || Math.abs(deltaY) > 50) { + resetStrip(); return; } if (deltaX < 0 && swipeContainer.dataset.nextDate) { didSwipe = true; + dayStrip.style.setProperty("--day-strip-offset", "-120%"); window.location.href = dashboardDayPath(swipeContainer.dataset.nextDate); } else if (deltaX > 0 && swipeContainer.dataset.prevDate) { didSwipe = true; + dayStrip.style.setProperty("--day-strip-offset", "120%"); window.location.href = dashboardDayPath(swipeContainer.dataset.prevDate); + } else { + resetStrip(); } }; - swipeContainer.addEventListener("pointerdown", event => { + dayStrip.addEventListener("pointerdown", event => { if (event.target.closest("input, textarea, select, label, [data-stepper], .dashboard-overlay")) { dragging = false; return; @@ -1474,24 +1486,57 @@ didSwipe = false; dragging = true; + activePointerId = event.pointerId; pointerStartX = event.clientX; pointerStartY = event.clientY; + dayStrip.classList.add("is-dragging"); + dayStrip.setPointerCapture?.(event.pointerId); }); - swipeContainer.addEventListener("pointerup", event => { + dayStrip.addEventListener("pointermove", event => { + if (!dragging || (activePointerId !== null && event.pointerId !== activePointerId)) { + return; + } + + const deltaX = event.clientX - pointerStartX; + const deltaY = event.clientY - pointerStartY; + if (Math.abs(deltaY) > Math.abs(deltaX) && Math.abs(deltaY) > 20) { + dragging = false; + activePointerId = null; + resetStrip(); + return; + } + + dayStrip.style.setProperty("--day-strip-offset", `${Math.max(-120, Math.min(120, deltaX))}px`); + }); + + dayStrip.addEventListener("pointerup", event => { if (!dragging) { return; } dragging = false; + activePointerId = null; handleSwipe(event.clientX - pointerStartX, event.clientY - pointerStartY); }); - swipeContainer.addEventListener("pointercancel", () => { + dayStrip.addEventListener("pointercancel", () => { dragging = false; + activePointerId = null; + resetStrip(); }); - swipeContainer.addEventListener("click", event => { + dayStrip.addEventListener("lostpointercapture", () => { + if (!dragging) { + return; + } + + dragging = false; + activePointerId = null; + resetStrip(); + }); + + dayStrip.addEventListener("click", event => { if (!didSwipe) { return; } diff --git a/templates/pages/dashboard.php b/templates/pages/dashboard.php index 6b73c34..47cafe7 100644 --- a/templates/pages/dashboard.php +++ b/templates/pages/dashboard.php @@ -56,7 +56,7 @@ $formatBalanceValue = static function (?array $entry) use ($settings): string {

-