This commit is contained in:
lubiana 2025-07-12 19:26:44 +02:00
parent e3d1512145
commit 1b67c213a4
Signed by: lubiana
SSH key fingerprint: SHA256:vW1EA0fRR3Fw+dD/sM0K+x3Il2gSry6YRYHqOeQwrfk
2 changed files with 44 additions and 15 deletions

View file

@ -1,4 +1,4 @@
import React, { useState, useCallback, useMemo } from 'react';
import React, { useState, useCallback, useMemo, useRef, useEffect } from 'react';
import Card from './components/Card';
import TierCard from './components/TierCard';
import HistoryList from './components/HistoryList';
@ -55,6 +55,27 @@ function App() {
const [upgrades, setUpgrades] = useState<Upgrades>({ evenDouble: false, oddDouble: false, allTripple: false, gubbleDouble: false, addFourthNumber: false });
const [upgradeBought, setUpgradeBought] = useState(false);
// Ref for scroll container
const tierRowRef = useRef<HTMLDivElement>(null);
// Ref for the highest unlocked card
const highestUnlockedRef = useRef<HTMLDivElement>(null);
// Find the index of the highest unlocked tier
const highestUnlockedIndex = useMemo(() => {
let maxIdx = -1;
TIERS.forEach((tier, idx) => {
if (unlockedTiers.includes(tier.name)) maxIdx = idx;
});
return maxIdx;
}, [unlockedTiers]);
// Scroll the highest unlocked card into view when unlockedTiers changes
useEffect(() => {
if (highestUnlockedRef.current && tierRowRef.current) {
highestUnlockedRef.current.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' });
}
}, [highestUnlockedIndex]);
// --- Tier Unlock Logic ---
const handleUnlockTier = useCallback((tier: Tier) => {
setMoney(m => {
@ -121,20 +142,20 @@ function App() {
// --- Memoized winnings for current card ---
const currentWinnings = useMemo(() => card ? card.fields.reduce((sum, f) => sum + (f.won || 0), 0) : 0, [card]);
const formatter = Intl.NumberFormat(
'en',
{ 'notation': 'compact' }
)
// --- UI ---
return (
<div className="min-h-screen bg-gray-100 flex flex-col items-center ">
<h1 className="text-3xl font-bold text-blue-600 mb-2">Scratch Card Game</h1>
<div className="mb-4 text-lg">Money: <span className="font-mono font-bold">${money}</span></div>
<div className="mb-4 text-lg">Gubble Points: <span className="font-mono font-bold">{gubblePoints}</span></div>
<button
className="mb-4 px-4 py-2 bg-purple-600 hover:bg-purple-700 text-white font-bold rounded disabled:opacity-50"
disabled={gubblePoints < 1}
onClick={() => setIsStoreOpen(true)}
>
Open Gubble Point Store
</button>
<div className="flex flex-row items-center gap-6 mb-6 mt-2 w-full max-w-4xl justify-center">
<h1 className="text-3xl font-bold text-blue-600">Scratch Card Game</h1>
<div className="text-lg">Money: <span className="font-mono font-bold">${formatter.format(money)}</span></div>
<div className="text-lg">Gubble Points: <span className="font-mono font-bold">{formatter.format(gubblePoints)}</span></div>
<button className="px-4 py-2 bg-purple-600 hover:bg-purple-700 text-white font-bold rounded disabled:opacity-50" disabled={gubblePoints < 1} onClick={() => setIsStoreOpen(true)}>Open Gubble Point Store</button>
</div>
<GubbleStore
isOpen={isStoreOpen}
onClose={handleStoreClose}
@ -142,9 +163,13 @@ function App() {
upgrades={upgrades}
onBuyUpgrade={handleBuyUpgrade}
/>
<div className="mb-8 w-full max-w-5xl overflow-x-auto whitespace-nowrap flex flex-row items-center space-x-4 scrollbar-thin scrollbar-thumb-gray-400 scrollbar-track-gray-100">
{TIERS.map((tier) => (
<div key={tier.name} className="inline-block min-w-[8rem]">
<div ref={tierRowRef} className="mb-8 w-full max-w-5xl overflow-x-auto whitespace-nowrap flex flex-row items-center space-x-4 scrollbar-thin scrollbar-thumb-gray-400 scrollbar-track-gray-100">
{TIERS.map((tier, idx) => (
<div
key={tier.name}
className="inline-block min-w-[8rem]"
ref={idx === highestUnlockedIndex ? highestUnlockedRef : undefined}
>
<TierCard
tier={tier}
unlocked={unlockedTiers.includes(tier.name)}

View file

@ -19,6 +19,10 @@ type CardProps = {
};
const Card: React.FC<CardProps> = ({ card, setCard, setMoney, setGubblePoints, upgrades }) => {
const formatter = Intl.NumberFormat(
'en',
{ 'notation': 'compact' }
)
return (
<div className="bg-white rounded-lg shadow p-6 flex flex-col items-center mb-6 w-full max-w-md">
<div className="mb-2 text-lg font-semibold text-gray-700">{card.tier?.name} Scratch Card</div>
@ -70,7 +74,7 @@ const Card: React.FC<CardProps> = ({ card, setCard, setMoney, setGubblePoints, u
}}
>
{field.scratched ? (
<span>{field.value}{field.won ? <span className="block text-xs font-normal text-green-700">+${field.won}</span> : ''}</span>
<span>{field.value}{field.won ? <span className="block text-xs font-normal text-green-700">+${formatter.format(field.won)}</span> : ''}</span>
) : (
<span className="text-3xl select-none">?</span>
)}