Skip to content

Commit

Permalink
Merge pull request #10 from GO-SOPT-WEB/week7-TS
Browse files Browse the repository at this point in the history
[ 7주차 과제 ] 🌈 카드게임 리팩토링
  • Loading branch information
eunbeann authored Jul 16, 2023
2 parents 1c1f088 + 603b641 commit a98969a
Show file tree
Hide file tree
Showing 43 changed files with 3,785 additions and 387 deletions.
14 changes: 14 additions & 0 deletions TS-cardGame/ts-card/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module.exports = {
env: { browser: true,; es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
],
parser: '@typescript-eslint/parser',
parserOptions: { ecmaVersion: 'latest',; sourceType: 'module' },
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': 'warn',
},
}
24 changes: 24 additions & 0 deletions TS-cardGame/ts-card/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
9 changes: 9 additions & 0 deletions TS-cardGame/ts-card/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"singleQuote": true,
"parser": "typescript",
"semi": true,
"useTabs": false,
"tabWidth": 4,
"printWidth": 120,
"arrowParens": "always"
}
17 changes: 17 additions & 0 deletions TS-cardGame/ts-card/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="ko">

<head>
<meta charset="UTF-8" />
<link rel="icon" type="icon" href="/src/assets/imgs/Goomba.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title> 슈퍼마리오🍄-TS </title>
</head>

<body>
<div id="root"></div>
<div id="modal-root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>

</html>
38 changes: 38 additions & 0 deletions TS-cardGame/ts-card/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "ts-card",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"prettier": "prettier --write 'src/**/*.{js,jsx,ts,tsx,css,md,json}' --config ./.prettierrc",
"preview": "vite preview",
"lint:fix": "eslint --fix 'src/**/*.{js,jsx,ts,tsx,json}'"
},
"dependencies": {
"@types/recoil": "^0.0.9",
"@types/styled-components": "^5.1.26",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-prettier": "^4.2.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"recoil": "^0.7.7",
"styled-components": "^5.3.11",
"styled-reset": "^4.4.7"
},
"devDependencies": {
"@types/react": "^18.2.8",
"@types/react-dom": "^18.0.11",
"@typescript-eslint/eslint-plugin": "^5.59.8",
"@typescript-eslint/parser": "^5.59.8",
"@vitejs/plugin-react": "^4.0.0",
"eslint": "^8.41.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.3.4",
"typescript": "^5.1.3",
"vite": "^4.3.9"
}
}
57 changes: 57 additions & 0 deletions TS-cardGame/ts-card/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { ThemeProvider, createGlobalStyle } from 'styled-components';
import theme from './styles/theme';
import reset from 'styled-reset';
import GmarketSansBold from './assets/fonts/GmarketSansBold.ttf';
import GmarketSansMedium from './assets/fonts/GmarketSansMedium.ttf';
import GmarketSansLight from './assets/fonts/GmarketSansLight.ttf';
import CardGame from './pages/CardGame';
import { RecoilRoot } from 'recoil';

const App = () => {
return (
<RecoilRoot>
<ThemeProvider theme={theme}>
<GlobalStyle />
<CardGame />
</ThemeProvider>
</RecoilRoot>
);
};

const GlobalStyle = createGlobalStyle`
${reset}
@font-face {
font-family: 'GmarketSansMedium';
src: url(${GmarketSansMedium}) format('ttf');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'GmarketSansBold';
src: url(${GmarketSansBold}) format('ttf');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'GmarketSansLight';
src: url(${GmarketSansLight}) format('ttf');
font-weight: normal;
font-style: normal;
}
body, html {
font-family: 'GmarketSansMedium';
font-size: 16px;
}
button {
all:unset;
:hover {
cursor: pointer;
}
}
`;
export default App;
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 29 additions & 0 deletions TS-cardGame/ts-card/src/components/CardContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import styled from 'styled-components';
import SingleCard from './SingleCard';
import { useRecoilValue } from 'recoil';
import { cardsNumData } from '../recoil/atoms';

const CardContainer = () => {
const cardsNum = useRecoilValue(cardsNumData);

return (
<>
<StCardContainer>
{cardsNum &&
Array.isArray(cardsNum) &&
cardsNum.map((card) => <SingleCard key={card.cardId} card={card} />)}
</StCardContainer>
</>
);
};

export default CardContainer;

const StCardContainer = styled.div`
display: flex;
flex-wrap: wrap;
justify-content: center;
flex-wrap: wrap;
justify-content: center;
`;
66 changes: 66 additions & 0 deletions TS-cardGame/ts-card/src/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { useRecoilValue } from 'recoil';
import styled, { css, keyframes } from 'styled-components';
import { cardsNumData, scoreData } from '../recoil/atoms';
import { useEffect } from 'react';
import { useState } from 'react';

const Header = () => {
const score = useRecoilValue(scoreData);
const cardsNum = useRecoilValue(cardsNumData);

const [animateScore, setAnimateScore] = useState<boolean>(false);

// 점수 애니메이션
useEffect(() => {
setAnimateScore(true);
const timeoutId = setTimeout(() => setAnimateScore(false), 500);
return () => clearTimeout(timeoutId);
}, [score]);

return (
<StHeader>
<p> Match the MARIO! 🍄 </p>
{cardsNum && cardsNum.length > 0 && (
<StScore animate={animateScore}>
{score}/{cardsNum.length / 2}
</StScore>
)}
</StHeader>
);
};

export default Header;

const StHeader = styled.header`
display: flex;
height: 20vh;
flex-direction: column;
justify-content: center;
> p {
color: ${({ theme }) => theme.color.red};
font-size: 3rem;
text-align: center;
font-weight: 600;
}
`;

const StScore = styled.div<{ animate: boolean }>`
color: ${({ theme }) => theme.color.green};
font-size: 2.6rem;
text-align: center;
font-weight: 600;
margin-top: 1.5rem;
animation: ${({ animate }) =>
animate
? css`
${blinkScore} 1s
`
: 'none'};
`;

const blinkScore = keyframes`
0% { opacity: 1; }
50% { opacity: 0; }
100% { opacity: 1; }
`;
67 changes: 67 additions & 0 deletions TS-cardGame/ts-card/src/components/LevelContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import styled from 'styled-components';
import { Level } from '../data/level';
import { useRecoilState, useSetRecoilState } from 'recoil';
import { cardsNumData, nowLevelData, scoreData } from '../recoil/atoms';

// 카드 데이터 가지고 오기
import { EasyVersion, NormalVersion, HardVersion } from '../utils/ShuffledCard';

const LevelContainer = () => {
const [nowLevel, setNowLevel] = useRecoilState(nowLevelData);
const setScore = useSetRecoilState(scoreData);
const setCardsNum = useSetRecoilState(cardsNumData);

// 레벨 버튼 클릭 시 카드 수 변경
const ClickedLv = (e: React.ChangeEvent<HTMLInputElement>) => {
setScore(0);
setNowLevel(e.target.value);
switch (e.target.value) {
case 'EASY':
return setCardsNum(EasyVersion);
case 'NORMAL':
return setCardsNum(NormalVersion);
case 'HARD':
return setCardsNum(HardVersion);
}
};
return (
<StLevenContainer>
{Level.map((level) => {
return (
<StLevelBtn
key={level.id}
value={level.lv}
type="button"
className={'btn' + (level.lv == nowLevel ? ' active' : '')}
onClick={() => ClickedLv}
>
{level.lv}
</StLevelBtn>
);
})}
</StLevenContainer>
);
};

export default LevelContainer;

const StLevenContainer = styled.nav`
display: flex;
justify-content: center;
`;

const StLevelBtn = styled.button`
width: 5rem;
margin: 1rem 1rem;
background-color: ${({ theme }) => theme.color.yellow};
padding: 1rem 1rem;
text-align: center;
border-radius: 1rem;
box-shadow: 0.2rem 0.2rem ${({ theme }) => theme.color.blue};
&.active {
background-color: ${({ theme }) => theme.color.blue};
box-shadow: 0.2rem 0.2rem ${({ theme }) => theme.color.yellow};
}
`;
Loading

0 comments on commit a98969a

Please sign in to comment.