Spaces:
Sleeping
Sleeping
| import { useEffect, useRef, useState } from 'react'; | |
| import { ChevronLeft, ChevronRight, Play, Star, Calendar, Clock } from 'lucide-react'; | |
| export default function MovieSlider({ movies = [] }) { | |
| const containerRef = useRef(null); | |
| const [canScrollLeft, setCanScrollLeft] = useState(false); | |
| const [canScrollRight, setCanScrollRight] = useState(true); | |
| const checkScroll = () => { | |
| const el = containerRef.current; | |
| if (!el) return; | |
| setCanScrollLeft(el.scrollLeft > 0); | |
| setCanScrollRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 10); | |
| }; | |
| useEffect(() => { | |
| checkScroll(); | |
| const el = containerRef.current; | |
| if (el) { | |
| el.addEventListener('scroll', checkScroll); | |
| window.addEventListener('resize', checkScroll); | |
| return () => { | |
| el.removeEventListener('scroll', checkScroll); | |
| window.removeEventListener('resize', checkScroll); | |
| }; | |
| } | |
| }, [movies]); | |
| const scroll = (dir) => { | |
| const el = containerRef.current; | |
| if (!el) return; | |
| el.scrollBy({ left: dir * el.clientWidth * 0.8, behavior: 'smooth' }); | |
| }; | |
| if (!movies?.length) return null; | |
| return ( | |
| <div className="relative mx-auto max-w-7xl px-4 py-4"> | |
| <div className="mb-3 flex items-center gap-2 text-lg font-bold text-white md:text-xl"> | |
| <span className="h-5 w-1 rounded bg-[#e50914]" /> | |
| Đề xuất hot | |
| </div> | |
| <div className="relative"> | |
| <div | |
| ref={containerRef} | |
| className="flex snap-x snap-mandatory gap-4 overflow-x-auto pb-4 scrollbar-hide" | |
| style={{ scrollbarWidth: 'none', msOverflowStyle: 'none' }} | |
| > | |
| {movies.map((movie) => { | |
| const thumb = movie.thumb_url || movie.poster_url || ''; | |
| const year = movie.year || (movie.modified?.time ? new Date(movie.modified.time).getFullYear() : ''); | |
| return ( | |
| <a | |
| key={movie._id || movie.slug} | |
| href={`/phim/${movie.slug}`} | |
| className="group relative w-[260px] flex-shrink-0 snap-start overflow-hidden rounded-xl sm:w-[300px] md:w-[340px]" | |
| > | |
| <div className="relative aspect-[16/10] overflow-hidden"> | |
| <img | |
| src={thumb} | |
| alt={movie.name} | |
| loading="lazy" | |
| className="h-full w-full object-cover transition-transform duration-500 group-hover:scale-105" | |
| onError={(e) => { e.currentTarget.src = 'https://via.placeholder.com/640x360/1a1a1a/888?text=No+Image'; }} | |
| /> | |
| <div className="absolute inset-0 bg-gradient-to-t from-black via-black/30 to-transparent" /> | |
| <div className="absolute inset-0 flex items-center justify-center opacity-0 transition-opacity duration-300 group-hover:opacity-100"> | |
| <div className="flex h-14 w-14 items-center justify-center rounded-full bg-[#e50914]/90 shadow-xl backdrop-blur-sm"> | |
| <Play size={24} className="ml-1 text-white" fill="white" /> | |
| </div> | |
| </div> | |
| <div className="absolute left-3 top-3"> | |
| {movie.quality && ( | |
| <span className="rounded bg-[#e50914] px-2 py-0.5 text-[10px] font-bold uppercase text-white"> | |
| {movie.quality} | |
| </span> | |
| )} | |
| </div> | |
| </div> | |
| <div className="p-3"> | |
| <h3 className="line-clamp-1 text-sm font-bold text-white group-hover:text-[#e50914]"> | |
| {movie.name} | |
| </h3> | |
| <div className="mt-1 flex flex-wrap items-center gap-3 text-[11px] text-gray-400"> | |
| {year && <span className="flex items-center gap-1"><Calendar size={10} /> {year}</span>} | |
| {movie.time && <span className="flex items-center gap-1"><Clock size={10} /> {movie.time}</span>} | |
| {movie.tmdb?.vote_average && ( | |
| <span className="flex items-center gap-1 text-yellow-400"> | |
| <Star size={10} fill="currentColor" /> {movie.tmdb.vote_average.toFixed(1)} | |
| </span> | |
| )} | |
| </div> | |
| </div> | |
| </a> | |
| ); | |
| })} | |
| </div> | |
| {canScrollLeft && ( | |
| <button | |
| onClick={() => scroll(-1)} | |
| className="absolute left-0 top-1/2 -translate-y-1/2 z-10 flex h-9 w-9 items-center justify-center rounded-full bg-black/60 text-white backdrop-blur-sm hover:bg-[#e50914] transition-colors" | |
| > | |
| <ChevronLeft size={20} /> | |
| </button> | |
| )} | |
| {canScrollRight && ( | |
| <button | |
| onClick={() => scroll(1)} | |
| className="absolute right-0 top-1/2 -translate-y-1/2 z-10 flex h-9 w-9 items-center justify-center rounded-full bg-black/60 text-white backdrop-blur-sm hover:bg-[#e50914] transition-colors" | |
| > | |
| <ChevronRight size={20} /> | |
| </button> | |
| )} | |
| </div> | |
| </div> | |
| ); | |
| } | |