UNPKG

kawkab-frontend

Version:

Kawkab frontend is a frontend library for the Kawkab framework

78 lines (77 loc) 4.19 kB
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import { useState, useRef, useEffect } from 'react'; const defaultLoader = (src, width, quality) => { const url = new URL(src, 'http://localhost'); url.searchParams.set('w', width.toString()); url.searchParams.set('q', quality.toString()); return url.toString(); }; const defaultWidths = [320, 480, 768, 1024, 1280, 1600, 1920]; const generateSrcSetString = (src, quality, loader) => defaultWidths.map((w) => `${loader(src, w, quality)} ${w}w`).join(', '); export const Image = ({ src, alt, width, height, layout = 'intrinsic', objectFit = 'cover', objectPosition = 'center', className = '', style = {}, placeholder = 'empty', blurDataURL, priority = false, loading = 'lazy', onLoad, onError, quality = 75, sizes = '100vw', generateSrcSet = true, loader = defaultLoader, unoptimized = false, }) => { const [isLoaded, setIsLoaded] = useState(false); const [isInView, setIsInView] = useState(priority || loading === 'eager'); const imageRef = useRef(null); const isFill = layout === 'fill'; useEffect(() => { if (priority || loading === 'eager') return; const observer = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { setIsInView(true); observer.unobserve(entry.target); } }); }, { rootMargin: '100px', threshold: 0.1 }); const current = imageRef.current; if (current) observer.observe(current); return () => { if (current) observer.unobserve(current); }; }, [src, priority, loading]); const aspectRatioStyle = layout === 'intrinsic' && width && height ? { aspectRatio: `${width} / ${height}` } : {}; const containerStyle = { position: isFill ? 'relative' : 'initial', width: isFill ? '100%' : width, height: isFill ? '100%' : height, overflow: 'hidden', display: layout === 'intrinsic' ? 'inline-block' : 'block', ...aspectRatioStyle, ...style, }; const imageStyle = { objectFit, objectPosition, width: layout === 'intrinsic' || layout === 'fixed' ? width : '100%', height: layout === 'intrinsic' || layout === 'fixed' ? height : '100%', position: isFill ? 'absolute' : 'relative', top: isFill ? 0 : undefined, left: isFill ? 0 : undefined, transition: 'filter 0.3s ease, transform 0.3s ease', filter: placeholder === 'blur' && !isLoaded && blurDataURL ? 'blur(20px)' : 'none', transform: placeholder === 'blur' && !isLoaded && blurDataURL ? 'scale(1.02)' : 'none', }; const shouldUseSrcSet = !unoptimized && generateSrcSet && isInView; const finalSrc = !unoptimized && isInView ? loader(src, width || 800, quality) : undefined; const srcSet = shouldUseSrcSet ? generateSrcSetString(src, quality, loader) : undefined; return (_jsxs(_Fragment, { children: [priority && _jsx("link", { rel: "preload", as: "image", href: finalSrc }), _jsxs("div", { style: containerStyle, className: className, children: [_jsx("img", { ref: imageRef, src: finalSrc, srcSet: srcSet, sizes: srcSet ? sizes : undefined, alt: alt, width: layout === 'fixed' || layout === 'intrinsic' ? width : undefined, height: layout === 'fixed' || layout === 'intrinsic' ? height : undefined, style: imageStyle, loading: loading, onLoad: () => { setIsLoaded(true); onLoad?.(); }, onError: onError }), placeholder === 'blur' && blurDataURL && !isLoaded && (_jsx("img", { src: blurDataURL, alt: "", "aria-hidden": "true", style: { ...imageStyle, position: 'absolute', top: 0, left: 0, filter: 'blur(20px)', zIndex: -1, } }))] })] })); };