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

[2주차] 이규호 미션 제출합니다. #9

Open
wants to merge 16 commits into
base: master
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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1 @@
node_modules
node_modules
439 changes: 406 additions & 33 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"styled-components": "^6.0.8",
"styled-reset": "^4.5.1",
"web-vitals": "^2.1.4"
},
"scripts": {
Expand Down
Binary file modified public/favicon.ico
Binary file not shown.
2 changes: 1 addition & 1 deletion public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<title>TODO</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
Expand Down
87 changes: 87 additions & 0 deletions src/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
@font-face {
Copy link

Choose a reason for hiding this comment

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

GlobalStyle 따로 정의하신 만큼 전체 컴포넌트에 중복되는 속성의 경우 GlobalStyle 사용해도 좋을 것 같아요!

font-family: 'GmarketSansMedium';
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/[email protected]/GmarketSansMedium.woff') format('woff');
font-weight: normal;
font-style: normal;
}
/* 색상 변수 (코드리뷰 반영) */
:root{
--card-color: #ECECEC;
--bg-color: black;
}
Copy link

Choose a reason for hiding this comment

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

색상을 따로 지정해두고 사용하니까 코드가 더 깔끔하네요!

.wrapper{
display: flex;
flex-direction: column;
align-items: center;
}
.header{
display: flex;
height: 150px;
width: 480px;
align-items: center;

}
.logo{
display: flex;
font-size: 2em;
font-weight: 700;
width: 300px;
}
.detail{
font-size: 1em;
width: 180px;
}
#clock-js{
padding-top: 5px;
}
.todoList{
display:flex;
flex-direction: column;
text-align: center;
width: 28em;
}
.todoInput{/*input container*/
display:flex;
height:35px;
padding-bottom: 5px;
}
input[type=text]{/*input 태그 따로 설정*/
background-color: var(--bg-color);
color: var(--card-color);
border: solid 2px var(--card-color);
border-radius: 6px;
width: 400px;
}
#plusButton{
background-color: var(--bg-color);
color: var(--card-color);
border: solid 2px var(--card-color);
border-radius: 6px;
width: 35px;
margin-left: 5px;
transition : border 0.3s, color 0.3s;
cursor: pointer;
}
#plusButton:hover{
border : solid 2px #5f5f5f;
color : #5f5f5f;
}

input[type=checkbox]{
position:absolute; /*checkbox 위치 고정 (코드리뷰 반영)*/
top : 40px;
right : 15px;
cursor: pointer;
}
.todoDelete{
border: none;
margin-bottom: 80px;
margin-right: 7px;
font-size: 7px;
border-radius: 2px;
transition : background-color 0.3s;
cursor: pointer;
}
.todoDelete:hover{
background-color: #b1b1b1;
}
78 changes: 73 additions & 5 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,77 @@
import "./App.css";
import { useState, useEffect } from "react";
import Cards from "./components/Cards.js";
import Clock from "./components/Clock.js";
function App() {
return (
<div>
<h1>18기 프론트 화이팅~ 푸하항ㅋ</h1>
</div>
);
const storedTodo = JSON.parse(localStorage.getItem("data")) || [];
const [todo, setTodo] = useState(storedTodo);
const [newTodo, setNewTodo] = useState("");
//count로 수 올려가면서 차례로 색 지정
const [cardColor, setCardColor] = useState([
Copy link
Member

Choose a reason for hiding this comment

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

리스트 만들어질 때마다 다른 색 배경으로 한 거 신박하고 멋있어요~!

"#ECECEC",
"#A6DDF5",
"#F5AEAE",
"#87DDCE",
"#F5EDB6",
]);
const [colorCnt, setColorCnt] = useState(0);
//todo 변경 시 localStorage 저장
useEffect(() => {
localStorage.setItem("data", JSON.stringify(todo));
}, [todo]);
//input handler
const handleInputChange = (e) => {
setNewTodo(e.target.value);
};
// submit 시 todo 추가
const handleAddTodo = () => {
if (newTodo.trim() !== "") {
Copy link

Choose a reason for hiding this comment

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

trim()으로 처리해주신 디테일 좋아요~~

const newTodoItem = {
content: newTodo,
checked: false,
color: cardColor[colorCnt % 5],
};
setTodo([newTodoItem, ...todo]);
setColorCnt((colorCnt + 1) % 5);
setNewTodo("");
}
};
Copy link

Choose a reason for hiding this comment

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

카드 넘어갈 때마다 다른 색으로 바뀌는 거 신박하네요! 색 지정해주는 방식도 나머지 연산자 활용해서 하신 거 정말 좋은 아이디어인 것 같습니다👍

const handleSubmit = (e) => {
e.preventDefault();
if (newTodo.length >= 80) {
alert("80자 이하로 입력해주세요");
} else {
handleAddTodo();
}
};
return (
<div className="wrapper">
<header className="header">
<div className="logo">TODO-list</div>
<div className="detail">
<div>
투두리스트를 작성하고
<br /> 오늘 하루를 기록해요
</div>
<Clock />
</div>
</header>
<form className="todoInput" onSubmit={handleSubmit}>
<input
type="text"
placeholder=" ADD TODO"
value={newTodo}
onChange={handleInputChange}
/>
<button id="plusButton" type="submit">
+
</button>
</form>
<main>
<Cards todo={todo} setTodo={setTodo}></Cards>
</main>
</div>
);
}

export default App;
12 changes: 12 additions & 0 deletions src/GlobalStyle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { createGlobalStyle } from "styled-components";
import reset from "styled-reset";

const GlobalStyle = createGlobalStyle`
${reset};
Copy link
Member

Choose a reason for hiding this comment

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

저는 css reset한거 좋은 것 같아요! 저도 다음과제부터 해야겠어요

Copy link

Choose a reason for hiding this comment

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

저도 배워갑니다!!! 써봐야겠어요!~!

Copy link

Choose a reason for hiding this comment

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

저도 배워갑니다•••

body{
background-color: black;
color : #ECECEC;
font-family: 'GmarketSansMedium';
}
`;
export default GlobalStyle;
90 changes: 90 additions & 0 deletions src/components/Cards.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import "../App";
import styled, { css } from "styled-components";
import { useEffect, useState } from "react";
const TodoCard = styled.div`
Copy link

Choose a reason for hiding this comment

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

저도 1주차 피드백으로 받았던 부분인데, card container div를 만든 다음 height를 지정한 뒤 overflow-y: scroll 속성을 주면 정해진 크기 안에서 카드들을 볼 수 있어서 좋더라구요. 제일 밑에 있는 항목을 삭제하러 내려갔다가 다시 입력하러 스크롤을 올리지 않아도 되는 방식이라 제안 드려봅니다...ㅎㅎ 그리고 스크롤이 생기면 조금 투박하게 생기는데, 이 역시 따로 스타일을 입힐 수 있어요! 저도 공유받은 링크 공유 드리고 갑니다..
스크롤 스타일링

Copy link
Author

Choose a reason for hiding this comment

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

감사합니다!! 지혜님 투두리스트보면서 저도 다음에 써봐야겠다 생각했는데 링크 읽어보겠습니다 ㅎㅎ

background-color: ${(props) => props.color};
color: var(--bg-color);
display: flex;
align-items: center;
justify-content: space-between;
height: 100px;
margin: 5px 0;
border-radius: 8px;
position: relative;
transition: opacity 0.3s;
opacity: ${(props) => (props.checked ? "0.5" : "1")};
&:hover {
Copy link

Choose a reason for hiding this comment

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

이렇게 스타일 컴포넌트 안에 props 받아와서 설정해주는 거 정말 좋은 것 같아요! 또 3항 연산자 사용해서 깔끔하게 코드 작성된 것 너무 좋습니다...👍

opacity: ${(props) => (props.checked ? "0.6" : "1")};
}
`;
const TodoElem = styled.div`
margin-left: 30px;
text-decoration: ${(props) => (props.checked ? "line-through" : "none")};
Copy link
Member

Choose a reason for hiding this comment

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

체크 누르면 line-through 설정한 것 좋은 것 같습니다

max-width: 350px;
word-wrap: break-word;
`;
function Cards({ todo, setTodo }) {
//checked card 구분
const unCheckedTodo = todo.filter((element) => !element.checked);
const checkedTodo = todo.filter((element) => element.checked);

//todo state 에서 삭제
const handleOnClick = (element) => {
const copyTodo = [...todo];
const index = copyTodo.findIndex((e) => e === element);
copyTodo.splice(index, 1);
setTodo(copyTodo);
};
//element에 해당하는 index를 찾아서 checked 바꿔주고 state 변경
const handleChecked = (element) => {
const copyTodo = [...todo];
const index = copyTodo.findIndex((e) => e === element);
copyTodo[index].checked = !copyTodo[index].checked;
Copy link
Member

Choose a reason for hiding this comment

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

저는 done이랑 todo를 아예 나눠서 만들었는데 규호님처럼 checked boolean값을 이용해서 나누는게 더 좋은 것 같습니다~

setTodo(copyTodo);
};

return (
<>
<div className="todoList">
{unCheckedTodo.map((element, i) => {
return (
<TodoCard color={element.color}>
<TodoElem>{element.content}</TodoElem>
<input
type="checkbox"
checked={element.checked}
onChange={() => handleChecked(element)}
></input>
<div
className="todoDelete"
onClick={() => handleOnClick(element)}
>
x
</div>
</TodoCard>
);
})}
{checkedTodo.map((element, i) => {
return (
<TodoCard checked color={element.color}>
<TodoElem checked>{element.content}</TodoElem>
<input
type="checkbox"
checked={element.checked}
onChange={() => handleChecked(element)}
></input>
<div
className="todoDelete"
onClick={() => handleOnClick(element)}
>
x
</div>
</TodoCard>
);
})}
</div>
</>
);
}

export default Cards;
34 changes: 34 additions & 0 deletions src/components/Clock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import "../App.css";
import { useState, useEffect } from "react";

function Clock() {
const [currentTime, setCurrentTime] = useState(new Date());

// 시간 업데이트
const updateClock = () => {
setCurrentTime(new Date());
};

useEffect(() => {
//mount 시 timer 셋팅
const timer = setInterval(updateClock, 1000);
return () => {
clearInterval(timer);
};
}, []);
const options = {
Copy link
Member

Choose a reason for hiding this comment

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

저는 라이브러리를 사용했는데 이렇게 option으로 하는것도 좋은 것 같습니다

weekday: "long",
month: "numeric",
day: "numeric",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false, //오전 오후 나누는거 false 처리
};
//string으로 만들어 변수로 변환한다.
const stringTime = currentTime.toLocaleDateString("ko-KR", options);
Copy link

Choose a reason for hiding this comment

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

날짜뿐만 아니라 시,분,초까지 나타내는 것, 그리고 그 형식을 따로 이렇게 지정할 수 있는 것도 좋네요!!


return <div id="clock-js">{stringTime}</div>;
}

export default Clock;
17 changes: 9 additions & 8 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import GlobalStyle from "./GlobalStyle";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<GlobalStyle />
<App />
</React.StrictMode>,
document.getElementById('root')
);
</React.StrictMode>
);