From d8055c00e5dad951bd7dece63d04667e561f1dd3 Mon Sep 17 00:00:00 2001 From: caadiq Date: Wed, 21 Jan 2026 11:59:23 +0900 Subject: [PATCH] =?UTF-8?q?X=20=EC=9D=BC=EC=A0=95=20=EC=83=81=EC=84=B8=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=EC=97=90=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=20=EB=9D=BC=EC=9D=B4=ED=8A=B8=EB=B0=95=EC=8A=A4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PC/모바일 XSection에 라이트박스 기능 추가 - 이미지 클릭 시 전체 화면으로 보기 - 좌우 화살표로 이미지 탐색 - 인디케이터로 현재 이미지 위치 표시 Co-Authored-By: Claude Opus 4.5 --- .../pages/mobile/public/ScheduleDetail.jsx | 108 +++++++++++++++++- .../pc/public/schedule-sections/XSection.jsx | 26 ++++- 2 files changed, 129 insertions(+), 5 deletions(-) diff --git a/frontend/src/pages/mobile/public/ScheduleDetail.jsx b/frontend/src/pages/mobile/public/ScheduleDetail.jsx index ffe0507..8abea95 100644 --- a/frontend/src/pages/mobile/public/ScheduleDetail.jsx +++ b/frontend/src/pages/mobile/public/ScheduleDetail.jsx @@ -2,7 +2,7 @@ import { useParams, Link } from 'react-router-dom'; import { useQuery, keepPreviousData } from '@tanstack/react-query'; import { useEffect, useState, useRef } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; -import { Calendar, Clock, ChevronLeft, Check, Link2, MapPin, Navigation, ExternalLink } from 'lucide-react'; +import { Calendar, Clock, ChevronLeft, Check, Link2, MapPin, Navigation, ExternalLink, X, ChevronRight } from 'lucide-react'; import Linkify from 'react-linkify'; import { getSchedule } from '../../../api/public/schedules'; import '../../../mobile.css'; @@ -308,6 +308,39 @@ function XSection({ schedule }) { const displayName = profile?.displayName || username; const avatarUrl = profile?.avatarUrl; + // 라이트박스 상태 + const [lightboxOpen, setLightboxOpen] = useState(false); + const [lightboxIndex, setLightboxIndex] = useState(0); + + const openLightbox = (index) => { + setLightboxIndex(index); + setLightboxOpen(true); + }; + + const goToPrev = () => { + if (schedule.imageUrls?.length > 1) { + setLightboxIndex((lightboxIndex - 1 + schedule.imageUrls.length) % schedule.imageUrls.length); + } + }; + + const goToNext = () => { + if (schedule.imageUrls?.length > 1) { + setLightboxIndex((lightboxIndex + 1) % schedule.imageUrls.length); + } + }; + + // 라이트박스 열릴 때 body 스크롤 방지 + useEffect(() => { + if (lightboxOpen) { + document.body.style.overflow = 'hidden'; + } else { + document.body.style.overflow = ''; + } + return () => { + document.body.style.overflow = ''; + }; + }, [lightboxOpen]); + // 링크 데코레이터 (새 탭에서 열기) const linkDecorator = (href, text, key) => ( openLightbox(0)} /> ) : (
openLightbox(i)} /> ))}
@@ -433,6 +469,72 @@ function XSection({ schedule }) {
+ + {/* 모바일 라이트박스 */} + + {lightboxOpen && schedule.imageUrls?.length > 0 && ( + setLightboxOpen(false)} + > + {/* 닫기 버튼 */} + + + {/* 이미지 */} + e.stopPropagation()} + /> + + {/* 이전/다음 버튼 */} + {schedule.imageUrls.length > 1 && ( + <> + + + + )} + + {/* 인디케이터 */} + {schedule.imageUrls.length > 1 && ( +
+ {schedule.imageUrls.map((_, i) => ( +
+ )} +
+ )} +
+ ); } diff --git a/frontend/src/pages/pc/public/schedule-sections/XSection.jsx b/frontend/src/pages/pc/public/schedule-sections/XSection.jsx index 6d3fe33..498dad1 100644 --- a/frontend/src/pages/pc/public/schedule-sections/XSection.jsx +++ b/frontend/src/pages/pc/public/schedule-sections/XSection.jsx @@ -1,6 +1,8 @@ +import { useState } from 'react'; import { motion } from 'framer-motion'; import Linkify from 'react-linkify'; import { decodeHtmlEntities } from './utils'; +import Lightbox from '../../../../components/common/Lightbox'; // datetime 포맷팅 (2026-01-18 19:00 → 오후 7:00 · 2026년 1월 18일) const formatXDateTime = (datetime) => { @@ -27,6 +29,15 @@ function XSection({ schedule }) { const displayName = profile?.displayName || username; const avatarUrl = profile?.avatarUrl; + // 라이트박스 상태 + const [lightboxOpen, setLightboxOpen] = useState(false); + const [lightboxIndex, setLightboxIndex] = useState(0); + + const openLightbox = (index) => { + setLightboxIndex(index); + setLightboxOpen(true); + }; + // 링크 데코레이터 (새 탭에서 열기) const linkDecorator = (href, text, key) => ( openLightbox(0)} /> ) : (
openLightbox(i)} /> ))}
@@ -141,6 +154,15 @@ function XSection({ schedule }) {
+ + {/* 라이트박스 */} + setLightboxOpen(false)} + onIndexChange={setLightboxIndex} + /> ); }