From 588eab0cafb7f8427cabe791eab8caf0a62f540f Mon Sep 17 00:00:00 2001 From: UnEpicier Date: Mon, 19 Feb 2024 12:04:56 +0100 Subject: [PATCH] :sparkles: Add lang toggler --- src/App.scss | 2 +- src/App.tsx | 101 ++++++++++++++------- src/assets/en.png | Bin 0 -> 18066 bytes src/assets/fr.png | Bin 0 -> 2972 bytes src/assets/question.tsx | 26 ------ src/components/Grid/Grid.tsx | 5 +- src/components/Home/Home.tsx | 24 ++--- src/components/LangToggler/LangToggler.tsx | 45 +++++++++ src/components/LangToggler/styles.scss | 23 +++++ src/components/Leaderboard/Leaderboard.tsx | 25 +++-- src/components/Leaderboard/styles.scss | 12 +-- src/contexts/langState.ts | 9 ++ src/langs/en.json | 32 +++++++ src/langs/fr.json | 32 +++++++ 14 files changed, 249 insertions(+), 87 deletions(-) create mode 100644 src/assets/en.png create mode 100644 src/assets/fr.png delete mode 100644 src/assets/question.tsx create mode 100644 src/components/LangToggler/LangToggler.tsx create mode 100644 src/components/LangToggler/styles.scss create mode 100644 src/contexts/langState.ts create mode 100644 src/langs/en.json create mode 100644 src/langs/fr.json diff --git a/src/App.scss b/src/App.scss index a4c9552..4d8b7f7 100644 --- a/src/App.scss +++ b/src/App.scss @@ -20,7 +20,7 @@ & > .helpButton { display: grid; - grid-template-columns: 20px 1fr; + grid-template-columns: 1fr; align-items: center; gap: 5px; padding: 10px 15px; diff --git a/src/App.tsx b/src/App.tsx index 3943e4b..1495c83 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,12 +3,13 @@ // --------------------------------------------------------------------------------------------------------------------- // ------------------------------------------------------ React -------------------------------------------------------- -import { useState } from 'react'; +import { useEffect, useState } from 'react'; // --------------------------------------------------------------------------------------------------------------------- // ----------------------------------------------------- Context ------------------------------------------------------- import { useRecoilState } from 'recoil'; import { gameStateAtom } from './contexts/gameState'; +import { langAtom } from './contexts/langState'; // --------------------------------------------------------------------------------------------------------------------- // --------------------------------------------------- Components ------------------------------------------------------ @@ -20,47 +21,83 @@ import Help from './components/Help/Help'; import LeaderBoard from './components/Leaderboard/Leaderboard'; // --------------------------------------------------------------------------------------------------------------------- +// ------------------------------------------------------ Langs -------------------------------------------------------- +import frConf from './langs/fr.json'; +import enConf from './langs/en.json'; +// --------------------------------------------------------------------------------------------------------------------- + // ----------------------------------------------------- Styles -------------------------------------------------------- import Logo from './assets/logo'; -import Question from './assets/question'; import './App.scss'; +import LangToggler from './components/LangToggler/LangToggler'; // --------------------------------------------------------------------------------------------------------------------- const App = () => { const [helpDisplayed, displayHelp] = useState(false); const [gameState] = useRecoilState(gameStateAtom); + const [lang, setLang] = useRecoilState(langAtom); - return ( -
- {gameState.status === 'settings' && } - {(gameState.status === 'idle' || gameState.status === 'playing') && ( - <> -
- - - -
- - - {helpDisplayed && ( - { - displayHelp(false); - }} - /> - )} - - )} - {gameState.status === 'board' && } -
- ); + useEffect(() => { + const storedLang = localStorage.getItem('lang'); + + if (storedLang === null) { + localStorage.setItem('lang', 'fr'); + setLang({ + key: 'fr', + config: frConf, + }); + return; + } + + if (storedLang === 'fr') { + setLang({ + key: 'fr', + config: frConf, + }); + return; + } + + setLang({ + key: 'en', + config: enConf, + }); + }, [lang.key]); + + if (lang.config) { + return ( +
+ + {gameState.status === 'settings' && } + {(gameState.status === 'idle' || gameState.status === 'playing') && ( + <> +
+ + + +
+ + + {helpDisplayed && ( + { + displayHelp(false); + }} + /> + )} + + )} + {gameState.status === 'board' && } +
+ ); + } + + return <>; }; export default App; diff --git a/src/assets/en.png b/src/assets/en.png new file mode 100644 index 0000000000000000000000000000000000000000..9670356322c7791101efd7e06fce031d6b6f783e GIT binary patch literal 18066 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 Nc)I$ztaD0e0stV3At(R< literal 0 HcmV?d00001 diff --git a/src/assets/question.tsx b/src/assets/question.tsx deleted file mode 100644 index b558b4a..0000000 --- a/src/assets/question.tsx +++ /dev/null @@ -1,26 +0,0 @@ -const Question = (props: any) => { - return ( - - - - - ); -}; - -export default Question; diff --git a/src/components/Grid/Grid.tsx b/src/components/Grid/Grid.tsx index 6195b38..123fa75 100644 --- a/src/components/Grid/Grid.tsx +++ b/src/components/Grid/Grid.tsx @@ -9,6 +9,7 @@ import { useState } from 'react'; // ----------------------------------------------------- Context ------------------------------------------------------- import { useRecoilState } from 'recoil'; import { gameStateAtom } from '../../contexts/gameState'; +import { langAtom } from '../../contexts/langState'; // --------------------------------------------------------------------------------------------------------------------- // ------------------------------------------------------ Hooks -------------------------------------------------------- @@ -29,6 +30,8 @@ const Grid = () => { const [gameState, setGameState] = useRecoilState(gameStateAtom); const [grid, setGrid] = useState(genGrid(gameState.gridSize)); + const [lang] = useRecoilState(langAtom); + const { handleLeftClick, handleRightClick } = useActions(grid, setGrid, gameState, setGameState); return ( @@ -69,7 +72,7 @@ const Grid = () => { } as React.CSSProperties } > -

{gameState.endType === 'win' ? 'Gagné !' : 'Perdu !'}

+

{gameState.endType === 'win' ? lang.config.win : lang.config.loose}

diff --git a/src/components/LangToggler/LangToggler.tsx b/src/components/LangToggler/LangToggler.tsx new file mode 100644 index 0000000..a8a9245 --- /dev/null +++ b/src/components/LangToggler/LangToggler.tsx @@ -0,0 +1,45 @@ +import { useRecoilState } from 'recoil'; +import { langAtom } from '../../contexts/langState'; + +import fr from '../../assets/fr.png'; +import en from '../../assets/en.png'; +import './styles.scss'; + +const LangToggler = () => { + const [lang, setLang] = useRecoilState(langAtom); + + return ( + + ); +}; + +export default LangToggler; diff --git a/src/components/LangToggler/styles.scss b/src/components/LangToggler/styles.scss new file mode 100644 index 0000000..7fe7c57 --- /dev/null +++ b/src/components/LangToggler/styles.scss @@ -0,0 +1,23 @@ +.langToggler { + position: fixed; + top: 10px; + right: 10px; + display: flex; + justify-self: center; + align-items: center; + border: none; + border-radius: 5px; + cursor: pointer; + transition: scale 0.3s ease; + overflow: hidden; + + &:hover { + scale: 1.1; + } + + & > img { + width: 45px; + height: 25px; + object-fit: cover; + } +} diff --git a/src/components/Leaderboard/Leaderboard.tsx b/src/components/Leaderboard/Leaderboard.tsx index 4212dd4..1216684 100644 --- a/src/components/Leaderboard/Leaderboard.tsx +++ b/src/components/Leaderboard/Leaderboard.tsx @@ -10,6 +10,7 @@ import { useEffect, useState } from 'react'; import localforage from 'localforage'; import { useRecoilState } from 'recoil'; import { gameStateAtom } from '../../contexts/gameState'; +import { langAtom } from '../../contexts/langState'; // --------------------------------------------------------------------------------------------------------------------- // -------------------------------------------------- Utils & Types ---------------------------------------------------- @@ -40,6 +41,8 @@ const LeaderBoard = () => { const [gameState, setGameState] = useRecoilState(gameStateAtom); const [data, setData] = useState([]); + const [lang] = useRecoilState(langAtom); + const getData = async () => { const storeData = await store.getItem(`${gameState.difficulty}:${gameState.gridSize}`); @@ -63,26 +66,26 @@ const LeaderBoard = () => { return ( <> -

Time Leaderboard

+

{lang.config.leaderboardTitle}

#

- Date + {lang.config.date}

- Difficulté + {lang.config.settings.difficulty.label}

- Taille + {lang.config.settings.size.label}

- Time + {lang.config.time}

@@ -95,7 +98,7 @@ const LeaderBoard = () => { > {index + 1} {item.date} - {getDifficultyLabel(gameState.difficulty as Difficulty)} + {lang.config.settings.difficulty.values[gameState.difficulty]} {gameState.gridSize} {item.time}
@@ -110,7 +113,8 @@ const LeaderBoard = () => { onClick={() => setGameState((prev) => ({ ...prev, status: 'settings', endType: '', placedFlags: 0, bombs: 0, gameTime: '' }))} > - Paramètres + + {lang.config.settingsBtn} diff --git a/src/components/Leaderboard/styles.scss b/src/components/Leaderboard/styles.scss index 6475ae2..85c2b16 100644 --- a/src/components/Leaderboard/styles.scss +++ b/src/components/Leaderboard/styles.scss @@ -16,8 +16,8 @@ & > .header { display: grid; - grid-template-columns: 1em 1fr 1fr 0.7fr 1fr; - gap: 30px; + grid-template-columns: 1em 1fr 1fr 0.8fr 1fr; + gap: 20px; padding: 15px 20px; background-color: #333; @@ -26,12 +26,12 @@ flex-wrap: nowrap; align-items: center; gap: 10px; - font-size: 1.3em; + font-size: 1.1em; font-weight: 400; color: #fff; & > svg { - height: 1.3em; + height: 1.2em; } &:last-child { @@ -47,10 +47,10 @@ & > .score { display: grid; - grid-template-columns: 1em 1fr 1fr 0.7fr 1fr; + grid-template-columns: 1em 1fr 1fr 0.8fr 1fr; align-items: center; justify-content: center; - gap: 30px; + gap: 20px; padding: 10px 20px; background-color: #e1e1e1; color: #333; diff --git a/src/contexts/langState.ts b/src/contexts/langState.ts new file mode 100644 index 0000000..a768a6e --- /dev/null +++ b/src/contexts/langState.ts @@ -0,0 +1,9 @@ +import { atom } from 'recoil'; + +export const langAtom = atom({ + key: 'lang', + default: { + key: null as any, + config: null as any, + }, +}); diff --git a/src/langs/en.json b/src/langs/en.json new file mode 100644 index 0000000..68a66d1 --- /dev/null +++ b/src/langs/en.json @@ -0,0 +1,32 @@ +{ + "appTitle": "Minesweeper", + "settings": { + "size": { + "label": "Grid size", + "values": { + "small": "Small", + "medium": "Medium", + "large": "Large" + } + }, + "difficulty": { + "label": "Difficulty", + "values": { + "beginner": "Beginner", + "intermediate": "Intermediate", + "expert": "Expert" + } + } + }, + "start": "Start", + "how": "How to play?", + "win": "Win!", + "loose": "Loose!", + "leaderboardTitle": "Leaderboard", + "date": "Date", + "time": "Time", + "settingsBtn": "Settings", + "replay": "Replay", + "delete": "Delete", + "deleteAll": "Delete all" +} diff --git a/src/langs/fr.json b/src/langs/fr.json new file mode 100644 index 0000000..bc61c56 --- /dev/null +++ b/src/langs/fr.json @@ -0,0 +1,32 @@ +{ + "appTitle": "Démineur", + "settings": { + "size": { + "label": "Taille de grille", + "values": { + "small": "Petit", + "medium": "Moyen", + "large": "Grand" + } + }, + "difficulty": { + "label": "Difficulté", + "values": { + "beginner": "Facile", + "intermediate": "Intermédiaire", + "expert": "Difficile" + } + } + }, + "start": "Commencer", + "how": "Comment jouer ?", + "win": "Gagné !", + "loose": "Perdu !", + "leaderboardTitle": "Tableau des scores", + "date": "Date", + "time": "Temps", + "settingsBtn": "Paramètres", + "replay": "Rejouer", + "delete": "Supprimer", + "deleteAll": "Tout supprimer" +}