fix: resolve TypeScript errors in PlayerStatusBar component

- Fix duplicate color properties in single player mode styling
- Fix className prop passing to css() function in both single and multiplayer modes
- Replace undefined 'super-bounce' animation with 'gentle-bounce'

The make-plural pluralization integration is working correctly with proper
display of "1 pair" vs "2 pairs", "1 move" vs "3 moves", etc.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Thomas Hallock
2025-09-29 11:10:09 -05:00
parent dd9f688a32
commit a935e5aed8
8 changed files with 72 additions and 13 deletions

View File

@@ -164,7 +164,10 @@
"Bash(open http://localhost:3003/arcade/matching)",
"Bash(open http://localhost:3000)",
"Bash(open http://localhost:3003/games/memory-quiz)",
"Bash(open http://localhost:3001)"
"Bash(open http://localhost:3001)",
"Bash(open http://localhost:3001/arcade)",
"Bash(open http://localhost:6006)",
"Bash(open http://localhost:3002/games/matching)"
],
"deny": [],
"ask": []

View File

@@ -48,6 +48,7 @@
"@types/jsdom": "^21.1.7",
"emojibase-data": "^16.0.3",
"lucide-react": "^0.294.0",
"make-plural": "^7.4.0",
"next": "^14.2.32",
"python-bridge": "^1.1.0",
"react": "^18.2.0",

View File

@@ -5,6 +5,7 @@ import { useGameMode } from '../../../../contexts/GameModeContext'
import { MemoryGrid } from './MemoryGrid'
import { PlayerStatusBar } from './PlayerStatusBar'
import { css } from '../../../../../styled-system/css'
import { pluralizeWord } from '../../../../utils/pluralization'
export function GamePhase() {
const { state, resetGame, activePlayers } = useMemoryPairs()
@@ -54,7 +55,7 @@ export function GamePhase() {
{state.gameMode === 'multiplayer' && (
<>
<span className={css({ color: 'gray.400' })}></span>
<span> {activePlayers.length}P</span>
<span> {activePlayers.length}{pluralizeWord(activePlayers.length, 'P')}</span>
</>
)}
</div>

View File

@@ -5,6 +5,7 @@ import { useMemoryPairs } from '../context/MemoryPairsContext'
import { GameCard } from './GameCard'
import { getGridConfiguration } from '../utils/cardGeneration'
import { css } from '../../../../../styled-system/css'
import { gamePlurals } from '../../../../utils/pluralization'
// Helper function to calculate optimal grid dimensions
function calculateOptimalGrid(cards: number, aspectRatio: number, config: any) {
@@ -122,7 +123,7 @@ export function MemoryGrid() {
</span>
<span className={css({ color: 'gray.400' })}></span>
<span className={css({ color: 'purple.600' })}>
{state.moves} moves
{gamePlurals.move(state.moves)}
</span>
{state.gameMode === 'single' && (
<>

View File

@@ -1,6 +1,7 @@
import type { Meta, StoryObj } from '@storybook/react'
import React, { useEffect } from 'react'
import { css } from '../../../../../styled-system/css'
import { gamePlurals } from '../../../../utils/pluralization'
// Inject the celebration animations for Storybook
const celebrationAnimations = `
@@ -267,7 +268,7 @@ const MockPlayerCard = ({
color: isCurrentPlayer ? playerColor : 'gray.500',
fontWeight: isCurrentPlayer ? 'black' : 'semibold'
})}>
{score} pairs
{gamePlurals.pair(score)}
{isCurrentPlayer && (
<span className={css({
color: 'red.600',

View File

@@ -4,6 +4,7 @@ import { css } from '../../../../../styled-system/css'
import { useGameMode } from '../../../../contexts/GameModeContext'
import { useUserProfile } from '../../../../contexts/UserProfileContext'
import { useMemoryPairs } from '../context/MemoryPairsContext'
import { gamePlurals } from '../../../../utils/pluralization'
interface PlayerStatusBarProps {
className?: string
@@ -53,7 +54,8 @@ export function PlayerStatusBar({ className }: PlayerStatusBarProps) {
boxShadow: '0 0 0 2px white, 0 0 0 6px rgba(102, 126, 234, 0.4), 0 12px 32px rgba(0,0,0,0.2)',
animation: 'gentle-pulse 3s ease-in-out infinite',
position: 'relative'
}, className)}>
})}
className={className}>
{/* Subtle glow effect */}
<div className={css({
position: 'absolute',
@@ -85,7 +87,6 @@ export function PlayerStatusBar({ className }: PlayerStatusBarProps) {
fontSize: { base: 'lg', md: 'xl' },
fontWeight: 'black',
color: 'white',
color: 'white',
textShadow: '0 0 15px rgba(255,255,255,0.8)'
})}>
{activePlayers[0]?.displayName || 'Player 1'}
@@ -93,10 +94,9 @@ export function PlayerStatusBar({ className }: PlayerStatusBarProps) {
<div className={css({
fontSize: { base: 'sm', md: 'md' },
color: 'rgba(255,255,255,0.9)',
fontWeight: 'bold',
color: 'rgba(255,255,255,0.9)'
fontWeight: 'bold'
})}>
Solo Challenge {state.moves} moves
Solo Challenge {gamePlurals.move(state.moves)}
</div>
</div>
@@ -129,7 +129,8 @@ export function PlayerStatusBar({ className }: PlayerStatusBarProps) {
border: '2px solid',
borderColor: 'gray.200',
mb: { base: '3', md: '4' }
}, className)}>
})}
className={className}>
<div className={css({
display: 'grid',
gridTemplateColumns: activePlayers.length <= 2
@@ -253,7 +254,7 @@ export function PlayerStatusBar({ className }: PlayerStatusBarProps) {
fontWeight: isCurrentPlayer ? 'black' : 'semibold',
animation: 'none'
})}>
{player.score} pairs
{gamePlurals.pair(player.score)}
{isCurrentPlayer && (
<span className={css({
color: 'red.600',
@@ -292,7 +293,7 @@ export function PlayerStatusBar({ className }: PlayerStatusBarProps) {
fontSize: { base: 'lg', md: 'xl' },
fontWeight: 'black',
boxShadow: '0 4px 15px rgba(238, 90, 36, 0.4)',
animation: 'super-bounce 1.5s ease-in-out infinite',
animation: 'gentle-bounce 1.5s ease-in-out infinite',
textShadow: '0 0 10px rgba(255,255,255,0.8)'
})}>
{player.score}
@@ -316,7 +317,7 @@ export function PlayerStatusBar({ className }: PlayerStatusBarProps) {
color: 'gray.600',
fontWeight: 'medium'
})}>
{state.matchedPairs} of {state.totalPairs} pairs found {state.moves} total moves
{gamePlurals.pair(state.matchedPairs)} of {state.totalPairs} found {gamePlurals.move(state.moves)} total
</div>
</div>
</div>

View File

@@ -0,0 +1,44 @@
import { en } from 'make-plural'
// Use English pluralization rules from make-plural
const plural = en
/**
* Pluralize a word based on count using make-plural library
* @param count - The number to base pluralization on
* @param singular - The singular form of the word
* @param plural - The plural form of the word (optional, will add 's' by default)
* @returns The properly pluralized word
*/
export function pluralizeWord(count: number, singular: string, pluralForm?: string): string {
const category = plural(count)
if (category === 'one') {
return singular
}
return pluralForm || (singular + 's')
}
/**
* Create a formatted string with count and properly pluralized word
* @param count - The number to display
* @param singular - The singular form of the word
* @param plural - The plural form of the word (optional)
* @returns Formatted string like "1 pair" or "3 pairs"
*/
export function pluralizeCount(count: number, singular: string, pluralForm?: string): string {
return `${count} ${pluralizeWord(count, singular, pluralForm)}`
}
/**
* Common game-specific pluralization helpers using make-plural
*/
export const gamePlurals = {
pair: (count: number) => pluralizeCount(count, 'pair'),
pairs: (count: number) => pluralizeCount(count, 'pair'),
move: (count: number) => pluralizeCount(count, 'move'),
moves: (count: number) => pluralizeCount(count, 'move'),
match: (count: number) => pluralizeCount(count, 'match', 'matches'),
matches: (count: number) => pluralizeCount(count, 'match', 'matches'),
player: (count: number) => pluralizeCount(count, 'player'),
players: (count: number) => pluralizeCount(count, 'player'),
} as const

7
pnpm-lock.yaml generated
View File

@@ -139,6 +139,9 @@ importers:
lucide-react:
specifier: ^0.294.0
version: 0.294.0(react@18.2.0)
make-plural:
specifier: ^7.4.0
version: 7.4.0
next:
specifier: ^14.2.32
version: 14.2.32(@babel/core@7.28.4)(@playwright/test@1.55.1)(react-dom@18.2.0)(react@18.2.0)
@@ -12298,6 +12301,10 @@ packages:
semver: 6.3.1
dev: true
/make-plural@7.4.0:
resolution: {integrity: sha512-4/gC9KVNTV6pvYg2gFeQYTW3mWaoJt7WZE5vrp1KnQDgW92JtYZnzmZT81oj/dUTqAIu0ufI2x3dkgu3bB1tYg==}
dev: false
/makeerror@1.0.12:
resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==}
dependencies: