RouterViewにScrollPositiionManagerを埋め込む
This commit is contained in:
parent
4bef4953b8
commit
e7251220d5
|
@ -18,7 +18,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div ref="contents" :class="$style.root" style="container-type: inline-size;">
|
<div ref="contents" :class="$style.root" style="container-type: inline-size;">
|
||||||
<RouterView :key="reloadCount" :router="router"/>
|
<RouterView :key="reloadCount" :router="router" :scrollContainer="contents"/>
|
||||||
</div>
|
</div>
|
||||||
</MkWindow>
|
</MkWindow>
|
||||||
</template>
|
</template>
|
||||||
|
@ -32,12 +32,11 @@ import copyToClipboard from '@/scripts/copy-to-clipboard';
|
||||||
import { url } from '@/config';
|
import { url } from '@/config';
|
||||||
import { mainRouter, routes, page } from '@/router';
|
import { mainRouter, routes, page } from '@/router';
|
||||||
import { $i } from '@/account';
|
import { $i } from '@/account';
|
||||||
import { Router, useScrollPositionManager } from '@/nirax';
|
import { Router } from '@/nirax';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata';
|
import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata';
|
||||||
import { openingWindowsCount } from '@/os';
|
import { openingWindowsCount } from '@/os';
|
||||||
import { claimAchievement } from '@/scripts/achievements';
|
import { claimAchievement } from '@/scripts/achievements';
|
||||||
import { getScrollContainer } from '@/scripts/scroll';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
initialPath: string;
|
initialPath: string;
|
||||||
|
@ -141,8 +140,6 @@ function popout() {
|
||||||
windowEl.close();
|
windowEl.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
useScrollPositionManager(() => getScrollContainer(contents.value), router);
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
openingWindowsCount.value++;
|
openingWindowsCount.value++;
|
||||||
if (openingWindowsCount.value >= 3) {
|
if (openingWindowsCount.value >= 3) {
|
||||||
|
|
|
@ -11,12 +11,18 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { inject, onBeforeUnmount, provide } from 'vue';
|
import { computed, inject, onBeforeUnmount, provide, nextTick } from 'vue';
|
||||||
import { Resolved, Router } from '@/nirax';
|
import { NiraxChangeEvent, Resolved, Router } from '@/nirax';
|
||||||
import { defaultStore } from '@/store';
|
import { defaultStore } from '@/store';
|
||||||
|
import { getScrollContainer } from '@/scripts/scroll';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
router?: Router;
|
router?: Router;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set any element if scroll position management needed
|
||||||
|
*/
|
||||||
|
scrollContainer?: HTMLElement | null;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const router = props.router ?? inject('router');
|
const router = props.router ?? inject('router');
|
||||||
|
@ -45,17 +51,49 @@ let currentPageComponent = $shallowRef(current.route.component);
|
||||||
let currentPageProps = $ref(current.props);
|
let currentPageProps = $ref(current.props);
|
||||||
let key = $ref(current.route.path + JSON.stringify(Object.fromEntries(current.props)));
|
let key = $ref(current.route.path + JSON.stringify(Object.fromEntries(current.props)));
|
||||||
|
|
||||||
function onChange({ resolved, key: newKey }) {
|
const scrollContainer = computed(() => props.scrollContainer ? (getScrollContainer(props.scrollContainer) ?? document.getElementsByTagName('html')[0]) : undefined);
|
||||||
const current = resolveNested(resolved);
|
|
||||||
|
const scrollPosStore = new Map<string, number>();
|
||||||
|
|
||||||
|
function onChange(ctx: NiraxChangeEvent) {
|
||||||
|
// save scroll position
|
||||||
|
if (scrollContainer.value) scrollPosStore.set(key, scrollContainer.value.scrollTop);
|
||||||
|
|
||||||
|
//#region change page
|
||||||
|
const current = resolveNested(ctx.resolved);
|
||||||
if (current == null) return;
|
if (current == null) return;
|
||||||
currentPageComponent = current.route.component;
|
currentPageComponent = current.route.component;
|
||||||
currentPageProps = current.props;
|
currentPageProps = current.props;
|
||||||
key = current.route.path + JSON.stringify(Object.fromEntries(current.props));
|
key = current.route.path + JSON.stringify(Object.fromEntries(current.props));
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region scroll
|
||||||
|
nextTick(() => {
|
||||||
|
if (!scrollContainer.value) return;
|
||||||
|
|
||||||
|
const scrollPos = scrollPosStore.get(key) ?? 0;
|
||||||
|
scrollContainer.value.scroll({ top: scrollPos, behavior: 'instant' });
|
||||||
|
if (scrollPos !== 0) {
|
||||||
|
window.setTimeout(() => { // 遷移直後はタイミングによってはコンポーネントが復元し切ってない可能性も考えられるため少し時間を空けて再度スクロール
|
||||||
|
if (!scrollContainer.value) return;
|
||||||
|
scrollContainer.value.scroll({ top: scrollPos, behavior: 'instant' });
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
router.addListener('change', onChange);
|
router.addListener('change', onChange);
|
||||||
|
|
||||||
|
function onSame() {
|
||||||
|
if (!scrollContainer.value) return;
|
||||||
|
scrollContainer.value.scroll({ top: 0, behavior: 'smooth' });
|
||||||
|
}
|
||||||
|
|
||||||
|
router.addListener('same', onSame);
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
router.removeListener('change', onChange);
|
router.removeListener('change', onChange);
|
||||||
|
router.removeListener('same', onSame);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -49,24 +49,30 @@ function parsePath(path: string): ParsedPath {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Router extends EventEmitter<{
|
export type NiraxChangeEvent = {
|
||||||
change: (ctx: {
|
|
||||||
beforePath: string;
|
beforePath: string;
|
||||||
path: string;
|
path: string;
|
||||||
resolved: Resolved;
|
resolved: Resolved;
|
||||||
key: string;
|
key: string;
|
||||||
}) => void;
|
};
|
||||||
replace: (ctx: {
|
|
||||||
|
export type NiraxExportEvent = {
|
||||||
path: string;
|
path: string;
|
||||||
key: string;
|
key: string;
|
||||||
}) => void;
|
};
|
||||||
push: (ctx: {
|
|
||||||
|
export type NiraxPushEvent = {
|
||||||
beforePath: string;
|
beforePath: string;
|
||||||
path: string;
|
path: string;
|
||||||
route: RouteDef | null;
|
route: RouteDef | null;
|
||||||
props: Map<string, string> | null;
|
props: Map<string, string> | null;
|
||||||
key: string;
|
key: string;
|
||||||
}) => void;
|
};
|
||||||
|
|
||||||
|
export class Router extends EventEmitter<{
|
||||||
|
change: (ctx: NiraxChangeEvent) => void;
|
||||||
|
replace: (ctx: NiraxExportEvent) => void;
|
||||||
|
push: (ctx: NiraxExportEvent) => void;
|
||||||
same: () => void;
|
same: () => void;
|
||||||
}> {
|
}> {
|
||||||
private routes: RouteDef[];
|
private routes: RouteDef[];
|
||||||
|
@ -271,29 +277,3 @@ export class Router extends EventEmitter<{
|
||||||
this.navigate(path, key);
|
this.navigate(path, key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useScrollPositionManager(getScrollContainer: () => HTMLElement, router: Router) {
|
|
||||||
const scrollPosStore = new Map<string, number>();
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
const scrollContainer = getScrollContainer();
|
|
||||||
|
|
||||||
scrollContainer.addEventListener('scroll', () => {
|
|
||||||
scrollPosStore.set(router.getCurrentKey(), scrollContainer.scrollTop);
|
|
||||||
}, { passive: true });
|
|
||||||
|
|
||||||
router.addListener('change', ctx => {
|
|
||||||
const scrollPos = scrollPosStore.get(ctx.key) ?? 0;
|
|
||||||
scrollContainer.scroll({ top: scrollPos, behavior: 'instant' });
|
|
||||||
if (scrollPos !== 0) {
|
|
||||||
window.setTimeout(() => { // 遷移直後はタイミングによってはコンポーネントが復元し切ってない可能性も考えられるため少し時間を空けて再度スクロール
|
|
||||||
scrollContainer.scroll({ top: scrollPos, behavior: 'instant' });
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.addListener('same', () => {
|
|
||||||
scrollContainer.scroll({ top: 0, behavior: 'smooth' });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div ref="contents">
|
<div ref="contents">
|
||||||
<RouterView @contextmenu.stop="onContextmenu"/>
|
<RouterView :scrollContainer="contents" @contextmenu.stop="onContextmenu"/>
|
||||||
</div>
|
</div>
|
||||||
</XColumn>
|
</XColumn>
|
||||||
</template>
|
</template>
|
||||||
|
@ -21,8 +21,6 @@ import * as os from '@/os';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
import { mainRouter } from '@/router';
|
import { mainRouter } from '@/router';
|
||||||
import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata';
|
import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata';
|
||||||
import { useScrollPositionManager } from '@/nirax';
|
|
||||||
import { getScrollContainer } from '@/scripts/scroll';
|
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
column: Column;
|
column: Column;
|
||||||
|
@ -66,6 +64,4 @@ function onContextmenu(ev: MouseEvent) {
|
||||||
},
|
},
|
||||||
}], ev);
|
}], ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
useScrollPositionManager(() => getScrollContainer(contents.value), mainRouter);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
<MkStickyContainer ref="contents" :class="$style.contents" style="container-type: inline-size;" @contextmenu.stop="onContextmenu">
|
<MkStickyContainer ref="contents" :class="$style.contents" style="container-type: inline-size;" @contextmenu.stop="onContextmenu">
|
||||||
<template #header><XStatusBars :class="$style.statusbars"/></template>
|
<template #header><XStatusBars :class="$style.statusbars"/></template>
|
||||||
<RouterView/>
|
<RouterView :scrollContainer="contents?.rootEl"/>
|
||||||
<div :class="$style.spacer"></div>
|
<div :class="$style.spacer"></div>
|
||||||
</MkStickyContainer>
|
</MkStickyContainer>
|
||||||
|
|
||||||
|
@ -95,7 +95,6 @@ import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata';
|
||||||
import { deviceKind } from '@/scripts/device-kind';
|
import { deviceKind } from '@/scripts/device-kind';
|
||||||
import { miLocalStorage } from '@/local-storage';
|
import { miLocalStorage } from '@/local-storage';
|
||||||
import { CURRENT_STICKY_BOTTOM } from '@/const';
|
import { CURRENT_STICKY_BOTTOM } from '@/const';
|
||||||
import { useScrollPositionManager } from '@/nirax';
|
|
||||||
|
|
||||||
const XWidgets = defineAsyncComponent(() => import('./universal.widgets.vue'));
|
const XWidgets = defineAsyncComponent(() => import('./universal.widgets.vue'));
|
||||||
const XSidebar = defineAsyncComponent(() => import('@/ui/_common_/navbar.vue'));
|
const XSidebar = defineAsyncComponent(() => import('@/ui/_common_/navbar.vue'));
|
||||||
|
@ -214,8 +213,6 @@ watch($$(navFooter), () => {
|
||||||
}, {
|
}, {
|
||||||
immediate: true,
|
immediate: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
useScrollPositionManager(() => contents.value.rootEl, mainRouter);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
Loading…
Reference in New Issue