@opensig/opendesign
Version:
<p align="center"><img src="../docs/public/opendesign-logo-light.png"/></p> <h1 align="center">opendesign</h1> <p align="center">一个 Vue 3 组件库</p> <p align="center"><b>皮肤可定制,使用 TypeScript</b></p>
328 lines (327 loc) • 11.2 kB
JavaScript
;
const vue = require("vue");
const ScrollbarRail_vue_vue_type_script_setup_true_lang = require("./ScrollbarRail.vue.js");
const types = require("./types.js");
const vueUtils = require("../_utils/vue-utils.js");
const useResizeObserver = require("../hooks/use-resize-observer.js");
const useScreen = require("../hooks/use-screen.js");
const _sfc_main = /* @__PURE__ */ vue.defineComponent({
__name: "OScrollbar",
props: types.scrollbarProps,
setup(__props, { expose: __expose }) {
const ScrollbarClass = {
container: "o-scrollbar-container"
};
const props = __props;
const { isPhonePad } = useScreen.useScreen();
let scrollTargetEl = null;
let scrollListenEl = null;
const rootRef = vue.ref(null);
const hasY = vue.ref(false);
const hasX = vue.ref(false);
const hThumbRate = vue.ref(0);
const vThumbRate = vue.ref(0);
const hOffsetRate = vue.ref(0);
const vOffsetRate = vue.ref(0);
const isBody = vue.ref(false);
const showXBar = vue.ref(false);
const showYBar = vue.ref(false);
let lastTop = -1;
let lastLeft = -1;
let xTimer = null;
let yTimer = null;
let ro = null;
let lastScrollWidth = -1;
let lastScrollHeight = -1;
const updateScrollbar = () => {
if (!scrollTargetEl) {
return;
}
const { clientWidth, clientHeight, scrollWidth, scrollHeight, scrollTop, scrollLeft } = scrollTargetEl;
lastScrollWidth = scrollWidth;
lastScrollHeight = scrollHeight;
hThumbRate.value = clientWidth / scrollWidth;
vThumbRate.value = clientHeight / scrollHeight;
hOffsetRate.value = scrollLeft / scrollWidth;
vOffsetRate.value = scrollTop / scrollHeight;
if (!props.disabledX) {
hasX.value = clientWidth < scrollWidth;
}
if (!props.disabledY) {
hasY.value = clientHeight < scrollHeight;
}
};
const updateScrollbarByScollSize = () => {
if (!scrollTargetEl) {
return;
}
const { scrollWidth, scrollHeight } = scrollTargetEl;
if (lastScrollWidth !== scrollWidth || lastScrollHeight !== scrollHeight) {
updateScrollbar();
}
};
const onScroll = () => {
if (!scrollTargetEl) {
return;
}
const { scrollLeft, scrollWidth, scrollTop, scrollHeight } = scrollTargetEl;
if (lastScrollWidth !== scrollWidth || lastScrollHeight !== scrollHeight) {
updateScrollbar();
}
hOffsetRate.value = scrollLeft / scrollWidth;
vOffsetRate.value = scrollTop / scrollHeight;
if (lastLeft >= 0) {
showXBar.value = scrollLeft !== lastLeft;
if (xTimer) {
clearTimeout(xTimer);
}
xTimer = window.setTimeout(() => {
showXBar.value = false;
xTimer = null;
}, props.duration);
}
lastLeft = scrollLeft;
if (lastTop >= 0) {
showYBar.value = scrollTop !== lastTop;
if (yTimer) {
clearTimeout(yTimer);
yTimer = null;
}
yTimer = window.setTimeout(() => {
showYBar.value = false;
}, props.duration);
}
lastTop = scrollTop;
};
let childToObserve = null;
const init = () => {
if (!scrollTargetEl) {
return;
}
scrollTargetEl.classList.add(ScrollbarClass.container);
ro = useResizeObserver.useResizeObserver();
ro.observe(scrollTargetEl, updateScrollbar);
if (scrollTargetEl.children.length === 1 && scrollTargetEl.children[0] instanceof HTMLElement) {
childToObserve = scrollTargetEl.children[0];
ro.observe(childToObserve, updateScrollbar);
}
updateScrollbar();
scrollListenEl = isBody.value ? window : scrollTargetEl;
scrollListenEl.addEventListener("scroll", onScroll, { passive: true });
handleWrapperHoverEvent();
};
let updateTimer;
let updateIdleTimer;
const updateScrollbarOnIdle = () => {
if (window.requestIdleCallback) {
updateTimer = window.setInterval(() => {
updateIdleTimer = window.requestIdleCallback(updateScrollbarByScollSize);
}, 1e3);
}
};
const cancelUpdateScrollbarOnIdle = () => {
if (updateTimer) {
clearInterval(updateTimer);
cancelIdleCallback && cancelIdleCallback(updateIdleTimer);
updateTimer = 0;
updateIdleTimer = 0;
}
};
const { target } = vue.toRefs(props);
vueUtils.resolveHtmlElement(target).then((el) => {
if (el === document.body) {
isBody.value = true;
scrollTargetEl = document.documentElement;
} else if (el) {
scrollTargetEl = el;
}
if (!scrollTargetEl) {
return;
}
init();
});
const isShowScrollbar = vue.ref(props.showType === "always");
vue.watchEffect(() => {
isShowScrollbar.value = props.showType === "always";
if (props.showType === "always") {
if (props.autoUpdateOnScrollSize) {
updateScrollbarOnIdle();
}
} else {
cancelUpdateScrollbarOnIdle();
}
});
let wrapperEl = null;
const onWrapperHoverIn = () => {
isShowScrollbar.value = true;
};
const onWrapperHoverOut = () => {
isShowScrollbar.value = false;
if (scrollTargetEl) {
const { scrollWidth, scrollHeight } = scrollTargetEl;
if (lastScrollWidth !== scrollWidth || lastScrollHeight !== scrollHeight) {
updateScrollbar();
}
}
};
const removeWrapperHoverEvent = () => {
if (wrapperEl) {
wrapperEl.removeEventListener("mouseenter", onWrapperHoverIn);
wrapperEl.removeEventListener("mouseleave", onWrapperHoverOut);
}
};
const handleWrapperHoverEvent = () => {
vue.watchEffect(() => {
var _a;
const isHoverShow = props.showType === "hover" && !isPhonePad.value;
wrapperEl = (_a = rootRef.value) == null ? void 0 : _a.offsetParent;
if (!wrapperEl) {
return;
}
if (isHoverShow) {
wrapperEl == null ? void 0 : wrapperEl.addEventListener("mouseenter", onWrapperHoverIn);
wrapperEl == null ? void 0 : wrapperEl.addEventListener("mouseleave", onWrapperHoverOut);
} else {
removeWrapperHoverEvent();
}
});
};
vue.onUnmounted(() => {
if (scrollTargetEl) {
ro == null ? void 0 : ro.unobserve(scrollTargetEl, updateScrollbar);
scrollListenEl == null ? void 0 : scrollListenEl.removeEventListener("scroll", onScroll);
}
if (childToObserve) {
ro == null ? void 0 : ro.unobserve(childToObserve, updateScrollbar);
}
removeWrapperHoverEvent();
cancelUpdateScrollbarOnIdle();
scrollTargetEl == null ? void 0 : scrollTargetEl.classList.remove(ScrollbarClass.container);
});
const onHBarScroll = (ratio) => {
if (scrollTargetEl) {
const d = ratio * scrollTargetEl.scrollWidth;
scrollTargetEl.scrollTo({
left: d
});
}
};
const onVBarScroll = (ratio) => {
if (scrollTargetEl) {
const d = ratio * scrollTargetEl.scrollHeight;
scrollTargetEl.scrollTo({
top: d
});
}
};
const onBarHoverIn = (d) => {
if (isPhonePad.value) {
return;
}
if (d === "x") {
showXBar.value = true;
if (xTimer) {
clearTimeout(xTimer);
yTimer = null;
}
} else if (d === "y") {
showYBar.value = true;
if (yTimer) {
clearTimeout(yTimer);
yTimer = null;
}
}
};
const onBarHoverOut = (d) => {
if (isPhonePad.value) {
return;
}
if (d === "x") {
xTimer = window.setTimeout(() => {
showXBar.value = false;
}, props.duration);
} else if (d === "y") {
yTimer = window.setTimeout(() => {
showYBar.value = false;
}, props.duration);
}
};
__expose({
update: updateScrollbar
});
return (_ctx, _cache) => {
return vue.openBlock(), vue.createElementBlock(
"div",
{
class: vue.normalizeClass(["o-scrollbar", [
props.barClass,
`o-scrollbar-${props.size}`,
{
"o-scrollbar-auto-show": props.showType === "auto",
"o-scrollbar-always-show": props.showType === "always",
"o-scrollbar-hover-show": props.showType === "hover" && !vue.unref(isPhonePad),
"o-scrollbar-visible": isShowScrollbar.value,
"o-scrollbar-both": hasX.value && hasY.value,
"o-scrollbar-visible-x": showXBar.value,
"o-scrollbar-visible-y": showYBar.value,
"o-scrollbar-to-body": isBody.value
}
]]),
ref_key: "rootRef",
ref: rootRef
},
[
props.showType !== "never" ? (vue.openBlock(), vue.createElementBlock(
vue.Fragment,
{ key: 0 },
[
hasX.value && !props.disabledX ? (vue.openBlock(), vue.createBlock(ScrollbarRail_vue_vue_type_script_setup_true_lang, {
key: 0,
size: props.size,
direction: "x",
"thumb-rate": hThumbRate.value,
"offset-rate": hOffsetRate.value,
onScroll: onHBarScroll,
onMouseenter: _cache[0] || (_cache[0] = ($event) => onBarHoverIn("x")),
onMouseleave: _cache[1] || (_cache[1] = ($event) => onBarHoverOut("x"))
}, {
thumb: vue.withCtx(() => [
vue.renderSlot(_ctx.$slots, "thumb")
]),
track: vue.withCtx(() => [
vue.renderSlot(_ctx.$slots, "track")
]),
_: 3
/* FORWARDED */
}, 8, ["size", "thumb-rate", "offset-rate"])) : vue.createCommentVNode("v-if", true),
hasY.value && !props.disabledY ? (vue.openBlock(), vue.createBlock(ScrollbarRail_vue_vue_type_script_setup_true_lang, {
key: 1,
direction: "y",
size: props.size,
"thumb-rate": vThumbRate.value,
"offset-rate": vOffsetRate.value,
onScroll: onVBarScroll,
onMouseenter: _cache[2] || (_cache[2] = ($event) => onBarHoverIn("y")),
onMouseleave: _cache[3] || (_cache[3] = ($event) => onBarHoverOut("y"))
}, {
thumb: vue.withCtx(() => [
vue.renderSlot(_ctx.$slots, "thumb")
]),
track: vue.withCtx(() => [
vue.renderSlot(_ctx.$slots, "track")
]),
_: 3
/* FORWARDED */
}, 8, ["size", "thumb-rate", "offset-rate"])) : vue.createCommentVNode("v-if", true)
],
64
/* STABLE_FRAGMENT */
)) : vue.createCommentVNode("v-if", true)
],
2
/* CLASS */
);
};
}
});
module.exports = _sfc_main;