Spaces:
Running
Running
import React, { useState, useRef, useEffect } from 'react'; | |
import ContentCard from './ContentCard'; | |
import { ChevronLeft, ChevronRight } from 'lucide-react'; | |
interface ContentItem { | |
type: 'movie' | 'tvshow'; | |
title: string; | |
image: string; | |
description?: string; | |
genre?: string[]; | |
year?: number | string; | |
} | |
interface ContentRowProps { | |
title: string; | |
items: ContentItem[]; | |
} | |
const ContentRow: React.FC<ContentRowProps> = ({ title, items }) => { | |
const rowRef = useRef<HTMLDivElement>(null); | |
const [showLeftButton, setShowLeftButton] = useState(false); | |
const [showRightButton, setShowRightButton] = useState(true); | |
// Handle scroll events to show/hide buttons | |
const handleScroll = () => { | |
if (rowRef.current) { | |
const { scrollLeft, scrollWidth, clientWidth } = rowRef.current; | |
setShowLeftButton(scrollLeft > 20); | |
setShowRightButton(scrollLeft < scrollWidth - clientWidth - 20); | |
} | |
}; | |
// Set up scroll listeners and initial state | |
useEffect(() => { | |
handleScroll(); | |
window.addEventListener('resize', handleScroll); | |
return () => window.removeEventListener('resize', handleScroll); | |
}, [items]); | |
const scroll = (direction: 'left' | 'right') => { | |
if (rowRef.current) { | |
const card = rowRef.current.querySelector('.card-hover'); | |
const cardWidth = card ? card.clientWidth + 16 : 280; // Card width + margin | |
const scrollAmount = direction === 'left' ? -cardWidth * 3 : cardWidth * 3; | |
rowRef.current.scrollBy({ left: scrollAmount, behavior: 'smooth' }); | |
} | |
}; | |
// Don't render the row if there are no items | |
if (items.length === 0) { | |
return null; | |
} | |
return ( | |
<div className="content-row mb-8"> | |
<h2 className="text-xl font-bold px-4 md:px-8 mb-4">{title}</h2> | |
<div className="relative group"> | |
{/* Left scroll button */} | |
<button | |
onClick={() => scroll('left')} | |
className={`absolute left-2 top-1/2 transform -translate-y-1/2 z-30 | |
bg-black/40 hover:bg-black/60 rounded-full p-2 transition-all duration-200 | |
${showLeftButton ? 'opacity-100' : 'opacity-0 pointer-events-none'}`} | |
aria-label="Scroll left" | |
> | |
<ChevronLeft className="w-6 h-6 text-white" /> | |
</button> | |
{/* Content row */} | |
<div | |
ref={rowRef} | |
onScroll={handleScroll} | |
className="flex gap-4 overflow-x-auto py-4 px-4 md:px-8 scrollbar-none scroll-smooth" | |
style={{ scrollbarWidth: 'none' }} | |
> | |
{items.map((item, index) => ( | |
<div key={`${item.title}-${index}`} className="flex-shrink-0"> | |
<ContentCard | |
type={item.type} | |
title={item.title} | |
image={item.image} | |
description={item.description} | |
genre={item.genre} | |
year={item.year} | |
/> | |
</div> | |
))} | |
</div> | |
{/* Right scroll button */} | |
<button | |
onClick={() => scroll('right')} | |
className={`absolute right-2 top-1/2 transform -translate-y-1/2 z-30 | |
bg-black/40 hover:bg-black/60 rounded-full p-2 transition-all duration-200 | |
${showRightButton ? 'opacity-100' : 'opacity-0 pointer-events-none'}`} | |
aria-label="Scroll right" | |
> | |
<ChevronRight className="w-6 h-6 text-white" /> | |
</button> | |
</div> | |
</div> | |
); | |
}; | |
export default ContentRow; | |