11// Home.tsx
2- import { useEffect , useState } from 'react' ;
2+ import { useEffect , useState , useRef } from 'react' ;
33import axios from 'axios' ;
44import MovieCard from '../Components/MovieCard' ;
55import NavBar from '../Components/NavBar' ;
@@ -20,6 +20,126 @@ interface Genre {
2020 name : string ;
2121}
2222
23+ // Componente para el carrusel horizontal
24+ interface MovieCarouselProps {
25+ title : string ;
26+ movies : Movie [ ] ;
27+ }
28+
29+ const MovieCarousel = ( { title, movies } : MovieCarouselProps ) => {
30+ const carouselRef = useRef < HTMLDivElement > ( null ) ;
31+ const [ isDragging , setIsDragging ] = useState ( false ) ;
32+ const [ startX , setStartX ] = useState ( 0 ) ;
33+ const [ scrollLeft , setScrollLeft ] = useState ( 0 ) ;
34+
35+ // Función para deslizar hacia la izquierda
36+ const scrollLeft1 = ( ) => {
37+ if ( carouselRef . current ) {
38+ carouselRef . current . scrollTo ( {
39+ left : carouselRef . current . scrollLeft - 300 ,
40+ behavior : 'smooth'
41+ } ) ;
42+ }
43+ } ;
44+
45+ // Función para deslizar hacia la derecha
46+ const scrollRight = ( ) => {
47+ if ( carouselRef . current ) {
48+ carouselRef . current . scrollTo ( {
49+ left : carouselRef . current . scrollLeft + 300 ,
50+ behavior : 'smooth'
51+ } ) ;
52+ }
53+ } ;
54+
55+ // Gestionar eventos de mouse/touch para el deslizamiento manual
56+ const handleMouseDown = ( e : React . MouseEvent ) => {
57+ setIsDragging ( true ) ;
58+ setStartX ( e . pageX - ( carouselRef . current ?. offsetLeft || 0 ) ) ;
59+ setScrollLeft ( carouselRef . current ?. scrollLeft || 0 ) ;
60+ } ;
61+
62+ const handleTouchStart = ( e : React . TouchEvent ) => {
63+ setIsDragging ( true ) ;
64+ setStartX ( e . touches [ 0 ] . pageX - ( carouselRef . current ?. offsetLeft || 0 ) ) ;
65+ setScrollLeft ( carouselRef . current ?. scrollLeft || 0 ) ;
66+ } ;
67+
68+ const handleMouseUp = ( ) => {
69+ setIsDragging ( false ) ;
70+ } ;
71+
72+ const handleMouseMove = ( e : React . MouseEvent ) => {
73+ if ( ! isDragging ) return ;
74+ e . preventDefault ( ) ;
75+ if ( carouselRef . current ) {
76+ const x = e . pageX - ( carouselRef . current . offsetLeft || 0 ) ;
77+ const walk = ( x - startX ) * 2 ; // Velocidad de desplazamiento
78+ carouselRef . current . scrollLeft = scrollLeft - walk ;
79+ }
80+ } ;
81+
82+ const handleTouchMove = ( e : React . TouchEvent ) => {
83+ if ( ! isDragging ) return ;
84+ if ( carouselRef . current ) {
85+ const x = e . touches [ 0 ] . pageX - ( carouselRef . current . offsetLeft || 0 ) ;
86+ const walk = ( x - startX ) * 2 ;
87+ carouselRef . current . scrollLeft = scrollLeft - walk ;
88+ }
89+ } ;
90+
91+ return (
92+ < div className = "mb-8" >
93+ < div className = "flex justify-between items-center mb-4" >
94+ < h2 className = "text-2xl font-bold" > { title } </ h2 >
95+ < div className = "flex space-x-2" >
96+ < button
97+ onClick = { scrollLeft1 }
98+ className = "bg-gray-800 hover:bg-gray-700 text-white p-2 rounded-full"
99+ aria-label = "Desplazar a la izquierda"
100+ >
101+ < svg xmlns = "http://www.w3.org/2000/svg" className = "h-5 w-5" fill = "none" viewBox = "0 0 24 24" stroke = "currentColor" >
102+ < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 2 } d = "M15 19l-7-7 7-7" />
103+ </ svg >
104+ </ button >
105+ < button
106+ onClick = { scrollRight }
107+ className = "bg-gray-800 hover:bg-gray-700 text-white p-2 rounded-full"
108+ aria-label = "Desplazar a la derecha"
109+ >
110+ < svg xmlns = "http://www.w3.org/2000/svg" className = "h-5 w-5" fill = "none" viewBox = "0 0 24 24" stroke = "currentColor" >
111+ < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 2 } d = "M9 5l7 7-7 7" />
112+ </ svg >
113+ </ button >
114+ </ div >
115+ </ div >
116+ < div
117+ ref = { carouselRef }
118+ className = "flex overflow-x-auto scrollbar-hide gap-4 pb-4"
119+ style = { { scrollbarWidth : 'none' , msOverflowStyle : 'none' } }
120+ onMouseDown = { handleMouseDown }
121+ onMouseUp = { handleMouseUp }
122+ onMouseLeave = { handleMouseUp }
123+ onMouseMove = { handleMouseMove }
124+ onTouchStart = { handleTouchStart }
125+ onTouchEnd = { handleMouseUp }
126+ onTouchMove = { handleTouchMove }
127+ >
128+ { movies . map ( movie => (
129+ < div key = { movie . id } className = "flex-shrink-0" style = { { width : '200px' } } >
130+ < MovieCard
131+ id = { movie . id }
132+ title = { movie . title }
133+ poster = { `https://image.tmdb.org/t/p/w500${ movie . poster_path } ` }
134+ rating = { movie . vote_average }
135+ />
136+ </ div >
137+ ) ) }
138+ </ div >
139+ </ div >
140+ ) ;
141+ } ;
142+
23143export default function Home ( ) {
24144 const [ trendingMovies , setTrendingMovies ] = useState < Movie [ ] > ( [ ] ) ;
25145 const [ popularMovies , setPopularMovies ] = useState < Movie [ ] > ( [ ] ) ;
@@ -118,20 +238,28 @@ export default function Home() {
118238 </ div >
119239 ) ;
120240
241+ const CarouselSkeleton = ( ) => (
242+ < div className = "mb-8" >
243+ < div className = "h-8 bg-gray-800 rounded w-64 mb-4 animate-pulse" > </ div >
244+ < div className = "flex gap-4 overflow-x-auto pb-4" >
245+ { [ ...Array ( 5 ) ] . map ( ( _ , index ) => (
246+ < div key = { index } className = "flex-shrink-0" style = { { width : '200px' } } >
247+ < MovieCardSkeleton />
248+ </ div >
249+ ) ) }
250+ </ div >
251+ </ div >
252+ ) ;
253+
121254 if ( isLoading ) {
122255 return (
123256 < div className = "flex flex-col min-h-screen bg-gray-900 text-white" >
124257 < NavBar />
125258 < div className = "h-96 w-full bg-gray-800 animate-pulse mb-6" > </ div >
126259 < div className = "container mx-auto px-4" >
127- < div className = "mb-8" >
128- < div className = "h-8 bg-gray-800 rounded w-64 mb-4 animate-pulse" > </ div >
129- < div className = "grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4" >
130- { [ ...Array ( 10 ) ] . map ( ( _ , index ) => (
131- < MovieCardSkeleton key = { index } />
132- ) ) }
133- </ div >
134- </ div >
260+ < CarouselSkeleton />
261+ < CarouselSkeleton />
262+ < CarouselSkeleton />
135263 </ div >
136264 </ div >
137265 ) ;
@@ -196,12 +324,12 @@ export default function Home() {
196324 { /* Género */ }
197325 < div className = "mb-8" >
198326 < h2 className = "text-2xl font-bold mb-4" > Explorar por Género</ h2 >
199- < div className = "flex flex-wrap gap-2 mb-6" >
327+ < div className = "flex flex-wrap gap-2 mb-6 overflow-x-auto pb-2" style = { { scrollbarWidth : 'none' , msOverflowStyle : 'none' } } >
200328 { genres . map ( genre => (
201329 < button
202330 key = { genre . id }
203331 onClick = { ( ) => handleGenreSelect ( genre . id ) }
204- className = { `px-4 py-2 rounded-full text-sm font-medium transition-colors ${
332+ className = { `px-4 py-2 rounded-full text-sm font-medium transition-colors flex-shrink-0 ${
205333 selectedGenre === genre . id
206334 ? 'bg-red-600 text-white'
207335 : 'bg-gray-800 text-gray-300 hover:bg-gray-700'
@@ -213,74 +341,34 @@ export default function Home() {
213341 </ div >
214342
215343 { selectedGenre && genreMovies . length > 0 && (
216- < div className = "mb-8" >
217- < h3 className = "text-xl font-semibold mb-4" >
218- { genres . find ( g => g . id === selectedGenre ) ?. name || 'Género' } Movies
219- </ h3 >
220- < div className = "grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4" >
221- { genreMovies . slice ( 0 , 10 ) . map ( movie => (
222- < MovieCard
223- key = { movie . id }
224- id = { movie . id }
225- title = { movie . title }
226- poster = { `https://image.tmdb.org/t/p/w500${ movie . poster_path } ` }
227- rating = { movie . vote_average }
228- />
229- ) ) }
230- </ div >
231- </ div >
344+ < MovieCarousel
345+ title = { `${ genres . find ( g => g . id === selectedGenre ) ?. name || 'Género' } Movies` }
346+ movies = { genreMovies }
347+ />
232348 ) }
233349 </ div >
234350
235351 { /* Trending Movies */ }
236- < div className = "mb-8" >
237- < h2 className = "text-2xl font-bold mb-4" > Trending Hoy</ h2 >
238- < div className = "grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4" >
239- { trendingMovies . map ( movie => (
240- < MovieCard
241- key = { movie . id }
242- id = { movie . id }
243- title = { movie . title }
244- poster = { `https://image.tmdb.org/t/p/w500${ movie . poster_path } ` }
245- rating = { movie . vote_average }
246- />
247- ) ) }
248- </ div >
249- </ div >
352+ < MovieCarousel title = "Trending Hoy" movies = { trendingMovies } />
250353
251354 { /* Popular Movies */ }
252- < div className = "mb-8" >
253- < h2 className = "text-2xl font-bold mb-4" > Películas Populares</ h2 >
254- < div className = "grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4" >
255- { popularMovies . map ( movie => (
256- < MovieCard
257- key = { movie . id }
258- id = { movie . id }
259- title = { movie . title }
260- poster = { `https://image.tmdb.org/t/p/w500${ movie . poster_path } ` }
261- rating = { movie . vote_average }
262- />
263- ) ) }
264- </ div >
265- </ div >
355+ < MovieCarousel title = "Películas Populares" movies = { popularMovies } />
266356
267357 { /* Top Rated Movies */ }
268- < div className = "mb-8" >
269- < h2 className = "text-2xl font-bold mb-4" > Mejor Valoradas</ h2 >
270- < div className = "grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4" >
271- { topRatedMovies . map ( movie => (
272- < MovieCard
273- key = { movie . id }
274- id = { movie . id }
275- title = { movie . title }
276- poster = { `https://image.tmdb.org/t/p/w500${ movie . poster_path } ` }
277- rating = { movie . vote_average }
278- />
279- ) ) }
280- </ div >
281- </ div >
358+ < MovieCarousel title = "Mejor Valoradas" movies = { topRatedMovies } />
282359 </ div >
283360
361+ { /* Agregar estilos CSS para ocultar la barra de desplazamiento en todos los navegadores */ }
362+ < style jsx global > { `
363+ .scrollbar-hide::-webkit-scrollbar {
364+ display: none;
365+ }
366+ .scrollbar-hide {
367+ -ms-overflow-style: none;
368+ scrollbar-width: none;
369+ }
370+ ` } </ style >
371+
284372 { /* Footer */ }
285373 < footer className = "bg-gray-950 py-8 mt-auto" >
286374 < div className = "container mx-auto px-4 text-center text-gray-400" >
0 commit comments