revamp weather UI: cards, meteograms, units in header, location favorites and range controls

This commit is contained in:
2026-07-25 10:26:14 +02:00
committed by Vincent van der Wal
parent bac4759662
commit b4e509f9cb
18 changed files with 1023 additions and 275 deletions
+34 -27
View File
@@ -7,6 +7,7 @@
storedChartLayout,
storedLocation,
storedModel,
storedUnits,
storedVariablePrefs
} from '$lib/stores/settings';
@@ -32,6 +33,14 @@
...defaultParameters
});
// units live in a persisted store; mirror them into params so a change
// re-runs the fetch effect below (which reads params.*_unit)
$effect(() => {
params.temperature_unit = $storedUnits.temperature_unit;
params.wind_speed_unit = $storedUnits.wind_speed_unit;
params.precipitation_unit = $storedUnits.precipitation_unit;
});
let variableSidebarOpen = $state(false);
// Number of meteogram panels: reserves the chart area height before data
@@ -60,6 +69,11 @@
let loadError = $state<string | null>(null);
let requestVersion = 0;
// 7 by default; the user can extend to the model's longer range (up to 16 days)
let forecastDays = $state(7);
// 0 by default; the user can pull in a few recent past days
let pastDays = $state(0);
const selectedDay = new SvelteDate();
let fetchedHourly: FetchedHourly | null = $state(null);
@@ -97,8 +111,8 @@
temperature_unit: params.temperature_unit as 'celsius' | 'fahrenheit',
wind_speed_unit: params.wind_speed_unit as 'kmh' | 'ms' | 'mph' | 'kn',
precipitation_unit: params.precipitation_unit as 'mm' | 'inch',
forecast_days: 7,
past_days: 0,
forecast_days: forecastDays,
past_days: pastDays,
timezone: loc.timezone
})
.then((result: WeekForecastResult) => {
@@ -152,10 +166,10 @@
{location.name}
</h1>
<p class="truncate text-sm text-muted-foreground">
{#if location.admin1}{location.admin1},
{/if}{location.country ?? ''}
<span class="mx-1 opacity-50">·</span>
7-day forecast
<span class="lg:hidden"
>{#if location.admin1}{location.admin1},
{/if}{location.country ?? ''}<span class="mx-1 opacity-50">·</span></span
>7-day forecast
</p>
</div>
</div>
@@ -166,28 +180,11 @@
onModelChange={(model) => {
params.models = [model];
storedModel.set(model);
// a new model may not support the extended / past range
forecastDays = 7;
pastDays = 0;
}}
/>
<button
class="flex h-11 cursor-pointer items-center gap-2 rounded-xl border-2 border-border bg-card px-3.5 text-sm font-semibold text-muted-foreground shadow-sm transition-colors hover:border-primary/50 hover:text-foreground"
onclick={() => (variableSidebarOpen = true)}
aria-label="Choose visible variables"
>
<!-- sliders icon -->
<svg
class="h-4.5 w-4.5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
stroke-width="1.75"
>
<path
stroke-linecap="round"
d="M4 6h9m5 0h2M4 12h2m5 0h9M4 18h9m5 0h2M13 4.5v3M6 10.5v3M18 16.5v3"
/>
</svg>
<span class="hidden md:inline">Variables</span>
</button>
</div>
</div>
@@ -201,7 +198,16 @@
</div>
{/if}
<DailyCards daily={fetchedDaily} {selectedDay} units={params} onSelectDay={switchDay} />
<DailyCards
daily={fetchedDaily}
{selectedDay}
units={params}
onSelectDay={switchDay}
canExtend={forecastDays < 15}
onExtend={() => (forecastDays = 15)}
canExtendPast={pastDays < 3}
onExtendPast={() => (pastDays = 3)}
/>
{#if fetchedHourly && fetchedDaily}
<HourlyTable
@@ -210,6 +216,7 @@
{selectedDay}
units={params}
locationName={location.name ?? ''}
onCustomize={() => (variableSidebarOpen = true)}
/>
{:else}
<!-- placeholder with the table's approximate height: no layout shift -->