diff --git a/src/App.tsx b/src/App.tsx index 746342c..78d8146 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,11 +1,14 @@ import React from 'react'; import cls from './App.module.scss'; -import Field from './components/Field/Field'; +import MatchGame from './components/MatchGame/MatchGame'; +import { MatchGameProvider } from './context/MatchGameContext'; function App() { return (
- + + +
); } diff --git a/src/components/BottomPanel/BottomPanel.module.scss b/src/components/BottomPanel/BottomPanel.module.scss index ca57b39..a1f6e05 100644 --- a/src/components/BottomPanel/BottomPanel.module.scss +++ b/src/components/BottomPanel/BottomPanel.module.scss @@ -7,40 +7,5 @@ align-items: center; border: 2px solid #fff; border-radius: 10px; - - .Timer { - font-size: 40px; - font-weight: bold; - font-family: "Pacifico", sans-serif; - color: #ffffff; - } - - .StartButton { - text-decoration: none; - display: inline-block; - width: 140px; - height: 45px; - border-radius: 45px; - margin: 10px 20px; - font-family: "Pacifico", sans-serif; - font-size: 22px; - text-transform: uppercase; - text-align: center; - letter-spacing: 3px; - font-weight: 600; - color: #524f4e; - background: white; - box-shadow: 0 8px 15px rgba(0, 0, 0, .1); - //background: #7986cb; - transition: .3s; - - &:hover { - background: #7986cb; - box-shadow: 0 15px 20px rgba(121, 134, 203, 0.4); - color: white; - transform: translateY(-7px); - cursor: pointer; - } - } } diff --git a/src/components/BottomPanel/BottomPanel.tsx b/src/components/BottomPanel/BottomPanel.tsx index 1042b1b..7d72f7b 100644 --- a/src/components/BottomPanel/BottomPanel.tsx +++ b/src/components/BottomPanel/BottomPanel.tsx @@ -1,64 +1,81 @@ +import { useMatchGame } from '../../context/MatchGameContext'; +import Button from '../UI/Button/Button'; import React, { useEffect, useState } from 'react'; +import Timer from '../UI/Timer/Timer'; import cls from './BottomPanel.module.scss'; type BottomPanelProps = { - start: () => void; - retry?: () => void; + startGame: () => void; + retryGame: () => void; isFinishedGame?: boolean; + shuffleCards: () => void; } -const BottomPanel: React.FC = (props) => { - const [startTime, setStartTime] = useState(null); - const [timeInterval, setTimeInterval] = useState(); +type PanelState = 'start' | 'retry'; +const BottomPanel: React.FC = (props) => { + const [isTimerStarted, setIsTimerStarted] = useState(false); + const [isTimerStopped, setIsTimerStopped] = useState(false); + const [panelState, setPanelState] = useState('start'); function onStartBtnClickHandler(event: React.MouseEvent) { - setStartTime(0); - const interval = setInterval(() => { - setStartTime(prev => prev !== null ? prev + 1000 : 0) - }, 1000); - setTimeInterval(interval); - props.start(); + setIsTimerStarted(true); + props.startGame(); } useEffect(() => { - clearInterval(timeInterval) + setIsTimerStopped(true) + setPanelState(props.isFinishedGame ? 'retry' : 'start') + }, [props.isFinishedGame]) - function renderActiveStatePanel() { - return startTime !== null - ?

{getTimeString(new Date(startTime))}

- : - } - function onRetryBtnClickHandler() { - setStartTime(null); - props.retry?.(); + setIsTimerStarted(false); + setIsTimerStopped(false); + setPanelState('start'); + + props.retryGame?.(); } - function renderRetryButton() { + function renderRetryStatePanel() { return ( <> - + ) } + function onShuffleBtnClickHandler() { + props.shuffleCards?.(); + } + + function renderStartStatePanel() { + return isTimerStarted + ? + : <> + + + + } + + function renderPanelByState(state: PanelState) { + if (!state) { + console.error('Нет состояния у панели') + return; + } + + switch (state) { + case 'start': return renderStartStatePanel(); + case 'retry': return renderRetryStatePanel(); + default: return; + } + } + return (
- { props.isFinishedGame ? renderRetryButton() : renderActiveStatePanel() } + { renderPanelByState(panelState) }
); }; -function getTimeString(time: Date): string { - let minutes = time.getMinutes().toString(); - if (+minutes < 10) minutes = '0' + minutes; - - let seconds = time.getSeconds().toString(); - if (+seconds < 10) seconds = '0' + seconds; - - return `${minutes}:${seconds}` -} - export default BottomPanel; diff --git a/src/components/Card/Card.tsx b/src/components/Card/Card.tsx index dc7cf2a..6db3c79 100644 --- a/src/components/Card/Card.tsx +++ b/src/components/Card/Card.tsx @@ -1,32 +1,33 @@ -import React, {useMemo} from 'react'; +import React, { useMemo, useState } from 'react'; import cls from './Card.module.scss' -export type CardProps = { - title: string; - code: string; - onReverse?: (a: any) => void; +export type CardComponentProps = { + title?: string; imageURL: string; id: string; toggled: boolean; - disabled: boolean; isMatched: boolean; + disabled: boolean; + onClick?: (id: string) => void; + code?: string; } -const Card: React.FC = (props) => { +const Card: React.FC = (props) => { + const classes = [cls.Card, props.toggled ? cls.reverse : '']; - const canClick = useMemo(() => { - return !props.disabled && !props.isMatched - }, [props.disabled, props.isMatched]); + function cardClickHandler(id: string) { + if (props.disabled) return; - const classes = useMemo(() => { - return props.toggled ? [cls.Card, cls.reverse] : [cls.Card]; - }, [props.toggled]) + props.onClick?.(id); + } return ( -
-
canClick && props.onReverse?.(props)}> +
cardClickHandler(props.id)} + > +
{props.title}
{props.title}/ diff --git a/src/components/CardsField/CardsField.module.scss b/src/components/CardsField/CardsField.module.scss new file mode 100644 index 0000000..e6f5d04 --- /dev/null +++ b/src/components/CardsField/CardsField.module.scss @@ -0,0 +1,17 @@ +.CardsField { + background: #283593; + position: relative; + border: 2px solid #fff; + border-radius: 10px; + width: 500px; + height: 500px; + display: grid; + grid-template-columns: 1fr 1fr 1fr; + grid-template-rows: 1fr 1fr 1fr 1fr; + grid-auto-flow: row; + grid-template-areas: + ". . ." + ". . ." + ". . ." + ". . ."; +} diff --git a/src/components/CardsField/CardsField.tsx b/src/components/CardsField/CardsField.tsx new file mode 100644 index 0000000..b7491f0 --- /dev/null +++ b/src/components/CardsField/CardsField.tsx @@ -0,0 +1,139 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import { v4 as uuidv4 } from 'uuid'; +import { useMatchGame } from '../../context/MatchGameContext'; +import Card, { CardComponentProps } from '../Card/Card'; +import cls from './CardsField.module.scss'; + +type CardProps = { + code: string; + imageURL: string; +} + +type CardsFieldProps = { + cards: CardProps[]; + isGameStarted: boolean; + finishGameCallback: () => void; +} + +type CardHandlerType = + 'disableAll' | + 'enableAll' | + 'showAll' | + 'hideAll'; + +type CurrentCardPareType = { + first: CardComponentProps | null; + second: CardComponentProps | null; +} + +const CardsField: React.FC = ({cards, isGameStarted, finishGameCallback}) => { + // TODO use useReducer + const [cardsState, setCardsState] = useState(cards.map(el => createCard(el))); + const [currentCard, setCurrentCard] = useState(null) + const [currentCardPare, setCurrentCardPare] = useState({first: null, second: null}) + + const {addMistakesCount} = useMatchGame(); + + function matchHandler(prev: CardComponentProps, card: CardComponentProps) { + setCardsState(prevState => prevState.map(el => { + if (el.id === card.id || el.id === prev.id) return {...el, disabled: true, isMatched: true} + else return {...el, disabled: true} + })); + setTimeout(() => cardsHandler('enableAll'), 500) + } + + function notMatchHandler() { + cardsHandler('disableAll'); + addMistakesCount?.(); + setTimeout(() => { + setCardsState(prevState => prevState.map(el => { + return {...el, toggled: false, disabled: false, isMatched: false}; + })) + }, 1000); + } + + function cardsHandler(type: CardHandlerType) { + switch (type) { + case 'disableAll': + setCardsState(prevState => prevState.map(el => ({...el, disabled: true}))); + return; + case 'enableAll': + setCardsState(prevState => prevState.map(el => ({...el, disabled: false}))); + return; + case 'showAll': + setCardsState(prevState => prevState.map(el => ({...el, toggled: true}))); + return; + case 'hideAll': + setCardsState(prevState => prevState.map(el => ({...el, toggled: false}))); + return; + default: return; + } + } + + const cardClickHandler = useCallback((id: string) => { + const targetCard = cardsState.find(el => el.id === id); + if (!targetCard) return; + setCardsState(prevState => prevState.map(el => el.id === id ? {...el, toggled: !el.toggled, disabled: true} : el)) + setCurrentCardPare(prev => { + if (!prev.first) return {first: targetCard, second: null}; + if (prev.first && !prev.second) return {...prev, second: targetCard}; + + return {first: targetCard, second: null} + }) + }, [cardsState]) + + function isAllCardsMatched(cards: CardComponentProps[]) { + return cards.reduce((res, cur) => (res && cur.isMatched), true) + } + + useEffect(() => { + if (isAllCardsMatched(cardsState)) setTimeout(finishGameCallback, 800); + }, [cardsState]) + + useEffect(() => { + const {first, second} = currentCardPare; + if (first && second) { + if (first?.code === second?.code) matchHandler(first, second) + else notMatchHandler(); + } + }, [currentCardPare]) + + useEffect(() => { + if (!isGameStarted) return; + setCardsState(prev => (prev.map(el => ({...el, disabled: false, toggled: false})))); + }, [isGameStarted]) + + useEffect(() => { + setCardsState(cards.map(el => createCard(el))) + }, [cards]) + + function createCard (card: CardProps): CardComponentProps { + return { + id: uuidv4(), + toggled: !isGameStarted, + disabled: !isGameStarted, + isMatched: false, + ...card + }; + } + + function renderCards() { + return cardsState.map((el, index) => ( + cardClickHandler(el.id)} + > + + )); + } + + return ( +
+ {renderCards()} +
+ ); +}; + +export default CardsField; diff --git a/src/components/Field/Field.module.scss b/src/components/Field/Field.module.scss deleted file mode 100644 index d076ec6..0000000 --- a/src/components/Field/Field.module.scss +++ /dev/null @@ -1,51 +0,0 @@ -.Field { - background: #283593; - position: relative; - border: 2px solid #fff; - border-radius: 10px; - width: 500px; - height: 500px; - display: grid; - grid-template-columns: 1fr 1fr 1fr; - grid-template-rows: 1fr 1fr 1fr 1fr; - grid-auto-flow: row; - grid-template-areas: - ". . ." - ". . ." - ". . ." - ". . ."; -} - -.FieldFinish { - width: 500px; - height: 500px; - background: #283593; - position: relative; - border: 2px solid #fff; - box-sizing: border-box; - padding: 10px; - border-radius: 10px; - - h1 { - text-align: center; - font-size: 40px; - } - p { - display: flex; - justify-content: space-between; - font-size: 24px; - - - span { - font-style: italic; - font-weight: bold; - } - } -} - -.FinishBlock { - color: #ffffff; - max-width: 320px; - margin: 0 auto; - font-family: 'Pacifico', sans-serif; -} diff --git a/src/components/Field/Field.tsx b/src/components/Field/Field.tsx deleted file mode 100644 index 83592fa..0000000 --- a/src/components/Field/Field.tsx +++ /dev/null @@ -1,181 +0,0 @@ -import BottomPanel from '../BottomPanel/BottomPanel'; -import Alert from '../Alert/Alert'; -import React, { useEffect, useState } from 'react'; -import Card, { CardProps } from '../Card/Card'; -import cls from './Field.module.scss'; -import { v4 as uuidv4 } from 'uuid'; - -const Field: React.FC = () => { - - const img = { - react: 'https://w7.pngwing.com/pngs/79/518/png-transparent-js-react-js-logo-react-react-native-logos-icon-thumbnail.png', - js: 'https://www.freepnglogos.com/uploads/javascript-png/javascript-logo-transparent-logo-javascript-images-3.png', - angular: 'https://upload.wikimedia.org/wikipedia/commons/thumb/c/cf/Angular_full_color_logo.svg/1200px-Angular_full_color_logo.svg.png', - } - - function matchHandler(prev: CardProps, card: CardProps) { - setAlertState(true); - setCards(prevState => prevState.map(el => { - if (el.id === card.id || el.id === prev.id) return {...el, disabled: true, isMatched: true} - else return {...el, disabled: true} - })) - setTimeout(enableCards, 1000) - } - - function notMatchHandler() { - setAlertState(false); - setMistakesCount(prev => prev + 1); - disableCards(); - setTimeout(() => { - setAlertState(null); - setCards(prevState => prevState.map(el => { - return {...el, toggled: false, disabled: false, isMatched: false}; - })) - }, 1000) - } - - function reverseCardHandler(card: CardProps) { - setCards(prevState => prevState.map(el => el.id === card.id ? {...el, toggled: !el.toggled, disabled: true} : el)) - - setCurrentCard(prev => { - if (!prev) return card; - else { - if (prev.code === card.code) matchHandler(prev, card); - else notMatchHandler(); - - return null; - } - }); - } - - function disableCards() { - setCards(prevState => prevState.map(el => ({...el, disabled: true}))); - } - - function enableCards() { - setAlertState(null); - setCards(prevState => prevState.map(el => ({...el, disabled: false}))); - } - - function showCards() { - setCards(prevState => prevState.map(el => ({...el, toggled: true}))); - } - - function hideCards() { - setCards(prevState => prevState.map(el => ({...el, toggled: false}))); - } - - function hideCard(id: string) { - setCards(prevState => prevState.map((el) => ( - el.id === id ? {...el, toggled: false} : el - ))) - } - - function createCard (title: string, code: string, imageURL: string): CardProps { - return {id: uuidv4(), title, code, imageURL, onReverse: reverseCardHandler, toggled: true, disabled: false, isMatched: false}; - } - - const [currentCard, setCurrentCard] = useState(null) - - const [alertState, setAlertState] = useState(null); - - const [cards, setCards] = useState(randomizeElementInArray([ - createCard('1', 'react', img.react), - createCard('2', 'angular', img.angular), - createCard('3', 'angular', img.angular), - createCard('4', 'react', img.react), - createCard('5', 'vue', img.js), - createCard('6', 'angular', img.angular), - createCard('7', 'react', img.react), - createCard('8', 'vue', img.js), - createCard('9', 'angular', img.angular), - createCard('10', 'react', img.react), - createCard('11', 'vue', img.js), - createCard('12', 'vue', img.js), - ])); - - const [isGameFinished, setGameFinish] = useState(false); - - const [isGameStarted, setGameStart] = useState(false); - - const [mistakesCount, setMistakesCount] = useState(0); - - - useEffect(() => { - const isFinished = cards.reduce((res, cur) => res = res && cur.isMatched, true); - isFinished && setTimeout(() => setGameFinish(true), 1500); - }, [cards]); - - useEffect(() => { - showCards(); - }, []) - - function renderCards() { - return cards.map((el, index) => ); - } - - function renderAlert(state: boolean | null) { - if (state === null) return; - - if (state) return ; - - return - } - - function startGame() { - setGameStart(true); - cards.forEach((card, index) => { - setTimeout(() => hideCard(card.id), index*100) - }) - } - - function retryGame() { - setGameFinish(false); - setGameStart(false); - setCards(prev => randomizeElementInArray(prev.map(el => ({...el, disabled: false, isMatched: false, toggled: true})))) - } - - function getWrapperClassList() { - let classes = [cls.Field]; - - if (isGameFinished) classes = [cls.FieldFinish]; - - return classes; - } - - - return ( - <> -
- { - isGameFinished - ? ( -
-

GAME IS FINISHED !

-

Время: 10:10

-

Количество ошибок: {mistakesCount}

-

Сложность: Легко

-

Размер поля: 4х4

-
- ) - : ( - <> - {renderCards()} - {renderAlert(alertState)} - - ) - } -
- {} - - - ); -}; - -function randomizeElementInArray (arr: any[]) { - return arr.sort(() => { - return Math.random() > 0.5 ? 1 : -1; - }) -} - -export default Field; diff --git a/src/components/FinishedField/FinishedField.module.scss b/src/components/FinishedField/FinishedField.module.scss new file mode 100644 index 0000000..2d1a9c4 --- /dev/null +++ b/src/components/FinishedField/FinishedField.module.scss @@ -0,0 +1,6 @@ +.FinishedField { + color: #ffffff; + max-width: 320px; + margin: 0 auto; + font-family: 'Pacifico', sans-serif; +} diff --git a/src/components/FinishedField/FinishedField.tsx b/src/components/FinishedField/FinishedField.tsx new file mode 100644 index 0000000..e819489 --- /dev/null +++ b/src/components/FinishedField/FinishedField.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import { useMatchGame } from '../../context/MatchGameContext'; +import cls from './FinishedField.module.scss'; + +type FinishedFieldProps = { + mistakesCount?: number; + gameTime?: string; +} + +const FinishedField: React.FC = ({mistakesCount, gameTime}) => { + // const {mistakesCount, gameTime} = useMatchGame(); + + return ( +
+

GAME IS FINISHED !

+

Время: {gameTime}

+

Количество ошибок: {mistakesCount}

+

Сложность: difficultyLevel

+

Размер поля: fieldSize

+
+ ); +}; + +export default FinishedField; diff --git a/src/components/MatchGame/MatchGame.module.scss b/src/components/MatchGame/MatchGame.module.scss new file mode 100644 index 0000000..4139e4e --- /dev/null +++ b/src/components/MatchGame/MatchGame.module.scss @@ -0,0 +1,26 @@ +.FieldFinish { + width: 500px; + height: 500px; + background: #283593; + position: relative; + border: 2px solid #fff; + box-sizing: border-box; + padding: 10px; + border-radius: 10px; + + h1 { + text-align: center; + font-size: 40px; + } + p { + display: flex; + justify-content: space-between; + font-size: 24px; + + + span { + font-style: italic; + font-weight: bold; + } + } +} diff --git a/src/components/MatchGame/MatchGame.tsx b/src/components/MatchGame/MatchGame.tsx new file mode 100644 index 0000000..1588ec4 --- /dev/null +++ b/src/components/MatchGame/MatchGame.tsx @@ -0,0 +1,70 @@ +import React, { useState } from 'react'; +import { cardImages } from '../../constants'; +import { useMatchGame } from '../../context/MatchGameContext'; +import { randomizeElementInArray } from '../../utils'; +import BottomPanel from '../BottomPanel/BottomPanel'; +import CardsField from '../CardsField/CardsField'; +import FinishedField from '../FinishedField/FinishedField'; +import cls from './MatchGame.module.scss'; + +const MatchGame: React.FC = () => { + // TODO replace to CardsField + const [cards, setCards] = useState(randomizeElementInArray([ + {code: 'react', imageURL: cardImages.react}, + {code: 'angular', imageURL: cardImages.angular}, + {code: 'js', imageURL: cardImages.js}, + {code: 'react', imageURL: cardImages.react}, + {code: 'angular', imageURL: cardImages.angular}, + {code: 'js', imageURL: cardImages.js}, + ])); + + const [isGameFinished, setGameFinish] = useState(false); + const [isGameStarted, setGameStart] = useState(false); + + const {gameTime, mistakesCount, resetGameProgress} = useMatchGame(); + + function startGame() { + setGameStart(true); + } + + function retryGame() { + setGameFinish(false); + setGameStart(false); + setCards(prev => randomizeElementInArray(prev.map(el => ({...el, disabled: false, isMatched: false, toggled: true})))) + + resetGameProgress?.(); + } + + function finishGame() { + setGameFinish(true); + } + + // TODO replace to CardsField + function shuffleCards() { + setCards(randomizeElementInArray([...cards])); + } + + function getWrapperClassList() { + let classes = [cls.Field]; + + if (isGameFinished) classes = [cls.FieldFinish]; + + return classes; + } + + return ( + <> +
+ { + isGameFinished + ? + : + } +
+ + + + ); +}; + +export default MatchGame; diff --git a/src/components/UI/Button/Button.module.scss b/src/components/UI/Button/Button.module.scss new file mode 100644 index 0000000..9e06d60 --- /dev/null +++ b/src/components/UI/Button/Button.module.scss @@ -0,0 +1,26 @@ +.Button { + text-decoration: none; + display: inline-block; + width: 140px; + height: 45px; + border-radius: 45px; + margin: 10px 20px; + font-family: "Pacifico", sans-serif; + font-size: 22px; + text-transform: uppercase; + text-align: center; + letter-spacing: 3px; + font-weight: 600; + color: #524f4e; + background: white; + box-shadow: 0 8px 15px rgba(0, 0, 0, .1); + transition: .3s; + + &:hover { + background: #7986cb; + box-shadow: 0 15px 20px rgba(121, 134, 203, 0.4); + color: white; + transform: translateY(-7px); + cursor: pointer; + } +} diff --git a/src/components/UI/Button/Button.tsx b/src/components/UI/Button/Button.tsx new file mode 100644 index 0000000..0c24d96 --- /dev/null +++ b/src/components/UI/Button/Button.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import cls from './Button.module.scss'; + +type ButtonProps = { + className?: string, + onClick: (e: React.MouseEvent) => void +} + +const Button: React.FC = ({children, className, onClick}) => { + return ( + + ); +}; + +export default Button; diff --git a/src/components/UI/Timer/Timer.module.scss b/src/components/UI/Timer/Timer.module.scss new file mode 100644 index 0000000..d0c4042 --- /dev/null +++ b/src/components/UI/Timer/Timer.module.scss @@ -0,0 +1,6 @@ +.Timer { + font-size: 40px; + font-weight: bold; + font-family: "Pacifico", sans-serif; + color: #ffffff; +} diff --git a/src/components/UI/Timer/Timer.tsx b/src/components/UI/Timer/Timer.tsx new file mode 100644 index 0000000..fcf38b4 --- /dev/null +++ b/src/components/UI/Timer/Timer.tsx @@ -0,0 +1,38 @@ +import React, { useEffect, useState } from 'react'; +import { useMatchGame } from '../../../context/MatchGameContext'; +import { getTimeString } from '../../../utils'; +import cls from './Timer.module.scss' + +type TimerProps = { + startTime: number; + isStarted: boolean; + isFinished: boolean; +} + +const Timer: React.FC = ({startTime, isStarted, isFinished}) => { + const [time, setTime] = useState(startTime); + const [timeInterval, setTimeInterval] = useState(null) + const {changeGameTime} = useMatchGame(); + + useEffect(() => { + if (!isStarted) return; + + setTimeInterval(setInterval(() => setTime(prev => prev + 1000), 1000)); + }, [isStarted]) + + useEffect(() => { + clearInterval(timeInterval as NodeJS.Timeout); + }, [isFinished]) + + useEffect(() => { + changeGameTime?.(time); + }, [time]) + + return ( +
+

{getTimeString(new Date(time))}

+
+ ); +}; + +export default Timer; diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 0000000..1c2df0d --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,5 @@ +export const cardImages = { + react: 'https://w7.pngwing.com/pngs/79/518/png-transparent-js-react-js-logo-react-react-native-logos-icon-thumbnail.png', + js: 'https://www.freepnglogos.com/uploads/javascript-png/javascript-logo-transparent-logo-javascript-images-3.png', + angular: 'https://upload.wikimedia.org/wikipedia/commons/thumb/c/cf/Angular_full_color_logo.svg/1200px-Angular_full_color_logo.svg.png', +} diff --git a/src/context/MatchGameContext.tsx b/src/context/MatchGameContext.tsx new file mode 100644 index 0000000..d3e5546 --- /dev/null +++ b/src/context/MatchGameContext.tsx @@ -0,0 +1,42 @@ +import React, { useContext, useState } from 'react'; +import { getTimeString } from '../utils'; + +type MatchGameContextType = { + mistakesCount?: number; + addMistakesCount?: () => void; + gameTime?: string; + changeGameTime?: (time: number) => void; + resetGameProgress?: () => void; +} +const MatchGameContext = React.createContext({}); + +export const useMatchGame = () => useContext(MatchGameContext); +export const MatchGameProvider: React.FC = ({children}) => { + const [mistakesCount, setMistakesCount] = useState(0) + const [gameTime, setGameTime] = useState(''); + + const addMistakesCount = () => { + return setMistakesCount(prev => prev + 1) + }; + + const changeGameTime = (time: number) => { + setGameTime(getTimeString(new Date(time))); + } + + const resetGameProgress = () => { + setMistakesCount(0); + setGameTime(getTimeString(new Date(0))); + } + + return ( + + {children} + + ) +} diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..cb175de --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,18 @@ +export function randomizeElementInArray (arr: any[]) { + return arr.sort(() => { + return Math.random() > 0.5 ? 1 : -1; + }) +} + +export function getTimeString(time?: Date): string { + if (!time) return ''; + + let minutes = time.getMinutes().toString(); + if (+minutes < 10) minutes = '0' + minutes; + + let seconds = time.getSeconds().toString(); + if (+seconds < 10) seconds = '0' + seconds; + + return `${minutes}:${seconds}` +} + diff --git a/tsconfig.json b/tsconfig.json index 4abc702..14f9bcf 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,13 +19,13 @@ "isolatedModules": true, "noEmit": true, "jsx": "react-jsx", - "paths": { - "components/*": ["src/components/*"], - "core/*": ["src/core/*"], - "pages/*": ["src/pages/*"], - "redux/*": ["src/redux/*"], - "src/*": ["src/*"], - } +// "paths": { +// "components/*": ["src/components/*"], +// "core/*": ["src/core/*"], +// "pages/*": ["src/pages/*"], +// "redux/*": ["src/redux/*"], +// "src/*": ["src/*"], +// } }, "include": [ "src"