diff --git a/apps/front/package.json b/apps/front/package.json index b3bb797..5dce6b6 100644 --- a/apps/front/package.json +++ b/apps/front/package.json @@ -13,6 +13,7 @@ "@headlessui/react": "^1.7.18", "@heroicons/react": "^2.1.1", "@knucklebones/common": "workspace:*", + "@radix-ui/react-popover": "^1.0.7", "@radix-ui/react-toggle-group": "^1.0.4", "@use-gesture/react": "^10.3.0", "clsx": "^2.1.0", diff --git a/apps/front/src/components/Button.tsx b/apps/front/src/components/Button.tsx index 084d9b1..d5eb772 100644 --- a/apps/front/src/components/Button.tsx +++ b/apps/front/src/components/Button.tsx @@ -1,5 +1,6 @@ import type * as React from 'react' import { clsx } from 'clsx' +import { IconWrapper } from './IconWrapper' export interface ButtonProps { // Not a fan of having this prop @@ -46,13 +47,9 @@ export function Button({ props.className )} > - {leftIcon !== undefined && ( -
{leftIcon}
- )} + {leftIcon !== undefined && {leftIcon}}
{children}
- {rightIcon !== undefined && ( -
{rightIcon}
- )} + {rightIcon !== undefined && {rightIcon}} ) } diff --git a/apps/front/src/components/Dice.tsx b/apps/front/src/components/Dice.tsx index 5054d1c..4282e44 100644 --- a/apps/front/src/components/Dice.tsx +++ b/apps/front/src/components/Dice.tsx @@ -2,13 +2,19 @@ import * as React from 'react' import { clsx } from 'clsx' import { Transition } from '@headlessui/react' -type DiceVariant = 'base' | 'small' -interface DiceProps { +type DiceSize = 'medium' | 'small' +type DiceVariant = 'solid' | 'outline' + +interface DiceStyle { + size?: DiceSize + variant?: DiceVariant +} + +interface DiceProps extends DiceStyle { value?: number className?: string count?: number showUndefined?: boolean - variant?: DiceVariant } interface DotProps { className?: string @@ -18,28 +24,31 @@ const baseClassName = 'aspect-square h-12 portrait:md:h-16 landscape:md:h-12 landscape:lg:h-16' const smallClassName = 'aspect-square h-6' -const VariantContext = React.createContext('base') +const DiceStyleContext = React.createContext>({ + size: 'medium', + variant: 'solid' +}) function DicePlaceholder({ className }: DotProps) { - const variant = React.useContext(VariantContext) + const { size } = React.useContext(DiceStyleContext) return (
) } function DiceContainer({ children }: React.PropsWithChildren) { - const variant = React.useContext(VariantContext) + const { size } = React.useContext(DiceStyleContext) return (
@@ -49,7 +58,7 @@ function DiceContainer({ children }: React.PropsWithChildren) { } function Dot({ className }: DotProps) { - const variant = React.useContext(VariantContext) + const { size } = React.useContext(DiceStyleContext) return (
@@ -150,13 +159,17 @@ const DiceMap: Record = { function SimpleDice({ value, className, count = 1 }: DiceProps) { const DiceValue = DiceMap[value ?? 'undefined'] - const variant = React.useContext(VariantContext) + const { size, variant } = React.useContext(DiceStyleContext) return (
@@ -181,7 +194,8 @@ export function Dice({ className, count = 1, showUndefined = false, - variant = 'base' + variant = 'solid', + size = 'medium' }: DiceProps) { // Temporarily store the dice value to keep the dice shown during the // transition after it has been unset. While the animation is done, the cached @@ -198,7 +212,7 @@ export function Dice({ return ( // Sharing the variant in a context so it's easier to drill it down - + )} - + ) } diff --git a/apps/front/src/components/Footer.tsx b/apps/front/src/components/Footer.tsx index ebe1fbc..06f3556 100644 --- a/apps/front/src/components/Footer.tsx +++ b/apps/front/src/components/Footer.tsx @@ -1,5 +1,6 @@ import { Trans } from 'react-i18next' import { CodeBracketIcon, EnvelopeIcon } from '@heroicons/react/24/outline' +import { IconWrapper } from './IconWrapper' export function Footer() { return ( @@ -23,9 +24,9 @@ export function Footer() { target='_blank' rel='noreferrer' > -
+ -
+ -
+ -
+
diff --git a/apps/front/src/components/Game.tsx b/apps/front/src/components/Game.tsx index c43ddc4..2d37467 100644 --- a/apps/front/src/components/Game.tsx +++ b/apps/front/src/components/Game.tsx @@ -2,6 +2,7 @@ import * as React from 'react' import { useIsOnMobile } from '../hooks/detectDevice' import { useNoIndex } from '../hooks/useNoIndex' import { useGameWhileLoading } from './GameContext' +import { GameMode } from './GameMode' import { GameOutcome } from './GameOutcome' import { HowToPlayModal } from './HowToPlay' import { Loading } from './Loading' @@ -23,20 +24,18 @@ export function Game() { const { errorMessage, clearErrorMessage } = gameStore - // Pas tip top je trouve, mais virtuellement ça marche - const gameOutcome = - return ( <> - {isOnMobile && gameOutcome} + {isOnMobile && } +
- {!isOnMobile && gameOutcome} + {!isOnMobile && }
diff --git a/apps/front/src/components/GameContext/useGameSetup.ts b/apps/front/src/components/GameContext/useGameSetup.ts index 9492e87..71761ca 100644 --- a/apps/front/src/components/GameContext/useGameSetup.ts +++ b/apps/front/src/components/GameContext/useGameSetup.ts @@ -55,7 +55,11 @@ export function useGameSetup() { if (readyState === ReadyState.OPEN) { initGame( { roomKey, playerId }, - { playerType: 'human', boType: state?.boType } + { + playerType: 'human', + boType: state?.boType, + gameMode: state?.gameMode ?? 'classic' + } ) .then(async () => { // À déplacer côté serveur @@ -65,7 +69,8 @@ export function useGameSetup() { { playerType: 'ai', difficulty: state?.difficulty, - boType: state?.boType + boType: state?.boType, + gameMode: state?.gameMode } ) } diff --git a/apps/front/src/components/GameMode.tsx b/apps/front/src/components/GameMode.tsx new file mode 100644 index 0000000..02699d4 --- /dev/null +++ b/apps/front/src/components/GameMode.tsx @@ -0,0 +1,30 @@ +import { useTranslation } from 'react-i18next' +import { Button } from './Button' +import { Dice } from './Dice' +import { useGame } from './GameContext' +import { Popover, PopoverContent, PopoverTrigger } from './Popover' + +export function GameMode() { + const { gameMode } = useGame() + const { t } = useTranslation() + + return ( + + + {/* Va falloir adapter Button et IconButton pour passer des refs + Malheureusement c'est pas super compatible avec les composants + polymorphiques, mais en même temps, on en a pas tellement besoin ici */} + + + + {t(`game-settings.game-mode.info`)} + + + ) +} diff --git a/apps/front/src/components/GameSettings/GameSettingsModal.tsx b/apps/front/src/components/GameSettings/GameSettingsModal.tsx index 5bde88e..a2ffbde 100644 --- a/apps/front/src/components/GameSettings/GameSettingsModal.tsx +++ b/apps/front/src/components/GameSettings/GameSettingsModal.tsx @@ -5,20 +5,24 @@ import { v4 as uuidv4 } from 'uuid' import { type GameSettings, type Difficulty, - type PlayerType + type PlayerType, + type GameMode } from '@knucklebones/common' import { Button } from '../Button' import { Modal, type ModalProps } from '../Modal' +import { InfoPopover } from '../Popover' import { type Option, ToggleGroup } from '../ToggleGroup' import { getBoTypeOptions, getDifficultyOptions, convertToBoType, - type StringBoType + type StringBoType, + getGameModeOptions } from './options' interface GameSettingProps { label: string + info?: string options: Array> value: T onValueChange(value: T): void @@ -26,13 +30,17 @@ interface GameSettingProps { function GameSetting({ label, + info, options, value, onValueChange }: GameSettingProps) { return (
- +
+ + {info !== undefined && {info}} +
{/* Accessibility? */} ('medium') const [boType, setBoType] = React.useState('indefinite') + const [gameMode, setGameMode] = React.useState('classic') const { t } = useTranslation() return ( @@ -78,6 +87,14 @@ export function GameSettingsModal({ onValueChange={setBoType} options={getBoTypeOptions()} /> +