Improve day swipe and sleep handling

This commit is contained in:
2026-05-21 13:00:10 +02:00
parent 2047cae61c
commit a087eb508b
5 changed files with 206 additions and 53 deletions
+59 -2
View File
@@ -462,6 +462,51 @@ body.page-dashboard .content {
padding-top: 6rem;
}
.dashboard-day-slider {
--day-prev-hint: 0;
--day-next-hint: 0;
position: relative;
overflow: hidden;
border-radius: 1.8rem;
margin-bottom: 2rem;
}
.day-slide-hint {
position: absolute;
top: 50%;
z-index: 0;
display: inline-flex;
align-items: center;
min-height: 3rem;
max-width: min(12rem, 42vw);
padding: 0.8rem 1rem;
border: 1px solid rgba(255, 255, 255, 0.18);
border-radius: 999px;
background: rgba(255, 255, 255, 0.1);
color: rgba(255, 255, 255, 0.9);
font-size: 0.86rem;
font-weight: 700;
line-height: 1.1;
letter-spacing: -0.01em;
opacity: 0;
pointer-events: none;
transform: translateY(-50%) scale(0.94);
transition: opacity 120ms ease, transform 180ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.day-slide-hint--prev {
left: 0.75rem;
opacity: var(--day-prev-hint);
transform: translateY(-50%) translateX(calc((1 - var(--day-prev-hint)) * -0.7rem)) scale(calc(0.94 + (var(--day-prev-hint) * 0.06)));
}
.day-slide-hint--next {
right: 0.75rem;
opacity: var(--day-next-hint);
text-align: right;
transform: translateY(-50%) translateX(calc((1 - var(--day-next-hint)) * 0.7rem)) scale(calc(0.94 + (var(--day-next-hint) * 0.06)));
}
.dashboard-compare-strip {
position: relative;
width: min(100%, 20rem);
@@ -475,8 +520,11 @@ body.page-dashboard .content {
}
.dashboard-day__hero[data-day-slider] {
transform: translateX(var(--day-slider-offset, 0));
transition: transform 180ms ease;
position: relative;
z-index: 1;
margin-bottom: 0;
transform: translate3d(var(--day-slider-offset, 0), 0, 0) scale(var(--day-slider-scale, 1));
transition: transform 260ms cubic-bezier(0.2, 0.8, 0.2, 1);
will-change: transform;
}
@@ -1028,6 +1076,15 @@ body.page-dashboard .content {
background: linear-gradient(135deg, rgba(54, 147, 173, 0.92), rgba(38, 106, 135, 0.92));
}
.sleep-phase-bar__segment--total {
border-radius: 999px 0 0 999px;
background: linear-gradient(135deg, rgba(90, 188, 242, 0.9), rgba(44, 126, 190, 0.9));
}
.sleep-phase-bar__segment--total.is-after-phase {
border-radius: 0;
}
.sleep-phase-bar__segment--rest {
min-width: 0;
padding: 0;
+69 -12
View File
@@ -1223,7 +1223,7 @@
valueInput.placeholder = config.placeholder;
valueInput.required = !!config.showValue;
valueInput.value = config.showValue ? valueInput.value : "";
valueInput.step = type === "sleep" ? "0.25" : "1";
valueInput.step = type === "sleep" ? "0.01" : "1";
}
if (unitInput) {
unitInput.value = config.unit;
@@ -1454,11 +1454,61 @@
let dragging = false;
let didSwipe = false;
let activePointerId = null;
let currentOffset = 0;
let targetOffset = 0;
let animationFrame = null;
const prefetchedDays = new Set();
const setSlideProgress = offset => {
const progress = Math.min(1, Math.abs(offset) / 120);
daySlider.style.setProperty("--day-slider-offset", `${offset.toFixed(1)}px`);
daySlider.style.setProperty("--day-slider-scale", (1 - (progress * 0.025)).toFixed(3));
swipeContainer.style.setProperty("--day-prev-hint", offset > 0 ? progress.toFixed(3) : "0");
swipeContainer.style.setProperty("--day-next-hint", offset < 0 ? progress.toFixed(3) : "0");
};
const animateSlide = () => {
currentOffset += (targetOffset - currentOffset) * 0.34;
if (Math.abs(targetOffset - currentOffset) < 0.4) {
currentOffset = targetOffset;
}
setSlideProgress(currentOffset);
if (currentOffset !== targetOffset) {
animationFrame = window.requestAnimationFrame(animateSlide);
} else {
animationFrame = null;
}
};
const setTargetOffset = offset => {
targetOffset = offset;
if (animationFrame === null) {
animationFrame = window.requestAnimationFrame(animateSlide);
}
};
const preloadDay = date => {
if (!date || prefetchedDays.has(date)) {
return;
}
prefetchedDays.add(date);
window.fetch(dashboardDayPath(date), {
credentials: "same-origin",
cache: "force-cache",
priority: "low"
}).catch(() => {});
};
preloadDay(swipeContainer.dataset.prevDate);
preloadDay(swipeContainer.dataset.nextDate);
const resetStrip = () => {
dayStrip.classList.remove("is-dragging");
daySlider.classList.remove("is-dragging");
daySlider.style.setProperty("--day-slider-offset", "0px");
setTargetOffset(0);
};
const handleSwipe = (deltaX, deltaY) => {
@@ -1469,18 +1519,18 @@
if (deltaX < 0 && swipeContainer.dataset.nextDate) {
didSwipe = true;
daySlider.style.setProperty("--day-slider-offset", "-120%");
setSlideProgress(-window.innerWidth);
window.location.href = dashboardDayPath(swipeContainer.dataset.nextDate);
} else if (deltaX > 0 && swipeContainer.dataset.prevDate) {
didSwipe = true;
daySlider.style.setProperty("--day-slider-offset", "120%");
setSlideProgress(window.innerWidth);
window.location.href = dashboardDayPath(swipeContainer.dataset.prevDate);
} else {
resetStrip();
}
};
dayStrip.addEventListener("pointerdown", event => {
daySlider.addEventListener("pointerdown", event => {
if (event.target.closest("input, textarea, select, label, [data-stepper], .dashboard-overlay")) {
dragging = false;
return;
@@ -1493,10 +1543,10 @@
pointerStartY = event.clientY;
dayStrip.classList.add("is-dragging");
daySlider.classList.add("is-dragging");
dayStrip.setPointerCapture?.(event.pointerId);
daySlider.setPointerCapture?.(event.pointerId);
});
dayStrip.addEventListener("pointermove", event => {
daySlider.addEventListener("pointermove", event => {
if (!dragging || (activePointerId !== null && event.pointerId !== activePointerId)) {
return;
}
@@ -1510,10 +1560,17 @@
return;
}
daySlider.style.setProperty("--day-slider-offset", `${Math.max(-120, Math.min(120, deltaX))}px`);
const dampedOffset = Math.sign(deltaX) * Math.min(148, Math.pow(Math.abs(deltaX), 0.88) * 1.6);
setTargetOffset(dampedOffset);
if (deltaX < -32) {
preloadDay(swipeContainer.dataset.nextDate);
} else if (deltaX > 32) {
preloadDay(swipeContainer.dataset.prevDate);
}
});
dayStrip.addEventListener("pointerup", event => {
daySlider.addEventListener("pointerup", event => {
if (!dragging) {
return;
}
@@ -1523,13 +1580,13 @@
handleSwipe(event.clientX - pointerStartX, event.clientY - pointerStartY);
});
dayStrip.addEventListener("pointercancel", () => {
daySlider.addEventListener("pointercancel", () => {
dragging = false;
activePointerId = null;
resetStrip();
});
dayStrip.addEventListener("lostpointercapture", () => {
daySlider.addEventListener("lostpointercapture", () => {
if (!dragging) {
return;
}
@@ -1539,7 +1596,7 @@
resetStrip();
});
dayStrip.addEventListener("click", event => {
daySlider.addEventListener("click", event => {
if (!didSwipe) {
return;
}