Refine swipe affordance and sleep bar
This commit is contained in:
+48
-6
@@ -477,6 +477,7 @@ body.page-dashboard .content {
|
|||||||
z-index: 0;
|
z-index: 0;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
gap: 0.48rem;
|
||||||
min-height: 3rem;
|
min-height: 3rem;
|
||||||
max-width: min(12rem, 42vw);
|
max-width: min(12rem, 42vw);
|
||||||
padding: 0.8rem 1rem;
|
padding: 0.8rem 1rem;
|
||||||
@@ -494,6 +495,22 @@ body.page-dashboard .content {
|
|||||||
transition: opacity 120ms ease, transform 180ms cubic-bezier(0.2, 0.8, 0.2, 1);
|
transition: opacity 120ms ease, transform 180ms cubic-bezier(0.2, 0.8, 0.2, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.day-slide-hint__arrow {
|
||||||
|
width: 0.8rem;
|
||||||
|
height: 0.8rem;
|
||||||
|
border-top: 2px solid currentColor;
|
||||||
|
border-left: 2px solid currentColor;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-slide-hint--prev .day-slide-hint__arrow {
|
||||||
|
transform: rotate(-45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-slide-hint--next .day-slide-hint__arrow {
|
||||||
|
transform: rotate(135deg);
|
||||||
|
}
|
||||||
|
|
||||||
.day-slide-hint--prev {
|
.day-slide-hint--prev {
|
||||||
left: 0.75rem;
|
left: 0.75rem;
|
||||||
opacity: var(--day-prev-hint);
|
opacity: var(--day-prev-hint);
|
||||||
@@ -523,6 +540,7 @@ body.page-dashboard .content {
|
|||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
touch-action: pan-y;
|
||||||
transform: translate3d(var(--day-slider-offset, 0), 0, 0) scale(var(--day-slider-scale, 1));
|
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);
|
transition: transform 260ms cubic-bezier(0.2, 0.8, 0.2, 1);
|
||||||
will-change: transform;
|
will-change: transform;
|
||||||
@@ -978,7 +996,7 @@ body.page-dashboard .content {
|
|||||||
|
|
||||||
.sleep-phase-bar {
|
.sleep-phase-bar {
|
||||||
position: relative;
|
position: relative;
|
||||||
min-height: 2.35rem;
|
height: 2.35rem;
|
||||||
margin-top: 1.55rem;
|
margin-top: 1.55rem;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
@@ -986,6 +1004,17 @@ body.page-dashboard .content {
|
|||||||
background: rgba(255, 255, 255, 0.08);
|
background: rgba(255, 255, 255, 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sleep-phase-bar__fill {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0 auto 0 0;
|
||||||
|
display: flex;
|
||||||
|
width: var(--sleep-actual-width, 0);
|
||||||
|
min-width: 0.18rem;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: inherit;
|
||||||
|
box-shadow: 0 12px 28px rgba(6, 16, 28, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
.sleep-phase-bar__target {
|
.sleep-phase-bar__target {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: min(var(--sleep-optimal-left, 100%), calc(100% - 2px));
|
left: min(var(--sleep-optimal-left, 100%), calc(100% - 2px));
|
||||||
@@ -1012,11 +1041,10 @@ body.page-dashboard .content {
|
|||||||
|
|
||||||
.sleep-phase-bar__segment {
|
.sleep-phase-bar__segment {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
position: absolute;
|
position: relative;
|
||||||
left: var(--sleep-segment-left, 0);
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
width: var(--sleep-segment-width, auto);
|
width: var(--sleep-segment-width, auto);
|
||||||
|
height: 100%;
|
||||||
|
flex: 0 0 var(--sleep-segment-width, auto);
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -1092,10 +1120,24 @@ body.page-dashboard .content {
|
|||||||
background: rgba(255, 255, 255, 0.1);
|
background: rgba(255, 255, 255, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sleep-phase-bar__segment:last-of-type {
|
.sleep-phase-bar__fill .sleep-phase-bar__segment:last-child {
|
||||||
border-radius: 0 999px 999px 0;
|
border-radius: 0 999px 999px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sleep-phase-bar__fill .sleep-phase-bar__segment:first-child {
|
||||||
|
border-radius: 999px 0 0 999px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sleep-phase-bar__rest-label {
|
||||||
|
position: absolute;
|
||||||
|
right: 0.65rem;
|
||||||
|
top: 50%;
|
||||||
|
color: rgba(255, 255, 255, 0.52);
|
||||||
|
font-size: 0.68rem;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
.media-lightbox[hidden] {
|
.media-lightbox[hidden] {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -1512,7 +1512,9 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
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) {
|
const absX = Math.abs(deltaX);
|
||||||
|
const absY = Math.abs(deltaY);
|
||||||
|
if (document.body.classList.contains("is-dashboard-overlay-open") || absX < 55 || absY > Math.max(120, absX * 1.15)) {
|
||||||
resetStrip();
|
resetStrip();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1553,7 +1555,7 @@
|
|||||||
|
|
||||||
const deltaX = event.clientX - pointerStartX;
|
const deltaX = event.clientX - pointerStartX;
|
||||||
const deltaY = event.clientY - pointerStartY;
|
const deltaY = event.clientY - pointerStartY;
|
||||||
if (Math.abs(deltaY) > Math.abs(deltaX) && Math.abs(deltaY) > 20) {
|
if (Math.abs(deltaY) > Math.max(52, Math.abs(deltaX) * 1.35)) {
|
||||||
dragging = false;
|
dragging = false;
|
||||||
activePointerId = null;
|
activePointerId = null;
|
||||||
resetStrip();
|
resetStrip();
|
||||||
|
|||||||
@@ -56,8 +56,8 @@ $formatBalanceValue = static function (?array $entry) use ($settings): string {
|
|||||||
<?php if ($dashboardView === 'day'): ?>
|
<?php if ($dashboardView === 'day'): ?>
|
||||||
<div class="dashboard-day" data-day-swipe data-prev-date="<?= e($dashboardPrevDate) ?>" data-next-date="<?= e($dashboardNextDate) ?>">
|
<div class="dashboard-day" data-day-swipe data-prev-date="<?= e($dashboardPrevDate) ?>" data-next-date="<?= e($dashboardNextDate) ?>">
|
||||||
<div class="dashboard-day-slider" data-day-slider-shell>
|
<div class="dashboard-day-slider" data-day-slider-shell>
|
||||||
<span class="day-slide-hint day-slide-hint--prev" data-day-slide-prev-hint>Vorherigen Tag laden</span>
|
<span class="day-slide-hint day-slide-hint--prev" data-day-slide-prev-hint><span class="day-slide-hint__arrow" aria-hidden="true"></span>Vorherigen Tag laden</span>
|
||||||
<span class="day-slide-hint day-slide-hint--next" data-day-slide-next-hint>Nächster Tag laden</span>
|
<span class="day-slide-hint day-slide-hint--next" data-day-slide-next-hint>Nächster Tag laden<span class="day-slide-hint__arrow" aria-hidden="true"></span></span>
|
||||||
<div class="dashboard-day__hero" data-day-slider>
|
<div class="dashboard-day__hero" data-day-slider>
|
||||||
<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>
|
||||||
@@ -149,6 +149,7 @@ $formatBalanceValue = static function (?array $entry) use ($settings): string {
|
|||||||
$sleepUnclassified = max(0.0, $sleepActualTotal - $sleepPhaseTotal);
|
$sleepUnclassified = max(0.0, $sleepActualTotal - $sleepPhaseTotal);
|
||||||
$sleepPhaseRemainder = max(0.0, $sleepBarTotal - $sleepActualTotal);
|
$sleepPhaseRemainder = max(0.0, $sleepBarTotal - $sleepActualTotal);
|
||||||
$sleepOptimalPercent = $sleepBarTotal > 0 ? max(0, min(100, ($optimalSleepHours / $sleepBarTotal) * 100)) : 0;
|
$sleepOptimalPercent = $sleepBarTotal > 0 ? max(0, min(100, ($optimalSleepHours / $sleepBarTotal) * 100)) : 0;
|
||||||
|
$sleepActualPercent = $sleepBarTotal > 0 ? max(0, min(100, ($sleepActualTotal / $sleepBarTotal) * 100)) : 0;
|
||||||
$sleepPhaseLeft = 0.0;
|
$sleepPhaseLeft = 0.0;
|
||||||
?>
|
?>
|
||||||
<?php $eventStats = array_values(array_filter([
|
<?php $eventStats = array_values(array_filter([
|
||||||
@@ -219,32 +220,30 @@ $formatBalanceValue = static function (?array $entry) use ($settings): string {
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php if ($eventType === 'sleep' && $sleepActualTotal > 0): ?>
|
<?php if ($eventType === 'sleep' && $sleepActualTotal > 0): ?>
|
||||||
<div class="sleep-phase-bar" aria-label="Schlafphasen" style="--sleep-optimal-left: <?= e((string) $sleepOptimalPercent) ?>%">
|
<div class="sleep-phase-bar" aria-label="Schlafphasen" style="--sleep-optimal-left: <?= e((string) $sleepOptimalPercent) ?>%; --sleep-actual-width: <?= e((string) $sleepActualPercent) ?>%">
|
||||||
|
<div class="sleep-phase-bar__fill">
|
||||||
<?php if ($sleepPhaseTotal > 0): ?>
|
<?php if ($sleepPhaseTotal > 0): ?>
|
||||||
<?php foreach (['deep' => ['Tief', 'deep'], 'rem' => ['REM', 'rem'], 'core' => ['Kern', 'core']] as $phase => [$label, $class]): ?>
|
<?php foreach (['deep' => ['Tief', 'deep'], 'rem' => ['REM', 'rem'], 'core' => ['Kern', 'core']] as $phase => [$label, $class]): ?>
|
||||||
<?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 = $sleepActualTotal > 0 ? max(0.5, min(100, ($phaseHours / $sleepActualTotal) * 100)) : 0; ?>
|
||||||
<?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-width: <?= e((string) $phasePercent) ?>%" title="<?= e($label) ?>: <?= e(format_duration_hours($phaseHours)) ?>" data-tooltip="<?= e($label) ?>: <?= e(format_duration_hours($phaseHours)) ?>">
|
||||||
<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_duration_hours($phaseHours)) ?>" data-tooltip="<?= e($label) ?>: <?= e(format_duration_hours($phaseHours)) ?>">
|
|
||||||
<strong><?= e($label) ?></strong> <?= e(format_duration_hours($phaseHours)) ?>
|
<strong><?= e($label) ?></strong> <?= e(format_duration_hours($phaseHours)) ?>
|
||||||
</span>
|
</span>
|
||||||
<?php $sleepPhaseLeft += $phaseHours; ?>
|
<?php $sleepPhaseLeft += $phaseHours; ?>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<?php if ($sleepUnclassified > 0): ?>
|
<?php if ($sleepUnclassified > 0): ?>
|
||||||
<?php $unclassifiedPercent = $sleepBarTotal > 0 ? max(0.5, min(100, ($sleepUnclassified / $sleepBarTotal) * 100)) : 0; ?>
|
<?php $unclassifiedPercent = $sleepActualTotal > 0 ? max(0.5, min(100, ($sleepUnclassified / $sleepActualTotal) * 100)) : 0; ?>
|
||||||
<?php $unclassifiedLeftPercent = $sleepBarTotal > 0 ? max(0, min(100, ($sleepPhaseTotal / $sleepBarTotal) * 100)) : 0; ?>
|
<span class="sleep-phase-bar__segment sleep-phase-bar__segment--total<?= $sleepPhaseTotal > 0 ? ' is-after-phase' : '' ?><?= $unclassifiedPercent < 13 ? ' is-compact' : '' ?>" style="--sleep-segment-width: <?= e((string) $unclassifiedPercent) ?>%" title="Schlaf: <?= e(format_duration_hours($sleepUnclassified)) ?>" data-tooltip="Schlaf: <?= e(format_duration_hours($sleepUnclassified)) ?>">
|
||||||
<span class="sleep-phase-bar__segment sleep-phase-bar__segment--total<?= $sleepPhaseTotal > 0 ? ' is-after-phase' : '' ?><?= $unclassifiedPercent < 13 ? ' is-compact' : '' ?>" style="--sleep-segment-left: <?= e((string) $unclassifiedLeftPercent) ?>%; --sleep-segment-width: <?= e((string) $unclassifiedPercent) ?>%" title="Schlaf: <?= e(format_duration_hours($sleepUnclassified)) ?>" data-tooltip="Schlaf: <?= e(format_duration_hours($sleepUnclassified)) ?>">
|
|
||||||
<strong>Schlaf</strong> <?= e(format_duration_hours($sleepUnclassified)) ?>
|
<strong>Schlaf</strong> <?= e(format_duration_hours($sleepUnclassified)) ?>
|
||||||
</span>
|
</span>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<?php if ($sleepPhaseRemainder > 0): ?>
|
</div>
|
||||||
<?php $remainderPercent = $sleepBarTotal > 0 ? max(0.5, min(100, ($sleepPhaseRemainder / $sleepBarTotal) * 100)) : 0; ?>
|
|
||||||
<?php $remainderLeftPercent = $sleepBarTotal > 0 ? max(0, min(100, ($sleepActualTotal / $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_duration_hours($sleepPhaseRemainder)) ?>" data-tooltip="Bis Ziel-/Skalenende: <?= e(format_duration_hours($sleepPhaseRemainder)) ?>"></span>
|
|
||||||
<?php endif; ?>
|
|
||||||
<span class="sleep-phase-bar__target"><span><?= e(format_duration_hours($optimalSleepHours)) ?></span></span>
|
<span class="sleep-phase-bar__target"><span><?= e(format_duration_hours($optimalSleepHours)) ?></span></span>
|
||||||
|
<?php if ($sleepPhaseRemainder > 0): ?>
|
||||||
|
<span class="sleep-phase-bar__rest-label">noch <?= e(format_duration_hours($sleepPhaseRemainder)) ?> bis Skalenende</span>
|
||||||
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user