comparison react_games/src/Games.tsx @ 37:fb9bcd3145cb

[ReactGames] Few games I made using react just to practice few things.
author MrJuneJune <me@mrjunejune.com>
date Mon, 01 Dec 2025 20:22:47 -0800
parents
children 49b611c808e7
comparison
equal deleted inserted replaced
36:84672efec192 37:fb9bcd3145cb
1 import ReactDOM from 'react-dom/client';
2 import React, { CSSProperties, lazy, LazyExoticComponent, Suspense, useEffect, useState } from "react";
3
4 const LazyTicTacToe = lazy(() => import("./Tictactoe/main.tsx").then(m => ({ default: m.TicTacToe })));
5 const LazyConnectFour = lazy(() => import("./Connectfour/main.tsx").then(m => ({ default: m.ConnectFour })));
6 const LazyMemoryGame = lazy(() => import("./CardMatchiing/main.tsx").then(m => ({ default: m.MemoryGame })));
7 const LazyLightsOut = lazy(() => import("./LightsOut/main.tsx").then(m => ({ default: m.LightsOut })));
8 const LazyWordle = lazy(() => import("./Wordle/main.tsx").then(m => ({ default: m.Wordle })));
9 const LazyMinesweeper = lazy(() => import("./Minesweeper/main.tsx").then(m => ({ default: m.Minesweeper })));
10
11
12
13 const wrapperStyle: CSSProperties = {
14 display: 'flex',
15 justifyContent: 'center',
16 alignItems: 'center',
17 minHeight: '100vh',
18 fontFamily: 'system-ui, sans-serif',
19 };
20
21 const menuStyle: CSSProperties = {
22 listStyle: 'none',
23 padding: 0,
24 margin: '2rem auto',
25 width: 280,
26 display: 'flex',
27 flexDirection: 'column',
28 gap: '1rem',
29 cursor: 'pointer',
30 };
31
32 const menuItemStyle: CSSProperties = {
33 padding: '0.75rem 1rem',
34 borderRadius: 8,
35 background: '#f3f4f6',
36 textAlign: 'center',
37 transition: 'background 120ms',
38 };
39
40 type ComponentLabels =
41 | "tictactoe"
42 | "connectfour"
43 | "memorygame"
44 | "lightsout"
45 | "wordle"
46 | "minesweeper"
47
48 interface ComponentData {
49 LazyComponent: LazyExoticComponent<() => React.ReactNode>;
50 title: string;
51 }
52
53 const gameMap: (Record<ComponentLabels, ComponentData>) = {
54 tictactoe: {
55 LazyComponent: LazyTicTacToe,
56 title: "TicTacToe",
57 },
58 connectfour: {
59 LazyComponent: LazyConnectFour,
60 title: "Connect Four",
61 },
62 memorygame: {
63 LazyComponent: LazyMemoryGame,
64 title: "Memory Game",
65 },
66 lightsout: {
67 LazyComponent: LazyLightsOut,
68 title: "Lights out",
69 },
70 wordle: {
71 LazyComponent: LazyWordle,
72 title: "Wordle",
73 },
74 minesweeper: {
75 LazyComponent: LazyMinesweeper,
76 title: "Minesweeper",
77 },
78 }
79
80 type GameKey = ComponentLabels | null;
81
82 const BASE = "/games";
83 const Games = () => {
84 const getKey = (p: string): GameKey => {
85 const m = p.match(/^\/games\/?([^/]+)?/i);
86 const k = (m?.[1] || '').toLowerCase() as GameKey;
87 return k && k in gameMap ? k : null;
88 };
89
90 const [game, setGame] = useState<GameKey>(() => getKey(location.pathname));
91
92 useEffect(() => {
93 const onPop = () => setGame(getKey(location.pathname));
94 addEventListener('popstate', onPop);
95 return () => removeEventListener('popstate', onPop);
96 }, []);
97
98 useEffect(() => {
99 history.pushState(null, '', game ? `${BASE}/${game}` : BASE);
100 }, [game]);
101
102 const selected = game ? gameMap[game] : null;
103
104 return (
105 <main style={wrapperStyle}>
106 <section>
107 <h1 style={{ textAlign: "center" }}>MAI React Playground</h1>
108
109 {selected?.LazyComponent ? (
110 <Suspense fallback={<div>loading...</div>}>
111 <div style={
112 {
113 display: "flex",
114 flexDirection: "column",
115 justifyContent: "center",
116 alignItems: "center",
117 gap: 10,
118 }}>
119 <selected.LazyComponent />
120 </div>
121 </Suspense>
122 ) : (
123 <ul style={menuStyle}>
124 {Object.entries(gameMap).map(([key, value]) => (
125 <li key={key} style={menuItemStyle} onClick={() => setGame(key as GameKey)}>
126 {value.title}
127 </li>
128 ))}
129 </ul>
130 )}
131
132 {game && (
133 <button style={{ display: "flex", flexDirection: "column", justifyContent: "center", alignItems: "cetner", margin: "1.5rem auto 0" }} onClick={() => setGame(null)}>
134 Back to menu
135 </button>
136 )}
137
138 </section>
139 </main>
140 );
141 };
142
143 export {
144 Games,
145 }
146
147 ReactDOM.createRoot(document.getElementById('root')!).render(<Games />);