fix: prominent target values and no layout jump on warnings

pull/4/head
lambda 6 days ago
parent b6ea23f02d
commit e1a1da6835
No known key found for this signature in database
GPG Key ID: CF45A8EE158BBCB6

@ -56,17 +56,15 @@
<section class="metrics"> <section class="metrics">
<div class="metric metric--power" id="power-metric"> <div class="metric metric--power" id="power-metric">
<span class="metric__label">мощность</span> <span class="metric__label">мощность</span>
<span class="metric__value" id="power-value">—</span> <span class="metric__target" id="power-target">—</span>
<span class="metric__unit">W</span> <span class="metric__actual" id="power-value">—</span>
<span class="metric__target" id="power-target"></span> <span class="metric__warn" id="power-warn"></span>
<span class="metric__warn" id="power-warn" hidden></span>
</div> </div>
<div class="metric metric--cadence" id="cadence-metric"> <div class="metric metric--cadence" id="cadence-metric">
<span class="metric__label">каденс</span> <span class="metric__label">каденс</span>
<span class="metric__value" id="cadence-value">—</span> <span class="metric__target" id="cadence-target">—</span>
<span class="metric__unit">rpm</span> <span class="metric__actual" id="cadence-value">—</span>
<span class="metric__target" id="cadence-target"></span> <span class="metric__warn" id="cadence-warn"></span>
<span class="metric__warn" id="cadence-warn" hidden></span>
</div> </div>
<div class="metric metric--hr"> <div class="metric metric--hr">
<span class="metric__label">чсс</span> <span class="metric__label">чсс</span>

@ -153,11 +153,11 @@ listen("telemetry", (event) => {
const t = event.payload.telemetry; const t = event.payload.telemetry;
if (t.Power) { if (t.Power) {
actualWatts = t.Power.watts; actualWatts = t.Power.watts;
setMetric("power-value", actualWatts); setMetric("power-value", `${actualWatts} W`);
updatePowerDeviation(); updatePowerDeviation();
} else if (t.Cadence) { } else if (t.Cadence) {
actualCadence = Math.round(t.Cadence.rpm); actualCadence = Math.round(t.Cadence.rpm);
setMetric("cadence-value", actualCadence); setMetric("cadence-value", `${actualCadence} rpm`);
updateCadenceDeviation(); updateCadenceDeviation();
} else if (t.HeartRate) { } else if (t.HeartRate) {
setMetric("hr-value", t.HeartRate.bpm); setMetric("hr-value", t.HeartRate.bpm);
@ -224,30 +224,29 @@ function powerTolerance(target) {
return Math.max(POWER_TOL_ABS, Math.round(target * POWER_TOL_PCT)); return Math.max(POWER_TOL_ABS, Math.round(target * POWER_TOL_PCT));
} }
function updateDeviation(metricEl, targetEl, warnEl, actual, target, tolerance) { function updateDeviation(metricEl, targetEl, warnEl, actual, target, tolerance, unit) {
targetEl.textContent = target != null ? `цель ${target}` : ""; targetEl.textContent = target != null ? `цель ${target} ${unit}` : "—";
if (actual == null || target == null) { if (actual == null || target == null) {
metricEl.classList.remove("metric--off"); metricEl.classList.remove("metric--off");
warnEl.hidden = true; warnEl.textContent = "";
return; return;
} }
const diff = actual - target; const diff = actual - target;
if (Math.abs(diff) > tolerance) { if (Math.abs(diff) > tolerance) {
metricEl.classList.add("metric--off"); metricEl.classList.add("metric--off");
warnEl.textContent = diff > 0 ? "выше цели" : "ниже цели"; warnEl.textContent = diff > 0 ? "выше цели" : "ниже цели";
warnEl.hidden = false;
} else { } else {
metricEl.classList.remove("metric--off"); metricEl.classList.remove("metric--off");
warnEl.hidden = true; warnEl.textContent = "";
} }
} }
function updatePowerDeviation() { function updatePowerDeviation() {
updateDeviation(powerMetric, powerTarget, powerWarn, actualWatts, targetWatts, powerTolerance(targetWatts ?? 0)); updateDeviation(powerMetric, powerTarget, powerWarn, actualWatts, targetWatts, powerTolerance(targetWatts ?? 0), "W");
} }
function updateCadenceDeviation() { function updateCadenceDeviation() {
updateDeviation(cadenceMetric, cadenceTarget, cadenceWarn, actualCadence, targetCadence, CADENCE_TOL_ABS); updateDeviation(cadenceMetric, cadenceTarget, cadenceWarn, actualCadence, targetCadence, CADENCE_TOL_ABS, "rpm");
} }
function clearTargets() { function clearTargets() {

@ -316,36 +316,44 @@ body {
} }
.metric__value { .metric__value {
font-size: var(--text-3xl); font-size: var(--text-xl);
font-weight: var(--fw-semibold); font-weight: var(--fw-semibold);
line-height: 1; line-height: 1;
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
} }
.metric--cadence .metric__value,
.metric--hr .metric__value {
font-size: var(--text-xl);
}
.metric__unit { .metric__unit {
font-size: var(--text-sm); font-size: var(--text-sm);
color: var(--c-text-muted); color: var(--c-text-muted);
} }
/* целевое значение — главное */
.metric__target { .metric__target {
font-size: var(--text-xs); font-size: var(--text-2xl);
font-weight: var(--fw-semibold);
line-height: 1;
font-variant-numeric: tabular-nums;
}
/* фактическое значение — вторичное, краснеет при отклонении */
.metric__actual {
font-size: var(--text-base);
color: var(--c-text-muted); color: var(--c-text-muted);
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
} }
.metric__warn { .metric--off .metric__actual {
font-size: var(--text-xs);
font-weight: var(--fw-medium);
color: var(--c-danger); color: var(--c-danger);
font-weight: var(--fw-semibold);
} }
.metric--off .metric__value { /* предупреждение — резервируем строку, чтобы не прыгало */
.metric__warn {
min-height: var(--text-base);
font-size: var(--text-xs);
font-weight: var(--fw-medium);
color: var(--c-danger); color: var(--c-danger);
line-height: 1.2;
} }
/* --- Управление --- */ /* --- Управление --- */

Loading…
Cancel
Save