summaryrefslogtreecommitdiff
path: root/packages/frontend/src/components/global/RouterView.vue
diff options
context:
space:
mode:
Diffstat (limited to 'packages/frontend/src/components/global/RouterView.vue')
-rw-r--r--packages/frontend/src/components/global/RouterView.vue142
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>