Merge pull request #18 from Salastil/development
Weather: label which day each hourly forecast entry belongs to
This commit is contained in:
@@ -5,6 +5,30 @@
|
|||||||
let { data }: { data: PageData } = $props();
|
let { data }: { data: PageData } = $props();
|
||||||
const weather = $derived(data.weather);
|
const weather = $derived(data.weather);
|
||||||
const unitLabel = $derived(weather.unit === 'celsius' ? 'C' : 'F');
|
const unitLabel = $derived(weather.unit === 'celsius' ? 'C' : 'F');
|
||||||
|
|
||||||
|
// The hourly strip covers the next 24h starting from "now" (see weather/client.ts), which
|
||||||
|
// almost always crosses a day boundary partway through — grouping by calendar day and
|
||||||
|
// labeling each group is what actually answers "which day is this hour in", rather than
|
||||||
|
// leaving it to be inferred from the hour-of-day alone (ambiguous for anything after
|
||||||
|
// midnight, and easy to misread near the boundary either way).
|
||||||
|
function dayLabel(d: Date): string {
|
||||||
|
const startOfDay = (x: Date) => new Date(x.getFullYear(), x.getMonth(), x.getDate()).getTime();
|
||||||
|
const diffDays = Math.round((startOfDay(d) - startOfDay(new Date())) / 86_400_000);
|
||||||
|
if (diffDays === 0) return 'Today';
|
||||||
|
if (diffDays === 1) return 'Tomorrow';
|
||||||
|
return d.toLocaleDateString([], { weekday: 'long' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const hourlyGroups = $derived.by(() => {
|
||||||
|
const groups: { label: string; hours: typeof weather.hourly }[] = [];
|
||||||
|
for (const hour of weather.hourly) {
|
||||||
|
const label = dayLabel(new Date(hour.time));
|
||||||
|
const last = groups[groups.length - 1];
|
||||||
|
if (last && last.label === label) last.hours.push(hour);
|
||||||
|
else groups.push({ label, hours: [hour] });
|
||||||
|
}
|
||||||
|
return groups;
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="head">
|
<div class="head">
|
||||||
@@ -75,8 +99,11 @@
|
|||||||
|
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<span class="section-title">Hourly</span>
|
<span class="section-title">Hourly</span>
|
||||||
|
{#each hourlyGroups as group (group.label + group.hours[0]?.time)}
|
||||||
|
<div class="hourly-day">
|
||||||
|
<span class="hourly-day-label">{group.label}</span>
|
||||||
<div class="hourly-strip">
|
<div class="hourly-strip">
|
||||||
{#each weather.hourly as hour (hour.time)}
|
{#each group.hours as hour (hour.time)}
|
||||||
<div class="hour-col">
|
<div class="hour-col">
|
||||||
<span class="hour-time">{new Date(hour.time).toLocaleTimeString([], { hour: 'numeric' })}</span>
|
<span class="hour-time">{new Date(hour.time).toLocaleTimeString([], { hour: 'numeric' })}</span>
|
||||||
<span class="hour-icon">{hour.icon}</span>
|
<span class="hour-icon">{hour.icon}</span>
|
||||||
@@ -85,6 +112,8 @@
|
|||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<span class="section-title">7-day forecast</span>
|
<span class="section-title">7-day forecast</span>
|
||||||
@@ -228,6 +257,19 @@
|
|||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
margin: 4px 0 0;
|
margin: 4px 0 0;
|
||||||
}
|
}
|
||||||
|
.hourly-day {
|
||||||
|
margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
.hourly-day:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
.hourly-day-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--text-muted);
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
.hourly-strip {
|
.hourly-strip {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(12, 1fr);
|
grid-template-columns: repeat(12, 1fr);
|
||||||
|
|||||||
Reference in New Issue
Block a user