mirror of
https://github.com/KuekHaoYang/KVideo.git
synced 2026-08-12 23:33:43 +08:00
92 lines
2.3 KiB
TypeScript
92 lines
2.3 KiB
TypeScript
/**
|
|
* MovieGrid - Grid layout for movie cards with infinite scroll
|
|
* Handles movie display and loading states
|
|
*/
|
|
|
|
import { MovieCard } from './MovieCard';
|
|
import { Icons } from '@/components/ui/Icon';
|
|
|
|
interface DoubanMovie {
|
|
id: string;
|
|
title: string;
|
|
cover: string;
|
|
rate: string;
|
|
url: string;
|
|
}
|
|
|
|
interface MovieGridProps {
|
|
movies: DoubanMovie[];
|
|
loading: boolean;
|
|
hasMore: boolean;
|
|
onMovieClick: (movie: DoubanMovie) => void;
|
|
prefetchRef: React.RefObject<HTMLDivElement | null>;
|
|
loadMoreRef: React.RefObject<HTMLDivElement | null>;
|
|
}
|
|
|
|
export function MovieGrid({
|
|
movies,
|
|
loading,
|
|
hasMore,
|
|
onMovieClick,
|
|
prefetchRef,
|
|
loadMoreRef
|
|
}: MovieGridProps) {
|
|
if (movies.length === 0 && !loading) {
|
|
return <MovieGridEmpty />;
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4 md:gap-6">
|
|
{movies.map((movie) => (
|
|
<MovieCard
|
|
key={movie.id}
|
|
movie={movie}
|
|
onMovieClick={onMovieClick}
|
|
/>
|
|
))}
|
|
</div>
|
|
|
|
{/* Prefetch Trigger - Earlier */}
|
|
{hasMore && !loading && <div ref={prefetchRef} className="h-1" />}
|
|
|
|
{/* Loading Indicator */}
|
|
{loading && <MovieGridLoading />}
|
|
|
|
{/* Intersection Observer Target */}
|
|
{hasMore && !loading && <div ref={loadMoreRef} className="h-20" />}
|
|
|
|
{/* No More Content */}
|
|
{!hasMore && movies.length > 0 && <MovieGridNoMore />}
|
|
</>
|
|
);
|
|
}
|
|
|
|
function MovieGridLoading() {
|
|
return (
|
|
<div className="flex justify-center py-12">
|
|
<div className="flex flex-col items-center gap-3">
|
|
<div className="animate-spin rounded-full h-12 w-12 border-4 border-[var(--accent-color)] border-t-transparent"></div>
|
|
<p className="text-sm text-[var(--text-color-secondary)]">加载中...</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function MovieGridNoMore() {
|
|
return (
|
|
<div className="text-center py-12">
|
|
<p className="text-[var(--text-color-secondary)]">没有更多内容了</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function MovieGridEmpty() {
|
|
return (
|
|
<div className="text-center py-20">
|
|
<Icons.Film size={64} className="text-[var(--text-color-secondary)] mx-auto mb-4" />
|
|
<p className="text-[var(--text-color-secondary)]">暂无内容</p>
|
|
</div>
|
|
);
|
|
}
|