Make sleep phase bars proportional
This commit is contained in:
+26
-1
@@ -926,13 +926,38 @@ body.page-dashboard .content {
|
||||
justify-content: center;
|
||||
gap: 0.25rem;
|
||||
min-width: 0;
|
||||
flex-basis: 0;
|
||||
flex: 0 0 var(--sleep-segment-width, auto);
|
||||
padding: 0.35rem 0.65rem;
|
||||
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::after {
|
||||
content: attr(data-tooltip);
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
bottom: calc(1.2rem + env(safe-area-inset-bottom));
|
||||
z-index: 1400;
|
||||
max-width: min(18rem, calc(100vw - 2rem));
|
||||
padding: 0.55rem 0.75rem;
|
||||
border-radius: 999px;
|
||||
background: rgba(6, 16, 28, 0.92);
|
||||
color: #fff;
|
||||
box-shadow: 0 12px 34px rgba(0, 0, 0, 0.32);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transform: translateX(-50%) translateY(0.35rem);
|
||||
transition: opacity 140ms ease, transform 140ms ease;
|
||||
}
|
||||
|
||||
.sleep-phase-bar__segment:hover::after,
|
||||
.sleep-phase-bar__segment.is-tooltip-visible::after {
|
||||
opacity: 1;
|
||||
transform: translateX(-50%) translateY(0);
|
||||
}
|
||||
|
||||
.sleep-phase-bar__segment--deep {
|
||||
|
||||
@@ -1703,6 +1703,31 @@
|
||||
});
|
||||
}
|
||||
|
||||
function initSleepPhaseTooltips() {
|
||||
document.querySelectorAll(".sleep-phase-bar__segment[data-tooltip]").forEach(segment => {
|
||||
segment.addEventListener("click", event => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
document.querySelectorAll(".sleep-phase-bar__segment.is-tooltip-visible").forEach(active => {
|
||||
if (active !== segment) {
|
||||
active.classList.remove("is-tooltip-visible");
|
||||
}
|
||||
});
|
||||
segment.classList.toggle("is-tooltip-visible");
|
||||
});
|
||||
});
|
||||
|
||||
document.addEventListener("click", event => {
|
||||
if (event.target.closest(".sleep-phase-bar__segment[data-tooltip]")) {
|
||||
return;
|
||||
}
|
||||
|
||||
document.querySelectorAll(".sleep-phase-bar__segment.is-tooltip-visible").forEach(segment => {
|
||||
segment.classList.remove("is-tooltip-visible");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function csrfToken() {
|
||||
return document.querySelector('meta[name="csrf-token"]')?.getAttribute("content") || "";
|
||||
}
|
||||
@@ -2036,6 +2061,7 @@
|
||||
initOptionsPanels();
|
||||
initHealthImportStatus();
|
||||
initMediaLightbox();
|
||||
initSleepPhaseTooltips();
|
||||
initSportTypeManager();
|
||||
initPwaShell();
|
||||
initPullToRefresh();
|
||||
|
||||
@@ -115,7 +115,7 @@ $optimalSleepHours = max(1.0, min(16.0, (float) ($settings['sleep']['optimal_hou
|
||||
}
|
||||
}
|
||||
$sleepPhaseTotal = max(0.0, array_sum($sleepPhases));
|
||||
$sleepBarTotal = $eventType === 'sleep' ? max((float) ($item['value'] ?? 0), $sleepPhaseTotal, $optimalSleepHours) : 0.0;
|
||||
$sleepBarTotal = $eventType === 'sleep' ? max((float) ($item['value'] ?? 0), $sleepPhaseTotal, $optimalSleepHours / 0.75) : 0.0;
|
||||
$sleepPhaseRemainder = max(0.0, $sleepBarTotal - $sleepPhaseTotal);
|
||||
$sleepOptimalPercent = $sleepBarTotal > 0 ? max(0, min(100, ($optimalSleepHours / $sleepBarTotal) * 100)) : 0;
|
||||
?>
|
||||
@@ -191,12 +191,14 @@ $optimalSleepHours = max(1.0, min(16.0, (float) ($settings['sleep']['optimal_hou
|
||||
<?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 if ($phaseHours <= 0) { continue; } ?>
|
||||
<span class="sleep-phase-bar__segment sleep-phase-bar__segment--<?= e($class) ?>" style="flex-grow: <?= e((string) max(0.1, $phaseHours)) ?>" title="<?= e($label) ?>: <?= e(format_points($phaseHours)) ?> h">
|
||||
<?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) ?>" style="--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
|
||||
</span>
|
||||
<?php endforeach; ?>
|
||||
<?php if ($sleepPhaseRemainder > 0): ?>
|
||||
<span class="sleep-phase-bar__segment sleep-phase-bar__segment--rest" style="flex-grow: <?= e((string) max(0.1, $sleepPhaseRemainder)) ?>" title="Nicht phasenzugeordnet: <?= e(format_points($sleepPhaseRemainder)) ?> h"></span>
|
||||
<?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 endif; ?>
|
||||
<span class="sleep-phase-bar__target"><span><?= e(format_points($optimalSleepHours)) ?> h</span></span>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user