vue-app-sdk
Version:
203 lines (202 loc) • 6.29 kB
JavaScript
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
import { logger } from "../../utils/index2.js";
import "../../vendors/nice-fns/dist/compactObject.js";
import "../../vendors/nice-fns/dist/pascalCase.js";
import { ref, toValue, isRef, watch, nextTick } from "vue";
import "../../vendors/easy-hookable/dist/callers.js";
import "../../vendors/easy-hookable/dist/ContextHelper.js";
import { NavigationDirection } from "../../core/Router.js";
import "vue-router";
import "../Tabs/Tabs.js";
import isFunction from "../../vendors/lodash-es/isFunction.js";
const ROUTER_SCROLLER_ID = Symbol("router scroller");
class RouterScroller {
constructor(options) {
__publicField(this, "id", ROUTER_SCROLLER_ID);
/**
* AppSDK 实例
*/
__publicField(this, "_sdk");
/**
* 滚动位置记录
*/
__publicField(this, "positions", /* @__PURE__ */ new Map());
/**
* 是否自动模式
*/
__publicField(this, "_isAuto");
/**
* 切换自动模式
* @param value 状态值
*/
__publicField(this, "toggleAuto", (value = !this._isAuto.value) => {
this._isAuto.value = value;
return value;
});
/**
* 启用自动模式
*/
__publicField(this, "enableAuto", () => {
return this.toggleAuto(true);
});
/**
* 禁用自动模式
*/
__publicField(this, "disableAuto", () => {
return this.toggleAuto(false);
});
/**
* 由于 `Transition` 动画可能导致元素自动还原滚动无效,此时可手动触发还原滚动
* @example
* ```html
* <script setup lang="ts">
* import { useAppSDK, ROUTER_SCROLLER_ID } from 'vue-app-sdk'
*
* const routerScroller = useAppSDK().getPlugin(ROUTER_SCROLLER_ID)!
*
* function handleAfterEnter() {
* routerScroller.trigger()
* }
* <\/script>
*
* <template>
* <Transition @after-enter="handleAfterEnter">
* ...
* </Transition>
* </template>
* ```
*/
__publicField(this, "trigger", () => {
const { router } = this._sdk;
const route = router.currentRoute.value;
const key = route.fullPath;
return this.applyPositions(
route,
route,
this.positions.get(key),
NavigationDirection.unchanged,
true
);
});
/**
* 获取滚动元素
* @param selector 选择器
* @returns 滚动元素
*/
__publicField(this, "querySelector", (selector) => {
if (typeof window === "undefined")
return void 0;
if (selector === "body")
return document.body;
if (selector === "window")
return window;
return document.querySelector(selector);
});
/**
* 获取滚动位置
* @param el 滚动元素
* @returns 滚动位置
*/
__publicField(this, "getScrollPosition", (el) => {
if (el instanceof Window)
return { left: window.scrollX, top: window.scrollY };
else return { left: el.scrollLeft, top: el.scrollTop };
});
/**
* 捕获滚动位置
*/
__publicField(this, "capturePositions", () => {
const pos = {};
for (const [selector] of Object.entries(this.options.selectors)) {
const element = this.querySelector(selector);
if (!element)
continue;
pos[selector] = this.getScrollPosition(element);
}
return pos;
});
/**
* 还原滚动位置
* @param to 目标路由
* @param from 来源路由
* @param position 滚动位置
* @param direction 导航方向
* @param isManual 是否手动触发
*/
__publicField(this, "applyPositions", async (to, from, position, direction, isManual = false) => {
for (const [selector, handler] of Object.entries(this.options.selectors)) {
const element = this.querySelector(selector);
if (!element)
continue;
let pos = position == null ? void 0 : position[selector];
if (isFunction(handler)) {
const result = await handler({
to,
from,
element,
selector,
direction,
savedPosition: pos,
isManual
});
if (!result)
continue;
if (result !== true)
pos = result;
} else if (handler === true) {
if (this.options.scrollOnlyBackward && direction !== NavigationDirection.backward)
pos = void 0;
}
element.scrollTo({
behavior: this.options.behavior,
...pos || { top: 0, left: 0 }
});
}
});
__publicField(this, "install", (sdk) => {
this._sdk = sdk;
const { router } = sdk;
if (router.options.scrollBehavior) {
logger.warn(
'"scrollBehavior" options in Vue Router is overwritten by "RouterScroller" plugin, you can remove it from "createRouter()".'
);
}
router.options.scrollBehavior = () => {
};
router.beforeResolve((to, from) => {
var _a;
if (((_a = history.state) == null ? void 0 : _a.current) === to.fullPath)
return;
if (!this.isAuto)
return;
const pos = this.capturePositions();
this.positions.set(from.fullPath, pos);
});
sdk.hook("sdk:router:navigate", (direction, to, from) => {
if (!this.isAuto)
return;
const pos = this.positions.get(to.fullPath);
nextTick(() => this.applyPositions(to, from, pos, direction, false));
});
sdk.hook("sdk:cleanup", () => this.positions.clear());
});
this.options = options;
const { autoCollect = true } = options;
this._isAuto = ref(toValue(autoCollect));
if (isRef(autoCollect) || isFunction(autoCollect))
watch(autoCollect, this.toggleAuto);
}
/**
* 是否自动模式
* @readonly
*/
get isAuto() {
return this._isAuto.value;
}
}
export {
ROUTER_SCROLLER_ID,
RouterScroller
};