Mobilni mozaik: prvi blok se vrti po 3 novice (9 najnovejsih, 8 s)
This commit is contained in:
parent
6f4cfa8ee9
commit
10af4563a5
@ -183,21 +183,75 @@ function MosaicHeading({ children }: { children: React.ReactNode }) {
|
||||
}
|
||||
|
||||
function MobileMosaic({ latest, popular, focalPoints }: { latest: Article[]; popular?: Article[]; focalPoints?: Record<string, { x: number; y: number }> }) {
|
||||
const l = (latest || []).slice(0, 3);
|
||||
const pageSize = 3;
|
||||
const pool = (latest || []).slice(0, 9);
|
||||
const totalPages = Math.max(1, Math.ceil(pool.length / pageSize));
|
||||
|
||||
const [page, setPage] = useState(0);
|
||||
const [displayPage, setDisplayPage] = useState(0);
|
||||
const [fading, setFading] = useState(false);
|
||||
const [paused, setPaused] = useState(false);
|
||||
|
||||
const changePage = useCallback((newPage: number) => {
|
||||
if (newPage === displayPage) return;
|
||||
setFading(true);
|
||||
setTimeout(() => {
|
||||
setDisplayPage(newPage);
|
||||
setFading(false);
|
||||
}, 300);
|
||||
}, [displayPage]);
|
||||
|
||||
useEffect(() => {
|
||||
changePage(page);
|
||||
}, [page]);
|
||||
|
||||
useEffect(() => {
|
||||
if (paused || totalPages <= 1) return;
|
||||
const timer = setInterval(() => setPage((p) => (p + 1) % totalPages), 8000);
|
||||
return () => clearInterval(timer);
|
||||
}, [paused, totalPages]);
|
||||
|
||||
const l = pool.slice(displayPage * pageSize, displayPage * pageSize + pageSize);
|
||||
if (l.length === 0) return null;
|
||||
const p = (popular || []).filter((a) => !l.some((x) => x.id === a.id)).slice(0, 3);
|
||||
|
||||
const usedIds = new Set(pool.map((a) => a.id));
|
||||
const p = (popular || []).filter((a) => !usedIds.has(a.id)).slice(0, 3);
|
||||
|
||||
const nextStart = ((displayPage + 1) % totalPages) * pageSize;
|
||||
const preload = pool.slice(nextStart, nextStart + pageSize);
|
||||
|
||||
return (
|
||||
<div className="md:hidden space-y-6" data-testid="section-mobile-mosaic">
|
||||
<section>
|
||||
<section onTouchStart={() => setPaused(true)}>
|
||||
<MosaicHeading>Neueste Artikel</MosaicHeading>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
{l.slice(0, 2).map((a) => (
|
||||
<MosaicTile key={a.id} article={a} focalPoints={focalPoints} />
|
||||
))}
|
||||
{preload.length > 0 && (
|
||||
<div className="hidden" aria-hidden="true">
|
||||
{preload.map((a) => a.coverImage && <img key={`pre-${a.id}`} src={thumbUrl(a.coverImage)} alt="" />)}
|
||||
</div>
|
||||
)}
|
||||
<div className="transition-opacity duration-300" style={{ opacity: fading ? 0 : 1 }}>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
{l.slice(0, 2).map((a) => (
|
||||
<MosaicTile key={a.id} article={a} focalPoints={focalPoints} />
|
||||
))}
|
||||
</div>
|
||||
{l[2] && (
|
||||
<div className="mt-2">
|
||||
<MosaicTile article={l[2]} focalPoints={focalPoints} tall />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{l[2] && (
|
||||
<div className="mt-2">
|
||||
<MosaicTile article={l[2]} focalPoints={focalPoints} tall />
|
||||
{totalPages > 1 && (
|
||||
<div className="flex justify-center gap-2 mt-3" data-testid="mosaic-dots">
|
||||
{Array.from({ length: totalPages }).map((_, i) => (
|
||||
<button
|
||||
key={i}
|
||||
onClick={() => setPage(i)}
|
||||
className={`h-2 rounded-full transition-all duration-300 ${i === page ? "bg-primary w-5" : "bg-muted-foreground/30 w-2"}`}
|
||||
data-testid={`button-mosaic-dot-${i}`}
|
||||
aria-label={`Seite ${i + 1}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user