Add interactive carousel to display featured articles
Implement a rotating carousel component for featured articles, allowing users to cycle through up to nine articles in a 3-page, 3-article per page layout, with navigation dots and auto-play functionality. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 517dfa7b-26ac-463d-a6e1-a58c6df97188 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: 5611beb8-5cb1-4ae9-98e5-29231d57fcf0 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/f209e72a-0939-48fa-84fc-57854de71967/517dfa7b-26ac-463d-a6e1-a58c6df97188/0ZGabQy Replit-Helium-Checkpoint-Created: true
This commit is contained in:
parent
0f4a569bf3
commit
81892e4360
@ -281,11 +281,27 @@ function FeaturedHeroCard({ article, focalPoints }: { article: Article; focalPoi
|
||||
}
|
||||
|
||||
function FeaturedCarousel({ articles, popular, galleryImages, focalPoints }: { articles: Article[]; popular?: Article[]; galleryImages?: GalleryImage[]; focalPoints?: Record<string, { x: number; y: number }> }) {
|
||||
const hero = articles[0];
|
||||
const bottom = articles.slice(1, 3);
|
||||
const pageSize = 3;
|
||||
const totalPages = Math.max(1, Math.ceil(Math.min(articles.length, 9) / pageSize));
|
||||
const [page, setPage] = useState(0);
|
||||
const [paused, setPaused] = useState(false);
|
||||
|
||||
const next = useCallback(() => {
|
||||
setPage((p) => (p + 1) % totalPages);
|
||||
}, [totalPages]);
|
||||
|
||||
useEffect(() => {
|
||||
if (paused || totalPages <= 1) return;
|
||||
const timer = setInterval(next, 8000);
|
||||
return () => clearInterval(timer);
|
||||
}, [paused, next, totalPages]);
|
||||
|
||||
const start = page * pageSize;
|
||||
const hero = articles[start];
|
||||
const bottom = articles.slice(start + 1, start + 3);
|
||||
|
||||
return (
|
||||
<section data-testid="featured-carousel">
|
||||
<section data-testid="featured-carousel" onMouseEnter={() => setPaused(true)} onMouseLeave={() => setPaused(false)}>
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
|
||||
<div className="lg:col-span-2">
|
||||
<div className="grid grid-cols-1 gap-4">
|
||||
@ -301,6 +317,13 @@ function FeaturedCarousel({ articles, popular, galleryImages, focalPoints }: { a
|
||||
{popular && popular.length > 0 && <TopStoriesList articles={popular} />}
|
||||
</div>
|
||||
</div>
|
||||
{totalPages > 1 && (
|
||||
<div className="flex justify-center gap-2 mt-3" data-testid="carousel-dots">
|
||||
{Array.from({ length: totalPages }).map((_, i) => (
|
||||
<button key={i} onClick={() => setPage(i)} className={`w-2 h-2 rounded-full transition-all duration-300 ${i === page ? "bg-primary w-5" : "bg-muted-foreground/30 hover:bg-muted-foreground/50"}`} data-testid={`button-carousel-dot-${i}`} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@ -346,10 +369,10 @@ export default function Home() {
|
||||
);
|
||||
}
|
||||
|
||||
const row2 = articles.slice(3, 5);
|
||||
const row3 = articles.slice(5, 7);
|
||||
const remaining = articles.slice(7);
|
||||
const topStoriesCount = Math.min(12, articles.length);
|
||||
const carouselEnd = Math.min(9, articles.length);
|
||||
const row2 = articles.slice(carouselEnd, carouselEnd + 2);
|
||||
const row3 = articles.slice(carouselEnd + 2, carouselEnd + 4);
|
||||
const remaining = articles.slice(carouselEnd + 4);
|
||||
const rows: Article[][] = [];
|
||||
for (let i = 0; i < remaining.length; i += 4) {
|
||||
rows.push(remaining.slice(i, i + 4));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user