feat: add Menu, DifficultySelect screens and App routing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-07 21:08:43 +07:00
co-authored by Claude Sonnet 4.6
parent 5e30ac3862
commit a6ea58703a
3 changed files with 133 additions and 1 deletions
+37 -1
View File
@@ -1,5 +1,41 @@
import { useState, useRef } from "react";
import { Difficulty } from "../types";
import { GameStateManager } from "../game/state";
import { Menu } from "./Menu";
import { DifficultySelect } from "./DifficultySelect";
type Screen = "menu" | "difficulty" | "game" | "gameover";
function App() {
return <div>Pikachu Onet Connect</div>;
const [screen, setScreen] = useState<Screen>("menu");
const [difficulty, setDifficulty] = useState<Difficulty>("easy");
const stateManager = useRef(new GameStateManager()).current;
const handleSelectDifficulty = (d: Difficulty) => {
setDifficulty(d);
stateManager.startGame(d);
setScreen("game");
};
return (
<div style={{ textAlign: "center" }}>
{screen === "menu" && (
<Menu onPlay={() => setScreen("difficulty")} />
)}
{screen === "difficulty" && (
<DifficultySelect
onSelect={handleSelectDifficulty}
onBack={() => setScreen("menu")}
/>
)}
{screen === "game" && (
<div>Game placeholder {difficulty}</div>
)}
{screen === "gameover" && (
<div>Game Over placeholder</div>
)}
</div>
);
}
export default App;
+63
View File
@@ -0,0 +1,63 @@
import { Difficulty } from "../types";
interface DifficultySelectProps {
onSelect: (difficulty: Difficulty) => void;
onBack: () => void;
}
const difficulties: { key: Difficulty; label: string; desc: string }[] = [
{ key: "easy", label: "Easy", desc: "6×4 grid • 5 min • 5 hints" },
{ key: "medium", label: "Medium", desc: "8×6 grid • 4 min • 3 hints" },
{ key: "hard", label: "Hard", desc: "10×8 grid • 3 min • 1 hint" },
];
export function DifficultySelect({ onSelect, onBack }: DifficultySelectProps) {
return (
<div style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
gap: "20px",
}}>
<h2 style={{ fontSize: "32px" }}>Select Difficulty</h2>
<div style={{ display: "flex", gap: "16px" }}>
{difficulties.map((d) => (
<button
key={d.key}
onClick={() => onSelect(d.key)}
style={{
padding: "20px 32px",
fontSize: "18px",
background: "#0f3460",
color: "#fff",
border: "2px solid #e94560",
borderRadius: "8px",
cursor: "pointer",
display: "flex",
flexDirection: "column",
alignItems: "center",
gap: "8px",
}}
>
<strong>{d.label}</strong>
<span style={{ fontSize: "12px", color: "#aaa" }}>{d.desc}</span>
</button>
))}
</div>
<button
onClick={onBack}
style={{
padding: "8px 24px",
fontSize: "16px",
background: "transparent",
color: "#aaa",
border: "1px solid #aaa",
borderRadius: "4px",
cursor: "pointer",
}}
>
Back
</button>
</div>
);
}
+33
View File
@@ -0,0 +1,33 @@
interface MenuProps {
onPlay: () => void;
}
export function Menu({ onPlay }: MenuProps) {
return (
<div style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
gap: "24px",
}}>
<h1 style={{ fontSize: "48px" }}> Pikachu Onet Connect</h1>
<p style={{ fontSize: "18px", color: "#aaa" }}>
Match pairs of tiles by connecting them with up to 3 lines
</p>
<button
onClick={onPlay}
style={{
padding: "16px 48px",
fontSize: "24px",
background: "#e94560",
color: "#fff",
border: "none",
borderRadius: "8px",
cursor: "pointer",
}}
>
Play
</button>
</div>
);
}