├── .eslintrc.cjs
├── .gitignore
├── README.md
├── index.html
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
└── vite.svg
├── src
├── App.css
├── App.jsx
├── assets
│ ├── Images
│ │ ├── 1.svg
│ │ ├── 2.svg
│ │ ├── 3.svg
│ │ ├── 4.svg
│ │ ├── 5.svg
│ │ ├── 6.svg
│ │ ├── 7.svg
│ │ └── 8.svg
│ └── react.svg
├── components
│ ├── Button
│ │ ├── Button.jsx
│ │ └── getButtonStyleType.js
│ ├── HangMan
│ │ └── HangMan.jsx
│ ├── LetterButtons
│ │ └── LetterButtons.jsx
│ ├── MaskedText
│ │ ├── MaskedText.jsx
│ │ └── MaskedTextUtility.jsx
│ ├── TextInput
│ │ └── TextInput.jsx
│ └── TextInputForm
│ │ ├── TextInputForm.jsx
│ │ └── TextInputFormContainer.jsx
├── index.css
├── main.jsx
└── pages
│ ├── PlayGame
│ └── PlayGame.jsx
│ └── StartGame
│ └── StartGame.jsx
├── tailwind.config.js
└── vite.config.js
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: { browser: true, es2020: true },
4 | extends: [
5 | 'eslint:recommended',
6 | 'plugin:react/recommended',
7 | 'plugin:react/jsx-runtime',
8 | 'plugin:react-hooks/recommended',
9 | ],
10 | ignorePatterns: ['dist', '.eslintrc.cjs'],
11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
12 | settings: { react: { version: '18.2' } },
13 | plugins: ['react-refresh'],
14 | rules: {
15 | "react/prop-types": "off",
16 | 'react/jsx-no-target-blank': 'off',
17 | 'react-refresh/only-export-components': [
18 | 'warn',
19 | { allowConstantExport: true },
20 | ],
21 | },
22 | }
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React + Vite
2 |
3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
4 |
5 | Currently, two official plugins are available:
6 |
7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
9 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hangman",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.3.1",
14 | "react-dom": "^18.3.1",
15 | "react-router-dom": "^6.25.1"
16 | },
17 | "devDependencies": {
18 | "@types/react": "^18.3.3",
19 | "@types/react-dom": "^18.3.0",
20 | "@vitejs/plugin-react": "^4.3.1",
21 | "autoprefixer": "^10.4.19",
22 | "eslint": "^8.57.0",
23 | "eslint-plugin-react": "^7.34.3",
24 | "eslint-plugin-react-hooks": "^4.6.2",
25 | "eslint-plugin-react-refresh": "^0.4.7",
26 | "postcss": "^8.4.39",
27 | "tailwindcss": "^3.4.6",
28 | "vite": "^5.3.4"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/singhsanket143/HangMan-React/286374f5a8e7c2e054559025d6cecfab42a4b885/src/App.css
--------------------------------------------------------------------------------
/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { Route, Routes } from "react-router-dom";
2 |
3 | import PlayGame from "./pages/PlayGame/PlayGame";
4 | import StartGame from "./pages/StartGame/StartGame";
5 |
6 | function App() {
7 | return (
8 | //
9 | //
Welcome to Hangman
10 | // console.log("Value coming from the hidden form is", value)} />
11 | //
12 |
13 | <>
14 |
15 | Navbar
16 |
17 |
18 | } />
19 | } />
20 | not found } />
21 |
22 | >
23 |
24 |
25 | );
26 | }
27 |
28 | export default App;
29 |
--------------------------------------------------------------------------------
/src/assets/Images/1.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/Images/2.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/Images/3.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/Images/4.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/Images/5.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/Images/6.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/Images/7.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/Images/8.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/react.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/Button/Button.jsx:
--------------------------------------------------------------------------------
1 | import getButtonStyling from "./getButtonStyleType";
2 |
3 |
4 | function Button({ buttonType = "button", text, styleType, onClickHandler }) {
5 |
6 |
7 | return (
8 |
15 |
16 | );
17 |
18 | }
19 |
20 | export default Button;
--------------------------------------------------------------------------------
/src/components/Button/getButtonStyleType.js:
--------------------------------------------------------------------------------
1 | function getButtonStyling(styleType) {
2 | const primaryButtonStyling = "bg-blue-500 border border-blue-700 hover:bg-blue-700 hover:border-blue-900";
3 | const secondaryButtonStyling = "bg-gray-500 border border-gray-700 hover:bg-gray-700 hover:border-gray-900";
4 | const warningButtonStyling = "bg-yellow-500 border border-yellow-700 hover:bg-yellow-600 hover:border-yellow-900";
5 | const errorButtonStyling = "bg-red-500 border border-red-700 hover:bg-red-600 hover:border-red-900";
6 |
7 | if(styleType === "primary") {
8 | return primaryButtonStyling;
9 | } else if(styleType === "secondary") {
10 | return secondaryButtonStyling;
11 | } else if(styleType === "warning") {
12 | return warningButtonStyling;
13 | } else if(styleType === "error") {
14 | return errorButtonStyling;
15 | } else {
16 | return primaryButtonStyling;
17 | }
18 | }
19 |
20 | export default getButtonStyling;
--------------------------------------------------------------------------------
/src/components/HangMan/HangMan.jsx:
--------------------------------------------------------------------------------
1 | import Level1 from '../../assets/Images/1.svg';
2 | import Level2 from '../../assets/Images/2.svg';
3 | import Level3 from '../../assets/Images/3.svg';
4 | import Level4 from '../../assets/Images/4.svg';
5 | import Level5 from '../../assets/Images/5.svg';
6 | import Level6 from '../../assets/Images/6.svg';
7 | import Level7 from '../../assets/Images/7.svg';
8 | import Level8 from '../../assets/Images/8.svg';
9 |
10 | function HangMan({ step }) {
11 | const images = [Level1, Level2, Level3, Level4, Level5, Level6, Level7, Level8];
12 | return (
13 |
14 |

= images.length ? images[images.length-1] : images[step]} />
15 |
16 | )
17 | }
18 |
19 | export default HangMan;
--------------------------------------------------------------------------------
/src/components/LetterButtons/LetterButtons.jsx:
--------------------------------------------------------------------------------
1 |
2 | const ALPHABETS = new Array(26).fill('').map((e, index) => String.fromCharCode(65 + index));
3 | // 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('')
4 |
5 | function LetterButtons({ text, usedLetters, onLetterClick }) {
6 | const originalCharacters = new Set(text.toUpperCase().split(''));
7 | const selectedLetters = new Set(usedLetters.join('').toUpperCase().split(''));
8 |
9 | const buttonStyle = function(letter) {
10 | if (selectedLetters.has(letter)) {
11 | return `${originalCharacters.has(letter) ? 'bg-green-600 border-green-700' : 'border-[#000] border-4 bg-red-600' } `
12 | } else {
13 | return 'bg-blue-600 border-blue-700 hover:bg-blue-700'
14 | }
15 | }
16 |
17 | const handleClick = function(event) {
18 | const character = event.target.value;
19 | onLetterClick?.(character);
20 | }
21 |
22 | const buttons = ALPHABETS.map(letter => {
23 | return (
24 |
33 | );
34 | })
35 |
36 | return (
37 | <>
38 | {buttons}
39 | >
40 | )
41 | }
42 |
43 | export default LetterButtons;
--------------------------------------------------------------------------------
/src/components/MaskedText/MaskedText.jsx:
--------------------------------------------------------------------------------
1 | import { getAllCharacters } from "./MaskedTextUtility";
2 |
3 | /**
4 | *
5 | * @param {text} The word to be guessed
6 | * @param {usedLetters} The array of letters that have been guessed so far
7 | * @returns
8 | */
9 | function MaskedText({ text, usedLetters }) {
10 | const letters = getAllCharacters(text, usedLetters).split('');
11 | return (
12 | <>
13 | {letters.map((letter, index) => {
14 | return (
15 | {letter}
16 | )
17 | })}
18 | >
19 | )
20 | }
21 |
22 |
23 |
24 | export default MaskedText;
25 |
26 | // H U M B L E (B and E are Guessed) -> _ _ _ B _ E
--------------------------------------------------------------------------------
/src/components/MaskedText/MaskedTextUtility.jsx:
--------------------------------------------------------------------------------
1 | export function getAllCharacters(word, usedLetters) {
2 | // This function will return a string with all the characters of the word that have been guessed so far
3 | usedLetters = usedLetters.map(letter => letter.toUpperCase()); // ['b', 'e'] -> ['B', 'E']
4 | const guessedLetters = new Set(usedLetters); // {'B', 'E'}
5 | const characters = word.toUpperCase().split('').map(char => {
6 | if (guessedLetters.has(char)) {
7 | return char;
8 | }
9 | return '_';
10 | }); // ['_', '_', '_', 'B', '_', 'E']
11 | return characters.join(''); // ___B_E
12 | }
--------------------------------------------------------------------------------
/src/components/TextInput/TextInput.jsx:
--------------------------------------------------------------------------------
1 | function TextInput({ label, type = 'text', value, onChange }) {
2 |
3 | return (
4 |
5 |
16 | );
17 |
18 | }
19 | export default TextInput;
--------------------------------------------------------------------------------
/src/components/TextInputForm/TextInputForm.jsx:
--------------------------------------------------------------------------------
1 | // Presentation component
2 | import TextInput from "../TextInput/TextInput";
3 | import Button from "../Button/Button" ;
4 |
5 | function TextInputForm({ handleFormSubmit, handleTextInputChange, value, inputType, setInputType }) {
6 | return (
7 |
32 | );
33 |
34 | }
35 |
36 | export default TextInputForm;
--------------------------------------------------------------------------------
/src/components/TextInputForm/TextInputFormContainer.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import TextInputForm from "./TextInputForm";
3 |
4 | // Container component for TextInputForm
5 | function TextInputFormContainer({ onSubmit}) {
6 | const [value, setValue] = useState('');
7 | const [inputType, setInputType] = useState('password');
8 |
9 | function handleFormSubmit(event) {
10 | event.preventDefault();
11 | console.log('Form submitted', value);
12 | onSubmit?.(value); // if onSubmit is defined, call it with the value
13 | }
14 |
15 | function handleTextInputChange(event) {
16 | console.log('Text input changed');
17 | console.log(event.target.value);
18 | setValue(event.target.value); // whenever I Type in the input field, it will update the value
19 | }
20 |
21 | return (
22 | // Calling the presentation layer
23 |
30 | );
31 |
32 | }
33 |
34 | export default TextInputFormContainer;
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
--------------------------------------------------------------------------------
/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.jsx'
4 | import './index.css'
5 | import { BrowserRouter } from 'react-router-dom'
6 |
7 | ReactDOM.createRoot(document.getElementById('root')).render(
8 |
9 |
10 |
11 |
12 |
13 | ,
14 | )
15 |
--------------------------------------------------------------------------------
/src/pages/PlayGame/PlayGame.jsx:
--------------------------------------------------------------------------------
1 | import { Link, useLocation } from "react-router-dom";
2 | import MaskedText from "../../components/MaskedText/MaskedText";
3 | import LetterButtons from "../../components/LetterButtons/LetterButtons";
4 | import { useState } from "react";
5 | import HangMan from "../../components/HangMan/HangMan";
6 |
7 | function PlayGame() {
8 |
9 | const [usedLetters, setUsedLetters] = useState([]);
10 |
11 | const [step, setStep] = useState(0);
12 |
13 | const location = useLocation();
14 | const wordSelected = location.state?.wordSelected; // If the state is not defined, it will return undefined, otherwise it will return the value of wordSelected
15 |
16 | const handleLetterClick = function(letter) {
17 | if (wordSelected.toUpperCase().includes(letter)) {
18 | console.log('Correct');
19 | } else {
20 | console.log('Incorrect');
21 | setStep(step + 1);
22 | }
23 | setUsedLetters([...usedLetters, letter]);
24 | }
25 |
26 | return (
27 |
28 |
Play Game
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
Start Game
50 |
51 | );
52 | }
53 | export default PlayGame;
--------------------------------------------------------------------------------
/src/pages/StartGame/StartGame.jsx:
--------------------------------------------------------------------------------
1 | import { useNavigate } from "react-router-dom";
2 | import TextInputFormContainer from "../../components/TextInputForm/TextInputFormContainer";
3 |
4 | function StartGame() {
5 |
6 | const navigate = useNavigate();
7 |
8 | function handleSubmit(value) {
9 | navigate('/play', { state: {wordSelected: value}});
10 | }
11 |
12 | return (
13 |
14 |
Start Game
15 |
16 |
17 | );
18 | }
19 | export default StartGame;
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | export default {
3 | content: [
4 | "./index.html",
5 | "./src/**/*.{js,ts,jsx,tsx}",
6 | ],
7 |
8 | theme: {
9 | extend: {},
10 | },
11 | plugins: [],
12 | }
13 |
14 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------