Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | 'use client' import { useEffect, useRef } from 'react' import { useRouter } from 'next/navigation' import { PageWithNav } from '@/components/PageWithNav' import { css } from '../../../../../styled-system/css' import { StandardGameLayout } from '@/components/StandardGameLayout' import { useFullscreen } from '@/contexts/FullscreenContext' import { GenericSetupPhase } from './GenericSetupPhase' import { GenericGamePhase } from './GenericGamePhase' import { GenericResultsPhase } from './GenericResultsPhase' import type { BaseMatchingCard, BaseMatchingConfig, MatchingPairsContextValue, MatchingPairsVariant, } from '../types' /** * Create a game component for a matching-pairs variant. * Returns a React component that renders the full game with phase routing. */ export function createMatchingPairsGameComponent< TCard extends BaseMatchingCard, TConfig extends BaseMatchingConfig, >( variant: MatchingPairsVariant<TCard, TConfig>, useMatchingPairs: () => MatchingPairsContextValue<TCard, TConfig> ): () => JSX.Element { function MatchingPairsGameComponent() { const router = useRouter() const ctx = useMatchingPairs() const { state, exitSession, resetGame, goToSetup } = ctx const { setFullscreenElement } = useFullscreen() const gameRef = useRef<HTMLDivElement>(null) useEffect(() => { if (gameRef.current) { setFullscreenElement(gameRef.current) } }, [setFullscreenElement]) // Get nav info from variant const config = state as unknown as TConfig const navInfo = variant.getNavInfo?.(config) ?? { title: variant.gameName, emoji: '🎮' } return ( <PageWithNav navTitle={navInfo.title} navEmoji={navInfo.emoji} emphasizePlayerSelection={state.gamePhase === 'setup'} onExitSession={() => { exitSession() router.push('/arcade') }} onSetup={ goToSetup ? () => { goToSetup() } : undefined } onNewGame={() => { resetGame() }} currentPlayerId={state.currentPlayer} playerScores={state.scores} playerStreaks={state.consecutiveMatches} > <StandardGameLayout> <div ref={gameRef} className={css({ flex: 1, padding: { base: '12px', sm: '16px', md: '20px' }, display: 'flex', flexDirection: 'column', alignItems: 'center', position: 'relative', overflow: 'auto', })} > <main className={css({ width: '100%', maxWidth: '1200px', background: 'rgba(255,255,255,0.95)', borderRadius: { base: '12px', md: '20px' }, padding: { base: '12px', sm: '16px', md: '24px', lg: '32px' }, boxShadow: '0 10px 30px rgba(0,0,0,0.2)', flex: 1, display: 'flex', flexDirection: 'column', overflow: 'hidden', })} > {state.gamePhase === 'setup' && ( <GenericSetupPhase ctx={ctx} SetupContent={variant.SetupContent} /> )} {state.gamePhase === 'playing' && <GenericGamePhase ctx={ctx} variant={variant} />} {state.gamePhase === 'results' && ( <GenericResultsPhase ctx={ctx} gameName={variant.gameName} /> )} </main> </div> </StandardGameLayout> </PageWithNav> ) } return MatchingPairsGameComponent } |