fix: modulekolom past zich aan het aantal modules aan
Er is een vierde binnenmodule bijgekomen. De kaart verscheen al vanzelf (StationMapper leest alle NAModule4-modules uit), maar de kolom was op drie kaarten ontworpen: vier kaarten van 120px duwden de rij op 481px en daarmee de onderste strook 39px voorbij de onderrand van het canvas. De kolom heeft nu een vaste hoogte van 412px met overflow verborgen, en fitRooms() verkleint de inhoud met een schaal zodra die niet past. Bij drie modules verandert er niets (schaal 1), bij vier wordt het 0,86 en met een vastgezette module 0,78. Zo blijft het scherm kloppen, ongeacht hoeveel modules erbij komen. De dev-fixture heeft er een vierde module bij, zodat dit lokaal te zien is zonder Netatmo-koppeling. Servicewerker naar v12. 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`;
|
"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
|
die loopt uur voor uur door het venster en raadpleegt telkens de lokale klok, zodat de zomertijdwissel
|
||||||
vanzelf goed gaat._
|
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
|
_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
|
(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
|
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,
|
f1, fInt, fmm, markerPos, barHeights, wmoIcon, trendLabel,
|
||||||
formatDate, formatTime, dayAbbr, monthName, isOlderThan, msUntil,
|
formatDate, formatTime, dayAbbr, monthName, isOlderThan, msUntil,
|
||||||
co2Level, fPct, fHpa, seriesRange, sparklinePath,
|
co2Level, fPct, fHpa, seriesRange, sparklinePath,
|
||||||
windArrowDeg, fKmh, formatHour, formatFullDate, pinRoom, hourTicks,
|
windArrowDeg, fKmh, formatHour, formatFullDate, pinRoom, hourTicks, scaleToFit,
|
||||||
} from './format.js';
|
} from './format.js';
|
||||||
import { icon, windArrow } from './icons.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 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 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 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 $ = (id) => document.getElementById(id);
|
||||||
const stage = $('stage');
|
const stage = $('stage');
|
||||||
@@ -112,6 +113,19 @@ function renderRooms(rooms) {
|
|||||||
</div>`;
|
</div>`;
|
||||||
});
|
});
|
||||||
$('rooms').innerHTML = html.join('');
|
$('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) {
|
function renderRain(rain, now) {
|
||||||
|
|||||||
@@ -246,3 +246,9 @@ export function hourTicks(startIso, endIso, tz, stepHours = 6, minGapPercent = 9
|
|||||||
}
|
}
|
||||||
return ticks;
|
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>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="a-inside" id="rooms"></section>
|
<section class="a-inside"><div id="rooms"></div></section>
|
||||||
|
|
||||||
<section class="a-bottom">
|
<section class="a-bottom">
|
||||||
<div id="rain-block">
|
<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-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; }
|
.a-spark-line { fill: none; stroke: var(--rain); stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
|
||||||
|
|
||||||
/* Binnen */
|
/* Binnen. Vaste hoogte met overflow verborgen: hoeveel modules er ook zijn, de kolom duwt nooit de
|
||||||
.a-inside { align-self: center; }
|
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 { padding: 12px 0; border-top: 1px solid var(--line); }
|
||||||
.a-room:first-child { border-top: 0; }
|
.a-room:first-child { border-top: 0; }
|
||||||
.a-rname { font-size: 19px; color: var(--muted); display: flex; justify-content: space-between; align-items: center; }
|
.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. */
|
/* 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 = [
|
const SHELL = [
|
||||||
'./',
|
'./',
|
||||||
'./index.html',
|
'./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']],
|
'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,
|
['_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']],
|
'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,
|
['_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]],
|
'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
|
'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:02' => [18.1, 19.6, 13], // slaapkamer
|
||||||
'03:00:00:00:00:03' => [19.0, 21.0, 14], // kantoor
|
'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
|
default => [20.4, 22.1, 12], // basisstation
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+14
-1
@@ -121,7 +121,7 @@ test('msUntil computes the wait until the next local time', () => {
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
co2Level, fPct, fHpa, seriesRange, sparklinePath, sparklinePoints,
|
co2Level, fPct, fHpa, seriesRange, sparklinePath, sparklinePoints,
|
||||||
windArrowDeg, fKmh, formatHour, formatFullDate, pinRoom, hourTicks,
|
windArrowDeg, fKmh, formatHour, formatFullDate, pinRoom, hourTicks, scaleToFit,
|
||||||
} from '../../public/format.js';
|
} from '../../public/format.js';
|
||||||
|
|
||||||
test('co2Level uses the Netatmo thresholds', () => {
|
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('kapot', '2026-09-13T10:00:00+02:00', TZ), []);
|
||||||
assert.deepEqual(hourTicks(undefined, undefined, 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