Merge feature/vierde-module: kolom past zich aan het aantal binnenmodules aan
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -266,7 +266,11 @@ De iconen `sun`, `partly`, `rain` en `showers` staan in de mockup. Maak `cloud`,
|
||||
"1009 – 1015 hPa in 24 u" in de kopregel is daarmee vervallen. Zie `hourTicks()` in `public/format.js`;
|
||||
die loopt uur voor uur door het venster en raadpleegt telkens de lokale klok, zodat de zomertijdwissel
|
||||
vanzelf goed gaat._
|
||||
- **Binnen (rechts):** 3 rijen met naam, temperatuur (1 decimaal) en "↓ min° ↑ max°". Een onbereikbare module toont "–" in `--muted`.
|
||||
- **Binnen (rechts):** een rij per module met naam, temperatuur (1 decimaal) en "↓ min° ↑ max°". Een onbereikbare module toont "–" in `--muted`.
|
||||
_Gewijzigd 2026-09-14: het aantal modules ligt niet vast op 3. De kolom heeft een vaste hoogte van 412px
|
||||
(744 − 2×30 padding − 40 kopregel − 232 onderstrook) en `fitRooms()` verkleint de inhoud met een schaal
|
||||
zodra die niet past. Zonder dat duwde een vierde module de onderste strook 39px van het scherm af. Bij
|
||||
drie modules verandert er niets (schaal 1); vier geeft ongeveer 0,86, en met een vastgezette module 0,78._
|
||||
_Toegevoegd 2026-09-13: rechts van elk temperatuurgetal staat het verloop van vandaag als klein lijntje
|
||||
(200x40, zonder labels: de schaal staat op de regel eronder en het venster is hetzelfde "vandaag" als bij
|
||||
buiten). Data per module uit een eigen `getmeasure`, apart gecachet onder `temperature-<module>` maar met
|
||||
|
||||
+15
-1
@@ -2,7 +2,7 @@ import {
|
||||
f1, fInt, fmm, markerPos, barHeights, wmoIcon, trendLabel,
|
||||
formatDate, formatTime, dayAbbr, monthName, isOlderThan, msUntil,
|
||||
co2Level, fPct, fHpa, seriesRange, sparklinePath,
|
||||
windArrowDeg, fKmh, formatHour, formatFullDate, pinRoom, hourTicks,
|
||||
windArrowDeg, fKmh, formatHour, formatFullDate, pinRoom, hourTicks, scaleToFit,
|
||||
} from './format.js';
|
||||
import { icon, windArrow } from './icons.js';
|
||||
|
||||
@@ -17,6 +17,7 @@ const FALLBACK_TZ = 'Europe/Berlin';
|
||||
const OVERLAY_MS = 20000; // hoe lang een overlay blijft staan zonder tik
|
||||
const RADAR_BUCKET_MS = 240000; // hergebruikvenster voor het radarbeeld, gelijk aan de servercache
|
||||
const HOURLY_WIDTH = 1045; // = stage-breedte 1133 min 2x 44px padding, zelfde marge als de rest van het scherm
|
||||
const INSIDE_H = 412; // hoogte van de middelste rij: 744 - 2x30 padding - 40 kopregel - 232 onderstrook
|
||||
|
||||
const $ = (id) => document.getElementById(id);
|
||||
const stage = $('stage');
|
||||
@@ -112,6 +113,19 @@ function renderRooms(rooms) {
|
||||
</div>`;
|
||||
});
|
||||
$('rooms').innerHTML = html.join('');
|
||||
fitRooms();
|
||||
}
|
||||
|
||||
/**
|
||||
* Houdt de modulekolom binnen zijn vaste hoogte. Bij drie modules past alles op ware grootte; komt er een
|
||||
* module bij (of groeit er een doordat je hem vastzet), dan wordt het geheel net genoeg verkleind. Zonder
|
||||
* dit duwt een vierde kaart de onderste strook van het scherm af.
|
||||
*/
|
||||
function fitRooms() {
|
||||
const el = $('rooms');
|
||||
el.style.transform = 'none';
|
||||
const scale = scaleToFit(el.scrollHeight, INSIDE_H);
|
||||
el.style.transform = scale < 1 ? `scale(${scale.toFixed(4)})` : '';
|
||||
}
|
||||
|
||||
function renderRain(rain, now) {
|
||||
|
||||
@@ -246,3 +246,9 @@ export function hourTicks(startIso, endIso, tz, stepHours = 6, minGapPercent = 9
|
||||
}
|
||||
return ticks;
|
||||
}
|
||||
|
||||
/** Schaal om `natural` px binnen `available` px te passen; nooit vergroten, nooit negatief. */
|
||||
export function scaleToFit(natural, available) {
|
||||
if (!(natural > 0) || !(available > 0)) return 1;
|
||||
return Math.min(1, available / natural);
|
||||
}
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="a-inside" id="rooms"></section>
|
||||
<section class="a-inside"><div id="rooms"></div></section>
|
||||
|
||||
<section class="a-bottom">
|
||||
<div id="rain-block">
|
||||
|
||||
+4
-2
@@ -109,8 +109,10 @@ html, body {
|
||||
.a-spark-grid { stroke: var(--line); stroke-width: 1; vector-effect: non-scaling-stroke; opacity: .6; }
|
||||
.a-spark-line { fill: none; stroke: var(--rain); stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
|
||||
|
||||
/* Binnen */
|
||||
.a-inside { align-self: center; }
|
||||
/* Binnen. Vaste hoogte met overflow verborgen: hoeveel modules er ook zijn, de kolom duwt nooit de
|
||||
onderste strook van het scherm af. Past de inhoud niet, dan verkleint fitRooms() hem met een schaal. */
|
||||
.a-inside { align-self: center; height: 412px; display: flex; align-items: center; overflow: hidden; }
|
||||
#rooms { width: 100%; transform-origin: center center; }
|
||||
.a-room { padding: 12px 0; border-top: 1px solid var(--line); }
|
||||
.a-room:first-child { border-top: 0; }
|
||||
.a-rname { font-size: 19px; color: var(--muted); display: flex; justify-content: space-between; align-items: center; }
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/* Service worker: app-shell cache-first, API network-first met timeout en fallback op de laatste response. */
|
||||
|
||||
const VERSION = 'weerscherm-v11';
|
||||
const VERSION = 'weerscherm-v12';
|
||||
const SHELL = [
|
||||
'./',
|
||||
'./index.html',
|
||||
|
||||
@@ -38,6 +38,8 @@ final class FixtureSource implements WeatherSource
|
||||
'dashboard_data' => ['time_utc' => $at('-4 minutes'), 'Temperature' => 18.9, 'CO2' => 1180, 'Humidity' => 52, 'min_temp' => 18.1, 'max_temp' => 19.6, 'temp_trend' => 'up']],
|
||||
['_id' => '03:00:00:00:00:03', 'type' => 'NAModule4', 'module_name' => 'Kantoor', 'reachable' => true,
|
||||
'dashboard_data' => ['time_utc' => $at('-4 minutes'), 'Temperature' => 20.2, 'CO2' => 1640, 'Humidity' => 44, 'min_temp' => 19.0, 'max_temp' => 21.0, 'temp_trend' => 'stable']],
|
||||
['_id' => '03:00:00:00:00:05', 'type' => 'NAModule4', 'module_name' => 'Zolder', 'reachable' => true,
|
||||
'dashboard_data' => ['time_utc' => $at('-5 minutes'), 'Temperature' => 17.4, 'CO2' => 780, 'Humidity' => 55, 'min_temp' => 16.2, 'max_temp' => 18.0, 'temp_trend' => 'up']],
|
||||
['_id' => '05:00:00:00:00:04', 'type' => 'NAModule3', 'module_name' => 'Regenmeter', 'reachable' => true,
|
||||
'dashboard_data' => ['time_utc' => $at('-3 minutes'), 'Rain' => 0, 'sum_rain_1' => 0, 'sum_rain_24' => 2.4]],
|
||||
],
|
||||
@@ -77,6 +79,7 @@ final class FixtureSource implements WeatherSource
|
||||
'02:00:00:00:00:01' => [9.2, 19.8, 10], // buiten
|
||||
'03:00:00:00:00:02' => [18.1, 19.6, 13], // slaapkamer
|
||||
'03:00:00:00:00:03' => [19.0, 21.0, 14], // kantoor
|
||||
'03:00:00:00:00:05' => [16.2, 18.0, 15], // zolder
|
||||
default => [20.4, 22.1, 12], // basisstation
|
||||
};
|
||||
|
||||
|
||||
+14
-1
@@ -121,7 +121,7 @@ test('msUntil computes the wait until the next local time', () => {
|
||||
|
||||
import {
|
||||
co2Level, fPct, fHpa, seriesRange, sparklinePath, sparklinePoints,
|
||||
windArrowDeg, fKmh, formatHour, formatFullDate, pinRoom, hourTicks,
|
||||
windArrowDeg, fKmh, formatHour, formatFullDate, pinRoom, hourTicks, scaleToFit,
|
||||
} from '../../public/format.js';
|
||||
|
||||
test('co2Level uses the Netatmo thresholds', () => {
|
||||
@@ -244,3 +244,16 @@ test('hourTicks gives nothing for an empty or invalid window', () => {
|
||||
assert.deepEqual(hourTicks('kapot', '2026-09-13T10:00:00+02:00', TZ), []);
|
||||
assert.deepEqual(hourTicks(undefined, undefined, TZ), []);
|
||||
});
|
||||
|
||||
test('scaleToFit shrinks to fit but never enlarges', () => {
|
||||
assert.equal(scaleToFit(481, 412), 412 / 481);
|
||||
assert.equal(scaleToFit(361, 412), 1, 'past al: niet vergroten');
|
||||
assert.equal(scaleToFit(412, 412), 1);
|
||||
});
|
||||
|
||||
test('scaleToFit is safe with nonsense input', () => {
|
||||
assert.equal(scaleToFit(0, 412), 1);
|
||||
assert.equal(scaleToFit(-10, 412), 1);
|
||||
assert.equal(scaleToFit(481, 0), 1);
|
||||
assert.equal(scaleToFit(undefined, 412), 1);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user