smarty
This commit is contained in:
parent
e3d1512145
commit
1b67c213a4
2 changed files with 44 additions and 15 deletions
53
src/App.tsx
53
src/App.tsx
|
@ -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)}
|
||||
|
|
|
@ -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>
|
||||
)}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue