11/* eslint-disable @next/next/no-img-element */
22"use client" ;
33import Polaroid , { type polaroidVariants } from "@/components/blocks/polaroid" ;
4- import {
5- Carousel ,
6- CarouselContent ,
7- CarouselItem ,
8- CarouselNext ,
9- CarouselPrevious ,
10- } from "@/components/ui/carousel" ;
11- import {
12- Dialog ,
13- DialogContent ,
14- DialogDescription ,
15- DialogHeader ,
16- DialogTitle ,
17- DialogTrigger ,
18- } from "@/components/ui/dialog" ;
19- import { useInView } from "motion/react" ;
4+ import { AnimatePresence , motion , useInView } from "motion/react" ;
205import Image from "next/image" ;
21- import { useEffect , useRef , useState } from "react" ;
6+ import { useCallback , useEffect , useRef , useState } from "react" ;
227
238type TImage = {
249 src : string ;
@@ -37,64 +22,228 @@ const PolaroidGallery = ({
3722 const ref = useRef ( null ) ;
3823 const isInView = useInView ( ref ) ;
3924 const [ isVisible , setIsVisible ] = useState ( false ) ;
40- const [ startIndex , setStartIndex ] = useState ( 0 ) ;
25+ const [ currentIndex , setCurrentIndex ] = useState ( 0 ) ;
26+ const [ isViewerOpen , setIsViewerOpen ] = useState ( false ) ;
4127
4228 useEffect ( ( ) => {
4329 if ( isInView && ! isVisible ) {
4430 setIsVisible ( true ) ;
4531 }
4632 } , [ isInView , isVisible ] ) ;
4733
34+ const openViewer = useCallback ( ( index : number ) => {
35+ setCurrentIndex ( index ) ;
36+ setIsViewerOpen ( true ) ;
37+ } , [ ] ) ;
38+
39+ const closeViewer = useCallback ( ( ) => {
40+ setIsViewerOpen ( false ) ;
41+ } , [ ] ) ;
42+
43+ const nextImage = useCallback ( ( ) => {
44+ setCurrentIndex ( ( prev ) => ( prev + 1 ) % images . length ) ;
45+ } , [ images . length ] ) ;
46+
47+ const prevImage = useCallback ( ( ) => {
48+ setCurrentIndex ( ( prev ) => ( prev - 1 + images . length ) % images . length ) ;
49+ } , [ images . length ] ) ;
50+
51+ const handleKeyDown = useCallback (
52+ ( e : KeyboardEvent ) => {
53+ if ( ! isViewerOpen ) return ;
54+
55+ if ( e . key === "Escape" ) closeViewer ( ) ;
56+ if ( e . key === "ArrowRight" ) nextImage ( ) ;
57+ if ( e . key === "ArrowLeft" ) prevImage ( ) ;
58+ } ,
59+ [ isViewerOpen , nextImage , prevImage , closeViewer ] ,
60+ ) ;
61+
62+ // Global keyboard listener
63+ useEffect ( ( ) => {
64+ if ( isViewerOpen ) {
65+ document . addEventListener ( "keydown" , handleKeyDown ) ;
66+ return ( ) => document . removeEventListener ( "keydown" , handleKeyDown ) ;
67+ }
68+ } , [ isViewerOpen , handleKeyDown ] ) ;
69+
4870 return (
49- < Carousel
50- opts = { {
51- startIndex,
52- } }
53- >
54- < Dialog >
55- < DialogTrigger asChild >
56- < div
57- ref = { ref }
58- className = "grid grid-cols-12 items-center -gap-10 mt-3"
71+ < >
72+ < div
73+ ref = { ref }
74+ className = "relative mb-4 mt-2 p-2"
75+ style = { { height : "160px" } }
76+ >
77+ { images . map ( ( image , index ) => (
78+ < Polaroid
79+ isVisible = { isVisible }
80+ index = { index }
81+ total = { images . length }
82+ key = { image . src }
83+ variant = { image . variant }
84+ onClick = { ( ) => openViewer ( index ) }
85+ src = { image . src }
86+ />
87+ ) ) }
88+ </ div >
89+
90+ { /* Amazing Full-Screen Image Viewer */ }
91+ < AnimatePresence >
92+ { isViewerOpen && (
93+ < motion . div
94+ className = "fixed inset-0 z-50 flex items-center justify-center"
95+ initial = { { opacity : 0 } }
96+ animate = { { opacity : 1 } }
97+ exit = { { opacity : 0 } }
98+ transition = { { duration : 0.3 , ease : "easeOut" } }
5999 >
60- { images . map ( ( image , index ) => (
61- < Polaroid
62- isVisible = { isVisible }
63- index = { index }
64- total = { images . length }
65- key = { image . src }
66- variant = { image . variant }
67- onClick = { ( ) => setStartIndex ( index ) }
68- src = { image . src }
69- />
70- ) ) }
71- </ div >
72- </ DialogTrigger >
73- < DialogContent >
74- < DialogHeader >
75- < DialogTitle > { event } </ DialogTitle >
76- < DialogDescription > { title } </ DialogDescription >
77- </ DialogHeader >
78- < div >
79- < CarouselContent >
80- { images . map ( ( image ) => (
81- < CarouselItem key = { image . src } >
82- < Image
83- alt = ""
84- src = { image . src }
85- width = { image . variant === "1x1" ? 640 : 480 }
86- height = { image . variant === "1x1" ? 640 : 960 }
87- // className="object-contain"
88- />
89- </ CarouselItem >
90- ) ) }
91- </ CarouselContent >
92- < CarouselPrevious />
93- < CarouselNext />
94- </ div >
95- </ DialogContent >
96- </ Dialog >
97- </ Carousel >
100+ { /* Immersive Background */ }
101+ < motion . div
102+ className = "absolute inset-0 bg-black/90 backdrop-blur-md"
103+ initial = { { backdropFilter : "blur(0px)" } }
104+ animate = { { backdropFilter : "blur(12px)" } }
105+ exit = { { backdropFilter : "blur(0px)" } }
106+ onClick = { closeViewer }
107+ />
108+
109+ { /* Main Content Container */ }
110+ < div className = "relative z-10 w-full h-full flex flex-col" >
111+ { /* Header */ }
112+ < motion . div
113+ className = "flex items-center justify-between p-6 text-white"
114+ initial = { { y : - 50 , opacity : 0 } }
115+ animate = { { y : 0 , opacity : 1 } }
116+ transition = { { delay : 0.1 , duration : 0.4 } }
117+ >
118+ < div >
119+ < h2 className = "text-2xl font-bold" > { event } </ h2 >
120+ { title && < p className = "text-gray-300 mt-1" > { title } </ p > }
121+ </ div >
122+ < div className = "flex items-center gap-4" >
123+ < span className = "text-sm text-gray-400" >
124+ { currentIndex + 1 } / { images . length }
125+ </ span >
126+ < button
127+ onClick = { closeViewer }
128+ className = "p-2 hover:bg-white/10 rounded-full transition-colors"
129+ >
130+ < svg
131+ className = "w-6 h-6"
132+ fill = "none"
133+ stroke = "currentColor"
134+ viewBox = "0 0 24 24"
135+ >
136+ < path
137+ strokeLinecap = "round"
138+ strokeLinejoin = "round"
139+ strokeWidth = { 2 }
140+ d = "M6 18L18 6M6 6l12 12"
141+ />
142+ </ svg >
143+ </ button >
144+ </ div >
145+ </ motion . div >
146+
147+ { /* Image Container */ }
148+ < div className = "flex-1 flex items-center justify-center p-6" >
149+ < motion . div
150+ key = { currentIndex }
151+ className = "relative w-full h-[80vh] max-w-4xl mx-auto"
152+ initial = { { scale : 0.8 , opacity : 0 , rotateY : 15 } }
153+ animate = { { scale : 1 , opacity : 1 , rotateY : 0 } }
154+ exit = { { scale : 0.8 , opacity : 0 , rotateY : - 15 } }
155+ transition = { {
156+ duration : 0.6 ,
157+ ease : [ 0.25 , 0.46 , 0.45 , 0.94 ] ,
158+ type : "spring" ,
159+ stiffness : 100 ,
160+ damping : 20 ,
161+ } }
162+ >
163+ < div className = "relative w-full h-full rounded-xl overflow-hidden flex items-center justify-center" >
164+ < Image
165+ src = { images [ currentIndex ] . src }
166+ alt = ""
167+ width = { 800 }
168+ height = { 600 }
169+ className = "object-contain max-w-full max-h-full rounded-xl"
170+ sizes = "(max-width: 768px) 100vw, (max-width: 1200px) 80vw, 70vw"
171+ priority
172+ />
173+
174+ { /* Subtle gradient overlay */ }
175+ < div className = "absolute inset-0 pointer-events-none" />
176+ </ div >
177+ </ motion . div >
178+ </ div >
179+
180+ { /* Navigation Controls */ }
181+ < motion . div
182+ className = "flex items-center justify-center gap-8 p-6"
183+ initial = { { y : 50 , opacity : 0 } }
184+ animate = { { y : 0 , opacity : 1 } }
185+ transition = { { delay : 0.2 , duration : 0.4 } }
186+ >
187+ < button
188+ onClick = { prevImage }
189+ className = "p-3 bg-white/10 hover:bg-white/20 rounded-full transition-all duration-200 backdrop-blur-sm"
190+ disabled = { images . length <= 1 }
191+ >
192+ < svg
193+ className = "w-6 h-6 text-white"
194+ fill = "none"
195+ stroke = "currentColor"
196+ viewBox = "0 0 24 24"
197+ >
198+ < path
199+ strokeLinecap = "round"
200+ strokeLinejoin = "round"
201+ strokeWidth = { 2 }
202+ d = "M15 19l-7-7 7-7"
203+ />
204+ </ svg >
205+ </ button >
206+
207+ { /* Progress Dots */ }
208+ < div className = "flex gap-2" >
209+ { images . map ( ( _ , index ) => (
210+ < button
211+ key = { index }
212+ onClick = { ( ) => setCurrentIndex ( index ) }
213+ className = { `w-2 h-2 rounded-full transition-all duration-200 ${
214+ index === currentIndex
215+ ? "bg-white scale-125"
216+ : "bg-white/40 hover:bg-white/60"
217+ } `}
218+ />
219+ ) ) }
220+ </ div >
221+
222+ < button
223+ onClick = { nextImage }
224+ className = "p-3 bg-white/10 hover:bg-white/20 rounded-full transition-all duration-200 backdrop-blur-sm"
225+ disabled = { images . length <= 1 }
226+ >
227+ < svg
228+ className = "w-6 h-6 text-white"
229+ fill = "none"
230+ stroke = "currentColor"
231+ viewBox = "0 0 24 24"
232+ >
233+ < path
234+ strokeLinecap = "round"
235+ strokeLinejoin = "round"
236+ strokeWidth = { 2 }
237+ d = "M9 5l7 7-7 7"
238+ />
239+ </ svg >
240+ </ button >
241+ </ motion . div >
242+ </ div >
243+ </ motion . div >
244+ ) }
245+ </ AnimatePresence >
246+ </ >
98247 ) ;
99248} ;
100249
0 commit comments