portrait almanac #167

This commit is contained in:
Matt Walsh
2026-04-22 21:35:53 -05:00
parent 033bed96b9
commit 0395e2cd08
7 changed files with 117 additions and 53 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

View File

@@ -30,12 +30,13 @@ class Almanac extends WeatherDisplay {
const superResponse = super.getData(weatherParameters, refresh); const superResponse = super.getData(weatherParameters, refresh);
// get sun/moon data // get sun/moon data
const { sun, moon } = this.calcSunMoonData(this.weatherParameters); const { sun, moon, moonTransit } = this.calcSunMoonData(this.weatherParameters);
// store the data // store the data
this.data = { this.data = {
sun, sun,
moon, moon,
moonTransit,
}; };
// share data // share data
this.getDataCallback(); this.getDataCallback();
@@ -47,7 +48,9 @@ class Almanac extends WeatherDisplay {
} }
calcSunMoonData(weatherParameters) { calcSunMoonData(weatherParameters) {
const sun = [0, 1, 2, 3, 4, 5, 6].map((days) => SunCalc.getTimes(DateTime.local().plus({ days }).toJSDate(), weatherParameters.latitude, weatherParameters.longitude)); const dayOffsets = [0, 1, 2, 3, 4, 5, 6];
const sun = dayOffsets.map((days) => SunCalc.getTimes(DateTime.local().plus({ days }).toJSDate(), weatherParameters.latitude, weatherParameters.longitude));
const moonTransit = dayOffsets.map((days) => SunCalc.getMoonTimes(DateTime.local().plus({ days }).toJSDate(), weatherParameters.latitude, weatherParameters.longitude));
// brute force the moon phases by scanning the next 30 days // brute force the moon phases by scanning the next 30 days
const moon = []; const moon = [];
@@ -74,6 +77,7 @@ class Almanac extends WeatherDisplay {
return { return {
sun, sun,
moon, moon,
moonTransit,
}; };
} }
@@ -121,21 +125,52 @@ class Almanac extends WeatherDisplay {
// Generate sun data grid in reading order (left-to-right, top-to-bottom) // Generate sun data grid in reading order (left-to-right, top-to-bottom)
// Set day names // Set day names and sunset times
const Today = DateTime.local(); const Today = DateTime.local();
// fill all three days, even if some are hidden by non-enhanced const portraitLines = [];
for (let i = 0; i < 3; i += 1) { const moonPortraitLines = [];
this.elem.querySelector(`.day-${i}`).textContent = Today.plus({ days: i }).toLocaleString({ weekday: 'long' }); // fill all days, even if some are hidden by the mode selection.
for (let i = 0; i < 7; i += 1) {
// format some data
const dayName = Today.plus({ days: i }).toLocaleString({ weekday: 'long' });
const sunrise = formatTimeForColumn(DateTime.fromJSDate(info.sun[i].sunrise));
const sunset = formatTimeForColumn(DateTime.fromJSDate(info.sun[i].sunset));
const sunrise = DateTime.fromJSDate(info.sun[i].sunrise); // these only use the first 3 for standard and wide
const sunset = DateTime.fromJSDate(info.sun[i].sunset); if (i < 3) {
const [sunriseFormatted, sunsetFormatted] = formatTimesForColumn([sunrise, sunset]); this.elem.querySelector(`.day-${i}`).textContent = dayName;
this.elem.querySelector(`.rise-${i}`).textContent = sunriseFormatted; this.elem.querySelector(`.rise-${i}`).textContent = sunrise;
this.elem.querySelector(`.set-${i}`).textContent = sunsetFormatted; this.elem.querySelector(`.set-${i}`).textContent = sunset;
} }
// and also fill the portrait oriented info
portraitLines.push(this.fillTemplate('dayname', { 'grid-item': dayName }));
portraitLines.push(this.fillTemplate('sunrise', { 'grid-item': sunrise }));
portraitLines.push(this.fillTemplate('sunset', { 'grid-item': sunset }));
// including the bonus moon rise/set data in portrait
const moonrise = formatTimeForColumn(DateTime.fromJSDate(info.moonTransit[i].rise));
const moonset = formatTimeForColumn(DateTime.fromJSDate(info.moonTransit[i].set));
moonPortraitLines.push(this.fillTemplate('dayname', { 'grid-item': dayName }));
moonPortraitLines.push(this.fillTemplate('sunrise', { 'grid-item': moonrise }));
moonPortraitLines.push(this.fillTemplate('sunset', { 'grid-item': moonset }));
}
// add the portrait lines to the page
const sunPortrait = this.elem.querySelector('.sun-portrait');
const replaceable = sunPortrait.querySelectorAll(':has(.replaceable)');
replaceable.forEach((elem) => elem.remove());
sunPortrait.append(...portraitLines);
// and the moon too
const moonPortrait = this.elem.querySelector('.moonrise.sun-portrait');
const moonReplaceable = moonPortrait.querySelectorAll(':has(.replaceable)');
moonReplaceable.forEach((elem) => elem.remove());
moonPortrait.append(...moonPortraitLines);
// Moon data // Moon data
const days = info.moon.map((MoonPhase) => { const days = info.moon.map((MoonPhase, idx) => {
const fill = {}; const fill = {};
const date = MoonPhase.date.toLocaleString({ month: 'short', day: 'numeric' }); const date = MoonPhase.date.toLocaleString({ month: 'short', day: 'numeric' });
@@ -144,7 +179,14 @@ class Almanac extends WeatherDisplay {
fill.type = MoonPhase.phase; fill.type = MoonPhase.phase;
fill.icon = { type: 'img', src: this.iconPaths[MoonPhase.phase] }; fill.icon = { type: 'img', src: this.iconPaths[MoonPhase.phase] };
return this.fillTemplate('day', fill); const filledTemplate = this.fillTemplate('day', fill);
// add class to hide >4 moon phases when not wide-enhanced
if (idx > 3) {
filledTemplate.classList.add('wide-enhanced');
}
return filledTemplate;
}); });
const daysContainer = this.elem.querySelector('.moon .days'); const daysContainer = this.elem.querySelector('.moon .days');
@@ -181,19 +223,16 @@ const imageName = (type) => {
} }
}; };
const formatTimesForColumn = (times) => { const formatTimeForColumn = (time) => {
const formatted = times.map((dt) => dt.setZone(timeZone()).toFormat('h:mm a').toUpperCase()); // moonrise and set may not have a time each day
if (!time.isValid) return '-';
// Check if any time has a 2-digit hour (starts with '1') const formatted = time.setZone(timeZone()).toFormat('h:mm a').toUpperCase();
const hasTwoDigitHour = formatted.some((time) => time.startsWith('1'));
// If mixed digit lengths, pad single-digit hours with non-breaking space // If mixed digit lengths, pad single-digit hours with non-breaking space
if (hasTwoDigitHour) { if (formatted.length === 8) {
return formatted.map((time) => (time.startsWith('1') ? time : `\u00A0${time}`));
}
// Otherwise, no padding needed
return formatted; return formatted;
}
return `\u00A0${formatted}`;
}; };
// register display // register display

View File

@@ -4,10 +4,15 @@
#almanac-html.weather-display { #almanac-html.weather-display {
background-image: url('../images/backgrounds/3.png'); background-image: url('../images/backgrounds/3.png');
// repeat the background if wide-enhanced // change the backgrounds for enhanced modes
.wide.enhanced & { .wide.enhanced & {
background-image: url('../images/backgrounds/3-wide-enhanced.png'); background-image: url('../images/backgrounds/3-wide-enhanced.png');
} }
// change background for portrait
.portrait.enhanced & {
background-image: url(../images/backgrounds/3-portrait.png);
}
} }
.weather-display .main.almanac { .weather-display .main.almanac {
@@ -30,6 +35,10 @@
grid-template-columns: repeat(4, auto); grid-template-columns: repeat(4, auto);
} }
.portrait.enhanced & {
display: none;
}
.grid-item { .grid-item {
// Reset inherited styles that interfere with grid layout // Reset inherited styles that interfere with grid layout
width: auto; width: auto;
@@ -65,6 +74,23 @@
} }
} }
.portrait.enhanced & {
font-size: 28pt;
}
.sun.sun-portrait {
display: none;
.portrait.enhanced & {
display: grid;
}
.grid-item {
margin: 8px 0px;
}
}
.moon { .moon {
position: relative; position: relative;
padding: 7px 50px; padding: 7px 50px;
@@ -73,6 +99,8 @@
.title { .title {
color: c.$column-header-text; color: c.$column-header-text;
padding-left: 13px; padding-left: 13px;
} }
.days { .days {
@@ -93,6 +121,14 @@
position: relative; position: relative;
top: -10px; top: -10px;
} }
&.wide-enhanced {
display: none;
.wide.enhanced & {
display: inline-block;
}
}
} }
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -14,6 +14,14 @@
<div class="grid-item time set-1"></div> <div class="grid-item time set-1"></div>
<div class="grid-item time set-2 wide-enhanced"></div> <div class="grid-item time set-2 wide-enhanced"></div>
</div> </div>
<div class="sun sun-portrait portrait-only">
<div class="grid-item empty"></div>
<div class="grid-item header sunrise">Sunrise</div>
<div class="grid-item header sunset">Sunset</div>
<div class="dayname template"><div class="grid-item row-label replaceable"></div></div>
<div class="sunrise template"><div class="grid-item time replaceable"></div></div>
<div class="sunset template"><div class="grid-item time replaceable"></div></div>
</div>
<div class="moon"> <div class="moon">
<div class="title">Moon Data:</div> <div class="title">Moon Data:</div>
<div class="days"> <div class="days">
@@ -23,5 +31,13 @@
<div class="date"></div> <div class="date"></div>
</div> </div>
</div> </div>
</div>
<div class="sun moonrise sun-portrait portrait-only">
<div class="grid-item empty"></div>
<div class="grid-item header sunrise">Moonrise</div>
<div class="grid-item header sunset">Moonset</div>
<div class="dayname template"><div class="grid-item row-label replaceable"></div></div>
<div class="moonrise template"><div class="grid-item time replaceable"></div></div>
<div class="moonset template"><div class="grid-item time replaceable"></div></div>
</div> </div>
</div> </div>