93 lines
3.1 KiB
Vue
93 lines
3.1 KiB
Vue
<!--
|
|
Ported from legacy henza_pwa/src/layout/master.vue.
|
|
This layout renders its own <router-view/> rather than a <slot/>, so the
|
|
keep-alive/suspense wrapper App.vue builds around its own <router-view> never
|
|
reaches these routes. The keep-alive below is the one that applies: the main
|
|
tabs stay mounted between switches instead of refetching on every visit.
|
|
-->
|
|
<template>
|
|
<section ref="content">
|
|
<router-view v-slot="{ Component, route }">
|
|
<transition
|
|
name="page"
|
|
mode="out-in"
|
|
@before-leave="pageLeaveStarted"
|
|
@after-leave="pageLeaveEnded"
|
|
@leave-cancelled="pageLeaveEnded"
|
|
>
|
|
<keep-alive :include="KEEP_ALIVE">
|
|
<component :is="Component" :key="route.name" />
|
|
</keep-alive>
|
|
</transition>
|
|
</router-view>
|
|
<Navigation />
|
|
<div class="bg-primary top-[-2rem] fixed h-[2rem] w-full z-10" />
|
|
</section>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { onBeforeMount, ref } from 'vue'
|
|
import { useResizeObserver } from '@vueuse/core'
|
|
import Navigation from '../components/ui/Navigation.vue'
|
|
import { useWindowDataStore } from '../stores/windowDataStore'
|
|
import { pageLeaveStarted, pageLeaveEnded } from '../composables/usePageTransition'
|
|
|
|
// Component names (from the SFC file names) of the bottom-nav tabs. Forms and
|
|
// the product page are left out: they should open fresh every time.
|
|
const KEEP_ALIVE = ['HomeView', 'SearchView', 'OrdersView', 'DashboardView']
|
|
|
|
const windowData = useWindowDataStore()
|
|
const content = ref<HTMLElement | null>(null)
|
|
|
|
// Route changes here are tab switches, not a hierarchy, so there is no "back"
|
|
// or "forward" direction to honour. The outgoing page fades out quickly and
|
|
// the new one fades in; the sense of the page settling into place comes from
|
|
// each screen's own section reveal (composables/useReveal.ts).
|
|
|
|
onBeforeMount(() => {
|
|
windowData.setWidth(window.innerWidth)
|
|
})
|
|
useResizeObserver(content, (entries) => {
|
|
const entry = entries[0]
|
|
const { width } = entry.contentRect
|
|
if (windowData.width !== width) {
|
|
windowData.setWidth(width)
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<!--
|
|
Not scoped: these classes land on the routed page component, which carries
|
|
its own scope id, not this layout's.
|
|
|
|
Opacity only, deliberately. A transform (or filter) on the page root makes it
|
|
the containing block for every position: fixed element inside the page — the
|
|
top bars, PageBar, the cart's checkout bar — so for the length of the
|
|
transition they would ride along with the page, then snap back to the screen
|
|
edge when it ends. Opacity has no such effect, so nothing jumps.
|
|
-->
|
|
<style lang="less">
|
|
// Leave is deliberately shorter than enter. With mode="out-in" the two run
|
|
// back to back, so a slow exit is dead time the user feels as lag; a quick
|
|
// fade out and an unhurried fade in reads as responsive but smooth.
|
|
.page-leave-active {
|
|
transition: opacity 0.16s cubic-bezier(0.4, 0, 1, 1);
|
|
}
|
|
|
|
.page-enter-active {
|
|
transition: opacity 0.42s cubic-bezier(0.32, 0.72, 0, 1);
|
|
}
|
|
|
|
.page-enter-from,
|
|
.page-leave-to {
|
|
opacity: 0;
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.page-enter-active,
|
|
.page-leave-active {
|
|
transition: opacity 0.12s linear;
|
|
}
|
|
}
|
|
</style>
|