1+ import { useState , useEffect } from 'react' ;
2+
3+ type Item = { title : string ; tag : string ; price ?: string ; } ;
4+ const items : Item [ ] = [
5+ { title : 'ArchViz: Interior Kit v1' , tag : 'LP sections' , price : '$39' } ,
6+ { title : 'Sci‑Fi Door Pack' , tag : 'Kitbash' , price : '$29' } ,
7+ { title : 'Parametric Stair Generator' , tag : 'Blueprint' , price : '$49' } ,
8+ { title : 'PBR Metals (12)' , tag : 'Textures' , price : '$19' } ,
9+ { title : 'Low‑Poly Props (30)' , tag : 'Game' , price : '$25' } ,
10+ { title : 'Facade Variants' , tag : 'ArchViz' , price : '$35' } ,
11+ { title : 'Greebles v2' , tag : 'Kitbash' , price : '$19' } ,
12+ { title : 'Lighting Rigs' , tag : 'Tooling' , price : '$15' } ,
13+ { title : 'Modular Stairs Pro' , tag : 'Blueprint' , price : '$42' } ,
14+ { title : 'Urban Props Pack' , tag : 'Game' , price : '$28' } ,
15+ ] ;
16+
17+ function ThumbSVG ( { index } : { index : number } ) {
18+ // Different gradient variations for visual variety
19+ const gradients = [
20+ 'linear-gradient(135deg, var(--fire-1), var(--fire-2))' ,
21+ 'linear-gradient(45deg, var(--fire-2), var(--fire-3))' ,
22+ 'linear-gradient(225deg, var(--fire-1), var(--fire-3))' ,
23+ ] ;
24+ const gradient = gradients [ index % gradients . length ] ;
25+
26+ return (
27+ < div
28+ className = "w-full h-40 rounded-lg border border-white/10 relative overflow-hidden group-hover:border-white/30 transition-all duration-300"
29+ style = { { background : gradient } }
30+ >
31+ < div className = "absolute inset-4 bg-black/45 rounded-lg" >
32+ < div className = "absolute inset-0 flex items-center justify-center opacity-80" >
33+ < div className = "w-8 h-8 rounded-full" style = { { background : gradient } } />
34+ < div className = "ml-3" >
35+ < div className = "w-20 h-2 mb-2 rounded" style = { { background : gradient } } />
36+ < div className = "w-16 h-2 rounded" style = { { background : gradient , opacity : 0.7 } } />
37+ </ div >
38+ </ div >
39+ </ div >
40+ { /* Hover fire effect */ }
41+ < div className = "absolute inset-0 bg-[linear-gradient(90deg,var(--fire-1),var(--fire-2),var(--fire-3))] opacity-0 group-hover:opacity-20 animate-flame transition-opacity duration-300" />
42+ </ div >
43+ ) ;
44+ }
45+
46+ export default function AssetCarousel ( ) {
47+ const [ currentIndex , setCurrentIndex ] = useState ( 0 ) ;
48+ const [ isAutoPlaying , setIsAutoPlaying ] = useState ( true ) ;
49+
50+ // Auto-advance carousel
51+ useEffect ( ( ) => {
52+ if ( ! isAutoPlaying ) return ;
53+
54+ const interval = setInterval ( ( ) => {
55+ setCurrentIndex ( ( prev ) => ( prev + 1 ) % Math . max ( 1 , items . length - 2 ) ) ;
56+ } , 4000 ) ;
57+
58+ return ( ) => clearInterval ( interval ) ;
59+ } , [ isAutoPlaying ] ) ;
60+
61+ const visibleItems = items . slice ( currentIndex , currentIndex + 3 ) ;
62+
63+ // Pad with items from beginning if needed
64+ while ( visibleItems . length < 3 && items . length > 0 ) {
65+ visibleItems . push ( items [ visibleItems . length % items . length ] ) ;
66+ }
67+
68+ const goToSlide = ( index : number ) => {
69+ setCurrentIndex ( index ) ;
70+ setIsAutoPlaying ( false ) ;
71+ // Resume auto-play after 10 seconds of inactivity
72+ setTimeout ( ( ) => setIsAutoPlaying ( true ) , 10000 ) ;
73+ } ;
74+
75+ const nextSlide = ( ) => {
76+ setCurrentIndex ( ( prev ) => ( prev + 1 ) % Math . max ( 1 , items . length - 2 ) ) ;
77+ setIsAutoPlaying ( false ) ;
78+ setTimeout ( ( ) => setIsAutoPlaying ( true ) , 10000 ) ;
79+ } ;
80+
81+ const prevSlide = ( ) => {
82+ setCurrentIndex ( ( prev ) => prev === 0 ? Math . max ( 0 , items . length - 3 ) : prev - 1 ) ;
83+ setIsAutoPlaying ( false ) ;
84+ setTimeout ( ( ) => setIsAutoPlaying ( true ) , 10000 ) ;
85+ } ;
86+
87+ return (
88+ < section className = "mt-12 md:mt-16" >
89+ < div className = "flex items-baseline justify-between mb-6" >
90+ < div className = "flex items-center gap-3" >
91+ < h2 className = "text-lg md:text-xl font-semibold text-white/90" > Featured assets</ h2 >
92+ < div className = "flex items-center gap-1" >
93+ { Array . from ( { length : Math . max ( 1 , items . length - 2 ) } , ( _ , i ) => (
94+ < button
95+ key = { i }
96+ onClick = { ( ) => goToSlide ( i ) }
97+ className = { `w-2 h-2 rounded-full transition-all duration-300 ${
98+ i === currentIndex
99+ ? 'bg-[var(--fire-2)] w-6'
100+ : 'bg-white/30 hover:bg-white/50'
101+ } `}
102+ />
103+ ) ) }
104+ </ div >
105+ </ div >
106+ < a href = "/waitlist" className = "text-sm text-sky-300 hover:underline" data-evt = "cta_waitlist_carousel" >
107+ Join the drop →
108+ </ a >
109+ </ div >
110+
111+ < div className = "relative" >
112+ { /* Navigation arrows */ }
113+ < button
114+ onClick = { prevSlide }
115+ className = "absolute left-0 top-1/2 -translate-y-1/2 -translate-x-4 z-10 w-10 h-10 rounded-full border border-white/20 bg-black/50 backdrop-blur text-white hover:bg-white/10 hover:border-white/40 transition-all duration-300"
116+ aria-label = "Previous assets"
117+ >
118+ ←
119+ </ button >
120+
121+ < button
122+ onClick = { nextSlide }
123+ className = "absolute right-0 top-1/2 -translate-y-1/2 translate-x-4 z-10 w-10 h-10 rounded-full border border-white/20 bg-black/50 backdrop-blur text-white hover:bg-white/10 hover:border-white/40 transition-all duration-300"
124+ aria-label = "Next assets"
125+ >
126+ →
127+ </ button >
128+
129+ { /* Carousel content */ }
130+ < div className = "overflow-hidden" >
131+ < div
132+ className = "flex transition-transform duration-500 ease-out gap-4"
133+ style = { { transform : `translateX(0%)` } }
134+ >
135+ { visibleItems . map ( ( item , i ) => (
136+ < div key = { `${ currentIndex } -${ i } ` } className = "w-full md:w-1/3 shrink-0" >
137+ < div className = "group rounded-xl border border-white/10 bg-white/5 backdrop-blur p-3 hover:border-white/20 hover:bg-white/8 transition-all duration-300 hover:transform hover:scale-[1.02]" >
138+ < ThumbSVG index = { currentIndex + i } />
139+ < div className = "mt-3" >
140+ < div className = "text-white/90 font-medium group-hover:text-white transition-colors" >
141+ { item . title }
142+ </ div >
143+ < div className = "text-xs text-white/60 group-hover:text-white/80 transition-colors" >
144+ { item . tag }
145+ </ div >
146+ </ div >
147+ < div className = "mt-3 flex items-center justify-between" >
148+ < span className = "text-sm text-white/80 font-medium" > { item . price } </ span >
149+ < button
150+ className = "text-sm px-3 py-1 rounded-md border border-white/15 bg-white/5 hover:bg-white/10 hover:border-white/30 transition-all duration-300"
151+ data-evt = "preview_asset_carousel"
152+ >
153+ Preview
154+ </ button >
155+ </ div >
156+ </ div >
157+ </ div >
158+ ) ) }
159+ </ div >
160+ </ div >
161+
162+ { /* Auto-play indicator */ }
163+ { isAutoPlaying && (
164+ < div className = "absolute bottom-0 left-0 right-0 h-0.5 bg-white/10 overflow-hidden" >
165+ < div
166+ className = "h-full bg-[var(--fire-2)] animate-pulse"
167+ style = { {
168+ animation : 'progress 4s linear infinite' ,
169+ background : 'linear-gradient(90deg, var(--fire-1), var(--fire-2), var(--fire-3))'
170+ } }
171+ />
172+ </ div >
173+ ) }
174+ </ div >
175+
176+ < style > { `
177+ @keyframes progress {
178+ 0% { width: 0%; }
179+ 100% { width: 100%; }
180+ }
181+ ` } </ style >
182+ </ section >
183+ ) ;
184+ }
0 commit comments