/** @jsxImportSource @emotion/react */ import { Skeleton } from '@/@dble_layout'; import { useUid } from '@/libs/hooks'; import Image from 'next/image'; import { ForwardedRef, forwardRef, HTMLAttributes, useCallback, useEffect, useRef, useState } from 'react'; import { PopupImageWrapper } from './instances/PopupImageWrapper'; type SizeThemeType = { isLoading?: boolean; size?: { width?: 'auto' | '100%' | string | number; minWidth?: number | string; maxWidth?: number | string; height?: 'auto' | '100%' | string | number; minHeight?: number | string; maxHeight?: number | string; }; ratio?: { x?: number; y?: number }; shadow?: { x?: number; y?: number; blur?: number; color?: string }; scale?: number; borderRadius?: string | number; }; type Types = { source: string; alt: string; zoomUp?: boolean; objectFit?: 'cover' | 'fill' | 'contain'; priority?: boolean; quality?: number; isHover?: boolean; isLoading?: boolean; } & SizeThemeType & Omit, 'objectFit'>; const ImageInstance = forwardRef(function ImageInstance( { source, alt, objectFit, zoomUp, isLoading, ...props }: Types, ref?: ForwardedRef ) { const uid = useUid(); const imgRef = useRef(null); const [isHover, setIsHover] = useState(false); const [zoomImg, setZoomImg] = useState(false); const [showZoomImage, setShowZoomImage] = useState(false); const [imageAspectRatio, setImageAspectRatio] = useState(undefined); const [isLoaded, setIsLoaded] = useState(false); const [imageDimensions, setImageDimensions] = useState<{ width: number; height: number } | null>(null); useEffect(() => { if (source && !imageDimensions) { const img = new window.Image(); img.src = source; img.onload = () => { setImageDimensions({ width: img.naturalWidth, height: img.naturalHeight, }); setImageAspectRatio(img.naturalWidth / img.naturalHeight); }; } }, [source, imageDimensions]); const handleImageLoad = (event: React.SyntheticEvent) => { const { naturalWidth, naturalHeight } = event.currentTarget; const aspectRatio = naturalWidth / naturalHeight; setImageAspectRatio(aspectRatio); setIsLoaded(true); }; const handleOnClick = (event: React.MouseEvent) => { if (source && zoomUp) { setZoomImg(true); props.onClick?.(event); } else { props.onClick?.(event); } }; const imageWrapperStyle = (styleProps: SizeThemeType) => ({ position: 'relative', width: styleProps.size?.width ?? '100%', minWidth: styleProps.size?.minWidth, maxWidth: styleProps.size?.maxWidth, minHeight: styleProps.size?.minHeight, maxHeight: styleProps.size?.maxHeight, height: styleProps.size?.height, borderRadius: styleProps.borderRadius, aspectRatio: styleProps.ratio ? `${styleProps.ratio.x}/${styleProps.ratio.y}` : imageAspectRatio ? `${imageAspectRatio}/1` : undefined, transition: '0.3s ease-in-out', boxShadow: styleProps.shadow ? `${styleProps.shadow.x}px ${styleProps.shadow.y}px ${styleProps.shadow.blur}px ${styleProps.shadow.color}` : undefined, userSelect: 'none', overflow: 'hidden', scale: styleProps.scale, }); const clickModalOutside = useCallback( (event: MouseEvent) => { if (zoomImg && imgRef.current && !imgRef.current.contains(event.target as Node)) setZoomImg(false); }, [zoomImg] ); useEffect(() => { if (zoomImg) { const scrollY = window.scrollY; document.body.style.position = 'fixed'; document.body.style.top = `-${scrollY}px`; document.body.style.overflowY = 'hidden'; const timer = setTimeout(() => { setShowZoomImage(true); }, 200); } else { const scrollY = document.body.style.top; document.body.style.position = ''; document.body.style.top = ''; document.body.style.overflowY = 'auto'; window.scrollTo(0, parseInt(scrollY || '0') * -1); setShowZoomImage(false); } document.addEventListener('mousedown', clickModalOutside); return () => document.removeEventListener('mousedown', clickModalOutside); }, [zoomImg]); const onHover = () => { if (props.isHover) setIsHover(!isHover); }; return ( <>
{isLoading ? ( ) : ( {alt} )}
{!isLoading && zoomImg && showZoomImage && ( setZoomImg(false)}>
{alt}
)} ); }); export default ImageInstance;