import {CloudinaryImage} from "@cloudinary/base/assets/CloudinaryImage";
import {plugin, htmlPluginState} from './types'
/**
* @namespace
* @description Image loads once it is in a certain margin in the viewport. This includes vertical and horizontal scrolling.
* @param rootMargin {string} The root element's bounding box before the intersection test is performed defaults to 0px
* @param threshold {number} A number which indicate at what percentage of the images's visibility the image should
* load. The default is 0.1 which indicates 1%
* @return {plugin}
* @example
* <CldImg transformation={img} plugins=[(lazyload('0px', 0.25))]/>
*/
export function lazyload(rootMargin?: string, threshold?: number): plugin{
return lazyloadPlugin.bind(null, rootMargin, threshold);
}
/**
* @description lazyload plugin
* @param rootMargin {string} The root element's bounding box before the intersection test is performed defaults to 0px
* @param threshold {number} A number which indicate at what percentage of the images's visibility the image should
* load. The default is 0.1 which indicates 1%
* @param element The image element
* @param element {HTMLImageElement} The image element
* @param cloudinaryImage {CloudinaryImage}
* @param htmlPluginState {htmlPluginState} holds cleanup callbacks and event subscriptions
*/
function lazyloadPlugin(rootMargin='0px', threshold=0.1 , element: HTMLImageElement, cloudinaryImage: CloudinaryImage, htmlPluginState: htmlPluginState): Promise<void | string> | string {
return new Promise((resolve) => {
const onIntersect = () => (resolve());
const unobserve = detectIntersection(element, onIntersect, rootMargin, threshold);
htmlPluginState.cleanupCallbacks.push(()=>{
unobserve();
resolve('canceled');
});
});
}
/**
* Check if IntersectionObserver is supported
* @return {boolean} true if window.IntersectionObserver is defined
*/
function isIntersectionObserverSupported() {
// Check that 'IntersectionObserver' property is defined on window
return window && 'IntersectionObserver' in window;
}
/**
* Calls onIntersect() to resolve when intersection is detected, or when
* no native lazy loading or when IntersectionObserver isn't supported.
* @param {Element} el - the element to observe
* @param {function} onIntersect - called when the given element is in view
* @param rootMargin {string} The root element's bounding box before the intersection test is performed defaults to 0px
* @param threshold {number} A number which indicate at what percentage of the images's visibility the image should
*/
function detectIntersection(el: HTMLImageElement, onIntersect: Function, rootMargin: string, threshold: number | number[]) {
try {
if (!isIntersectionObserverSupported()) {
// Return if there's no need or possibility to detect intersection
onIntersect();
return;
}
// Detect intersection with given element using IntersectionObserver
const observer = new IntersectionObserver(
(entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
observer.unobserve(entry.target);
onIntersect();
}
});
}, {rootMargin: rootMargin, threshold: threshold});
observer.observe(el);
return ()=>{el && observer.observe(el)};
} catch (e) {
onIntersect();
}
}