Files
soroban-abacus-flashcards/apps/web/src/components/PageWithNav.tsx
Thomas Hallock e85d0415f2 feat: update nav components for UUID players
- Update all player ID types from number to string
- Remove switch statements for player lookups
- Use Map/Set operations instead of array methods
- Support arbitrary number of players
- PlayerConfigDialog now accepts string IDs
2025-10-04 17:06:54 -05:00

87 lines
2.7 KiB
TypeScript

'use client'
import React from 'react'
import { AppNavBar } from './AppNavBar'
import { useGameMode } from '../contexts/GameModeContext'
import { GameContextNav } from './nav/GameContextNav'
import { PlayerConfigDialog } from './nav/PlayerConfigDialog'
interface PageWithNavProps {
navTitle?: string
navEmoji?: string
emphasizeGameContext?: boolean
children: React.ReactNode
}
export function PageWithNav({ navTitle, navEmoji, emphasizeGameContext = false, children }: PageWithNavProps) {
const { players, activePlayers, setActive, activePlayerCount } = useGameMode()
const [mounted, setMounted] = React.useState(false)
const [configurePlayerId, setConfigurePlayerId] = React.useState<string | null>(null)
// Delay mounting animation slightly for smooth transition
React.useEffect(() => {
const timer = setTimeout(() => setMounted(true), 50)
return () => clearTimeout(timer)
}, [])
const handleRemovePlayer = (playerId: string) => {
setActive(playerId, false)
}
const handleAddPlayer = (playerId: string) => {
setActive(playerId, true)
}
const handleConfigurePlayer = (playerId: string) => {
setConfigurePlayerId(playerId)
}
// Get active and inactive players as arrays
const activePlayerList = Array.from(activePlayers)
.map(id => players.get(id))
.filter(p => p !== undefined)
.map(p => ({ id: p.id, name: p.name, emoji: p.emoji }))
const inactivePlayerList = Array.from(players.values())
.filter(p => !activePlayers.has(p.id))
.map(p => ({ id: p.id, name: p.name, emoji: p.emoji }))
// Compute game mode from active player count
const gameMode = activePlayerCount === 0 ? 'none' :
activePlayerCount === 1 ? 'single' :
activePlayerCount === 2 ? 'battle' :
activePlayerCount >= 3 ? 'tournament' : 'none'
const shouldEmphasize = emphasizeGameContext && mounted
const showFullscreenSelection = shouldEmphasize && activePlayerCount === 0
// Create nav content if title is provided
const navContent = navTitle ? (
<GameContextNav
navTitle={navTitle}
navEmoji={navEmoji}
gameMode={gameMode}
activePlayers={activePlayerList}
inactivePlayers={inactivePlayerList}
shouldEmphasize={shouldEmphasize}
showFullscreenSelection={showFullscreenSelection}
onAddPlayer={handleAddPlayer}
onRemovePlayer={handleRemovePlayer}
onConfigurePlayer={handleConfigurePlayer}
/>
) : null
return (
<>
<AppNavBar navSlot={navContent} />
{children}
{configurePlayerId && (
<PlayerConfigDialog
playerId={configurePlayerId}
onClose={() => setConfigurePlayerId(null)}
/>
)}
</>
)
}