ImageCard.tsx
3.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import React, { useState } from 'react';
import { ImageItem } from '../types';
import { Download, Heart } from 'lucide-react';
interface ImageCardProps {
image: ImageItem;
onClick: (image: ImageItem) => void;
onLike: (image: ImageItem) => void;
currentUser?: string;
}
const ImageCard: React.FC<ImageCardProps> = ({ image, onClick, onLike, currentUser }) => {
const [isLoaded, setIsLoaded] = useState(false);
const [isHovered, setIsHovered] = useState(false);
const handleLike = (e: React.MouseEvent) => {
e.stopPropagation();
onLike(image);
};
return (
<div
className="group relative mb-4 break-inside-avoid rounded-2xl overflow-hidden bg-gray-200 cursor-zoom-in shadow-sm hover:shadow-xl transition-all duration-300"
onClick={() => onClick(image)}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
{/* Placeholder / Skeleton */}
{!isLoaded && (
<div className="absolute inset-0 bg-gray-200 animate-pulse min-h-[200px]" />
)}
{/* Main Image */}
<img
src={image.url}
alt={image.prompt}
className={`w-full h-auto object-cover transition-transform duration-700 ease-in-out group-hover:scale-105 ${isLoaded ? 'opacity-100' : 'opacity-0'}`}
onLoad={() => setIsLoaded(true)}
loading="lazy"
/>
{/* Hover Overlay */}
<div className="absolute inset-0 bg-gradient-to-t from-black/90 via-black/20 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex flex-col justify-end p-4 md:p-5">
{/* Top Right: Download */}
<div className="absolute top-4 right-4 translate-y-[-10px] opacity-0 group-hover:translate-y-0 group-hover:opacity-100 transition-all duration-300 delay-75">
<button
className="p-2 bg-white/20 backdrop-blur-md hover:bg-white/40 rounded-full text-white transition-colors"
title="Download"
onClick={(e) => {
e.stopPropagation();
const link = document.createElement('a');
link.href = image.url;
link.download = `z-image-${image.id}.png`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}}
>
<Download size={16} />
</button>
</div>
{/* Content Info */}
<div className="text-white transform translate-y-4 group-hover:translate-y-0 transition-transform duration-300">
<p className="font-medium text-sm line-clamp-2 mb-2 text-shadow-sm">
{image.prompt}
</p>
<div className="flex justify-between items-end">
{/* Author Info */}
<div className="text-xs text-gray-300 font-mono flex flex-col gap-1">
<span className="opacity-75">ID: {image.authorId || 'UNKNOWN'}</span>
<span className="bg-white/10 px-1.5 py-0.5 rounded w-fit">{image.width}x{image.height}</span>
</div>
{/* Like Button */}
<button
onClick={handleLike}
className={`flex items-center gap-1.5 px-3 py-1.5 rounded-full backdrop-blur-md transition-all duration-200 ${image.isLikedByCurrentUser ? 'bg-red-500/80 text-white' : 'bg-white/20 text-white hover:bg-white/30'}`}
title={currentUser ? "Like this image" : "Login to like"}
>
<Heart size={14} fill={image.isLikedByCurrentUser ? "currentColor" : "none"} />
<span className="text-xs font-bold">{image.likes}</span>
</button>
</div>
</div>
</div>
</div>
);
};
export default ImageCard;