deckhearth/src/components/CardImageDisplay.tsx

175 lines
5.6 KiB
TypeScript
Raw Normal View History

import React, { useState } from 'react';
import { use3DTilt } from '../hooks/use3DTilt';
interface CardImageDisplayProps {
card: {
id: string;
name: string;
game: string;
stock_image_url?: string;
image_url?: string;
rarity: string;
};
userImages?: string[];
size?: 'small' | 'medium' | 'large';
showUserPhotos?: boolean;
className?: string;
}
const CardImageDisplay: React.FC<CardImageDisplayProps> = ({
card,
userImages = [],
size = 'medium',
showUserPhotos: initialShowUserPhotos = false,
className = ''
}) => {
const [imageError, setImageError] = useState(false);
const [currentUserImageIndex, setCurrentUserImageIndex] = useState(0);
const [showUserPhotos, setShowUserPhotos] = useState(initialShowUserPhotos);
// 3D Tilt effect (no scaling - main container handles expansion)
const { ref: tiltRef, tiltStyles } = use3DTilt({
maxTilt: size === 'large' ? 15 : 10,
scale: 1.0, // No image scaling - main container expands
speed: 400,
easing: 'cubic-bezier(0.23, 1, 0.320, 1)'
});
// Determine which image to show
const getDisplayImage = () => {
if (showUserPhotos && userImages.length > 0) {
return userImages[currentUserImageIndex];
}
return card.stock_image_url || card.image_url;
};
// Determine if card should have foil effects
const isFoilCard = () => {
const foilRarities = ['super rare', 'legendary', 'mythic'];
return foilRarities.includes(card.rarity.toLowerCase());
};
// Get rarity border class
const getRarityBorderClass = () => {
const rarity = card.rarity.toLowerCase().replace(/\s+/g, '');
return `rarity-border-${rarity}`;
};
// Size classes
const sizeClasses = {
small: 'w-16 h-22',
medium: 'w-24 h-32',
large: 'w-48 h-64'
};
// Placeholder based on game
const getPlaceholder = () => {
const gameColors = {
MTG: 'from-orange-400 to-red-500',
POKEMON: 'from-yellow-400 to-red-500',
LORCANA: 'from-purple-400 to-pink-500'
};
const gradientClass = gameColors[card.game as keyof typeof gameColors] || 'from-gray-400 to-gray-600';
const rarityBorderClass = getRarityBorderClass();
const foilClass = isFoilCard() ? 'foil-rainbow' : '';
return (
<div
ref={tiltRef}
style={tiltStyles}
className={`${sizeClasses[size]} bg-gradient-to-br ${gradientClass} rounded-lg flex flex-col items-center justify-center text-white shadow-md card-transition card-depth ${rarityBorderClass} ${foilClass} ${className}`}
>
<div className="text-lg font-bold mb-1">🃏</div>
<div className="text-xs text-center px-2 leading-tight">
{card.name.split(' ').slice(0, 2).join(' ')}
</div>
<div className="text-xs opacity-75 mt-1">
{card.game}
</div>
</div>
);
};
const displayImage = getDisplayImage();
if (!displayImage || imageError) {
return getPlaceholder();
}
const rarityBorderClass = getRarityBorderClass();
const foilClass = isFoilCard() ? 'foil-card' : '';
const legendaryHoloClass = card.rarity.toLowerCase() === 'legendary' || card.rarity.toLowerCase() === 'mythic' ? 'holographic' : '';
return (
<div className={`relative ${className}`}>
<div
ref={tiltRef}
style={tiltStyles}
className={`card-transition card-depth ${rarityBorderClass} ${foilClass} ${legendaryHoloClass} rounded-lg`}
>
<img
src={displayImage}
alt={card.name}
className={`${sizeClasses[size]} object-cover rounded-lg`}
onError={() => setImageError(true)}
/>
</div>
{/* User photo indicator */}
{showUserPhotos && userImages.length > 0 && (
<div className="absolute top-1 left-1 bg-blue-500 text-white text-xs px-1 py-0.5 rounded">
📸 {currentUserImageIndex + 1}/{userImages.length}
</div>
)}
{/* Stock/User toggle */}
{userImages.length > 0 && (
<div className="absolute bottom-1 right-1">
<button
onClick={() => setShowUserPhotos(!showUserPhotos)}
className="bg-black bg-opacity-50 text-white text-xs px-1 py-0.5 rounded hover:bg-opacity-75"
title={showUserPhotos ? 'Show stock image' : 'Show your photos'}
>
{showUserPhotos ? '📋' : '📸'}
</button>
</div>
)}
{/* User photo navigation */}
{showUserPhotos && userImages.length > 1 && (
<div className="absolute bottom-1 left-1 flex gap-1">
<button
onClick={() => setCurrentUserImageIndex((prev) => Math.max(0, prev - 1))}
disabled={currentUserImageIndex === 0}
className="bg-black bg-opacity-50 text-white text-xs px-1 py-0.5 rounded disabled:opacity-50"
>
</button>
<button
onClick={() => setCurrentUserImageIndex((prev) => Math.min(userImages.length - 1, prev + 1))}
disabled={currentUserImageIndex === userImages.length - 1}
className="bg-black bg-opacity-50 text-white text-xs px-1 py-0.5 rounded disabled:opacity-50"
>
</button>
</div>
)}
{/* Image type indicator */}
<div className="absolute top-1 right-1">
{showUserPhotos ? (
<span className="bg-blue-500 text-white text-xs px-1 py-0.5 rounded" title="Your card photo">
👤
</span>
) : (
<span className="bg-green-500 text-white text-xs px-1 py-0.5 rounded" title="Stock image">
</span>
)}
</div>
</div>
);
};
export default CardImageDisplay;