UNPKG

@opentiny/vue-renderless

Version:

An enterprise-class UI component library, support both Vue.js 2 and Vue.js 3, as well as PC and mobile.

138 lines (137 loc) 4.75 kB
import "../../chunk-G2ADBYYC.js"; import { throttle } from "@opentiny/utils"; import { getScrollContainer } from "@opentiny/utils"; const CONTEXT_KEY = "@@infinitescrollContext"; const OBSERVER_CHECK_INTERVAL = 50; const ATTR_DEFAULT_DELAY = 200; const ATTR_DEFAULT_DISTANCE = 0; const attrs = { delay: { type: Number, default: ATTR_DEFAULT_DELAY }, disabled: { type: Boolean, default: false }, distance: { type: Number, default: ATTR_DEFAULT_DISTANCE }, immediate: { type: Boolean, default: true } }; const isNull = (val) => val === null; const parseAttrValue = (attrVal, type, defaultVal) => { if (isNull(attrVal)) return defaultVal; if (type === Boolean) { return attrVal !== "false"; } else if (type === Number) { return Number(attrVal); } }; const computeScrollOptions = (el, instance) => Object.entries(attrs).reduce((accumulator, [name, option]) => { const { type, default: defaultValue } = option; const attrKey = `infinite-scroll-${name}`; const $attrValue = instance.$el.getAttribute(attrKey); const attrValue = el.getAttribute(attrKey); let value; if (isNull(attrValue) && isNull($attrValue) || !isNull(attrValue)) { value = parseAttrValue(attrValue, type, defaultValue); } if (isNull(attrValue) && !isNull($attrValue)) { value = parseAttrValue($attrValue, type, defaultValue); } accumulator[name] = Number.isNaN(value) ? defaultValue : value; return accumulator; }, {}); const stopObserver = (el) => { const { observer } = el[CONTEXT_KEY]; if (observer) { observer.disconnect(); delete el[CONTEXT_KEY].observer; } }; const accumOffsetTop = (el) => { let totalOffset = 0; let parent = el; while (parent) { totalOffset += parent.offsetTop; parent = parent.offsetParent; } return totalOffset; }; const distanceOffsetTop = (el, containerEl) => Math.abs(accumOffsetTop(el) - accumOffsetTop(containerEl)); const scroller = (el, cb) => { const { container, containerEl, instance, observer, lastScrollTop } = el[CONTEXT_KEY]; const { disabled, distance } = computeScrollOptions(el, instance); const { clientHeight, scrollHeight, scrollTop } = containerEl; const deltaTop = scrollTop - lastScrollTop; el[CONTEXT_KEY].lastScrollTop = scrollTop; if (observer || disabled || deltaTop < 0) return; let isTrigger = false; if (container === el) { isTrigger = scrollHeight - (clientHeight + scrollTop) <= distance; } else { const { clientTop, scrollHeight: height } = el; const offsetTop = distanceOffsetTop(el, containerEl); isTrigger = scrollTop + clientHeight >= offsetTop + clientTop + height - distance; } if (isTrigger) { cb.call(instance); } }; function observerChecker(el, cb) { const { containerEl, instance } = el[CONTEXT_KEY]; const { disabled } = computeScrollOptions(el, instance); if (disabled || containerEl.clientHeight === 0) return; if (containerEl.scrollHeight <= containerEl.clientHeight) { cb.call(instance); } else { stopObserver(el); } } const bind = (el, binding, vnode) => { const instance = binding.instance || vnode.context; const { value: cb } = binding; if (typeof cb !== "function") { throw new TypeError('[TINY Error][InfiniteScroll] "v-infinite-scroll" binding value must be a function'); } instance.$nextTick(() => { const { delay, immediate } = computeScrollOptions(el, instance); const container = getScrollContainer(el, true); const containerEl = container === window ? document.documentElement : container; const onScroll = throttle(delay, scroller.bind(null, el, cb)); if (!container) return; el[CONTEXT_KEY] = { instance, container, containerEl, delay, cb, onScroll, lastScrollTop: containerEl.scrollTop }; if (immediate) { const observer = new MutationObserver(throttle(OBSERVER_CHECK_INTERVAL, observerChecker.bind(null, el, cb))); el[CONTEXT_KEY].observer = observer; observer.observe(el, { childList: true, subtree: true }); observerChecker(el, cb); } container.addEventListener("scroll", onScroll); }); }; const update = (el, binding, vnode) => { if (!el[CONTEXT_KEY]) { const instance = binding.instance || vnode.context; return instance.$nextTick(); } else { const { containerEl, cb, observer } = el[CONTEXT_KEY]; if (containerEl.clientHeight && observer) { observerChecker(el, cb); } } }; const unbind = (el) => { const { container, onScroll } = el[CONTEXT_KEY]; if (container) container.removeEventListener("scroll", onScroll); stopObserver(el); }; var infinite_scroll_default = { bind, update, unbind, beforeMount: bind, updated: update, unmounted: unbind }; export { infinite_scroll_default as default };