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:
sebastjanartic 2026-02-28 21:45:14 +00:00
parent 0f4a569bf3
commit 81892e4360

View File

@ -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));