add styles
This commit is contained in:
parent
9ab31e6c35
commit
534e185c55
@ -1,14 +1,14 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Pacifico&display=swap');
|
||||
|
||||
.CardWrapper{
|
||||
.CardWrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: opacity 1300ms ease-in;
|
||||
|
||||
.Card {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
font-family: 'Pacifico', sans-serif;
|
||||
font-size: 48px;
|
||||
display: flex;
|
||||
@ -56,3 +56,13 @@
|
||||
}
|
||||
|
||||
|
||||
@media(max-width: 400px) {
|
||||
.CardWrapper {
|
||||
.Card {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -3,15 +3,8 @@
|
||||
position: relative;
|
||||
border: 2px solid #fff;
|
||||
border-radius: 10px;
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
min-height: 500px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
grid-template-rows: 1fr 1fr 1fr 1fr;
|
||||
grid-auto-flow: row;
|
||||
grid-template-areas:
|
||||
". . ."
|
||||
". . ."
|
||||
". . ."
|
||||
". . .";
|
||||
gap: 15px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { DiffLevel, FieldSize } from 'pages/Options/Options';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
@ -34,7 +35,7 @@ const CardsField: React.FC<CardsFieldProps> = ({cards, isGameStarted = false, fi
|
||||
const [cardsState, setCardsState] = useState<CardComponentProps[]>(getInitialConfig(isNewGame));
|
||||
const [currentCardPare, setCurrentCardPare] = useState<CurrentCardPareType>({first: null, second: null})
|
||||
|
||||
const {addMistakesCount} = useMatchGame();
|
||||
const {addMistakesCount, filedSize, diffLevel} = useMatchGame();
|
||||
|
||||
function getInitialConfig(isNewGame: boolean): CardComponentProps[] {
|
||||
if (!isNewGame) {
|
||||
@ -142,8 +143,25 @@ const CardsField: React.FC<CardsFieldProps> = ({cards, isGameStarted = false, fi
|
||||
));
|
||||
}
|
||||
|
||||
function getStyles(): React.CSSProperties {
|
||||
const size = {
|
||||
gridTemplateColumns: '',
|
||||
gridTemplateRows: '',
|
||||
};
|
||||
|
||||
if (diffLevel === DiffLevel.Easy) size.gridTemplateColumns = 'repeat(3, 1fr)';
|
||||
if (diffLevel === DiffLevel.Normal) size.gridTemplateColumns = 'repeat(4, 1fr)';
|
||||
if (diffLevel === DiffLevel.Hard) size.gridTemplateColumns = 'repeat(5, 1fr)';
|
||||
|
||||
if (filedSize === FieldSize.Small) size.gridTemplateRows = 'repeat(2, 1fr)';
|
||||
if (filedSize === FieldSize.Normal) size.gridTemplateRows = 'repeat(4, 1fr)';
|
||||
if (filedSize === FieldSize.Big) size.gridTemplateRows = 'repeat(6, 1fr)';
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cls.CardsField}>
|
||||
<div className={cls.CardsField} style={getStyles()}>
|
||||
{renderCards()}
|
||||
</div>
|
||||
);
|
||||
|
||||
4
src/components/GoToMenuButton/GoToMenuButton.module.scss
Normal file
4
src/components/GoToMenuButton/GoToMenuButton.module.scss
Normal file
@ -0,0 +1,4 @@
|
||||
.Button {
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
}
|
||||
20
src/components/GoToMenuButton/GoToMenuButton.tsx
Normal file
20
src/components/GoToMenuButton/GoToMenuButton.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import Button from 'components/UI/Button/Button';
|
||||
import { useMatchGame } from 'context/MatchGameContext';
|
||||
|
||||
import cls from './GoToMenuButton.module.scss';
|
||||
|
||||
const GoToMenuButton = () => {
|
||||
const {setCurrentMenuItem, setIsGameStarted} = useMatchGame();
|
||||
|
||||
function onClickHandler() {
|
||||
setIsGameStarted?.(false);
|
||||
setCurrentMenuItem?.('menu')
|
||||
}
|
||||
|
||||
return (
|
||||
<Button className={cls.Button} onClick={onClickHandler}>⇦</Button>
|
||||
);
|
||||
};
|
||||
|
||||
export default GoToMenuButton;
|
||||
@ -7,5 +7,6 @@
|
||||
align-items: center;
|
||||
border: 2px solid #fff;
|
||||
border-radius: 10px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
|
||||
@ -2,11 +2,13 @@ import React from 'react';
|
||||
|
||||
import cls from './BottomPanel.module.scss';
|
||||
|
||||
type BottomPanelProps = {}
|
||||
type BottomPanelProps = {
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
const BottomPanel: React.FC<BottomPanelProps> = ({children}) => {
|
||||
const BottomPanel: React.FC<BottomPanelProps> = ({children, style}) => {
|
||||
return (
|
||||
<div className={cls.BottomPanel}>
|
||||
<div className={cls.BottomPanel} style={style}>
|
||||
{ children }
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -9,18 +9,32 @@
|
||||
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, &:focus {
|
||||
&:hover {
|
||||
background: #7986cb;
|
||||
box-shadow: 0 15px 20px rgba(121, 134, 203, 0.4);
|
||||
color: white;
|
||||
transform: translateY(-7px);
|
||||
cursor: pointer;
|
||||
}
|
||||
&:active {
|
||||
color: #524f4e;
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.Button {
|
||||
width: 120px;
|
||||
margin: 5px;
|
||||
font-size: 20px;
|
||||
letter-spacing: normal;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3,12 +3,13 @@ import cls from './Button.module.scss';
|
||||
|
||||
type ButtonProps = {
|
||||
className?: string,
|
||||
onClick: (e: React.MouseEvent<HTMLButtonElement>) => void
|
||||
onClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
const Button: React.FC<ButtonProps> = ({children, className, onClick}) => {
|
||||
const Button: React.FC<ButtonProps> = ({children, className, style, onClick}) => {
|
||||
return (
|
||||
<button className={[cls.Button, className].join(' ')} onClick={onClick}>
|
||||
<button className={[cls.Button, className].join(' ')} style={style} onClick={onClick}>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
|
||||
@ -22,21 +22,43 @@ interface MatchGameContextType {
|
||||
const MatchGameContext = React.createContext<MatchGameContextType>({});
|
||||
export const useMatchGame = () => useContext(MatchGameContext);
|
||||
|
||||
function getInitialSize() {
|
||||
const sizeFromLocalStorage = localStorage.getItem('match-game:size');
|
||||
return sizeFromLocalStorage ? JSON.parse(sizeFromLocalStorage) : FieldSize.Small;
|
||||
}
|
||||
|
||||
function getInitialDiffLevel() {
|
||||
const diffLevelFromLocalStorage = localStorage.getItem('match-game:diffLevel');
|
||||
return diffLevelFromLocalStorage ? JSON.parse(diffLevelFromLocalStorage) : DiffLevel.Easy;
|
||||
}
|
||||
|
||||
function getInitialTime() {
|
||||
const timeFromLocalStorage = localStorage.getItem('match-game:time');
|
||||
return timeFromLocalStorage ? JSON.parse(timeFromLocalStorage) : 0;
|
||||
}
|
||||
|
||||
function getInitialMistakesCount() {
|
||||
const mistakesCountFromLocalStorage = localStorage.getItem('match-game:mistakesCount');
|
||||
return mistakesCountFromLocalStorage ? JSON.parse(mistakesCountFromLocalStorage) : 0;
|
||||
}
|
||||
|
||||
export const MatchGameProvider: React.FC = ({children}) => {
|
||||
const [mistakesCount, setMistakesCount] = useState(0)
|
||||
const [gameTime, setGameTime] = useState(0);
|
||||
const [mistakesCount, setMistakesCount] = useState<number>(getInitialMistakesCount)
|
||||
const [gameTime, setGameTime] = useState<number>(getInitialTime);
|
||||
const [currentMenuItem, setCurrentMenuItem] = useState<MenuItem>('menu');
|
||||
const [isGameStarted, setIsGameStarted] = useState(false);
|
||||
const [isGameFinished, setIsGameFinished] = useState(false);
|
||||
const [filedSize, setFieldSize] = useState(FieldSize.Small);
|
||||
const [diffLevel, setDiffLevel] = useState(DiffLevel.Easy);
|
||||
const [filedSize, setFieldSize] = useState<number>(getInitialSize);
|
||||
const [diffLevel, setDiffLevel] = useState<number>(getInitialDiffLevel);
|
||||
|
||||
const addMistakesCount = () => {
|
||||
localStorage.setItem('match-game:mistakesCount', JSON.stringify(mistakesCount + 1));
|
||||
return setMistakesCount(prev => prev + 1)
|
||||
};
|
||||
|
||||
const changeGameTime = (time: number) => {
|
||||
setGameTime(time);
|
||||
localStorage.setItem('match-game:time', JSON.stringify(time));
|
||||
}
|
||||
|
||||
const resetGameProgress = () => {
|
||||
@ -44,12 +66,18 @@ export const MatchGameProvider: React.FC = ({children}) => {
|
||||
setGameTime(0);
|
||||
setIsGameStarted(false);
|
||||
setIsGameFinished(false);
|
||||
|
||||
localStorage.removeItem('match-game:cards');
|
||||
localStorage.removeItem('match-game:time');
|
||||
localStorage.removeItem('match-game:mistakesCount');
|
||||
}
|
||||
|
||||
const saveOptions = (options: OptionsType) => {
|
||||
setFieldSize(options.size);
|
||||
setDiffLevel(options.diffLevel);
|
||||
|
||||
localStorage.setItem('match-game:size', JSON.stringify(options.size));
|
||||
localStorage.setItem('match-game:diffLevel', JSON.stringify(options.diffLevel));
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@ -0,0 +1,4 @@
|
||||
.MatchGameField {
|
||||
width: 500px;
|
||||
max-width: 100%;
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
import GoToMenuButton from 'components/GoToMenuButton/GoToMenuButton';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import BottomPanel from 'components/UI/BottomPanel/BottomPanel';
|
||||
import Button from 'components/UI/Button/Button';
|
||||
@ -7,6 +8,8 @@ import Timer from 'components/UI/Timer/Timer';
|
||||
import { createCards, randomizeElementInArray } from 'src/utils';
|
||||
import { useMatchGame } from 'context/MatchGameContext';
|
||||
|
||||
import cls from './MatchGame.module.scss';
|
||||
|
||||
interface MatchGameProps {
|
||||
isNewGame: boolean;
|
||||
}
|
||||
@ -22,7 +25,6 @@ const MatchGame: React.FC<MatchGameProps> = ({isNewGame}) => {
|
||||
isGameFinished,
|
||||
isGameStarted,
|
||||
changeGameTime,
|
||||
setCurrentMenuItem,
|
||||
diffLevel,
|
||||
filedSize
|
||||
} = useMatchGame();
|
||||
@ -51,12 +53,10 @@ const MatchGame: React.FC<MatchGameProps> = ({isNewGame}) => {
|
||||
}
|
||||
|
||||
function retryGame() {
|
||||
setIsGameFinished?.(false);
|
||||
setIsGameStarted?.(false);
|
||||
setIsTimerStarted?.(false);
|
||||
setCards(prev => randomizeElementInArray(prev.map(el => ({...el, disabled: true, isMatched: false, toggled: true}))))
|
||||
|
||||
resetGameProgress?.();
|
||||
|
||||
setCards(prev => randomizeElementInArray(prev.map(el => ({...el, disabled: true, isMatched: false, toggled: true}))))
|
||||
}
|
||||
|
||||
function finishGame() {
|
||||
@ -68,15 +68,10 @@ const MatchGame: React.FC<MatchGameProps> = ({isNewGame}) => {
|
||||
setCards(randomizeElementInArray([...cards]));
|
||||
}
|
||||
|
||||
function goToMenu() {
|
||||
setIsGameStarted?.(false);
|
||||
setCurrentMenuItem?.('menu');
|
||||
}
|
||||
|
||||
function renderRetryStatePanel() {
|
||||
return (
|
||||
<>
|
||||
<Button onClick={goToMenu}>⇦</Button>
|
||||
<GoToMenuButton/>
|
||||
<Button onClick={retryGame}>RETRY</Button>
|
||||
</>
|
||||
)
|
||||
@ -85,12 +80,12 @@ const MatchGame: React.FC<MatchGameProps> = ({isNewGame}) => {
|
||||
function renderStartStatePanel() {
|
||||
return isTimerStarted
|
||||
? <>
|
||||
<Button onClick={goToMenu}>⇦</Button>
|
||||
<GoToMenuButton/>
|
||||
<Timer isStarted={isTimerStarted} startTime={gameTime || 0} isFinished={isTimerStopped} changeTime={changeGameTime}/>
|
||||
</>
|
||||
: <>
|
||||
<Button onClick={goToMenu}>⇦</Button>
|
||||
<Button onClick={startGame}>{isNewGame ? 'START' : 'Continue'}</Button>
|
||||
<GoToMenuButton/>
|
||||
<Button style={{width: 'auto', minWidth: '120px'}} onClick={startGame}>{isNewGame ? 'START' : 'CONTINUE'}</Button>
|
||||
{isNewGame && <Button onClick={shuffleCards}>SHUFFLE</Button>}
|
||||
</>
|
||||
}
|
||||
@ -109,7 +104,7 @@ const MatchGame: React.FC<MatchGameProps> = ({isNewGame}) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={cls.MatchGameField}>
|
||||
<div style={{marginBottom: 20}}>
|
||||
{
|
||||
isGameFinished
|
||||
@ -117,10 +112,10 @@ const MatchGame: React.FC<MatchGameProps> = ({isNewGame}) => {
|
||||
: <CardsField cards={cards} isGameStarted={isGameStarted} finishGameCallback={finishGame} isNewGame={isNewGame}/>
|
||||
}
|
||||
</div>
|
||||
<BottomPanel>
|
||||
<BottomPanel style={{width: '100%'}}>
|
||||
{ renderPanelByState(panelState) }
|
||||
</BottomPanel>
|
||||
</>
|
||||
</div>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
li {
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
|
||||
@ -25,6 +25,9 @@ const MatchMenu: React.FC<GameMenuProps> = () => {
|
||||
|
||||
function clickItemHandler(item: MenuItem) {
|
||||
setCurrentMenuItem?.(item);
|
||||
if (item === 'exit') {
|
||||
window.location = 'https://google.com' as any;
|
||||
}
|
||||
}
|
||||
|
||||
function canContinueGame() {
|
||||
|
||||
@ -9,10 +9,12 @@
|
||||
border-radius: 10px;
|
||||
color: #ffffff;
|
||||
margin-bottom: 20px;
|
||||
max-width: 100%;
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@ -24,7 +26,7 @@
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 30px;
|
||||
font-size: 1.5rem;
|
||||
|
||||
.button {
|
||||
width: 45px;
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import GoToMenuButton from 'components/GoToMenuButton/GoToMenuButton';
|
||||
import BottomPanel from 'components/UI/BottomPanel/BottomPanel';
|
||||
import Button from 'components/UI/Button/Button';
|
||||
import { useMatchGame } from 'context/MatchGameContext';
|
||||
@ -62,7 +63,7 @@ const Options: React.FC = () => {
|
||||
</ul>
|
||||
</div>
|
||||
<BottomPanel>
|
||||
<Button onClick={() => setCurrentMenuItem?.('menu')}>⇦</Button>
|
||||
<GoToMenuButton/>
|
||||
<Button onClick={saveOptionHandler}>Save</Button>
|
||||
</BottomPanel>
|
||||
</>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user