Added daily and hourly styling at least

This commit is contained in:
Lani Aung
2025-06-26 07:42:55 -07:00
parent 5d4d9ece64
commit b48b9d8af3
6 changed files with 275 additions and 73 deletions
+53
View File
@@ -51,7 +51,60 @@ body {
#time { #time {
font-size: 3rem; font-size: 3rem;
margin-bottom: 1rem;
text-align: center; text-align: center;
.material {
font-variation-settings: "FILL" 0, "wght" 700, "opsz" 24;
vertical-align: sub;
}
}
#hourly {
display: flex;
font-size: .75rem;
margin: 1rem 0;
text-align: center;
}
#daily {
display: flex;
.daily-slot {
display: flex;
flex-direction: column;
.range {
white-space: nowrap;
}
.uv {
&.moderate {
color: yellow;
-webkit-text-fill-color: yellow;
}
&.high {
color: orange;
-webkit-text-fill-color: orange;
}
&.very-high {
color: red;
-webkit-text-fill-color: red;
}
&.extreme {
color: purple;
-webkit-text-fill-color: purple;
}
}
}
}
#current {
display: grid;
grid-template-columns: 1fr 1fr;
} }
section header { section header {
+90 -40
View File
@@ -15,11 +15,11 @@ export default class Weather {
constructor(latitude?: number, longitude?: number) { constructor(latitude?: number, longitude?: number) {
this.options = new WeatherOptions(latitude ?? 47.61002138071677, longitude ?? -122.17906310779568) this.options = new WeatherOptions(latitude ?? 47.61002138071677, longitude ?? -122.17906310779568)
const time = document.getElementById("time") const me = this
const time = document.getElementById("time-value")
time.textContent = new Date().toLocaleTimeString("en-US", { hour12: false, timeStyle: "short" }) time.textContent = new Date().toLocaleTimeString("en-US", { hour12: false, timeStyle: "short" })
setInterval(() => time.textContent = new Date().toLocaleTimeString("en-US", { hour12: false, timeStyle: "short" }), 15000) setInterval(() => time.textContent = new Date().toLocaleTimeString("en-US", { hour12: false, timeStyle: "short" }), 15000)
const me = this
setInterval(() => me.fetchWeather().then(() => me.setDisplay()), 300000) setInterval(() => me.fetchWeather().then(() => me.setDisplay()), 300000)
me.fetchWeather().then(() => me.setDisplay()) me.fetchWeather().then(() => me.setDisplay())
} }
@@ -37,7 +37,7 @@ export default class Weather {
const hourly = response.hourly() const hourly = response.hourly()
const daily = response.daily() const daily = response.daily()
if (current != null) me.setCurrentData(current) if (current != null) me.current = new CurrentWeather(current)
if (hourly != null) me.setHourlyData(hourly) if (hourly != null) me.setHourlyData(hourly)
if (daily != null) me.setDailyData(daily) if (daily != null) me.setDailyData(daily)
} }
@@ -80,20 +80,6 @@ export default class Weather {
return false return false
} }
private setCurrentData(current: VariablesWithTime) {
this.current = {
time: new Date(Number(current.time()) * 1000),
temperature: current.variables(0)!.value(),
humidity: current.variables(1)!.value(),
feelsLike: current.variables(2)!.value(),
precipitation: current.variables(3)!.value(),
windSpeed: current.variables(4)!.value(),
windGusts: current.variables(5)!.value(),
windDirection: current.variables(6)!.value(),
cloudCoverage: current.variables(7)!.value()
}
}
private setHourlyData(hourly: VariablesWithTime) { private setHourlyData(hourly: VariablesWithTime) {
const length = Number(hourly.timeEnd() - hourly.time()) / hourly.interval() const length = Number(hourly.timeEnd() - hourly.time()) / hourly.interval()
const temperatures = hourly.variables(0)!.valuesArray() const temperatures = hourly.variables(0)!.valuesArray()
@@ -103,7 +89,7 @@ export default class Weather {
for (let i = 0; i < length; i++) { for (let i = 0; i < length; i++) {
this.hourly.push({ this.hourly.push({
time: new Date((Number(hourly.time()) + i * hourly.interval()) * 1000), time: new Date(new Date().getTime() + i * 3600000),
temperature: temperatures[i], temperature: temperatures[i],
precipitation: precipitationTotals[i], precipitation: precipitationTotals[i],
precipitationProbability: precipitationProbabilities[i] precipitationProbability: precipitationProbabilities[i]
@@ -127,31 +113,65 @@ export default class Weather {
this.daily.length = 0 this.daily.length = 0
for (let i = 0; i < length; i++) { for (let i = 0; i < length; i++) {
this.daily.push({ const weather = new DailyWeather()
time: new Date((Number(daily.time()) + i * daily.interval()) * 1000), weather.time = new Date((Number(daily.time()) + i * daily.interval()) * 1000)
maxTemperature: maxTemperatures[i], weather.maxTemperature = maxTemperatures[i]
minTemperature: minTemperatures[i], weather.minTemperature = minTemperatures[i]
sunrise: new Date((Number(sunrise.valuesInt64(i))) * 1000), weather.sunrise = new Date((Number(sunrise.valuesInt64(i))) * 1000)
sunset: new Date((Number(sunset.valuesInt64(i))) * 1000), weather.sunset = new Date((Number(sunset.valuesInt64(i))) * 1000)
maxUV: maxUVs[i], weather.maxUV = maxUVs[i]
totalPrecipitation: precipitationTotals[i], weather.totalPrecipitation = precipitationTotals[i]
hoursOfPrecipitation: precipitationHours[i], weather.hoursOfPrecipitation = precipitationHours[i]
maxWindSpeed: maxWindSpeeds[i], weather.maxWindSpeed = maxWindSpeeds[i]
maxWindGusts: maxWindGusts[i], weather.maxWindGusts = maxWindGusts[i]
prominentWindDirection: prominentWindDirections[i] weather.prominentWindDirection = prominentWindDirections[i]
})
this.daily.push(weather)
} }
} }
private setDisplay() { private setDisplay() {
console.debug(this)
const current = document.getElementById("current") const current = document.getElementById("current")
current.innerHTML = `<header title="Temperature (feels like)"><span class="material">thermostat</span>${Math.round(this.current.temperature)} (${Math.round(this.current.feelsLike)})</header> current.innerHTML = `<div><span class="material">thermostat</span>${Math.round(this.current.temperature)} (${Math.round(this.current.feelsLike)})</div>
<main> <div><span class="material">humidity_percentage</span> ${Math.round(this.current.humidity)}%</div>
<div><span class="material">water_drop</span> ${Math.round(this.current.humidity)}%</div> <div><span class="material">cloud</span> ${Math.round(this.current.cloudCoverage)}%</div>
<div><span class="material">cloud</span> ${Math.round(this.current.cloudCoverage)}%</div> <div><span class="material">weather_mix</span> ${Math.round(this.current.precipitation)}%</div>
<div><span class="material">weather_mix</span> ${Math.round(this.current.precipitation)}%</div> <div><span class="material">air</span> ${Math.round(this.current.windSpeed)} (${Math.round(this.current.windGusts)}) ${this.inCardinals(this.current.windDirection)}</div>`
<div><span class="material">air</span> ${Math.round(this.current.windSpeed)} (${Math.round(this.current.windGusts)}) ${this.inCardinals(this.current.windDirection)}</div> if (!this.daily) return
</main>`
const icon = document.getElementById("time-icon")
icon.innerText = this.current.getCloudCoverageIcon(this.daily[0].sunrise, this.daily[0].sunset)
const dailySlots: string[] = []
for (const day of this.daily) {
dailySlots.push(`<div class="daily-slot">
<span class="date">${day.time.getDate()}</span>
<span class="range">${Math.round(day.minTemperature)} - ${Math.round(day.maxTemperature)}</span>
<span class="uv ${day.uvRating()}">${Math.round(day.maxUV)}</span>
<span class="sunrise">${day.sunrise.toLocaleTimeString("en-US", { timeStyle: "short", hour12: false })}</span>
<span class="sunset">${day.sunset.toLocaleTimeString("en-US", { timeStyle: "short", hour12: false })}</span>
</div>`)
}
const daily = document.getElementById("daily")
daily.innerHTML = dailySlots.join("\r\n")
if (!this.hourly) return
const hourlySlots: string[] = []
for (let i = 0; i < 12; i++) {
const hour = this.hourly[i]
hourlySlots.push(`<div class="hourly-slot">
<span class="hour">${hour.time.getHours()}</span>
<span class="temp">${Math.round(hour.temperature)}</span>
<span class="precipitation">${hour.precipitation}<sup>"</sup></span>
<span class="chance">${hour.precipitationProbability}<sup>%</sup></span>
</div>`)
}
const hourly = document.getElementById("hourly")
hourly.innerHTML = hourlySlots.join("\r\n")
} }
private inCardinals(direction: number): string { private inCardinals(direction: number): string {
@@ -174,6 +194,7 @@ class WeatherOptions {
"wind_speed_unit" = "mph" "wind_speed_unit" = "mph"
"temperature_unit" = "fahrenheit" "temperature_unit" = "fahrenheit"
"precipitation_unit" = "inch" "precipitation_unit" = "inch"
"forecast_hours": 12
constructor(latitude: number, longitude: number, timezone = "America/Los_Angeles") { constructor(latitude: number, longitude: number, timezone = "America/Los_Angeles") {
this.latitude = latitude this.latitude = latitude
@@ -182,7 +203,7 @@ class WeatherOptions {
} }
} }
interface CurrentWeather { class CurrentWeather {
time: Date time: Date
temperature: number temperature: number
humidity: number humidity: number
@@ -192,9 +213,30 @@ interface CurrentWeather {
windGusts: number windGusts: number
windDirection: number windDirection: number
cloudCoverage: number cloudCoverage: number
constructor(current: VariablesWithTime) {
this.time = new Date(Number(current.time()) * 1000)
this.temperature = current.variables(0)!.value()
this.humidity = current.variables(1)!.value()
this.feelsLike = current.variables(2)!.value()
this.precipitation = current.variables(3)!.value()
this.windSpeed = current.variables(4)!.value()
this.windGusts = current.variables(5)!.value()
this.windDirection = current.variables(6)!.value()
this.cloudCoverage = current.variables(7)!.value()
}
getCloudCoverageIcon(sunrise?: Date, sunset?: Date): string {
if (this.cloudCoverage > 75 || !sunrise || !sunset) return "cloud"
if (this.time.getTime() > sunrise.getTime() && this.time.getTime() < sunset.getTime())
return this.cloudCoverage > 25 ? "partly_cloudy_day" : "clear_day"
return this.cloudCoverage > 25 ? "partly_cloudy_night" : "bedtime"
}
} }
interface DailyWeather { class DailyWeather {
time: Date time: Date
hoursOfPrecipitation: number hoursOfPrecipitation: number
totalPrecipitation: number totalPrecipitation: number
@@ -206,6 +248,14 @@ interface DailyWeather {
prominentWindDirection: number prominentWindDirection: number
maxWindGusts: number maxWindGusts: number
maxWindSpeed: number maxWindSpeed: number
uvRating(): string {
if (this.maxUV < 3) return "low"
if (this.maxUV < 6) return "moderate"
if (this.maxUV < 8) return "high"
if (this.maxUV < 11) return "very-high"
return "extreme"
}
} }
interface HourlyWeather { interface HourlyWeather {
+4 -1
View File
@@ -7,7 +7,10 @@
</head> </head>
<body> <body>
<main id="weather-panel"> <main id="weather-panel">
<header id="time"></header> <header id="time">
<span class="material" id="time-icon"></span>
<span id="time-value"></span>
</header>
<section id="current"></section> <section id="current"></section>
<section id="hourly"></section> <section id="hourly"></section>
<section id="daily"></section> <section id="daily"></section>
+76 -31
View File
@@ -2323,10 +2323,10 @@ var Weather = /** @class */ (function () {
this.hourly = []; this.hourly = [];
this.daily = []; this.daily = [];
this.options = new WeatherOptions(latitude !== null && latitude !== void 0 ? latitude : 47.61002138071677, longitude !== null && longitude !== void 0 ? longitude : -122.17906310779568); this.options = new WeatherOptions(latitude !== null && latitude !== void 0 ? latitude : 47.61002138071677, longitude !== null && longitude !== void 0 ? longitude : -122.17906310779568);
var time = document.getElementById("time"); var me = this;
var time = document.getElementById("time-value");
time.textContent = new Date().toLocaleTimeString("en-US", { hour12: false, timeStyle: "short" }); time.textContent = new Date().toLocaleTimeString("en-US", { hour12: false, timeStyle: "short" });
setInterval(function () { return time.textContent = new Date().toLocaleTimeString("en-US", { hour12: false, timeStyle: "short" }); }, 15000); setInterval(function () { return time.textContent = new Date().toLocaleTimeString("en-US", { hour12: false, timeStyle: "short" }); }, 15000);
var me = this;
setInterval(function () { return me.fetchWeather().then(function () { return me.setDisplay(); }); }, 300000); setInterval(function () { return me.fetchWeather().then(function () { return me.setDisplay(); }); }, 300000);
me.fetchWeather().then(function () { return me.setDisplay(); }); me.fetchWeather().then(function () { return me.setDisplay(); });
} }
@@ -2351,7 +2351,7 @@ var Weather = /** @class */ (function () {
hourly = response.hourly(); hourly = response.hourly();
daily = response.daily(); daily = response.daily();
if (current != null) if (current != null)
me.setCurrentData(current); me.current = new CurrentWeather(current);
if (hourly != null) if (hourly != null)
me.setHourlyData(hourly); me.setHourlyData(hourly);
if (daily != null) if (daily != null)
@@ -2396,19 +2396,6 @@ var Weather = /** @class */ (function () {
} }
return false; return false;
}; };
Weather.prototype.setCurrentData = function (current) {
this.current = {
time: new Date(Number(current.time()) * 1000),
temperature: current.variables(0).value(),
humidity: current.variables(1).value(),
feelsLike: current.variables(2).value(),
precipitation: current.variables(3).value(),
windSpeed: current.variables(4).value(),
windGusts: current.variables(5).value(),
windDirection: current.variables(6).value(),
cloudCoverage: current.variables(7).value()
};
};
Weather.prototype.setHourlyData = function (hourly) { Weather.prototype.setHourlyData = function (hourly) {
var length = Number(hourly.timeEnd() - hourly.time()) / hourly.interval(); var length = Number(hourly.timeEnd() - hourly.time()) / hourly.interval();
var temperatures = hourly.variables(0).valuesArray(); var temperatures = hourly.variables(0).valuesArray();
@@ -2417,7 +2404,7 @@ var Weather = /** @class */ (function () {
this.hourly.length = 0; this.hourly.length = 0;
for (var i = 0; i < length; i++) { for (var i = 0; i < length; i++) {
this.hourly.push({ this.hourly.push({
time: new Date((Number(hourly.time()) + i * hourly.interval()) * 1000), time: new Date(new Date().getTime() + i * 3600000),
temperature: temperatures[i], temperature: temperatures[i],
precipitation: precipitationTotals[i], precipitation: precipitationTotals[i],
precipitationProbability: precipitationProbabilities[i] precipitationProbability: precipitationProbabilities[i]
@@ -2438,24 +2425,45 @@ var Weather = /** @class */ (function () {
var prominentWindDirections = daily.variables(9).valuesArray(); var prominentWindDirections = daily.variables(9).valuesArray();
this.daily.length = 0; this.daily.length = 0;
for (var i = 0; i < length; i++) { for (var i = 0; i < length; i++) {
this.daily.push({ var weather = new DailyWeather();
time: new Date((Number(daily.time()) + i * daily.interval()) * 1000), weather.time = new Date((Number(daily.time()) + i * daily.interval()) * 1000);
maxTemperature: maxTemperatures[i], weather.maxTemperature = maxTemperatures[i];
minTemperature: minTemperatures[i], weather.minTemperature = minTemperatures[i];
sunrise: new Date((Number(sunrise.valuesInt64(i))) * 1000), weather.sunrise = new Date((Number(sunrise.valuesInt64(i))) * 1000);
sunset: new Date((Number(sunset.valuesInt64(i))) * 1000), weather.sunset = new Date((Number(sunset.valuesInt64(i))) * 1000);
maxUV: maxUVs[i], weather.maxUV = maxUVs[i];
totalPrecipitation: precipitationTotals[i], weather.totalPrecipitation = precipitationTotals[i];
hoursOfPrecipitation: precipitationHours[i], weather.hoursOfPrecipitation = precipitationHours[i];
maxWindSpeed: maxWindSpeeds[i], weather.maxWindSpeed = maxWindSpeeds[i];
maxWindGusts: maxWindGusts[i], weather.maxWindGusts = maxWindGusts[i];
prominentWindDirection: prominentWindDirections[i] weather.prominentWindDirection = prominentWindDirections[i];
}); this.daily.push(weather);
} }
}; };
Weather.prototype.setDisplay = function () { Weather.prototype.setDisplay = function () {
console.debug(this);
var current = document.getElementById("current"); var current = document.getElementById("current");
current.innerHTML = "<header title=\"Temperature (feels like)\"><span class=\"material\">thermostat</span>".concat(Math.round(this.current.temperature), " (").concat(Math.round(this.current.feelsLike), ")</header>\n<main>\n <div><span class=\"material\">water_drop</span> ").concat(Math.round(this.current.humidity), "%</div>\n <div><span class=\"material\">cloud</span> ").concat(Math.round(this.current.cloudCoverage), "%</div>\n <div><span class=\"material\">weather_mix</span> ").concat(Math.round(this.current.precipitation), "%</div>\n <div><span class=\"material\">air</span> ").concat(Math.round(this.current.windSpeed), " (").concat(Math.round(this.current.windGusts), ") ").concat(this.inCardinals(this.current.windDirection), "</div>\n</main>"); current.innerHTML = "<div><span class=\"material\">thermostat</span>".concat(Math.round(this.current.temperature), " (").concat(Math.round(this.current.feelsLike), ")</div>\n<div><span class=\"material\">humidity_percentage</span> ").concat(Math.round(this.current.humidity), "%</div>\n<div><span class=\"material\">cloud</span> ").concat(Math.round(this.current.cloudCoverage), "%</div>\n<div><span class=\"material\">weather_mix</span> ").concat(Math.round(this.current.precipitation), "%</div>\n<div><span class=\"material\">air</span> ").concat(Math.round(this.current.windSpeed), " (").concat(Math.round(this.current.windGusts), ") ").concat(this.inCardinals(this.current.windDirection), "</div>");
if (!this.daily)
return;
var icon = document.getElementById("time-icon");
icon.innerText = this.current.getCloudCoverageIcon(this.daily[0].sunrise, this.daily[0].sunset);
var dailySlots = [];
for (var _i = 0, _a = this.daily; _i < _a.length; _i++) {
var day = _a[_i];
dailySlots.push("<div class=\"daily-slot\">\n <span class=\"date\">".concat(day.time.getDate(), "</span>\n <span class=\"range\">").concat(Math.round(day.minTemperature), " - ").concat(Math.round(day.maxTemperature), "</span>\n <span class=\"uv ").concat(day.uvRating(), "\">").concat(Math.round(day.maxUV), "</span>\n <span class=\"sunrise\">").concat(day.sunrise.toLocaleTimeString("en-US", { timeStyle: "short", hour12: false }), "</span>\n <span class=\"sunset\">").concat(day.sunset.toLocaleTimeString("en-US", { timeStyle: "short", hour12: false }), "</span>\n</div>"));
}
var daily = document.getElementById("daily");
daily.innerHTML = dailySlots.join("\r\n");
if (!this.hourly)
return;
var hourlySlots = [];
for (var i = 0; i < 12; i++) {
var hour = this.hourly[i];
hourlySlots.push("<div class=\"hourly-slot\">\n <span class=\"hour\">".concat(hour.time.getHours(), "</span>\n <span class=\"temp\">").concat(Math.round(hour.temperature), "</span>\n <span class=\"precipitation\">").concat(hour.precipitation, "<sup>\"</sup></span>\n <span class=\"chance\">").concat(hour.precipitationProbability, "<sup>%</sup></span>\n</div>"));
}
var hourly = document.getElementById("hourly");
hourly.innerHTML = hourlySlots.join("\r\n");
}; };
Weather.prototype.inCardinals = function (direction) { Weather.prototype.inCardinals = function (direction) {
if (direction > 337.5 || direction <= 22.5) if (direction > 337.5 || direction <= 22.5)
@@ -2492,6 +2500,43 @@ var WeatherOptions = /** @class */ (function () {
} }
return WeatherOptions; return WeatherOptions;
}()); }());
var CurrentWeather = /** @class */ (function () {
function CurrentWeather(current) {
this.time = new Date(Number(current.time()) * 1000);
this.temperature = current.variables(0).value();
this.humidity = current.variables(1).value();
this.feelsLike = current.variables(2).value();
this.precipitation = current.variables(3).value();
this.windSpeed = current.variables(4).value();
this.windGusts = current.variables(5).value();
this.windDirection = current.variables(6).value();
this.cloudCoverage = current.variables(7).value();
}
CurrentWeather.prototype.getCloudCoverageIcon = function (sunrise, sunset) {
if (this.cloudCoverage > 75 || !sunrise || !sunset)
return "cloud";
if (this.time.getTime() > sunrise.getTime() && this.time.getTime() < sunset.getTime())
return this.cloudCoverage > 25 ? "partly_cloudy_day" : "clear_day";
return this.cloudCoverage > 25 ? "partly_cloudy_night" : "bedtime";
};
return CurrentWeather;
}());
var DailyWeather = /** @class */ (function () {
function DailyWeather() {
}
DailyWeather.prototype.uvRating = function () {
if (this.maxUV < 3)
return "low";
if (this.maxUV < 6)
return "moderate";
if (this.maxUV < 8)
return "high";
if (this.maxUV < 11)
return "very-high";
return "extreme";
};
return DailyWeather;
}());
var LastFetched = /** @class */ (function () { var LastFetched = /** @class */ (function () {
function LastFetched() { function LastFetched() {
this.current = new Date(); this.current = new Date();
+1 -1
View File
File diff suppressed because one or more lines are too long
+51
View File
@@ -51,8 +51,59 @@ body #weather-panel .material {
html #weather-panel #time, html #weather-panel #time,
body #weather-panel #time { body #weather-panel #time {
font-size: 3rem; font-size: 3rem;
margin-bottom: 1rem;
text-align: center; text-align: center;
} }
html #weather-panel #time .material,
body #weather-panel #time .material {
font-variation-settings: "FILL" 0, "wght" 700, "opsz" 24;
vertical-align: sub;
}
html #weather-panel #hourly,
body #weather-panel #hourly {
display: flex;
font-size: 0.75rem;
margin: 1rem 0;
text-align: center;
}
html #weather-panel #daily,
body #weather-panel #daily {
display: flex;
}
html #weather-panel #daily .daily-slot,
body #weather-panel #daily .daily-slot {
display: flex;
flex-direction: column;
}
html #weather-panel #daily .daily-slot .range,
body #weather-panel #daily .daily-slot .range {
white-space: nowrap;
}
html #weather-panel #daily .daily-slot .uv.moderate,
body #weather-panel #daily .daily-slot .uv.moderate {
color: yellow;
-webkit-text-fill-color: yellow;
}
html #weather-panel #daily .daily-slot .uv.high,
body #weather-panel #daily .daily-slot .uv.high {
color: orange;
-webkit-text-fill-color: orange;
}
html #weather-panel #daily .daily-slot .uv.very-high,
body #weather-panel #daily .daily-slot .uv.very-high {
color: red;
-webkit-text-fill-color: red;
}
html #weather-panel #daily .daily-slot .uv.extreme,
body #weather-panel #daily .daily-slot .uv.extreme {
color: purple;
-webkit-text-fill-color: purple;
}
html #weather-panel #current,
body #weather-panel #current {
display: grid;
grid-template-columns: 1fr 1fr;
}
html #weather-panel section header, html #weather-panel section header,
body #weather-panel section header { body #weather-panel section header {
font-size: 1.5rem; font-size: 1.5rem;