diff options
Diffstat (limited to 'packages/frontend/src/components/global/RouterView.vue')
| -rw-r--r-- | packages/frontend/src/components/global/RouterView.vue | 142 |
1 files changed, 77 insertions, 65 deletions
diff --git a/packages/frontend/src/components/global/RouterView.vue b/packages/frontend/src/components/global/RouterView.vue index 38bdfc52d4..78ac6900a3 100644 --- a/packages/frontend/src/components/global/RouterView.vue +++ b/packages/frontend/src/components/global/RouterView.vue @@ -4,98 +4,110 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<KeepAlive - :max="defaultStore.state.numberOfPageCache" - :exclude="pageCacheController" -> - <Suspense :timeout="0"> - <component :is="currentPageComponent" :key="key" v-bind="Object.fromEntries(currentPageProps)"/> +<div ref="rootEl" class="_pageContainer" :class="$style.root"> + <KeepAlive :max="prefer.s.numberOfPageCache"> + <Suspense :timeout="0"> + <component :is="currentPageComponent" :key="key" v-bind="Object.fromEntries(currentPageProps)"/> - <template #fallback> - <MkLoading/> - </template> - </Suspense> -</KeepAlive> + <template #fallback> + <MkLoading/> + </template> + </Suspense> + </KeepAlive> +</div> </template> <script lang="ts" setup> -import { inject, onBeforeUnmount, provide, ref, shallowRef, computed, nextTick } from 'vue'; -import { IRouter, Resolved, RouteDef } from '@/nirax.js'; -import { defaultStore } from '@/store.js'; -import { globalEvents } from '@/events.js'; +import { inject, nextTick, onMounted, provide, ref, shallowRef, useTemplateRef } from 'vue'; +import type { Router } from '@/router.js'; +import { prefer } from '@/preferences.js'; import MkLoadingPage from '@/pages/_loading_.vue'; +import { DI } from '@/di.js'; +import { randomId } from '@/utility/random-id.js'; +import { deepEqual } from '@/utility/deep-equal.js'; const props = defineProps<{ - router?: IRouter; - nested?: boolean; + router?: Router; }>(); -const router = props.router ?? inject('router'); +const router = props.router ?? inject(DI.router); if (router == null) { throw new Error('no router provided'); } -const currentDepth = inject('routerCurrentDepth', 0); -provide('routerCurrentDepth', currentDepth + 1); +const viewId = randomId(); +provide(DI.viewId, viewId); -function resolveNested(current: Resolved, d = 0): Resolved | null { - if (!props.nested) return current; +const currentDepth = inject(DI.routerCurrentDepth, 0); +provide(DI.routerCurrentDepth, currentDepth + 1); - if (d === currentDepth) { - return current; - } else { - if (current.child) { - return resolveNested(current.child, d + 1); - } else { - return null; - } +const rootEl = useTemplateRef('rootEl'); +onMounted(() => { + if (prefer.s.animation) { + rootEl.value.style.viewTransitionName = viewId; // view-transition-nameにcss varが使えないっぽいため直接代入 } +}); + +// view-transition-newなどの<pt-name-selector>にはcss varが使えず、v-bindできないため直接スタイルを生成 +const viewTransitionStylesTag = window.document.createElement('style'); +viewTransitionStylesTag.textContent = ` +@keyframes ${viewId}-old { + to { transform: scale(0.95); opacity: 0; } } -const current = resolveNested(router.current)!; -const currentPageComponent = shallowRef('component' in current.route ? current.route.component : MkLoadingPage); -const currentPageProps = ref(current.props); -const key = ref(router.getCurrentKey() + JSON.stringify(Object.fromEntries(current.props))); +@keyframes ${viewId}-new { + from { transform: scale(0.95); opacity: 0; } +} -function onChange({ resolved, key: newKey }) { - const current = resolveNested(resolved); - if (current == null || 'redirect' in current.route) return; - currentPageComponent.value = current.route.component; - currentPageProps.value = current.props; - key.value = newKey + JSON.stringify(Object.fromEntries(current.props)); +::view-transition-old(${viewId}) { + animation-duration: 0.2s; + animation-name: ${viewId}-old; +} - nextTick(() => { - // ページ遷移完了後に再びキャッシュを有効化 - if (clearCacheRequested.value) { - clearCacheRequested.value = false; - } - }); +::view-transition-new(${viewId}) { + animation-duration: 0.2s; + animation-name: ${viewId}-new; } +`; -router.addListener('change', onChange); +window.document.head.appendChild(viewTransitionStylesTag); -// #region キャッシュ制御 +const current = router.current!; +const currentPageComponent = shallowRef('component' in current.route ? current.route.component : MkLoadingPage); +const currentPageProps = ref(current.props); +let currentRoutePath = current.route.path; +const key = ref(router.getCurrentFullPath()); -/** - * キャッシュクリアが有効になったら、全キャッシュをクリアする - * - * keepAlive側にwatcherがあるのですぐ消えるとはおもうけど、念のためページ遷移完了まではキャッシュを無効化しておく。 - * キャッシュ有効時向けにexcludeを使いたい場合は、pageCacheControllerに並列に突っ込むのではなく、下に追記すること - */ -const pageCacheController = computed(() => clearCacheRequested.value ? /.*/ : undefined); -const clearCacheRequested = ref(false); +router.useListener('change', ({ resolved }) => { + if (resolved == null || 'redirect' in resolved.route) return; + if (resolved.route.path === currentRoutePath && deepEqual(resolved.props, currentPageProps.value)) return; -globalEvents.on('requestClearPageCache', () => { - if (_DEV_) console.log('clear page cache requested'); - if (!clearCacheRequested.value) { - clearCacheRequested.value = true; + function _() { + currentPageComponent.value = resolved.route.component; + currentPageProps.value = resolved.props; + key.value = router.getCurrentFullPath(); + currentRoutePath = resolved.route.path; } -}); -// #endregion - -onBeforeUnmount(() => { - router.removeListener('change', onChange); + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (prefer.s.animation && window.document.startViewTransition) { + window.document.startViewTransition(() => new Promise((res) => { + _(); + nextTick(() => { + res(); + //setTimeout(res, 100); + }); + })); + } else { + _(); + } }); </script> + +<style lang="scss" module> +.root { + height: 100%; + background-color: var(--MI_THEME-bg); +} +</style> |