This commit is contained in:
Sergey Krylov 2022-07-22 11:28:06 +05:00
parent 1421bc549e
commit aff0ac7330
20 changed files with 514 additions and 326 deletions

View File

@ -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 (
<div className={cls.App}>
<Field/>
<MatchGameProvider>
<MatchGame/>
</MatchGameProvider>
</div>
);
}

View File

@ -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;
}
}
}

View File

@ -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<BottomPanelProps> = (props) => {
const [startTime, setStartTime] = useState<number | null>(null);
const [timeInterval, setTimeInterval] = useState<NodeJS.Timeout>();
type PanelState = 'start' | 'retry';
const BottomPanel: React.FC<BottomPanelProps> = (props) => {
const [isTimerStarted, setIsTimerStarted] = useState(false);
const [isTimerStopped, setIsTimerStopped] = useState(false);
const [panelState, setPanelState] = useState<PanelState>('start');
function onStartBtnClickHandler(event: React.MouseEvent<HTMLButtonElement>) {
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
? <p className={cls.Timer}>{getTimeString(new Date(startTime))}</p>
: <button className={cls.StartButton} onClick={onStartBtnClickHandler}>START</button>
}
function onRetryBtnClickHandler() {
setStartTime(null);
props.retry?.();
setIsTimerStarted(false);
setIsTimerStopped(false);
setPanelState('start');
props.retryGame?.();
}
function renderRetryButton() {
function renderRetryStatePanel() {
return (
<>
<button className={cls.StartButton} onClick={onRetryBtnClickHandler}>Retry</button>
<Button onClick={onRetryBtnClickHandler}>RETRY</Button>
</>
)
}
function onShuffleBtnClickHandler() {
props.shuffleCards?.();
}
function renderStartStatePanel() {
return isTimerStarted
? <Timer isStarted={isTimerStarted} startTime={0} isFinished={isTimerStopped}/>
: <>
<Button onClick={onStartBtnClickHandler}>START</Button>
<Button onClick={onShuffleBtnClickHandler}>SHUFFLE</Button>
</>
}
function renderPanelByState(state: PanelState) {
if (!state) {
console.error('Нет состояния у панели')
return;
}
switch (state) {
case 'start': return renderStartStatePanel();
case 'retry': return renderRetryStatePanel();
default: return;
}
}
return (
<div className={cls.BottomPanel}>
{ props.isFinishedGame ? renderRetryButton() : renderActiveStatePanel() }
{ renderPanelByState(panelState) }
</div>
);
};
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;

View File

@ -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<CardProps> = (props) => {
const Card: React.FC<CardComponentProps> = (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 (
<div className={cls.CardWrapper} style={{
opacity: props.isMatched ? 0 : 1
}}>
<div className={classes.join(' ')} onClick={() => canClick && props.onReverse?.(props)}>
<div
className={cls.CardWrapper}
style={{opacity: props.isMatched ? 0 : 1}}
onClick={() => cardClickHandler(props.id)}
>
<div className={classes.join(' ')}>
<div className={cls.frontside}>{props.title}</div>
<div className={cls.backside}>
<img src={props.imageURL} alt={props.title}/>

View File

@ -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:
". . ."
". . ."
". . ."
". . .";
}

View File

@ -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<CardsFieldProps> = ({cards, isGameStarted, finishGameCallback}) => {
// TODO use useReducer
const [cardsState, setCardsState] = useState(cards.map(el => createCard(el)));
const [currentCard, setCurrentCard] = useState<CardComponentProps | null>(null)
const [currentCardPare, setCurrentCardPare] = useState<CurrentCardPareType>({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) => (
<Card
{...el}
key={el.id}
title={index + 1 + ''}
onClick={() => cardClickHandler(el.id)}
>
</Card>
));
}
return (
<div className={cls.CardsField}>
{renderCards()}
</div>
);
};
export default CardsField;

View File

@ -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;
}

View File

@ -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<CardProps | null>(null)
const [alertState, setAlertState] = useState<boolean | null>(null);
const [cards, setCards] = useState<CardProps[]>(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<boolean>(false);
const [isGameStarted, setGameStart] = useState<boolean>(false);
const [mistakesCount, setMistakesCount] = useState<number>(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) => <Card key={el.id} {...el} title={index + 1 + ''} disabled={isGameStarted ? el.disabled : true}></Card>);
}
function renderAlert(state: boolean | null) {
if (state === null) return;
if (state) return <Alert success={true}/>;
return <Alert success={false}/>
}
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 (
<>
<div className={getWrapperClassList().join(' ')} style={{marginBottom: 20}}>
{
isGameFinished
? (
<div className={cls.FinishBlock}>
<h1>GAME IS FINISHED !</h1>
<p>Время: <span>10:10</span></p>
<p>Количество ошибок: <span>{mistakesCount}</span></p>
<p>Сложность: <span>Легко</span></p>
<p>Размер поля: <span>4х4</span></p>
</div>
)
: (
<>
{renderCards()}
{renderAlert(alertState)}
</>
)
}
</div>
{<BottomPanel start={startGame} isFinishedGame={isGameFinished} retry={retryGame}/>}
</>
);
};
function randomizeElementInArray (arr: any[]) {
return arr.sort(() => {
return Math.random() > 0.5 ? 1 : -1;
})
}
export default Field;

View File

@ -0,0 +1,6 @@
.FinishedField {
color: #ffffff;
max-width: 320px;
margin: 0 auto;
font-family: 'Pacifico', sans-serif;
}

View File

@ -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<FinishedFieldProps> = ({mistakesCount, gameTime}) => {
// const {mistakesCount, gameTime} = useMatchGame();
return (
<div className={cls.FinishedField}>
<h1>GAME IS FINISHED !</h1>
<p>Время: <span>{gameTime}</span></p>
<p>Количество ошибок: <span>{mistakesCount}</span></p>
<p>Сложность: <span>difficultyLevel</span></p>
<p>Размер поля: <span>fieldSize</span></p>
</div>
);
};
export default FinishedField;

View File

@ -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;
}
}
}

View File

@ -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<any[]>(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<boolean>(false);
const [isGameStarted, setGameStart] = useState<boolean>(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 (
<>
<div className={getWrapperClassList().join(' ')} style={{marginBottom: 20}}>
{
isGameFinished
? <FinishedField gameTime={gameTime} mistakesCount={mistakesCount}/>
: <CardsField cards={cards} isGameStarted={isGameStarted} finishGameCallback={finishGame}/>
}
</div>
<BottomPanel startGame={startGame} isFinishedGame={isGameFinished} retryGame={retryGame} shuffleCards={shuffleCards}/>
</>
);
};
export default MatchGame;

View File

@ -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;
}
}

View File

@ -0,0 +1,17 @@
import React from 'react';
import cls from './Button.module.scss';
type ButtonProps = {
className?: string,
onClick: (e: React.MouseEvent<HTMLButtonElement>) => void
}
const Button: React.FC<ButtonProps> = ({children, className, onClick}) => {
return (
<button className={[cls.Button, className].join('')} onClick={onClick}>
{children}
</button>
);
};
export default Button;

View File

@ -0,0 +1,6 @@
.Timer {
font-size: 40px;
font-weight: bold;
font-family: "Pacifico", sans-serif;
color: #ffffff;
}

View File

@ -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<TimerProps> = ({startTime, isStarted, isFinished}) => {
const [time, setTime] = useState(startTime);
const [timeInterval, setTimeInterval] = useState<NodeJS.Timer | null>(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 (
<div>
<p className={cls.Timer}>{getTimeString(new Date(time))}</p>
</div>
);
};
export default Timer;

5
src/constants.ts Normal file
View File

@ -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',
}

View File

@ -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<MatchGameContextType>({});
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 (
<MatchGameContext.Provider value={{
mistakesCount,
addMistakesCount,
gameTime,
changeGameTime,
resetGameProgress
}}>
{children}
</MatchGameContext.Provider>
)
}

18
src/utils.ts Normal file
View File

@ -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}`
}

View File

@ -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"