import { useState, useEffect, useCallback, memo } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { X, ChevronLeft, ChevronRight } from 'lucide-react';
// 인디케이터 컴포넌트 - CSS transition 사용으로 GPU 가속
const LightboxIndicator = memo(function LightboxIndicator({ count, currentIndex, goToIndex }) {
const translateX = -(currentIndex * 18) + 100 - 6;
return (
{/* 양옆 페이드 그라데이션 */}
{/* 슬라이딩 컨테이너 */}
{Array.from({ length: count }).map((_, i) => (
);
});
// 라이트박스 공통 컴포넌트
function Lightbox({ images, currentIndex, isOpen, onClose, onIndexChange }) {
const [imageLoaded, setImageLoaded] = useState(false);
const [slideDirection, setSlideDirection] = useState(0);
// 이전/다음 네비게이션
const goToPrev = useCallback(() => {
if (images.length <= 1) return;
setImageLoaded(false);
setSlideDirection(-1);
onIndexChange((currentIndex - 1 + images.length) % images.length);
}, [images.length, currentIndex, onIndexChange]);
const goToNext = useCallback(() => {
if (images.length <= 1) return;
setImageLoaded(false);
setSlideDirection(1);
onIndexChange((currentIndex + 1) % images.length);
}, [images.length, currentIndex, onIndexChange]);
const goToIndex = useCallback((index) => {
if (index === currentIndex) return;
setImageLoaded(false);
setSlideDirection(index > currentIndex ? 1 : -1);
onIndexChange(index);
}, [currentIndex, onIndexChange]);
// 라이트박스 열릴 때 body 스크롤 숨기기
useEffect(() => {
if (isOpen) {
document.documentElement.style.overflow = 'hidden';
document.body.style.overflow = 'hidden';
} else {
document.documentElement.style.overflow = '';
document.body.style.overflow = '';
}
return () => {
document.documentElement.style.overflow = '';
document.body.style.overflow = '';
};
}, [isOpen]);
// 키보드 이벤트 핸들러
useEffect(() => {
if (!isOpen) return;
const handleKeyDown = (e) => {
switch (e.key) {
case 'ArrowLeft':
goToPrev();
break;
case 'ArrowRight':
goToNext();
break;
case 'Escape':
onClose();
break;
default:
break;
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [isOpen, goToPrev, goToNext, onClose]);
// 이미지가 바뀔 때 로딩 상태 리셋
useEffect(() => {
setImageLoaded(false);
}, [currentIndex]);
return (
{isOpen && images.length > 0 && (
{/* 내부 컨테이너 */}
{/* 닫기 버튼 */}
{/* 이전 버튼 */}
{images.length > 1 && (
)}
{/* 로딩 스피너 */}
{!imageLoaded && (
)}
{/* 이미지 */}
e.stopPropagation()}
onLoad={() => setImageLoaded(true)}
initial={{ x: slideDirection * 100 }}
animate={{ x: 0 }}
transition={{ duration: 0.25, ease: 'easeOut' }}
/>
{/* 다음 버튼 */}
{images.length > 1 && (
)}
{/* 인디케이터 */}
{images.length > 1 && (
)}
)}
);
}
export default Lightbox;