This commit is contained in:
ct
2025-07-15 02:38:23 +08:00
parent 3a2818473c
commit d57c75d5d7
4 changed files with 135 additions and 58 deletions

View File

@@ -5,7 +5,7 @@ const GridSkeleton = ({ itemCount = 6 }) => {
<div className="grid grid-cols-2 gap-2 p-2">
{Array.from({ length: itemCount }, (_, index) => (
<div key={index} className="relative">
<div className="aspect-[9/16] w-full rounded-lg bg-gradient-to-r from-gray-200 via-white to-gray-200 bg-[length:200%_100%] animate-shimmer" />
<div className="aspect-[9/16] w-full rounded-lg bg-gradient-to-r from-muted via-background to-muted bg-[length:200%_100%] animate-shimmer" />
</div>
))}
</div>

View File

@@ -0,0 +1,7 @@
const MediaItemSkeleton = () => {
return (
<div className="aspect-[9/16] w-full rounded-lg bg-gradient-to-r from-muted via-background to-muted bg-[length:200%_100%] animate-shimmer" />
);
};
export { MediaItemSkeleton };

View File

@@ -0,0 +1,48 @@
import { useState } from 'react';
import { MediaItemSkeleton } from './media-item-skeleton';
const MediaItem = ({ src, alt, onClick, isSelected, className = "" }) => {
const [isLoading, setIsLoading] = useState(true);
const [hasError, setHasError] = useState(false);
const handleImageLoad = () => {
setIsLoading(false);
};
const handleImageError = () => {
setIsLoading(false);
setHasError(true);
};
return (
<div className={`relative aspect-[9/16] w-full overflow-hidden rounded-lg bg-muted ${className}`}>
{isLoading && (
<div className="absolute inset-0 z-10">
<MediaItemSkeleton />
</div>
)}
{hasError ? (
<div className="flex h-full w-full items-center justify-center bg-muted text-muted-foreground text-xs p-2 text-center">
Failed to load
</div>
) : (
<img
src={src}
alt={alt}
className={`h-full w-full object-cover cursor-pointer transition-opacity duration-300 ${isLoading ? 'opacity-0' : 'opacity-100'}`}
onLoad={handleImageLoad}
onError={handleImageError}
onClick={onClick}
/>
)}
{/* Selection border overlay */}
{isSelected && !isLoading && (
<div className="absolute inset-0 rounded-lg ring-2 ring-blue-500 pointer-events-none" />
)}
</div>
);
};
export { MediaItem };