diff --git a/src/components/Banner/Banner.js b/src/components/Banner/Banner.js new file mode 100644 index 000000000..6cd8bc311 --- /dev/null +++ b/src/components/Banner/Banner.js @@ -0,0 +1,23 @@ +import React from 'react'; + +function Banner({ isWinner, answer, words }) { + return ( + <> + {isWinner ? ( +
+

+ Congratulations! Got it in + {" "} + {words.length} guesses. +

+
+ ) : ( +
+

Sorry, the correct answer is {answer}.

+
+ )} + + ); +} + +export default Banner; diff --git a/src/components/Banner/index.js b/src/components/Banner/index.js new file mode 100644 index 000000000..1a3d55b83 --- /dev/null +++ b/src/components/Banner/index.js @@ -0,0 +1,2 @@ +export * from './Banner'; +export { default } from './Banner'; diff --git a/src/components/Cell/Cell.js b/src/components/Cell/Cell.js new file mode 100644 index 000000000..177a4879c --- /dev/null +++ b/src/components/Cell/Cell.js @@ -0,0 +1,9 @@ +import React from 'react'; + +function Cell({ letter, status }) { + const className = status ? `cell ${status}` : 'cell'; + + return {letter}; +} + +export default Cell; diff --git a/src/components/Cell/index.js b/src/components/Cell/index.js new file mode 100644 index 000000000..07cf6981b --- /dev/null +++ b/src/components/Cell/index.js @@ -0,0 +1,2 @@ +export * from './Cell'; +export { default } from './Cell'; diff --git a/src/components/Game/Game.js b/src/components/Game/Game.js index fc7615d24..176b9260b 100644 --- a/src/components/Game/Game.js +++ b/src/components/Game/Game.js @@ -1,7 +1,10 @@ -import React from 'react'; +import React, { useState } from 'react'; import { sample } from '../../utils'; import { WORDS } from '../../data'; +import Input from '../Input/Input'; +import PreviousWord from '../PreviousWord/PreviousWord'; +import Banner from '../Banner/Banner'; // Pick a random word on every pageload. const answer = sample(WORDS); @@ -9,7 +12,58 @@ const answer = sample(WORDS); console.info({ answer }); function Game() { - return <>Put a game here!; + const [words, setWords] = useState([]); + const [isOver, setIsOver] = useState(false); + const [isWinner, setIsWinner] = useState(false); + + const handleSaveWord = (input) => { + setWords([...words, input]); + + if (answer === input.value) { + setIsWinner(true); + setIsOver(true); + } else if (words.length >= 5) { + setIsOver(true); + setIsWinner(false); + } else { + setIsOver(false); + } + }; + + const handleResetGame = () => { + const newGame = []; + + setWords(newGame); + setIsOver(false); + window.location.reload(); + }; + + return ( + <> + + + + {isOver ? + : + undefined + } + + ); } export default Game; diff --git a/src/components/Guess/Guess.js b/src/components/Guess/Guess.js new file mode 100644 index 000000000..bead1f974 --- /dev/null +++ b/src/components/Guess/Guess.js @@ -0,0 +1,26 @@ +import React from 'react'; +import { range } from '../../utils'; +import Cell from '../Cell/Cell'; +import { checkGuess } from '../../game-helpers'; + +function Guess({ word, answer }) { + const result = checkGuess(word, answer); + + return ( +

+ { + range(5).map(num => { + return ( + + ); + }) + } +

+ ); +} + +export default Guess; diff --git a/src/components/Guess/index.js b/src/components/Guess/index.js new file mode 100644 index 000000000..afbf137b5 --- /dev/null +++ b/src/components/Guess/index.js @@ -0,0 +1,2 @@ +export * from './Guess'; +export { default } from './Guess'; diff --git a/src/components/Input/Input.js b/src/components/Input/Input.js new file mode 100644 index 000000000..d3f81d463 --- /dev/null +++ b/src/components/Input/Input.js @@ -0,0 +1,42 @@ +import React, { useState } from 'react'; + +const Input = ({ handleSaveWord, isOver }) => { + const [tentativeGuess, setTentativeGuess] = useState(''); + + const handleSubmit = (e) => { + e.preventDefault(); + if (tentativeGuess.length !== 5) { + alert('The word must be equal to 5 letters'); + return; + }; + + const newWord = { + id: crypto.randomUUID(), + value: tentativeGuess + } + + handleSaveWord(newWord); + setTentativeGuess(''); + }; + + return ( +
+ + { + setTentativeGuess(event.target.value.toUpperCase()) + }} + /> +
+ ); +}; + +export default Input; diff --git a/src/components/Input/index.js b/src/components/Input/index.js new file mode 100644 index 000000000..998f66c56 --- /dev/null +++ b/src/components/Input/index.js @@ -0,0 +1,2 @@ +export * from './Input'; +export { default } from './Input'; \ No newline at end of file diff --git a/src/components/PreviousWord/PreviousWord.js b/src/components/PreviousWord/PreviousWord.js new file mode 100644 index 000000000..f1a8c9edb --- /dev/null +++ b/src/components/PreviousWord/PreviousWord.js @@ -0,0 +1,18 @@ +import React from 'react'; +import Guess from '../Guess/Guess'; +import { NUM_OF_GUESSES_ALLOWED } from '../../constants'; +import { range } from '../../utils'; + +function PreviousWord({ words, answer }) { + return ( +
+ {range(NUM_OF_GUESSES_ALLOWED).map(num => { + return ( + + ); + })} +
+ ); +} + +export default PreviousWord; diff --git a/src/components/PreviousWord/index.js b/src/components/PreviousWord/index.js new file mode 100644 index 000000000..633d27d9b --- /dev/null +++ b/src/components/PreviousWord/index.js @@ -0,0 +1,2 @@ +export * from './PreviousWord'; +export { default } from './PreviousWord'; diff --git a/src/styles.css b/src/styles.css index f54c1cfd7..820c4d8b6 100644 --- a/src/styles.css +++ b/src/styles.css @@ -4,7 +4,7 @@ html { overflow-y: scroll; - --game-spacing: 32px; + --game-spacing: 20px; --header-height: 4rem; --color-success: hsl(150deg 70% 30%); @@ -77,17 +77,16 @@ h1 { display: flex; flex-direction: column; gap: var(--game-spacing); - padding: var(--game-spacing) 32px; + padding: var(--game-spacing) 15px; margin: 0 auto; min-width: 250px; max-width: min(500px, 58vh, 100%); } .guess-results { - flex: 1; display: flex; flex-direction: column; - justify-content: center; + justify-content: flex-start; } .guess { @@ -114,12 +113,15 @@ h1 { .guess:first-of-type .cell:first-of-type { --radius: 4px 0px 0px 0px; } + .guess:first-of-type .cell:last-of-type { --radius: 0px 4px 0px 0px; } + .guess:last-of-type .cell:last-of-type { --radius: 0px 0px 4px 0px; } + .guess:last-of-type .cell:first-of-type { --radius: 0px 0px 0px 4px; } @@ -129,11 +131,13 @@ h1 { border-color: var(--color-success); color: white; } + .cell.incorrect { background: var(--color-gray-300); border-color: var(--color-gray-300); color: white; } + .cell.misplaced { background: var(--color-warning); border-color: var(--color-warning); @@ -159,6 +163,7 @@ h1 { border-radius: 4px; padding: 8px 16px; outline-offset: 4px; + outline: none; } .banner { @@ -180,6 +185,7 @@ h1 { background: var(--color-success); color: white; } + .sad.banner { background: var(--color-error); color: white; @@ -208,6 +214,7 @@ h1 { border-radius: 8px; padding: 24px 32px; } + .modal-close-btn { position: absolute; top: 0; @@ -215,10 +222,19 @@ h1 { padding: 16px; cursor: pointer; } + .modal-title { margin-bottom: 0.5em; } +.restart { + text-align: center; + padding: 10px 15px; + background-color: var(--color-gray-300); + color: #fff; + border-radius: var(--radius); +} + /* Keyframe animations */ @@ -226,7 +242,8 @@ h1 { from { transform: translateY(100%); } + to { transform: translateY(0%); } -} +} \ No newline at end of file