Make day strip draggable and fix sleep bars
This commit is contained in:
+15
-4
@@ -467,6 +467,15 @@ body.page-dashboard .content {
|
|||||||
width: min(100%, 20rem);
|
width: min(100%, 20rem);
|
||||||
height: 11rem;
|
height: 11rem;
|
||||||
margin-top: 0.95rem;
|
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 {
|
.compare-day {
|
||||||
@@ -915,7 +924,6 @@ body.page-dashboard .content {
|
|||||||
|
|
||||||
.sleep-phase-bar {
|
.sleep-phase-bar {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
|
||||||
min-height: 2.35rem;
|
min-height: 2.35rem;
|
||||||
margin-top: 1.55rem;
|
margin-top: 1.55rem;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
@@ -949,20 +957,23 @@ body.page-dashboard .content {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.sleep-phase-bar__segment {
|
.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;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 0.25rem;
|
gap: 0.25rem;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
flex: 0 0 var(--sleep-segment-width, auto);
|
|
||||||
width: var(--sleep-segment-width, auto);
|
|
||||||
padding: 0.35rem 0.45rem;
|
padding: 0.35rem 0.45rem;
|
||||||
color: rgba(255, 255, 255, 0.94);
|
color: rgba(255, 255, 255, 0.94);
|
||||||
font-size: 0.82rem;
|
font-size: 0.82rem;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
position: relative;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sleep-phase-bar__segment.is-compact {
|
.sleep-phase-bar__segment.is-compact {
|
||||||
|
|||||||
+50
-5
@@ -1013,6 +1013,7 @@
|
|||||||
const typeSelect = document.querySelector("[data-event-type-select]");
|
const typeSelect = document.querySelector("[data-event-type-select]");
|
||||||
const unitInput = document.querySelector("[data-event-unit]");
|
const unitInput = document.querySelector("[data-event-unit]");
|
||||||
const swipeContainer = document.querySelector("[data-day-swipe]");
|
const swipeContainer = document.querySelector("[data-day-swipe]");
|
||||||
|
const dayStrip = document.querySelector("[data-day-strip]");
|
||||||
const periodRail = document.querySelector(".range-period-rail");
|
const periodRail = document.querySelector(".range-period-rail");
|
||||||
|
|
||||||
const walkMode = document.body.dataset.walkMode || "time";
|
const walkMode = document.body.dataset.walkMode || "time";
|
||||||
@@ -1446,27 +1447,38 @@
|
|||||||
typeSelect.addEventListener("change", syncUnit);
|
typeSelect.addEventListener("change", syncUnit);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (swipeContainer) {
|
if (swipeContainer && dayStrip) {
|
||||||
let pointerStartX = 0;
|
let pointerStartX = 0;
|
||||||
let pointerStartY = 0;
|
let pointerStartY = 0;
|
||||||
let dragging = false;
|
let dragging = false;
|
||||||
let didSwipe = 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) => {
|
const handleSwipe = (deltaX, deltaY) => {
|
||||||
if (document.body.classList.contains("is-dashboard-overlay-open") || Math.abs(deltaX) < 70 || Math.abs(deltaY) > 50) {
|
if (document.body.classList.contains("is-dashboard-overlay-open") || Math.abs(deltaX) < 70 || Math.abs(deltaY) > 50) {
|
||||||
|
resetStrip();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deltaX < 0 && swipeContainer.dataset.nextDate) {
|
if (deltaX < 0 && swipeContainer.dataset.nextDate) {
|
||||||
didSwipe = true;
|
didSwipe = true;
|
||||||
|
dayStrip.style.setProperty("--day-strip-offset", "-120%");
|
||||||
window.location.href = dashboardDayPath(swipeContainer.dataset.nextDate);
|
window.location.href = dashboardDayPath(swipeContainer.dataset.nextDate);
|
||||||
} else if (deltaX > 0 && swipeContainer.dataset.prevDate) {
|
} else if (deltaX > 0 && swipeContainer.dataset.prevDate) {
|
||||||
didSwipe = true;
|
didSwipe = true;
|
||||||
|
dayStrip.style.setProperty("--day-strip-offset", "120%");
|
||||||
window.location.href = dashboardDayPath(swipeContainer.dataset.prevDate);
|
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")) {
|
if (event.target.closest("input, textarea, select, label, [data-stepper], .dashboard-overlay")) {
|
||||||
dragging = false;
|
dragging = false;
|
||||||
return;
|
return;
|
||||||
@@ -1474,24 +1486,57 @@
|
|||||||
|
|
||||||
didSwipe = false;
|
didSwipe = false;
|
||||||
dragging = true;
|
dragging = true;
|
||||||
|
activePointerId = event.pointerId;
|
||||||
pointerStartX = event.clientX;
|
pointerStartX = event.clientX;
|
||||||
pointerStartY = event.clientY;
|
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) {
|
if (!dragging) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dragging = false;
|
dragging = false;
|
||||||
|
activePointerId = null;
|
||||||
handleSwipe(event.clientX - pointerStartX, event.clientY - pointerStartY);
|
handleSwipe(event.clientX - pointerStartX, event.clientY - pointerStartY);
|
||||||
});
|
});
|
||||||
|
|
||||||
swipeContainer.addEventListener("pointercancel", () => {
|
dayStrip.addEventListener("pointercancel", () => {
|
||||||
dragging = false;
|
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) {
|
if (!didSwipe) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ $formatBalanceValue = static function (?array $entry) use ($settings): string {
|
|||||||
<p class="dashboard-day__eyebrow"><?= e((string) $dayWeekday) ?></p>
|
<p class="dashboard-day__eyebrow"><?= e((string) $dayWeekday) ?></p>
|
||||||
<h1><?= e(format_display_date((string) $dayEntry['date'], false)) ?></h1>
|
<h1><?= e(format_display_date((string) $dayEntry['date'], false)) ?></h1>
|
||||||
|
|
||||||
<nav class="dashboard-compare-strip" aria-label="Tagesvergleich">
|
<nav class="dashboard-compare-strip" aria-label="Tagesvergleich" data-day-strip>
|
||||||
<span class="score-scale score-scale--day" aria-hidden="true"><span>+2</span><span>+1</span><span>0</span><span>-1</span><span>-2</span></span>
|
<span class="score-scale score-scale--day" aria-hidden="true"><span>+2</span><span>+1</span><span>0</span><span>-1</span><span>-2</span></span>
|
||||||
<?php foreach ($dashboardCompareDays as $compareDay): ?>
|
<?php foreach ($dashboardCompareDays as $compareDay): ?>
|
||||||
<a class="compare-day offset-<?= e((string) ($compareDay['offset'] ?? 0)) ?><?= !empty($compareDay['is_current']) ? ' is-current' : '' ?><?= empty($compareDay['has_content']) ? ' is-empty' : '' ?>" href="/?view=day&date=<?= e(rawurlencode((string) $compareDay['date'])) ?>">
|
<a class="compare-day offset-<?= e((string) ($compareDay['offset'] ?? 0)) ?><?= !empty($compareDay['is_current']) ? ' is-current' : '' ?><?= empty($compareDay['has_content']) ? ' is-empty' : '' ?>" href="/?view=day&date=<?= e(rawurlencode((string) $compareDay['date'])) ?>">
|
||||||
@@ -140,6 +140,7 @@ $formatBalanceValue = static function (?array $entry) use ($settings): string {
|
|||||||
$sleepBarTotal = $eventType === 'sleep' ? max((float) ($item['value'] ?? 0), $sleepPhaseTotal, $optimalSleepHours / 0.75) : 0.0;
|
$sleepBarTotal = $eventType === 'sleep' ? max((float) ($item['value'] ?? 0), $sleepPhaseTotal, $optimalSleepHours / 0.75) : 0.0;
|
||||||
$sleepPhaseRemainder = max(0.0, $sleepBarTotal - $sleepPhaseTotal);
|
$sleepPhaseRemainder = max(0.0, $sleepBarTotal - $sleepPhaseTotal);
|
||||||
$sleepOptimalPercent = $sleepBarTotal > 0 ? max(0, min(100, ($optimalSleepHours / $sleepBarTotal) * 100)) : 0;
|
$sleepOptimalPercent = $sleepBarTotal > 0 ? max(0, min(100, ($optimalSleepHours / $sleepBarTotal) * 100)) : 0;
|
||||||
|
$sleepPhaseLeft = 0.0;
|
||||||
?>
|
?>
|
||||||
<?php $eventStats = array_values(array_filter([
|
<?php $eventStats = array_values(array_filter([
|
||||||
$eventType !== 'sleep' ? (string) ($item['duration_label'] ?? '') : '',
|
$eventType !== 'sleep' ? (string) ($item['duration_label'] ?? '') : '',
|
||||||
@@ -214,13 +215,16 @@ $formatBalanceValue = static function (?array $entry) use ($settings): string {
|
|||||||
<?php $phaseHours = max(0.0, (float) ($sleepPhases[$phase] ?? 0)); ?>
|
<?php $phaseHours = max(0.0, (float) ($sleepPhases[$phase] ?? 0)); ?>
|
||||||
<?php if ($phaseHours <= 0) { continue; } ?>
|
<?php if ($phaseHours <= 0) { continue; } ?>
|
||||||
<?php $phasePercent = $sleepBarTotal > 0 ? max(0.5, min(100, ($phaseHours / $sleepBarTotal) * 100)) : 0; ?>
|
<?php $phasePercent = $sleepBarTotal > 0 ? max(0.5, min(100, ($phaseHours / $sleepBarTotal) * 100)) : 0; ?>
|
||||||
<span class="sleep-phase-bar__segment sleep-phase-bar__segment--<?= e($class) ?><?= $phasePercent < 13 ? ' is-compact' : '' ?>" style="--sleep-segment-width: <?= e((string) $phasePercent) ?>%" title="<?= e($label) ?>: <?= e(format_points($phaseHours)) ?> h" data-tooltip="<?= e($label) ?>: <?= e(format_points($phaseHours)) ?> h">
|
<?php $phaseLeftPercent = $sleepBarTotal > 0 ? max(0, min(100, ($sleepPhaseLeft / $sleepBarTotal) * 100)) : 0; ?>
|
||||||
|
<span class="sleep-phase-bar__segment sleep-phase-bar__segment--<?= e($class) ?><?= $phasePercent < 13 ? ' is-compact' : '' ?>" style="--sleep-segment-left: <?= e((string) $phaseLeftPercent) ?>%; --sleep-segment-width: <?= e((string) $phasePercent) ?>%" title="<?= e($label) ?>: <?= e(format_points($phaseHours)) ?> h" data-tooltip="<?= e($label) ?>: <?= e(format_points($phaseHours)) ?> h">
|
||||||
<strong><?= e($label) ?></strong> <?= e(format_points($phaseHours)) ?> h
|
<strong><?= e($label) ?></strong> <?= e(format_points($phaseHours)) ?> h
|
||||||
</span>
|
</span>
|
||||||
|
<?php $sleepPhaseLeft += $phaseHours; ?>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
<?php if ($sleepPhaseRemainder > 0): ?>
|
<?php if ($sleepPhaseRemainder > 0): ?>
|
||||||
<?php $remainderPercent = $sleepBarTotal > 0 ? max(0.5, min(100, ($sleepPhaseRemainder / $sleepBarTotal) * 100)) : 0; ?>
|
<?php $remainderPercent = $sleepBarTotal > 0 ? max(0.5, min(100, ($sleepPhaseRemainder / $sleepBarTotal) * 100)) : 0; ?>
|
||||||
<span class="sleep-phase-bar__segment sleep-phase-bar__segment--rest" style="--sleep-segment-width: <?= e((string) $remainderPercent) ?>%" title="Bis Ziel-/Skalenende: <?= e(format_points($sleepPhaseRemainder)) ?> h" data-tooltip="Bis Ziel-/Skalenende: <?= e(format_points($sleepPhaseRemainder)) ?> h"></span>
|
<?php $remainderLeftPercent = $sleepBarTotal > 0 ? max(0, min(100, ($sleepPhaseTotal / $sleepBarTotal) * 100)) : 0; ?>
|
||||||
|
<span class="sleep-phase-bar__segment sleep-phase-bar__segment--rest" style="--sleep-segment-left: <?= e((string) $remainderLeftPercent) ?>%; --sleep-segment-width: <?= e((string) $remainderPercent) ?>%" title="Bis Ziel-/Skalenende: <?= e(format_points($sleepPhaseRemainder)) ?> h" data-tooltip="Bis Ziel-/Skalenende: <?= e(format_points($sleepPhaseRemainder)) ?> h"></span>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<span class="sleep-phase-bar__target"><span><?= e(format_points($optimalSleepHours)) ?> h</span></span>
|
<span class="sleep-phase-bar__target"><span><?= e(format_points($optimalSleepHours)) ?> h</span></span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user