-
Notifications
You must be signed in to change notification settings - Fork 10
[2주차] 송아영 미션 제출합니다. #2
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
base: main
Are you sure you want to change the base?
Changes from all commits
51fa296
b97ff75
4be0981
a782662
26d7533
743e764
1248bc9
bd11a74
80d7439
53604c5
f6d8071
25dade1
8f444d4
93650c4
79fa4a3
bdfbced
0527380
4c21334
dac736e
0844997
21a42e4
ee0ee7f
2b97bba
c9a296b
b8633e7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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? |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"trailingComma": "all", | ||
"tabWidth": 2, | ||
"semi": true, | ||
"singleQuote": true, | ||
"printWidth": 120, | ||
"arrowParens": "always", | ||
"useTabs": true | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import js from '@eslint/js'; | ||
import globals from 'globals'; | ||
import reactHooks from 'eslint-plugin-react-hooks'; | ||
import reactRefresh from 'eslint-plugin-react-refresh'; | ||
import tseslint from 'typescript-eslint'; | ||
|
||
export default tseslint.config( | ||
{ ignores: ['dist'] }, | ||
{ | ||
extends: [js.configs.recommended, ...tseslint.configs.recommended, 'prettier'], | ||
files: ['**/*.{ts,tsx}'], | ||
languageOptions: { | ||
ecmaVersion: 2020, | ||
globals: globals.browser, | ||
}, | ||
plugins: { | ||
'react-hooks': reactHooks, | ||
'react-refresh': reactRefresh, | ||
}, | ||
rules: { | ||
...reactHooks.configs.recommended.rules, | ||
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }], | ||
'@typescript-eslint/no-unused-vars': ['warn'], | ||
'@typescript-eslint/no-explicit-any': 'warn', | ||
}, | ||
}, | ||
); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<!doctype html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<title>To Do List</title> | ||
</head> | ||
<body> | ||
<div id="root"></div> | ||
<script type="module" src="/src/main.tsx"></script> | ||
</body> | ||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
{ | ||
"name": "react-todo-21th", | ||
"private": true, | ||
"version": "0.0.0", | ||
"type": "module", | ||
"scripts": { | ||
"dev": "vite", | ||
"build": "tsc -b && vite build", | ||
"lint": "eslint .", | ||
"preview": "vite preview" | ||
}, | ||
"dependencies": { | ||
"@types/react-router-dom": "^5.3.3", | ||
"react": "^19.0.0", | ||
"react-dom": "^19.0.0", | ||
"react-router-dom": "^7.3.0", | ||
"styled-components": "^6.1.16", | ||
"styled-reset": "^4.5.2" | ||
}, | ||
"devDependencies": { | ||
"@eslint/js": "^9.21.0", | ||
"@types/node": "^22.13.10", | ||
"@types/react": "^19.0.10", | ||
"@types/react-dom": "^19.0.4", | ||
"@vitejs/plugin-react": "^4.3.4", | ||
"eslint": "^9.22.0", | ||
"eslint-config-prettier": "^10.1.1", | ||
"eslint-plugin-react-hooks": "^5.1.0", | ||
"eslint-plugin-react-refresh": "^0.4.19", | ||
"globals": "^15.15.0", | ||
"prettier": "^3.5.3", | ||
"typescript": "~5.7.2", | ||
"typescript-eslint": "^8.24.1", | ||
"vite": "^6.2.0" | ||
}, | ||
"main": "index.js", | ||
"repository": "https://github.com/gustn99/react-todo-21th.git", | ||
"author": "young <[email protected]>", | ||
"license": "MIT" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { BrowserRouter, Route, Routes } from 'react-router-dom'; | ||
import Home from './pages/Home'; | ||
|
||
function App() { | ||
return ( | ||
<BrowserRouter> | ||
<Routes> | ||
<Route path="/" element={<Home />} /> | ||
</Routes> | ||
</BrowserRouter> | ||
); | ||
} | ||
|
||
export default App; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { createGrassCalendar } from '@/utils/createGrassCalendar'; | ||
import { GrassItem, GrassLayout, GrassWrapper } from './style'; | ||
import { memo, useMemo } from 'react'; | ||
|
||
export default memo(function Grass() { | ||
const calendar = useMemo(() => createGrassCalendar(), []); | ||
|
||
return ( | ||
<GrassWrapper> | ||
<GrassLayout> | ||
{calendar.map((date) => ( | ||
<GrassItem $hasDoneTodo={date.hasDoneTodo} title={date.date} key={date.date} /> | ||
))} | ||
</GrassLayout> | ||
</GrassWrapper> | ||
); | ||
}); |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 헉스. 좋은 피드백 감사합니다. 스크롤 테스트를 깜빡해버렸네요. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import styled from 'styled-components'; | ||
|
||
export const GrassWrapper = styled.div` | ||
border-left: 2px solid darkslategrey; | ||
padding: 8px; | ||
|
||
@media (max-width: 639px) { | ||
display: none; | ||
} | ||
`; | ||
|
||
export const GrassLayout = styled.div` | ||
display: grid; | ||
grid-template-columns: repeat(5, 1fr); | ||
grid-auto-rows: minmax(auto); | ||
gap: 4px; | ||
height: fit-content; | ||
`; | ||
|
||
export const GrassItem = styled.div<{ $hasDoneTodo: boolean }>` | ||
width: 100%; | ||
aspect-ratio: 1; | ||
margin: auto; | ||
background-color: ${({ $hasDoneTodo }) => ($hasDoneTodo ? 'lightpink' : 'rgb(233, 233, 233)')}; | ||
border-radius: 2px; | ||
transition: opacity 0.2s; | ||
|
||
&:hover { | ||
opacity: 0.5; | ||
} | ||
`; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { ChangeEvent } from 'react'; | ||
import type { TodoDto } from '@/pages/Home/dto'; | ||
|
||
export interface TodoItemProps extends Omit<TodoDto, 'date'> { | ||
onChange: (e: ChangeEvent<HTMLInputElement>) => void; | ||
onClickDeleteButton: (id: string) => void; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { useState } from 'react'; | ||
import type { TodoItemProps } from './dto'; | ||
import { Button, Checkbox, TodoContent, TodoItemLayout } from './style'; | ||
|
||
export default function TodoItem({ id, isDone, content, onChange, onClickDeleteButton }: TodoItemProps) { | ||
const [isExpended, setIsExpended] = useState(false); | ||
const todoId = String(id); | ||
|
||
return ( | ||
<TodoItemLayout> | ||
<Checkbox onChange={onChange} checked={isDone} type="checkbox" id={todoId} /> | ||
<TodoContent | ||
htmlFor={todoId} | ||
$isDone={isDone} | ||
$isHovered={isExpended} | ||
onMouseEnter={() => { | ||
setIsExpended(true); | ||
}} | ||
onMouseLeave={() => { | ||
setTimeout(() => setIsExpended(false), 700); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 여기세어 set time out을 사용하신 이유가 무엇인가요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hover 시 white-space와 max-height를 조절해 transition을 주었는데요, mouseleave 시 white-space가 먼저 변경되면서 max-height에 대한 transition이 적용되지 않는 문제가 있었습니다. mouseleave 시에 white-space보다 max-height가 먼저 줄어들게 함으로써 transition을 적용시키려고 해 봤습니다! |
||
}} | ||
> | ||
{content} | ||
</TodoContent> | ||
<Button onClick={() => onClickDeleteButton(todoId)}>삭제</Button> | ||
</TodoItemLayout> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import styled from 'styled-components'; | ||
import { CommonButton } from '@/styles/CommonStyles'; | ||
import { memo } from 'react'; | ||
|
||
export const TodoItemLayout = styled.li` | ||
position: relative; | ||
|
||
width: 100%; | ||
padding: 5px 8px; | ||
border-bottom: 1.5px solid darkslategrey; | ||
|
||
display: grid; | ||
grid-template-columns: auto 1fr 50px; | ||
gap: 4px; | ||
align-items: center; | ||
|
||
transition: background-color 0.2s; | ||
|
||
&:hover { | ||
background-color: rgb(245, 245, 245); | ||
} | ||
`; | ||
|
||
export const Checkbox = styled.input` | ||
appearance: none; | ||
width: 16px; | ||
height: 16px; | ||
border: 1.5px solid darkslategrey; | ||
border-radius: 2px; | ||
cursor: pointer; | ||
|
||
&:checked { | ||
background-color: darkslategrey; | ||
} | ||
`; | ||
|
||
export const TodoContent = memo(styled.label<{ $isDone: boolean; $isHovered: boolean }>` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 여기에 memo 사용하는건 생각도 못했는데 좋은 것 같습니다! |
||
padding: 3px 0; | ||
margin: auto 0; | ||
cursor: pointer; | ||
|
||
line-height: 24px; | ||
|
||
max-height: 30px; | ||
white-space: ${({ $isHovered }) => ($isHovered ? 'pre-line' : 'nowrap')}; | ||
overflow: hidden; | ||
text-overflow: ellipsis; | ||
transition: max-height 1.5s; | ||
|
||
${({ $isDone }) => ($isDone ? 'color: rgb(150, 150, 150); text-decoration: line-through;' : '')} | ||
|
||
&:hover { | ||
max-height: 300px; | ||
} | ||
`); | ||
|
||
export const Button = memo(styled(CommonButton)``); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { StrictMode } from 'react'; | ||
import { createRoot } from 'react-dom/client'; | ||
import App from './App.tsx'; | ||
import GlobalStyles from './styles/GlobalStyles.ts'; | ||
|
||
createRoot(document.getElementById('root')!).render( | ||
<StrictMode> | ||
<GlobalStyles /> | ||
<App /> | ||
</StrictMode>, | ||
); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export interface TodoDto { | ||
id: number; | ||
date: string; // 로컬스토리지 저장을 위해 사용 | ||
isDone: boolean; | ||
content: string; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오늘 투두는 반영되지 않습니다!