라이트박스 뒤로가기 버튼 처리 추가
- PC/모바일 앨범 상세, 갤러리 페이지에 뒤로가기 처리 - PC/모바일 X 일정 상세 페이지에 뒤로가기 처리 - 라이트박스 열릴 때 history.pushState 호출 - popstate 이벤트로 라이트박스 닫기 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
d8055c00e5
commit
67e9992cf1
4 changed files with 92 additions and 30 deletions
|
|
@ -315,6 +315,7 @@ function XSection({ schedule }) {
|
|||
const openLightbox = (index) => {
|
||||
setLightboxIndex(index);
|
||||
setLightboxOpen(true);
|
||||
window.history.pushState({ lightbox: true }, '');
|
||||
};
|
||||
|
||||
const goToPrev = () => {
|
||||
|
|
@ -341,6 +342,18 @@ function XSection({ schedule }) {
|
|||
};
|
||||
}, [lightboxOpen]);
|
||||
|
||||
// 뒤로가기 처리
|
||||
useEffect(() => {
|
||||
const handlePopState = () => {
|
||||
if (lightboxOpen) {
|
||||
setLightboxOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('popstate', handlePopState);
|
||||
return () => window.removeEventListener('popstate', handlePopState);
|
||||
}, [lightboxOpen]);
|
||||
|
||||
// 링크 데코레이터 (새 탭에서 열기)
|
||||
const linkDecorator = (href, text, key) => (
|
||||
<a
|
||||
|
|
@ -478,12 +491,12 @@ function XSection({ schedule }) {
|
|||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="fixed inset-0 bg-black z-50 flex items-center justify-center"
|
||||
onClick={() => setLightboxOpen(false)}
|
||||
onClick={() => window.history.back()}
|
||||
>
|
||||
{/* 닫기 버튼 */}
|
||||
<button
|
||||
className="absolute top-4 right-4 p-2 text-white/70 z-10"
|
||||
onClick={() => setLightboxOpen(false)}
|
||||
onClick={() => window.history.back()}
|
||||
>
|
||||
<X size={28} />
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -45,10 +45,30 @@ function AlbumDetail() {
|
|||
}));
|
||||
}, [lightbox.images.length]);
|
||||
|
||||
// 라이트박스 열기 - 히스토리 추가
|
||||
const openLightbox = useCallback((images, index, options = {}) => {
|
||||
setLightbox({ open: true, images, index, teasers: options.teasers });
|
||||
window.history.pushState({ lightbox: true }, '');
|
||||
}, []);
|
||||
|
||||
const closeLightbox = useCallback(() => {
|
||||
setLightbox(prev => ({ ...prev, open: false }));
|
||||
}, []);
|
||||
|
||||
// 뒤로가기 처리
|
||||
useEffect(() => {
|
||||
const handlePopState = () => {
|
||||
if (showDescriptionModal) {
|
||||
setShowDescriptionModal(false);
|
||||
} else if (lightbox.open) {
|
||||
setLightbox(prev => ({ ...prev, open: false }));
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('popstate', handlePopState);
|
||||
return () => window.removeEventListener('popstate', handlePopState);
|
||||
}, [showDescriptionModal, lightbox.open]);
|
||||
|
||||
// 라이트박스 열릴 때 body 스크롤 숨기기
|
||||
useEffect(() => {
|
||||
if (lightbox.open) {
|
||||
|
|
@ -98,7 +118,7 @@ function AlbumDetail() {
|
|||
goToNext();
|
||||
break;
|
||||
case 'Escape':
|
||||
closeLightbox();
|
||||
window.history.back();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -202,11 +222,10 @@ function AlbumDetail() {
|
|||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ duration: 0.4 }}
|
||||
className="w-80 h-80 flex-shrink-0 rounded-2xl overflow-hidden shadow-lg cursor-pointer group"
|
||||
onClick={() => setLightbox({
|
||||
open: true,
|
||||
images: [album.cover_original_url || album.cover_medium_url],
|
||||
index: 0
|
||||
})}
|
||||
onClick={() => openLightbox(
|
||||
[album.cover_original_url || album.cover_medium_url],
|
||||
0
|
||||
)}
|
||||
>
|
||||
<img
|
||||
src={album.cover_medium_url || album.cover_original_url}
|
||||
|
|
@ -253,6 +272,7 @@ function AlbumDetail() {
|
|||
<button
|
||||
onClick={() => {
|
||||
setShowDescriptionModal(true);
|
||||
window.history.pushState({ description: true }, '');
|
||||
setShowMenu(false);
|
||||
}}
|
||||
className="w-full flex items-center gap-2 px-4 py-2.5 text-sm text-gray-700 hover:bg-gray-50 transition-colors"
|
||||
|
|
@ -293,14 +313,13 @@ function AlbumDetail() {
|
|||
{album.teasers.map((teaser, index) => (
|
||||
<div
|
||||
key={index}
|
||||
onClick={() => setLightbox({
|
||||
open: true,
|
||||
images: album.teasers.map(t =>
|
||||
onClick={() => openLightbox(
|
||||
album.teasers.map(t =>
|
||||
t.media_type === 'video' ? (t.video_url || t.original_url) : t.original_url
|
||||
),
|
||||
index,
|
||||
teasers: album.teasers // media_type 정보 전달
|
||||
})}
|
||||
{ teasers: album.teasers }
|
||||
)}
|
||||
className="w-24 h-24 bg-gray-200 rounded-lg overflow-hidden cursor-pointer transition-all duration-300 ease-out hover:scale-105 hover:shadow-xl hover:z-10 relative"
|
||||
>
|
||||
<>
|
||||
|
|
@ -399,7 +418,7 @@ function AlbumDetail() {
|
|||
{previewPhotos.map((photo, idx) => (
|
||||
<div
|
||||
key={photo.id}
|
||||
onClick={() => setLightbox({ open: true, images: [photo.original_url], index: 0 })}
|
||||
onClick={() => openLightbox([photo.original_url], 0)}
|
||||
className="aspect-square bg-gray-200 rounded-xl overflow-hidden cursor-pointer transition-all duration-300 ease-out hover:scale-[1.03] hover:shadow-xl hover:z-10"
|
||||
>
|
||||
<img
|
||||
|
|
@ -447,7 +466,7 @@ function AlbumDetail() {
|
|||
{/* 닫기 버튼 */}
|
||||
<button
|
||||
className="text-white/70 hover:text-white transition-colors"
|
||||
onClick={closeLightbox}
|
||||
onClick={() => window.history.back()}
|
||||
>
|
||||
<X size={32} />
|
||||
</button>
|
||||
|
|
@ -537,7 +556,7 @@ function AlbumDetail() {
|
|||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4"
|
||||
onClick={() => setShowDescriptionModal(false)}
|
||||
onClick={() => window.history.back()}
|
||||
>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.95, y: 20 }}
|
||||
|
|
@ -550,7 +569,7 @@ function AlbumDetail() {
|
|||
<div className="flex items-center justify-between p-5 border-b border-gray-100">
|
||||
<h3 className="text-lg font-bold">앨범 소개</h3>
|
||||
<button
|
||||
onClick={() => setShowDescriptionModal(false)}
|
||||
onClick={() => window.history.back()}
|
||||
className="p-1.5 hover:bg-gray-100 rounded-full transition-colors"
|
||||
>
|
||||
<X size={20} className="text-gray-500" />
|
||||
|
|
|
|||
|
|
@ -70,17 +70,30 @@ function AlbumGallery() {
|
|||
return allPhotos;
|
||||
}, [album]);
|
||||
|
||||
// 라이트박스 열기
|
||||
const openLightbox = (index) => {
|
||||
// 라이트박스 열기 - 히스토리 추가
|
||||
const openLightbox = useCallback((index) => {
|
||||
setImageLoaded(false);
|
||||
setLightbox({ open: true, index });
|
||||
};
|
||||
window.history.pushState({ lightbox: true }, '');
|
||||
}, []);
|
||||
|
||||
// 라이트박스 닫기
|
||||
const closeLightbox = useCallback(() => {
|
||||
setLightbox(prev => ({ ...prev, open: false }));
|
||||
}, []);
|
||||
|
||||
// 뒤로가기 처리
|
||||
useEffect(() => {
|
||||
const handlePopState = () => {
|
||||
if (lightbox.open) {
|
||||
setLightbox(prev => ({ ...prev, open: false }));
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('popstate', handlePopState);
|
||||
return () => window.removeEventListener('popstate', handlePopState);
|
||||
}, [lightbox.open]);
|
||||
|
||||
// 라이트박스 열릴 때 body 스크롤 숨기기
|
||||
useEffect(() => {
|
||||
if (lightbox.open) {
|
||||
|
|
@ -146,7 +159,7 @@ function AlbumGallery() {
|
|||
switch (e.key) {
|
||||
case 'ArrowLeft': goToPrev(); break;
|
||||
case 'ArrowRight': goToNext(); break;
|
||||
case 'Escape': closeLightbox(); break;
|
||||
case 'Escape': window.history.back(); break;
|
||||
default: break;
|
||||
}
|
||||
};
|
||||
|
|
@ -269,7 +282,7 @@ function AlbumGallery() {
|
|||
</button>
|
||||
<button
|
||||
className="text-white/70 hover:text-white transition-colors"
|
||||
onClick={closeLightbox}
|
||||
onClick={() => window.history.back()}
|
||||
>
|
||||
<X size={32} />
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useState } from 'react';
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import Linkify from 'react-linkify';
|
||||
import { decodeHtmlEntities } from './utils';
|
||||
|
|
@ -33,11 +33,28 @@ function XSection({ schedule }) {
|
|||
const [lightboxOpen, setLightboxOpen] = useState(false);
|
||||
const [lightboxIndex, setLightboxIndex] = useState(0);
|
||||
|
||||
const openLightbox = (index) => {
|
||||
const openLightbox = useCallback((index) => {
|
||||
setLightboxIndex(index);
|
||||
setLightboxOpen(true);
|
||||
window.history.pushState({ lightbox: true }, '');
|
||||
}, []);
|
||||
|
||||
const closeLightbox = useCallback(() => {
|
||||
setLightboxOpen(false);
|
||||
}, []);
|
||||
|
||||
// 뒤로가기 처리
|
||||
useEffect(() => {
|
||||
const handlePopState = () => {
|
||||
if (lightboxOpen) {
|
||||
setLightboxOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('popstate', handlePopState);
|
||||
return () => window.removeEventListener('popstate', handlePopState);
|
||||
}, [lightboxOpen]);
|
||||
|
||||
// 링크 데코레이터 (새 탭에서 열기)
|
||||
const linkDecorator = (href, text, key) => (
|
||||
<a
|
||||
|
|
@ -160,7 +177,7 @@ function XSection({ schedule }) {
|
|||
images={schedule.imageUrls || []}
|
||||
currentIndex={lightboxIndex}
|
||||
isOpen={lightboxOpen}
|
||||
onClose={() => setLightboxOpen(false)}
|
||||
onClose={() => window.history.back()}
|
||||
onIndexChange={setLightboxIndex}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue