Mercurial
view react_games/src/Games.tsx @ 214:4c725fde6999
[MrJuneJune] Fixed linkedin path and images modules.
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Sun, 15 Feb 2026 22:21:27 -0800 |
| parents | 49b611c808e7 |
| children |
line wrap: on
line source
import ReactDOM from 'react-dom/client'; import React, { CSSProperties, lazy, LazyExoticComponent, Suspense, useEffect, useState } from "react"; const LazyTicTacToe = lazy(() => import("./Tictactoe/main.tsx").then(m => ({ default: m.TicTacToe }))); const LazyConnectFour = lazy(() => import("./Connectfour/main.tsx").then(m => ({ default: m.ConnectFour }))); const LazyMemoryGame = lazy(() => import("./CardMatchiing/main.tsx").then(m => ({ default: m.MemoryGame }))); const LazyLightsOut = lazy(() => import("./LightsOut/main.tsx").then(m => ({ default: m.LightsOut }))); const LazyWordle = lazy(() => import("./Wordle/main.tsx").then(m => ({ default: m.Wordle }))); const LazyMinesweeper = lazy(() => import("./Minesweeper/main.tsx").then(m => ({ default: m.Minesweeper }))); const wrapperStyle: CSSProperties = { display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: '100vh', fontFamily: 'system-ui, sans-serif', }; const menuStyle: CSSProperties = { listStyle: 'none', padding: 0, margin: '2rem auto', width: 280, display: 'flex', flexDirection: 'column', gap: '1rem', cursor: 'pointer', }; const menuItemStyle: CSSProperties = { padding: '0.75rem 1rem', borderRadius: 8, background: '#f3f4f6', textAlign: 'center', transition: 'background 120ms', }; type ComponentLabels = | "tictactoe" | "connectfour" | "memorygame" | "lightsout" | "wordle" | "minesweeper" interface ComponentData { LazyComponent: LazyExoticComponent<() => React.ReactNode>; title: string; } const gameMap: (Record<ComponentLabels, ComponentData>) = { tictactoe: { LazyComponent: LazyTicTacToe, title: "TicTacToe", }, connectfour: { LazyComponent: LazyConnectFour, title: "Connect Four", }, memorygame: { LazyComponent: LazyMemoryGame, title: "Memory Game", }, lightsout: { LazyComponent: LazyLightsOut, title: "Lights out", }, wordle: { LazyComponent: LazyWordle, title: "Wordle", }, minesweeper: { LazyComponent: LazyMinesweeper, title: "Minesweeper", }, } type GameKey = ComponentLabels | null; const BASE = "/games"; const Games = () => { const getKey = (p: string): GameKey => { const m = p.match(/^\/games\/?([^/]+)?/i); const k = (m?.[1] || '').toLowerCase() as GameKey; return k && k in gameMap ? k : null; }; const [game, setGame] = useState<GameKey>(() => getKey(location.pathname)); useEffect(() => { const onPop = () => setGame(getKey(location.pathname)); addEventListener('popstate', onPop); return () => removeEventListener('popstate', onPop); }, []); useEffect(() => { history.pushState(null, '', game ? `${BASE}/${game}` : BASE); }, [game]); const selected = game ? gameMap[game] : null; return ( <main style={wrapperStyle}> <section> <h1 style={{ textAlign: "center" }}>React Game Ground</h1> {selected?.LazyComponent ? ( <Suspense fallback={<div>loading...</div>}> <div style={ { display: "flex", flexDirection: "column", justifyContent: "center", alignItems: "center", gap: 10, }}> <selected.LazyComponent /> </div> </Suspense> ) : ( <ul style={menuStyle}> {Object.entries(gameMap).map(([key, value]) => ( <li key={key} style={menuItemStyle} onClick={() => setGame(key as GameKey)}> {value.title} </li> ))} </ul> )} {game && ( <button style={{ display: "flex", flexDirection: "column", justifyContent: "center", alignItems: "cetner", margin: "1.5rem auto 0" }} onClick={() => setGame(null)}> Back to menu </button> )} </section> </main> ); }; export { Games, } ReactDOM.createRoot(document.getElementById('root')!).render(<Games />);