UNPKG

@empathyco/x-components

Version:
110 lines (107 loc) 3.38 kB
import '@vue/devtools-api'; import '../plugins/devtools/timeline.devtools.js'; import '@empathyco/x-utils'; import 'rxjs/operators'; import 'rxjs'; import '../plugins/devtools/colors.utils.js'; import '../plugins/x-bus.js'; import { XPlugin } from '../plugins/x-plugin.js'; const VIEWPORT_ID = 'viewport'; /** * A record which contains {@link IntersectionObserver} objects indexed by the ID of the scrollable * container. */ const state = {}; /** * Retrieves the root element for the provided ID. * * @param element - Target element where directive is set. * @param id - String identifier. * @returns HTMLElement or null. */ function getRoot(element, id) { switch (id) { case 'html': return null; case 'body': return document.body; case VIEWPORT_ID: return null; default: return element.closest(`#${id}`); } } /** * Custom Vue directive for infinite scroll. * * This directive uses the IntersectionObserver API to handle the intersection between the * children and the scrollable container. The content of the children moves up on scroll and when it * reaches the end, the IntersectionObserver triggers that both elements are intersecting. * * How it works. * * As a summary, if the scroll reaches the end, `UserReachedResultsListEnd` event is emitted to the * xBus. If you provide a margin in the directive options, this function is triggered when the * scroll reaches the end minus that amount of pixels. A default margin of 200px is set. * * Usage. * * The directive has to be set in the target element. It can receive an argument which will be used * to determine the scrollable container. Possible values: * `html`: will set the <html> as the scrollable container. * `body`: will set the <body> as the scrollable container. * ID: will set the DOM element with the provided id as the scrollable container. * * If no argument is provided the scrollable container fallbacks to the viewport. * * @example How to use it. * * ```html * <ResultsList v-infinite-scroll:html> * ``` * * ```html * <ResultsList v-infinite-scroll:body> * ``` * * ```html * <BaseScroll id='scroll-test'> * <ResultsList v-infinite-scroll:scroll-test> * </BaseScroll> * ``` * * ```html * <ResultsList v-infinite-scroll="{ margin: 40 }"> * ``` * * @public */ const infiniteScroll = { mounted(element, { arg: id = VIEWPORT_ID, value: { margin = 200 } = {} }) { /* * This hack allows the root element to always contain the observed element. * Not overpass the top margin more than 1700000 because it doesn't work in Android chrome */ const rootMargin = `1000000% 0px ${margin}px 0px`; const root = getRoot(element, id); const xBus = XPlugin.bus; state[id] = new IntersectionObserver(([entry]) => { if (entry.isIntersecting) { void xBus.emit('UserReachedResultsListEnd', undefined); } }, { threshold: 0.99, rootMargin, root, }); state[id].observe(element); }, unmounted(_, { arg: id = VIEWPORT_ID }) { if (state[id]) { state[id].disconnect(); delete state[id]; } }, }; export { infiniteScroll }; //# sourceMappingURL=infinite-scroll.js.map