95 lines
2.4 KiB
Svelte
95 lines
2.4 KiB
Svelte
<script lang="ts">
|
|
import { onMount } from 'svelte';
|
|
import { navigating, page } from '$app/stores';
|
|
import { afterNavigate, disableScrollHandling } from '$app/navigation';
|
|
import { initClickTracking, trackPageView } from '$lib/analytics';
|
|
import MobileBookBar from '$lib/components/MobileBookBar.svelte';
|
|
import RouteSkeleton from '$lib/components/RouteSkeleton.svelte';
|
|
import '$lib/styles/variables.css';
|
|
import '$lib/styles/base.css';
|
|
import '$lib/styles/layout.css';
|
|
import '$lib/styles/typography.css';
|
|
import '$lib/styles/buttons.css';
|
|
import '$lib/styles/forms.css';
|
|
import '$lib/styles/sections.css';
|
|
import '$lib/styles/responsive.css';
|
|
|
|
onMount(() => initClickTracking());
|
|
|
|
function shouldShowSkeleton() {
|
|
const navigation = $navigating;
|
|
|
|
if (!navigation?.to?.url) {
|
|
return false;
|
|
}
|
|
|
|
const fromPath = navigation.from?.url.pathname ?? $page.url.pathname;
|
|
const toPath = navigation.to.url.pathname;
|
|
|
|
if (navigation.to.url.hash && toPath === fromPath) {
|
|
return false;
|
|
}
|
|
|
|
return toPath !== fromPath;
|
|
}
|
|
|
|
$: loadingPath = $navigating?.to?.url.pathname ?? $page.url.pathname;
|
|
$: showRouteSkeleton = shouldShowSkeleton();
|
|
|
|
afterNavigate(({ from, to }) => {
|
|
if (!from || !to || to.url.hash) {
|
|
return;
|
|
}
|
|
|
|
if (from.url.pathname !== to.url.pathname) {
|
|
disableScrollHandling();
|
|
|
|
if (document.activeElement instanceof HTMLElement) {
|
|
document.activeElement.blur();
|
|
}
|
|
|
|
requestAnimationFrame(() => {
|
|
requestAnimationFrame(() => {
|
|
window.scrollTo({ top: 0, left: 0, behavior: 'auto' });
|
|
document.documentElement.scrollTop = 0;
|
|
document.body.scrollTop = 0;
|
|
});
|
|
});
|
|
|
|
trackPageView(to.url.pathname + to.url.search, document.title);
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<div class="layout-shell">
|
|
<div class:layout-content-loading={showRouteSkeleton} class="layout-content" aria-hidden={showRouteSkeleton}>
|
|
<slot />
|
|
</div>
|
|
|
|
{#if showRouteSkeleton}
|
|
<div class="layout-skeleton-layer" role="status" aria-label="Loading page" aria-live="polite">
|
|
<RouteSkeleton pathname={loadingPath} />
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
|
|
<MobileBookBar />
|
|
|
|
<style>
|
|
.layout-shell {
|
|
position: relative;
|
|
}
|
|
|
|
.layout-content-loading {
|
|
visibility: hidden;
|
|
}
|
|
|
|
.layout-skeleton-layer {
|
|
position: fixed;
|
|
inset: 0;
|
|
z-index: 30;
|
|
overflow-y: auto;
|
|
background: rgba(251, 251, 251, 0.98);
|
|
}
|
|
</style>
|