feat: Implement dynamic art cropping for small cards and refine preview suppression for large cards.
This commit is contained in:
@@ -83,7 +83,7 @@ export const FloatingPreview: React.FC<{ card: DraftCard; x: number; y: number;
|
||||
};
|
||||
|
||||
// --- Hover Wrapper to handle mouse events ---
|
||||
export const CardHoverWrapper: React.FC<{ card: DraftCard; children: React.ReactNode; className?: string }> = ({ card, children, className }) => {
|
||||
export const CardHoverWrapper: React.FC<{ card: DraftCard; children: React.ReactNode; className?: string; preventPreview?: boolean }> = ({ card, children, className, preventPreview }) => {
|
||||
const [isHovering, setIsHovering] = useState(false);
|
||||
const [isLongPressing, setIsLongPressing] = useState(false);
|
||||
const [renderPreview, setRenderPreview] = useState(false);
|
||||
@@ -127,11 +127,12 @@ export const CardHoverWrapper: React.FC<{ card: DraftCard; children: React.React
|
||||
|
||||
const handleMouseEnter = (e: React.MouseEvent) => {
|
||||
if (isMobile) return;
|
||||
if (preventPreview) return;
|
||||
|
||||
// Check if the card is already "big enough" on screen
|
||||
const rect = e.currentTarget.getBoundingClientRect();
|
||||
// Width > 240 && Height > 300 targets large grid items but excludes thin list rows
|
||||
if (rect.width > 240 && rect.height > 300) {
|
||||
// Width > 200 && Height > 270 targets readable cards (Stack/Grid) but excludes list rows
|
||||
if (rect.width > 200 && rect.height > 270) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -99,31 +99,36 @@ export const PackCard: React.FC<PackCardProps> = ({ pack, viewMode, cardWidth =
|
||||
|
||||
{viewMode === 'grid' && (
|
||||
<div className="flex flex-wrap gap-3">
|
||||
{pack.cards.map((card) => (
|
||||
<CardHoverWrapper key={card.id} card={card}>
|
||||
<div style={{ width: cardWidth }} className="relative group bg-slate-900 rounded-lg shrink-0">
|
||||
{/* Visual Card */}
|
||||
<div className={`relative aspect-[2.5/3.5] overflow-hidden rounded-lg shadow-xl border transition-all duration-200 group-hover:ring-2 group-hover:ring-purple-400 group-hover:shadow-purple-500/30 cursor-pointer ${isFoil(card) ? 'border-purple-400 shadow-purple-500/20' : 'border-slate-800'}`}>
|
||||
{isFoil(card) && <FoilOverlay />}
|
||||
{isFoil(card) && <div className="absolute top-1 right-1 z-30 text-[10px] font-bold text-white bg-purple-600/80 px-1 rounded backdrop-blur-sm">FOIL</div>}
|
||||
{pack.cards.map((card) => {
|
||||
const useArtCrop = cardWidth < 170 && !!card.imageArtCrop;
|
||||
const displayImage = useArtCrop ? card.imageArtCrop : card.image;
|
||||
|
||||
{card.image ? (
|
||||
<img src={card.image} alt={card.name} className="w-full h-full object-cover" />
|
||||
) : (
|
||||
<div className="w-full h-full flex items-center justify-center text-xs text-center p-1 text-slate-500 font-bold border-2 border-slate-700 m-1 rounded">
|
||||
{card.name}
|
||||
</div>
|
||||
)}
|
||||
{/* Rarity Stripe */}
|
||||
<div className={`absolute bottom-0 left-0 right-0 h-1.5 ${card.rarity === 'mythic' ? 'bg-gradient-to-r from-orange-500 to-red-600' :
|
||||
card.rarity === 'rare' ? 'bg-gradient-to-r from-yellow-400 to-yellow-600' :
|
||||
card.rarity === 'uncommon' ? 'bg-gradient-to-r from-gray-300 to-gray-500' :
|
||||
'bg-black'
|
||||
}`} />
|
||||
return (
|
||||
<CardHoverWrapper key={card.id} card={card} preventPreview={cardWidth >= 200}>
|
||||
<div style={{ width: cardWidth }} className="relative group bg-slate-900 rounded-lg shrink-0">
|
||||
{/* Visual Card */}
|
||||
<div className={`relative aspect-[2.5/3.5] overflow-hidden rounded-lg shadow-xl border transition-all duration-200 group-hover:ring-2 group-hover:ring-purple-400 group-hover:shadow-purple-500/30 cursor-pointer ${isFoil(card) ? 'border-purple-400 shadow-purple-500/20' : 'border-slate-800'}`}>
|
||||
{isFoil(card) && <FoilOverlay />}
|
||||
{isFoil(card) && <div className="absolute top-1 right-1 z-30 text-[10px] font-bold text-white bg-purple-600/80 px-1 rounded backdrop-blur-sm">FOIL</div>}
|
||||
|
||||
{displayImage ? (
|
||||
<img src={displayImage} alt={card.name} className="w-full h-full object-cover" />
|
||||
) : (
|
||||
<div className="w-full h-full flex items-center justify-center text-xs text-center p-1 text-slate-500 font-bold border-2 border-slate-700 m-1 rounded">
|
||||
{card.name}
|
||||
</div>
|
||||
)}
|
||||
{/* Rarity Stripe */}
|
||||
<div className={`absolute bottom-0 left-0 right-0 h-1.5 ${card.rarity === 'mythic' ? 'bg-gradient-to-r from-orange-500 to-red-600' :
|
||||
card.rarity === 'rare' ? 'bg-gradient-to-r from-yellow-400 to-yellow-600' :
|
||||
card.rarity === 'uncommon' ? 'bg-gradient-to-r from-gray-300 to-gray-500' :
|
||||
'bg-black'
|
||||
}`} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardHoverWrapper>
|
||||
))}
|
||||
</CardHoverWrapper>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -73,9 +73,11 @@ export const StackView: React.FC<StackViewProps> = ({ cards, cardWidth = 150 })
|
||||
// Margin calculation: Negative margin to pull up next cards.
|
||||
// To show a "strip" of say 35px at the top of each card.
|
||||
const isLast = index === catCards.length - 1;
|
||||
const useArtCrop = cardWidth < 170 && !!card.imageArtCrop;
|
||||
const displayImage = useArtCrop ? card.imageArtCrop : card.image;
|
||||
|
||||
return (
|
||||
<CardHoverWrapper key={card.id} card={card} className="relative w-full z-0 hover:z-50 transition-all duration-200">
|
||||
<CardHoverWrapper key={card.id} card={card} className="relative w-full z-0 hover:z-50 transition-all duration-200" preventPreview={cardWidth >= 200}>
|
||||
<div
|
||||
className={`relative w-full rounded-lg bg-slate-800 shadow-md border border-slate-950 overflow-hidden cursor-pointer group`}
|
||||
style={{
|
||||
@@ -85,7 +87,7 @@ export const StackView: React.FC<StackViewProps> = ({ cards, cardWidth = 150 })
|
||||
aspectRatio: '2.5/3.5'
|
||||
}}
|
||||
>
|
||||
<img src={card.image} alt={card.name} className="w-full h-full object-cover" />
|
||||
<img src={displayImage} alt={card.name} className="w-full h-full object-cover" />
|
||||
{/* Optional: Shine effect for foils if visible? */}
|
||||
{card.finish === 'foil' && <FoilOverlay />}
|
||||
</div>
|
||||
|
||||
@@ -9,6 +9,7 @@ export interface DraftCard {
|
||||
layout?: string; // Add layout
|
||||
colors: string[];
|
||||
image: string;
|
||||
imageArtCrop?: string;
|
||||
set: string;
|
||||
setCode: string;
|
||||
setType: string;
|
||||
@@ -107,6 +108,7 @@ export class PackGeneratorService {
|
||||
image: useLocalImages
|
||||
? `${window.location.origin}/cards/images/${cardData.set}/${cardData.id}.jpg`
|
||||
: (cardData.image_uris?.normal || cardData.card_faces?.[0]?.image_uris?.normal || ''),
|
||||
imageArtCrop: cardData.image_uris?.art_crop || cardData.card_faces?.[0]?.image_uris?.art_crop || '',
|
||||
set: cardData.set_name,
|
||||
setCode: cardData.set,
|
||||
setType: setType,
|
||||
|
||||
Reference in New Issue
Block a user