1+ import { useEffect , useRef , useState } from 'react' ;
2+ import Swiper from 'swiper' ;
3+ import { Navigation , A11y } from 'swiper/modules' ;
4+ import 'swiper/css' ;
5+ import 'swiper/css/navigation' ;
6+
7+ interface Props {
8+ images : string [ ] ;
9+ }
10+
11+ export default function FullscreenSliderModal ( { images } : Props ) {
12+ const [ isFullscreen , setIsFullscreen ] = useState ( false ) ;
13+ const previewSwiperRef = useRef < HTMLDivElement > ( null ) ;
14+ const fullscreenSwiperRef = useRef < HTMLDivElement > ( null ) ;
15+ const nextButtonRef = useRef < HTMLDivElement > ( null ) ;
16+ const prevButtonRef = useRef < HTMLDivElement > ( null ) ;
17+ const fullNextButtonRef = useRef < HTMLDivElement > ( null ) ;
18+ const fullPrevButtonRef = useRef < HTMLDivElement > ( null ) ;
19+
20+ const [ previewSwiper , setPreviewSwiper ] = useState < Swiper | null > ( null ) ;
21+ const [ fullscreenSwiper , setFullscreenSwiper ] = useState < Swiper | null > ( null ) ;
22+
23+ // Initialize preview slider
24+ useEffect ( ( ) => {
25+ if ( previewSwiperRef . current ) {
26+ const swiper = new Swiper ( previewSwiperRef . current , {
27+ modules : [ Navigation , A11y ] ,
28+ slidesPerView : 1 ,
29+ spaceBetween : 20 ,
30+ navigation : {
31+ nextEl : nextButtonRef . current ,
32+ prevEl : prevButtonRef . current ,
33+ } ,
34+ loop : true ,
35+ a11y : {
36+ prevSlideMessage : 'Previous slide' ,
37+ nextSlideMessage : 'Next slide' ,
38+ } ,
39+ } ) ;
40+ setPreviewSwiper ( swiper ) ;
41+ return ( ) => swiper . destroy ( ) ;
42+ }
43+ } , [ ] ) ;
44+
45+ // Initialize fullscreen slider when opened
46+ useEffect ( ( ) => {
47+ if ( isFullscreen && fullscreenSwiperRef . current ) {
48+ const swiper = new Swiper ( fullscreenSwiperRef . current , {
49+ modules : [ Navigation , A11y ] ,
50+ slidesPerView : 1 ,
51+ navigation : {
52+ nextEl : fullNextButtonRef . current ,
53+ prevEl : fullPrevButtonRef . current ,
54+ } ,
55+ loop : true ,
56+ a11y : {
57+ prevSlideMessage : 'Previous slide' ,
58+ nextSlideMessage : 'Next slide' ,
59+ } ,
60+ } ) ;
61+
62+ // Sync with preview slider if it exists
63+ if ( previewSwiper ) {
64+ swiper . slideTo ( previewSwiper . realIndex ) ;
65+ }
66+
67+ setFullscreenSwiper ( swiper ) ;
68+ document . body . style . overflow = 'hidden' ;
69+
70+ return ( ) => {
71+ swiper . destroy ( ) ;
72+ document . body . style . overflow = 'auto' ;
73+ } ;
74+ }
75+ } , [ isFullscreen , previewSwiper ] ) ;
76+
77+ const openFullscreen = ( ) => {
78+ setIsFullscreen ( true ) ;
79+ } ;
80+
81+ const closeFullscreen = ( ) => {
82+ // Sync back to preview slider before closing
83+ if ( previewSwiper && fullscreenSwiper ) {
84+ previewSwiper . slideTo ( fullscreenSwiper . realIndex ) ;
85+ }
86+ setIsFullscreen ( false ) ; } ;
87+
88+ return (
89+ < >
90+ { /* Preview Slider */ }
91+ < div className = "relative mt-8" >
92+ < button
93+ onClick = { openFullscreen }
94+ className = "absolute top-4 right-4 bg-black/70 text-white px-3 py-1 rounded text-sm flex items-center gap-1 z-10"
95+ aria-label = "View in fullscreen"
96+ >
97+ Fullscreen
98+ </ button >
99+ < div className = "swiper" ref = { previewSwiperRef } >
100+ < div className = "swiper-wrapper" >
101+ { images . map ( ( image , index ) => (
102+ < div key = { `preview-${ index } ` } className = "swiper-slide" >
103+ < img
104+ src = { image }
105+ alt = { `Preview ${ index + 1 } ` }
106+ className = "w-full h-auto object-contain rounded-lg cursor-zoom-in"
107+ loading = "lazy"
108+ onClick = { openFullscreen }
109+ />
110+ </ div >
111+ ) ) }
112+ </ div >
113+ < div ref = { prevButtonRef } className = "swiper-button-prev" > </ div >
114+ < div ref = { nextButtonRef } className = "swiper-button-next" > </ div >
115+ </ div >
116+ </ div >
117+
118+ { /* Fullscreen Modal */ }
119+ { isFullscreen && (
120+ < div className = "fixed inset-0 bg-black/70 z-50 p-4 w-full h-full" >
121+ < button
122+ className = "absolute top-4 right-4 text-center text-white z-10 hover:cursor-pointer text-2xl rounded-full"
123+ onClick = { closeFullscreen }
124+ aria-label = "Close fullscreen"
125+ >
126+ ×
127+ </ button >
128+
129+ < div className = "flex justify-center items-center swiper w-full h-full" ref = { fullscreenSwiperRef } >
130+ < div className = "swiper-wrapper" onClick = { closeFullscreen } >
131+ { images . map ( ( image , index ) => (
132+ < div key = { `full-${ index } ` } className = "swiper-slide" >
133+ < img
134+ src = { image }
135+ alt = { `Slide ${ index + 1 } ` }
136+ className = "w-full h-full object-contain"
137+ loading = "lazy"
138+ />
139+ </ div >
140+ ) ) }
141+ </ div >
142+ < div ref = { fullPrevButtonRef } className = "swiper-button-prev text-white" > </ div >
143+ < div ref = { fullNextButtonRef } className = "swiper-button-next text-white" > </ div >
144+ </ div >
145+ </ div >
146+ ) }
147+ </ >
148+ ) ;
149+ }
0 commit comments