docs: 모바일 앨범 갤러리 UI 구현 문서 추가
- Swiper ViewPager 라이트박스 사용법 - LightboxIndicator 컴포넌트 (width prop) - 2열 지그재그 Masonry 그리드 패턴 - 뒤로가기 처리 패턴 - 바텀시트 드래그 닫기 패턴
This commit is contained in:
parent
d6bc8d79ba
commit
5a5e601f63
1 changed files with 124 additions and 0 deletions
|
|
@ -397,3 +397,127 @@ docker compose -f docker-compose.dev.yml up -d
|
|||
| `frontend/src/pages/mobile/public/Schedule.jsx` | 52KB | 모바일 일정 |
|
||||
| `backend/services/youtube-bot.js` | 17KB | YouTube 수집 |
|
||||
| `backend/services/x-bot.js` | 16KB | X 수집 |
|
||||
|
||||
---
|
||||
|
||||
## 13. 모바일 앨범 갤러리 UI
|
||||
|
||||
### 주요 컴포넌트
|
||||
|
||||
| 파일 | 설명 |
|
||||
| ------------------------------------------------------ | ----------------------------- |
|
||||
| `frontend/src/pages/mobile/public/AlbumGallery.jsx` | 모바일 앨범 갤러리 (전체보기) |
|
||||
| `frontend/src/pages/mobile/public/AlbumDetail.jsx` | 모바일 앨범 상세 |
|
||||
| `frontend/src/components/common/LightboxIndicator.jsx` | 공통 슬라이딩 점 인디케이터 |
|
||||
|
||||
### Swiper ViewPager 스타일 라이트박스
|
||||
|
||||
```jsx
|
||||
import { Swiper, SwiperSlide } from "swiper/react";
|
||||
import { Virtual } from "swiper/modules";
|
||||
|
||||
<Swiper
|
||||
modules={[Virtual]}
|
||||
virtual
|
||||
initialSlide={selectedIndex}
|
||||
onSwiper={(swiper) => {
|
||||
swiperRef.current = swiper;
|
||||
}}
|
||||
onSlideChange={(swiper) => setSelectedIndex(swiper.activeIndex)}
|
||||
slidesPerView={1}
|
||||
resistance={true}
|
||||
resistanceRatio={0.5}
|
||||
>
|
||||
{photos.map((photo, index) => (
|
||||
<SwiperSlide key={index} virtualIndex={index}>
|
||||
<img src={photo.medium_url} />
|
||||
</SwiperSlide>
|
||||
))}
|
||||
</Swiper>;
|
||||
```
|
||||
|
||||
### LightboxIndicator 사용법
|
||||
|
||||
```jsx
|
||||
import LightboxIndicator from '../../../components/common/LightboxIndicator';
|
||||
|
||||
// PC (기본 width 200px)
|
||||
<LightboxIndicator
|
||||
count={photos.length}
|
||||
currentIndex={selectedIndex}
|
||||
goToIndex={(i) => swiperRef.current?.slideTo(i)}
|
||||
/>
|
||||
|
||||
// 모바일 (width 120px로 축소)
|
||||
<LightboxIndicator
|
||||
count={photos.length}
|
||||
currentIndex={selectedIndex}
|
||||
goToIndex={(i) => swiperRef.current?.slideTo(i)}
|
||||
width={120}
|
||||
/>
|
||||
```
|
||||
|
||||
### 2열 지그재그 Masonry 그리드
|
||||
|
||||
```jsx
|
||||
// 1,3,5번 → 왼쪽 열 / 2,4,6번 → 오른쪽 열
|
||||
const distributePhotos = () => {
|
||||
const leftColumn = [];
|
||||
const rightColumn = [];
|
||||
photos.forEach((photo, index) => {
|
||||
if (index % 2 === 0) leftColumn.push({ ...photo, originalIndex: index });
|
||||
else rightColumn.push({ ...photo, originalIndex: index });
|
||||
});
|
||||
return { leftColumn, rightColumn };
|
||||
};
|
||||
```
|
||||
|
||||
### 뒤로가기 처리 패턴
|
||||
|
||||
```jsx
|
||||
// 모달/라이트박스 열 때 히스토리 추가
|
||||
const openLightbox = useCallback((images, index, options = {}) => {
|
||||
setLightbox({ open: true, images, index, ...options });
|
||||
window.history.pushState({ lightbox: true }, "");
|
||||
}, []);
|
||||
|
||||
// popstate 이벤트로 닫기
|
||||
useEffect(() => {
|
||||
const handlePopState = () => {
|
||||
if (showModal) setShowModal(false);
|
||||
else if (lightbox.open) setLightbox((prev) => ({ ...prev, open: false }));
|
||||
};
|
||||
window.addEventListener("popstate", handlePopState);
|
||||
return () => window.removeEventListener("popstate", handlePopState);
|
||||
}, [showModal, lightbox.open]);
|
||||
|
||||
// X 버튼도 history.back() 호출
|
||||
<button onClick={() => window.history.back()}>
|
||||
<X size={24} />
|
||||
</button>;
|
||||
```
|
||||
|
||||
### 바텀시트 (정보 표시)
|
||||
|
||||
```jsx
|
||||
<motion.div
|
||||
initial={{ y: "100%" }}
|
||||
animate={{ y: 0 }}
|
||||
exit={{ y: "100%" }}
|
||||
drag="y"
|
||||
dragConstraints={{ top: 0, bottom: 0 }}
|
||||
dragElastic={{ top: 0, bottom: 0.5 }}
|
||||
onDragEnd={(_, info) => {
|
||||
if (info.offset.y > 100 || info.velocity.y > 300) {
|
||||
window.history.back();
|
||||
}
|
||||
}}
|
||||
className="bg-zinc-900 rounded-t-3xl"
|
||||
>
|
||||
{/* 드래그 핸들 */}
|
||||
<div className="flex justify-center pt-3 pb-2">
|
||||
<div className="w-10 h-1 bg-zinc-600 rounded-full" />
|
||||
</div>
|
||||
{/* 내용 */}
|
||||
</motion.div>
|
||||
```
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue