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 Card from './components/Card';
|
||||||
import TierCard from './components/TierCard';
|
import TierCard from './components/TierCard';
|
||||||
import HistoryList from './components/HistoryList';
|
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 [upgrades, setUpgrades] = useState<Upgrades>({ evenDouble: false, oddDouble: false, allTripple: false, gubbleDouble: false, addFourthNumber: false });
|
||||||
const [upgradeBought, setUpgradeBought] = useState(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 ---
|
// --- Tier Unlock Logic ---
|
||||||
const handleUnlockTier = useCallback((tier: Tier) => {
|
const handleUnlockTier = useCallback((tier: Tier) => {
|
||||||
setMoney(m => {
|
setMoney(m => {
|
||||||
|
@ -121,20 +142,20 @@ function App() {
|
||||||
|
|
||||||
// --- Memoized winnings for current card ---
|
// --- Memoized winnings for current card ---
|
||||||
const currentWinnings = useMemo(() => card ? card.fields.reduce((sum, f) => sum + (f.won || 0), 0) : 0, [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 ---
|
// --- UI ---
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-100 flex flex-col items-center ">
|
<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="flex flex-row items-center gap-6 mb-6 mt-2 w-full max-w-4xl justify-center">
|
||||||
<div className="mb-4 text-lg">Money: <span className="font-mono font-bold">${money}</span></div>
|
<h1 className="text-3xl font-bold text-blue-600">Scratch Card Game</h1>
|
||||||
<div className="mb-4 text-lg">Gubble Points: <span className="font-mono font-bold">{gubblePoints}</span></div>
|
<div className="text-lg">Money: <span className="font-mono font-bold">${formatter.format(money)}</span></div>
|
||||||
<button
|
<div className="text-lg">Gubble Points: <span className="font-mono font-bold">{formatter.format(gubblePoints)}</span></div>
|
||||||
className="mb-4 px-4 py-2 bg-purple-600 hover:bg-purple-700 text-white font-bold rounded disabled:opacity-50"
|
<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>
|
||||||
disabled={gubblePoints < 1}
|
</div>
|
||||||
onClick={() => setIsStoreOpen(true)}
|
|
||||||
>
|
|
||||||
Open Gubble Point Store
|
|
||||||
</button>
|
|
||||||
<GubbleStore
|
<GubbleStore
|
||||||
isOpen={isStoreOpen}
|
isOpen={isStoreOpen}
|
||||||
onClose={handleStoreClose}
|
onClose={handleStoreClose}
|
||||||
|
@ -142,9 +163,13 @@ function App() {
|
||||||
upgrades={upgrades}
|
upgrades={upgrades}
|
||||||
onBuyUpgrade={handleBuyUpgrade}
|
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">
|
<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) => (
|
{TIERS.map((tier, idx) => (
|
||||||
<div key={tier.name} className="inline-block min-w-[8rem]">
|
<div
|
||||||
|
key={tier.name}
|
||||||
|
className="inline-block min-w-[8rem]"
|
||||||
|
ref={idx === highestUnlockedIndex ? highestUnlockedRef : undefined}
|
||||||
|
>
|
||||||
<TierCard
|
<TierCard
|
||||||
tier={tier}
|
tier={tier}
|
||||||
unlocked={unlockedTiers.includes(tier.name)}
|
unlocked={unlockedTiers.includes(tier.name)}
|
||||||
|
|
|
@ -19,6 +19,10 @@ type CardProps = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const Card: React.FC<CardProps> = ({ card, setCard, setMoney, setGubblePoints, upgrades }) => {
|
const Card: React.FC<CardProps> = ({ card, setCard, setMoney, setGubblePoints, upgrades }) => {
|
||||||
|
const formatter = Intl.NumberFormat(
|
||||||
|
'en',
|
||||||
|
{ 'notation': 'compact' }
|
||||||
|
)
|
||||||
return (
|
return (
|
||||||
<div className="bg-white rounded-lg shadow p-6 flex flex-col items-center mb-6 w-full max-w-md">
|
<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>
|
<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 ? (
|
{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>
|
<span className="text-3xl select-none">?</span>
|
||||||
)}
|
)}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue