This commit is contained in:
Vincent van der Wal
2026-08-06 21:25:29 +02:00
parent c4a74ba91c
commit 810f9c2605
3 changed files with 49 additions and 30 deletions
+3 -1
View File
@@ -142,7 +142,8 @@
}); });
// Routes that are not finished on arrival: the five that fetch a forecast // Routes that are not finished on arrival: the five that fetch a forecast
// after mounting, and the redirect stubs, which render nothing at all and // after mounting, the maps page (whose map is a cross-origin iframe that has
// to load first), and the redirect stubs, which render nothing at all and
// bounce to a located URL from `onMount`. Knowing this up front is what makes // bounce to a located URL from `onMount`. Knowing this up front is what makes
// the wait reliable - the layout clears the ready flag before the swap rather // the wait reliable - the layout clears the ready flag before the swap rather
// than trusting the incoming page to have done it. // than trusting the incoming page to have done it.
@@ -153,6 +154,7 @@
'/weather/compare', '/weather/compare',
'/weather/seasonal', '/weather/seasonal',
'/weather/historical', '/weather/historical',
'/weather/maps',
'/weather/week/[location]', '/weather/week/[location]',
'/weather/14-day/[location]', '/weather/14-day/[location]',
'/weather/compare/[location]', '/weather/compare/[location]',
+11
View File
@@ -1,6 +1,7 @@
<script lang="ts"> <script lang="ts">
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { reportPageReady } from '$lib/stores/page-transition.svelte';
import { storedLocation, storedModel, storedTheme } from '$lib/stores/settings'; import { storedLocation, storedModel, storedTheme } from '$lib/stores/settings';
import { mapsDomainForModel } from '$lib/utils/maps-domain'; import { mapsDomainForModel } from '$lib/utils/maps-domain';
@@ -16,6 +17,15 @@
let hashOverride = $state<string | null>(null); let hashOverride = $state<string | null>(null);
let iframeEl = $state<HTMLIFrameElement | null>(null); let iframeEl = $state<HTMLIFrameElement | null>(null);
let mapReady = $state(false); let mapReady = $state(false);
let frameLoaded = $state(false);
// The map is a cross-origin iframe: until it has loaded there is nothing here
// but an empty panel, and a page transition that ends before then cross-fades
// the old page into that blank. Reporting readiness holds the transition (and
// then the loading overlay) until there is actually a map to fade into.
// `om-maps:ready` is the good signal; the iframe's own load event is the
// backstop, so a map that fails to boot still releases the page.
reportPageReady(() => mapReady || frameLoaded);
const postToMap = (message: Record<string, unknown>) => { const postToMap = (message: Record<string, unknown>) => {
iframeEl?.contentWindow?.postMessage(message, MAPS_ORIGIN); iframeEl?.contentWindow?.postMessage(message, MAPS_ORIGIN);
@@ -95,6 +105,7 @@
allowfullscreen allowfullscreen
allow="cross-origin-isolated" allow="cross-origin-isolated"
referrerpolicy="no-referrer" referrerpolicy="no-referrer"
onload={() => (frameLoaded = true)}
class="block h-full w-full border-0" class="block h-full w-full border-0"
sandbox="allow-scripts allow-same-origin allow-forms allow-popups" sandbox="allow-scripts allow-same-origin allow-forms allow-popups"
></iframe> ></iframe>
@@ -197,12 +197,17 @@
and archive pages: the controls then cannot move the heading when they and archive pages: the controls then cannot move the heading when they
change size. --> change size. -->
<div class="flex w-full flex-wrap items-center gap-3 sm:w-auto lg:absolute lg:top-0 lg:right-0"> <div class="flex w-full flex-wrap items-center gap-3 sm:w-auto lg:absolute lg:top-0 lg:right-0">
<!-- Range buttons reslice the already-fetched horizon (no refetch). Kept <!-- Range buttons reslice the already-fetched horizon (no refetch). While a
mounted and merely hidden while the forecast is on its way: mounting forecast is on its way they stay mounted but invisible, because
them on arrival re-flowed the row and nudged the heading. --> mounting them on arrival re-flowed the row and nudged the heading.
Two exceptions, both of which would reserve a gap for something that
is never coming: below sm the group is a full-width row of its own,
and behind the paywall there is no forecast on its way at all. -->
{#if $isSupporter}
<div <div
class="flex w-full gap-1 rounded-lg border border-border bg-card p-1 sm:w-auto" class="flex w-full gap-1 rounded-lg border border-border bg-card p-1 sm:w-auto {result
class:invisible={!result} ? ''
: 'hidden sm:flex sm:invisible'}"
role="group" role="group"
aria-label={m.seasonal_range_aria()} aria-label={m.seasonal_range_aria()}
aria-hidden={!result} aria-hidden={!result}
@@ -226,6 +231,7 @@
</button> </button>
{/each} {/each}
</div> </div>
{/if}
<ModelSelector <ModelSelector
selectedModel={seasonalModel} selectedModel={seasonalModel}
groups={seasonalModelGroups} groups={seasonalModelGroups}