Spaces:
Sleeping
Sleeping
File size: 5,178 Bytes
4bea261 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | 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>
);
}
|