Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ 7주차 기본 과제 ] 카드게임 리팩토링 #12

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions week3-ts/findingGame/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module.exports = {
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint', 'prettier'],
extends: [
'airbnb',
'plugin:import/errors',
'plugin:import/warnings',
'plugin:prettier/recommended',
'plugin:@typescript-eslint/recommended',
'prettier/@typescript-eslint',
],
rules: {
'linebreak-style': 0,
'import/prefer-default-export': 0,
'prettier/prettier': 0,
'import/extensions': 0,
'import/no-unresolved': 0,
'import/no-extraneous-dependencies': 0,
'react/prop-types': 0,
'react/jsx-filename-extension': [2, { extensions: ['.js', '.jsx', '.ts', '.tsx'] }],
'jsx-a11y/no-noninteractive-element-interactions': 0,
},
};
24 changes: 24 additions & 0 deletions week3-ts/findingGame/.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?
33 changes: 33 additions & 0 deletions week3-ts/findingGame/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"singleQuote": true,

"printWidth": 100,

"tabWidth": 2,

"useTabs": false,

"semi": true,

"quoteProps": "as-needed",

"jsxSingleQuote": false,

"trailingComma": "es5",

"arrowParens": "always",

"endOfLine": "lf",

"bracketSpacing": true,

"jsxBracketSameLine": false,

"requirePragma": false,

"insertPragma": false,

"proseWrap": "preserve",

"vueIndentScriptAndStyle": false
}
18 changes: 18 additions & 0 deletions week3-ts/findingGame/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<link
rel="icon"
type="image/svg+xml"
href="https://item.kakaocdn.net/do/37c4c5de5246ba70b0f9a293b5aa24847154249a3890514a43687a85e6b6cc82"
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>잔망루피를 찾아라!</title>
</head>
<body>
<div id="root"></div>
<div id="modal-root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
29 changes: 29 additions & 0 deletions week3-ts/findingGame/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "findinggame",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint src --ext js,jsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.10.0",
"recoil": "^0.7.7",
"styled-components": "^6.0.0-rc.3"
},
"devDependencies": {
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11",
"@vitejs/plugin-react": "^4.0.0-beta.0",
"eslint": "^8.38.0",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.3.4",
"vite": "^4.3.0"
}
}
95 changes: 95 additions & 0 deletions week3-ts/findingGame/src/@components/card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import React from 'react';
import styled from 'styled-components';
import { idxType, selectCardType } from '../type/cardType';

interface CardProps {
idx: number;
img: string;
name: string;
selectedIdx: idxType[];
setSelectedIdx: React.Dispatch<React.SetStateAction<idxType[]>>;
selectCards: selectCardType[];
setSelectCards: React.Dispatch<React.SetStateAction<selectCardType[]>>;
}

export default function Card(props: CardProps) {
const { idx, img, name, selectedIdx, setSelectedIdx, selectCards, setSelectCards } = props;

function reverseCard() {
if (selectedIdx.length < 2) {
setSelectCards(
selectCards.map((selectCard) =>
selectCard.idx === idx ? { ...selectCard, selected: !selectCard.selected } : selectCard
)
);
setSelectedIdx((selectedIdx) => [...selectedIdx, { idx }]);
}
}

return (
<CardBoxWrapper onClick={reverseCard}>
<CardBox $isReverse={selectCards[idx].selected}>
<CardImgFrontWrapper $isReverse={selectCards[idx].selected}>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$ 붙여주는 건 뭔지 궁금해요!

<Img src={img} alt={name} />
</CardImgFrontWrapper>
<CardImgBackWrapper $isReverse={selectCards[idx].selected}>
<i>🌊</i>
</CardImgBackWrapper>
</CardBox>
</CardBoxWrapper>
);
}

const CardBoxWrapper = styled.article`
position: relative;
width: 18rem;
height: 18rem;
margin: 1rem;

border-radius: 1rem;
perspective: 50rem;
cursor: pointer;
`;

const CardBox = styled.div<{ $isReverse: boolean }>`
width: 100%;
height: 100%;
transition: all 1s;
transform-style: preserve-3d;
backface-visibility: hidden;
transform: rotateY(${({ $isReverse }) => $isReverse && 180}deg);
`;

const CardImgFrontWrapper = styled.div<{ $isReverse: boolean }>`
display: flex;
justify-content: center;
align-items: center;

position: absolute;
width: 18rem;
height: 18rem;

border-radius: 1rem;

background-color: ${({ theme }) => theme.colors.skyblue};
transform: rotateY(180deg);
`;

const CardImgBackWrapper = styled.div<{ $isReverse: boolean }>`
display: flex;
justify-content: center;
align-items: center;

position: absolute;
width: 18rem;
height: 18rem;
z-index: 2;

border-radius: 1rem;

background-color: ${({ theme }) => theme.colors.blue};
`;

const Img = styled.img`
width: 15rem;
`;
115 changes: 115 additions & 0 deletions week3-ts/findingGame/src/@components/cardList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import React, { useEffect, useState } from 'react';
import { useRecoilValue } from 'recoil';
import styled from 'styled-components';
import { levelType } from '../core/levelType';
import { reverseCardEasy, reverseCardHard, reverseCardNormal } from '../core/reverseCard';
import { cardsLevel, cardsReset } from '../recoil/card';
import { cardType, idxType } from '../type/cardType';
import Card from './card';

interface CardListProps {
cards: cardType[];
correct: number;
setCorrect: React.Dispatch<React.SetStateAction<number>>;
}

export default function CardList(props: CardListProps) {
const { cards, correct, setCorrect } = props;
//카드 전체의 선택여부
const [selectCards, setSelectCards] = useState(reverseCardEasy);
//선택된 카드의 인덱스 두 개 담기는 배열
const [selectedIdx, setSelectedIdx] = useState<idxType[]>([]);
const isReset = useRecoilValue(cardsReset);
const level = useRecoilValue(cardsLevel);

useEffect(() => {
switch (level) {
case levelType.EASY:
setSelectCards(reverseCardEasy);
break;
case levelType.NORMAL:
setSelectCards(reverseCardNormal);
break;
case levelType.HARD:
setSelectCards(reverseCardHard);
break;
default:
// setSelectCards();
break;
}
}, [level]);

//리셋한 경우 카드 다 뒤집기
useEffect(() => {
setSelectCards(
selectCards.map((selectCard) =>
selectCard.idx === 0
? { ...selectCard, selected: false }
: { ...selectCard, selected: false }
)
);
}, [isReset]);

function checkSameCards(selectedIdxLen: number) {
const idx1 = selectedIdx[selectedIdxLen - 2].idx;
const idx2 = selectedIdx[selectedIdxLen - 1].idx;

//카드 정보가 담긴 배열에서 이름이 동일하면 같은 카드
if (cards[idx1].name == cards[idx2].name) {
setCorrect(correct + 1);
setSelectCards(
selectCards.map((selectCard) =>
selectCard.idx === idx1 || selectCard.idx === idx2
? { ...selectCard, selected: true }
: selectCard
)
);
} else {
//다른 카드라면 1초 뒤 원상복귀
setTimeout(() => {
setSelectCards(
selectCards.map((selectCard) =>
selectCard.idx === idx1 || selectCard.idx === idx2
? { ...selectCard, selected: false }
: selectCard
)
);
}, 1000);
}
setSelectedIdx([]);
}

//selectedIdx 변경될 때마다 길이 2이상인지 확인
useEffect(() => {
const selectedIdxLen = selectedIdx.length;

if (selectedIdxLen === 2) {
checkSameCards(selectedIdxLen);
}
}, [selectCards]);

return (
<CardsContainer>
{cards.map((card, idx) => (
<Card
key={idx}
idx={idx}
name={card.name}
img={card.img}
selectedIdx={selectedIdx}
setSelectedIdx={setSelectedIdx}
selectCards={selectCards}
setSelectCards={setSelectCards}
/>
))}
</CardsContainer>
);
}

const CardsContainer = styled.section`
display: flex;
justify-content: center;
flex-wrap: wrap;

width: 110rem;
`;
36 changes: 36 additions & 0 deletions week3-ts/findingGame/src/@components/levelButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react';
import { useRecoilState } from 'recoil';
import styled from 'styled-components';
import { levelType } from '../core/levelType';
import { cardsLevel } from '../recoil/card';

export default function LevelButton() {
const [level, setLevel] = useRecoilState(cardsLevel);

function changeLevel(levelType: number) {
setLevel(levelType);
}

return (
<ButtonWrapper>
<Button onClick={() => changeLevel(levelType.EASY)}>easy</Button>
<Button onClick={() => changeLevel(levelType.NORMAL)}>normal</Button>
<Button onClick={() => changeLevel(levelType.HARD)}>hard</Button>
</ButtonWrapper>
);
}

const ButtonWrapper = styled.section`
display: flex;
`;

const Button = styled.button`
width: 8rem;
padding: 1rem 2rem;
margin: 1rem;

border-radius: 1rem;

background-color: ${({ theme }) => theme.colors.blue};
${({ theme }) => theme.fonts.text};
`;
Loading