feat: Introduce card size slider for unified scaling across grid and stack views, and add smart preview suppression.

This commit is contained in:
2025-12-17 01:20:17 +01:00
parent 58288e5195
commit f9819b324e
8 changed files with 98 additions and 9 deletions

View File

@@ -125,8 +125,17 @@ export const CardHoverWrapper: React.FC<{ card: DraftCard; children: React.React
setCoords({ x: e.clientX, y: e.clientY });
};
const handleMouseEnter = () => {
if (!isMobile) setIsHovering(true);
const handleMouseEnter = (e: React.MouseEvent) => {
if (isMobile) 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) {
return;
}
setIsHovering(true);
};
const handleMouseLeave = () => {

View File

@@ -6,6 +6,7 @@ import { StackView } from './StackView';
interface PackCardProps {
pack: Pack;
viewMode: 'list' | 'grid' | 'stack';
cardWidth?: number;
}
import { CardHoverWrapper, FoilOverlay } from './CardPreview';
@@ -41,7 +42,7 @@ const ListItem: React.FC<{ card: DraftCard }> = ({ card }) => {
);
};
export const PackCard: React.FC<PackCardProps> = ({ pack, viewMode }) => {
export const PackCard: React.FC<PackCardProps> = ({ pack, viewMode, cardWidth = 150 }) => {
const mythics = pack.cards.filter(c => c.rarity === 'mythic');
const rares = pack.cards.filter(c => c.rarity === 'rare');
const uncommons = pack.cards.filter(c => c.rarity === 'uncommon');
@@ -97,10 +98,10 @@ export const PackCard: React.FC<PackCardProps> = ({ pack, viewMode }) => {
)}
{viewMode === 'grid' && (
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-3">
<div className="flex flex-wrap gap-3">
{pack.cards.map((card) => (
<CardHoverWrapper key={card.id} card={card}>
<div className="relative group bg-slate-900 rounded-lg">
<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 />}
@@ -126,7 +127,7 @@ export const PackCard: React.FC<PackCardProps> = ({ pack, viewMode }) => {
</div>
)}
{viewMode === 'stack' && <StackView cards={pack.cards} />}
{viewMode === 'stack' && <StackView cards={pack.cards} cardWidth={cardWidth} />}
</div>
</div>
);

View File

@@ -4,6 +4,7 @@ import { CardHoverWrapper, FoilOverlay } from './CardPreview';
interface StackViewProps {
cards: DraftCard[];
cardWidth?: number;
}
const CATEGORY_ORDER = [
@@ -18,7 +19,7 @@ const CATEGORY_ORDER = [
'Other'
];
export const StackView: React.FC<StackViewProps> = ({ cards }) => {
export const StackView: React.FC<StackViewProps> = ({ cards, cardWidth = 150 }) => {
const categorizedCards = useMemo(() => {
const categories: Record<string, DraftCard[]> = {};
@@ -59,7 +60,7 @@ export const StackView: React.FC<StackViewProps> = ({ cards }) => {
if (catCards.length === 0) return null;
return (
<div key={category} className="flex-shrink-0 w-44 snap-start">
<div key={category} className="flex-shrink-0 snap-start" style={{ width: cardWidth }}>
{/* Header */}
<div className="flex justify-between items-center mb-2 px-1 border-b border-slate-700 pb-1">
<span className="text-xs font-bold text-slate-400 uppercase tracking-wider">{category}</span>

View File

@@ -101,6 +101,11 @@ export const CubeManager: React.FC<CubeManagerProps> = ({ packs, setPacks, onGoT
return saved ? parseInt(saved) : 3;
});
const [cardWidth, setCardWidth] = useState(() => {
const saved = localStorage.getItem('cube_cardWidth');
return saved ? parseInt(saved) : 140;
});
// --- Persistence Effects ---
useEffect(() => localStorage.setItem('cube_inputText', inputText), [inputText]);
useEffect(() => localStorage.setItem('cube_filters', JSON.stringify(filters)), [filters]);
@@ -108,6 +113,7 @@ export const CubeManager: React.FC<CubeManagerProps> = ({ packs, setPacks, onGoT
useEffect(() => localStorage.setItem('cube_sourceMode', sourceMode), [sourceMode]);
useEffect(() => localStorage.setItem('cube_selectedSets', JSON.stringify(selectedSets)), [selectedSets]);
useEffect(() => localStorage.setItem('cube_numBoxes', numBoxes.toString()), [numBoxes]);
useEffect(() => localStorage.setItem('cube_cardWidth', cardWidth.toString()), [cardWidth]);
const fileInputRef = useRef<HTMLInputElement>(null);
@@ -660,6 +666,22 @@ export const CubeManager: React.FC<CubeManagerProps> = ({ packs, setPacks, onGoT
{copySuccess ? <Check className="w-4 h-4 text-emerald-400" /> : <Copy className="w-4 h-4" />}
<span className="hidden sm:inline">{copySuccess ? 'Copied!' : 'Copy'}</span>
</button>
{/* Size Slider */}
<div className="flex items-center gap-2 bg-slate-800 rounded-lg px-2 py-1 border border-slate-700 h-9 mr-2 hidden sm:flex">
<div className="w-3 h-4 rounded border border-slate-500 bg-slate-700" title="Small Cards" />
<input
type="range"
min="100"
max="300"
step="10"
value={cardWidth}
onChange={(e) => setCardWidth(parseInt(e.target.value))}
className="w-24 accent-purple-500 cursor-pointer h-1.5 bg-slate-600 rounded-lg appearance-none"
title={`Card Size: ${cardWidth}px`}
/>
<div className="w-4 h-6 rounded border border-slate-500 bg-slate-700" title="Large Cards" />
</div>
</>
)}
@@ -679,7 +701,7 @@ export const CubeManager: React.FC<CubeManagerProps> = ({ packs, setPacks, onGoT
) : (
<div className="grid grid-cols-1 gap-6 pb-20">
{packs.map((pack) => (
<PackCard key={pack.id} pack={pack} viewMode={viewMode} />
<PackCard key={pack.id} pack={pack} viewMode={viewMode} cardWidth={cardWidth} />
))}
</div>
)}