UNPKG

react-lazy-img-observer

Version:

A React component for lazy loading images with Intersection Observer

91 lines (90 loc) 4.27 kB
"use strict"; "use client"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ImageLazy = void 0; const jsx_runtime_1 = require("react/jsx-runtime"); const react_1 = require("react"); const ImageLazy = ({ src, alt, srcSet, sizes, width, height, className, id, extraData, title, useTitleFromAlt = false, viewTransitionName, style, backgroundColor, animationDuration = "0.9s", blurAmount = "20px", fallbackSrc, threshold = 0.1, transitionType = "blur", onLoadComplete, visibleByDefault = false, loadingComponent, }) => { const imageRef = (0, react_1.useRef)(null); const [realSrc, setRealSrc] = (0, react_1.useState)(visibleByDefault ? src : null); const [loaded, setLoaded] = (0, react_1.useState)(false); const [hasError, setHasError] = (0, react_1.useState)(false); (0, react_1.useEffect)(() => { setLoaded(false); setHasError(false); setRealSrc(visibleByDefault ? src : null); }, [src, visibleByDefault]); (0, react_1.useEffect)(() => { const isDev = typeof window !== "undefined" && window?.location?.hostname?.includes("localhost"); if (isDev) { if (!alt) { console.warn("[ImageLazy] ⚠️ Se recomienda definir el atributo 'alt' por accesibilidad."); } if (!src) { console.warn("[ImageLazy] ⚠️ La prop 'src' está vacía o nula. La imagen no se cargará."); } } }, [alt, src]); (0, react_1.useEffect)(() => { if (typeof window === "undefined" || visibleByDefault) return; if (typeof IntersectionObserver === "undefined" || !imageRef.current) return; const observer = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { setRealSrc(src); observer.unobserve(entry.target); } }); }, { root: null, rootMargin: "0px", threshold }); observer.observe(imageRef.current); return () => observer.disconnect(); }, [src, threshold, visibleByDefault, realSrc]); const handleLoad = () => { setLoaded(true); onLoadComplete?.(); }; const handleError = () => { if (fallbackSrc && realSrc !== fallbackSrc) { setRealSrc(fallbackSrc); } else { setHasError(true); } }; const isCustom = transitionType === "custom"; const transitionStyles = (0, react_1.useMemo)(() => { if (isCustom) return {}; if (transitionType === "fade") { return { opacity: loaded ? 1 : 0, transition: `opacity ${animationDuration}`, }; } if (transitionType === "scale") { return { transform: loaded ? "scale(1)" : "scale(1.05)", opacity: loaded ? 1 : 0, transition: `transform ${animationDuration}, opacity ${animationDuration}`, }; } return { filter: loaded ? "none" : `blur(${blurAmount})`, transition: `filter ${animationDuration}`, }; }, [transitionType, loaded, animationDuration, blurAmount, isCustom]); const finalStyle = (0, react_1.useMemo)(() => ({ backgroundColor: !loaded && backgroundColor ? backgroundColor : undefined, backgroundSize: "cover", backgroundPosition: "center", ...(viewTransitionName ? { viewTransitionName } : {}), ...transitionStyles, ...style, }), [loaded, backgroundColor, viewTransitionName, transitionStyles, style]); return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [!loaded && loadingComponent, (!hasError || realSrc === null) && ((0, jsx_runtime_1.jsx)("img", { ref: imageRef, src: realSrc ?? undefined, alt: alt, title: title ?? (useTitleFromAlt ? alt : undefined), className: className, width: width, height: height, id: id?.toString(), onLoad: handleLoad, onError: handleError, srcSet: realSrc ? srcSet : undefined, sizes: realSrc ? sizes : undefined, style: finalStyle, ...extraData }))] })); }; exports.ImageLazy = ImageLazy; exports.default = ImageLazy;