Skip to content
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
421 changes: 388 additions & 33 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"styled-components": "^6.0.8",
"web-vitals": "^2.1.4"
},
"scripts": {
Expand Down
43 changes: 43 additions & 0 deletions public/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
@font-face {
font-family: 'omyu_pretty';
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_2304-01@1.0/omyu_pretty.woff2') format('woff2');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'SUITE-Regular';
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_2304-2@1.0/SUITE-Regular.woff2') format('woff2');
font-weight: 400;
font-style: normal;
}

@font-face {
font-family: 'TheJamsil5Bold';
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_2302_01@1.0/TheJamsil5Bold.woff2') format('woff2');
font-weight: 700;
font-style: normal;
}
@font-face {
font-family: 'Giants-Inline';
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_2307-1@1.1/Giants-Inline.woff2') format('woff2');
font-weight: normal;
font-style: normal;
}


html,
body{
margin:0
}

html{
font-size: 10px;
}
h2{
font-size:2.4rem
}
h3{
display: flex;
justify-content: center;
font-size:1.5rem;
}
5 changes: 3 additions & 2 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<link rel="icon" href="%PUBLIC_URL%/tori.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="stylesheet" href="index.css" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
Expand All @@ -24,7 +25,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>To do list</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
Expand Down
Binary file added public/tori.ico
Binary file not shown.
153 changes: 148 additions & 5 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,152 @@
import { useState, useEffect } from "react";
import TodoForm from "./components/TodoForm";
import TodoList from "./components/TodoList";
import styled from "styled-components";
import NameInput from "./components/NameInput";

const Container = styled.div`
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
width: 100vw;
background-color: #f0f0f0;
font-family: "omyu_pretty", sans-serif; // 폰트 지정
`;

Choose a reason for hiding this comment

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

전체 컨테이너에 vw 와 vh를 사용해서 전체 크기를 조정하니 더 괜찮네요 배워갑니다 !

const TodoBox = styled.div`
background-color: white;
height: 70rem;
width: 30rem;
border-radius: 0.8rem;
box-shadow: 0 0.4rem 0.8rem rgba(0, 0, 0, 0.2);
padding: 2rem;
`;

const Title = styled.h1`
text-align: center;
font-size: 3rem;
font-weight: 800;
margin-bottom: 1rem;
`;

function App() {
return (
<div>
<h1>18기 프론트 화이팅~ 푸하항ㅋ</h1>
</div>
);
//local storage에 name, todos 배열, dones 이 존재하면 초기값 설정해줌
const [name, setName] = useState(
() => JSON.parse(window.localStorage.getItem("name")) || ""
);
const [todo, setTodo] = useState("");
const [todos, setTodos] = useState(
() => JSON.parse(window.localStorage.getItem("todos")) || []
);
const [dones, setDones] = useState(
() => JSON.parse(window.localStorage.getItem("dones")) || []
);

//todos, dones, name의 변화가 일어날때마다 local storage에 저장
useEffect(() => {

Choose a reason for hiding this comment

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

저는 보통 바뀌는 요소를 한 배열안에 넣어서 모두 업데이트하는데 이렇게 따로따로 하는게 불필요한 리렌더링을 방지할 수 있을 것 같네요 !

window.localStorage.setItem("todos", JSON.stringify(todos));
}, [todos]);
useEffect(() => {
window.localStorage.setItem("dones", JSON.stringify(dones));
}, [dones]);
useEffect(() => {
window.localStorage.setItem("name", JSON.stringify(name));
}, [name]);

const onChange = (event) => setTodo(event.target.value);

const onSubmit = (event) => {
event.preventDefault();
if (todo.trim() === "") {
//input 예외처리
setTodo("");
alert("Please enter a input!");
return;
}
setTodos((currentArray) => [todo, ...currentArray]);
//todos 배열에 입력받은 todo를 추가해줌
setTodo("");
};

//delete Btn 누르면 -> isDone 여부에 따라 todos, dones 배열을 update
const deleteBtn = (index, isDone) => {
if (isDone === true) {

Choose a reason for hiding this comment

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

isDone이 bool값이라 그냥 if (isDone) {} / if(!isDone) {}으로도 쓸 수 있을 것 같네요!

setDones(dones.filter((item, doneIndex) => index !== doneIndex));
} else {
setTodos(todos.filter((item, todoIndex) => index !== todoIndex));
}
};

//move Btn을 누르면 -> isDone 여부에 따라 todos, dones 배열을 update
const moveBtn = (index, isDone) => {
if (isDone == false) {
//todo-> done
const itemToMove = todos[index];
setTodos(todos.filter((item, todoIndex) => index !== todoIndex));
setDones((currentArray) => [itemToMove, ...currentArray]);
} else {
//done->todo
const itemToMove = dones[index];
setDones(dones.filter((item, doneIndex) => index !== doneIndex));
setTodos((currentArray) => [itemToMove, ...currentArray]);
}
};

//name를 입력 -> name 저장
const handleNameSubmit = (enteredName) => {
setName(enteredName);
};

const titleText = name ? `${name}'s To-Do List` : "To-Do List";

const [currentDate, setCurrentDate] = useState(new Date());

useEffect(() => {
const intervalId = setInterval(() => {
setCurrentDate(new Date());
}, 1000); // 1초마다 현재 날짜 업데이트

return () => clearInterval(intervalId);
}, []);

return (
<Container>
<TodoBox>
{name ? (
<Title>{titleText}</Title>
) : (
<NameInput onNameSubmit={handleNameSubmit} />
)}

{name && (
<div>
<h3>{currentDate.toLocaleString()}</h3>
</div>
)}

<TodoForm onSubmit={onSubmit} onChange={onChange} value={todo} />

<hr />
<h2>To Do : {todos.length}</h2>
<TodoList
items={todos}
moveBtn={moveBtn}
deleteBtn={deleteBtn}
isDone={false}
/>

<hr />
<h2> Done : {dones.length}</h2>
<TodoList
items={dones}
moveBtn={moveBtn}
deleteBtn={deleteBtn}
isDone={true}
/>
</TodoBox>
</Container>
);
}

export default App;
60 changes: 60 additions & 0 deletions src/components/NameInput.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React, { useState } from "react";
import Button from "../utils/Button";
import styled from "styled-components";

Choose a reason for hiding this comment

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

사용자마다 이름을 추가해 개인 별 투두리스트로 만든 것 멋지네요

//name input 받는 component

const InputForm = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
border-radius: 0.5rem;
`;

const InputField = styled.input`
flex-grow: 1;
border: none;
padding: 0.3rem;
outline: none;
width: 15rem;
height: 4rem;
font-size: 1.5rem;
border: 0;
border-radius: 1.5rem;
outline: none;
padding-left: 1rem;
background-color: #f0f0f0;
margin-right: 2rem;
`;

function NameInput({ onNameSubmit }) {
const [name, setName] = useState("");

const handleSubmit = (e) => {
e.preventDefault();
onNameSubmit(name);
};

return (
<InputForm>
<form onSubmit={handleSubmit}>
<InputField
type="text"
placeholder="✏️Enter your name"
value={name}
onChange={(e) => setName(e.target.value)}

Choose a reason for hiding this comment

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

image
이름 설정 부분도 할일 추가 기능처럼 입력값 유효성 검사가 있으면 좋을 것 같아요! 이쪽은 trim처리가 안되어서 공백도 들어가네요

Copy link
Author

Choose a reason for hiding this comment

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

아 이름 부분 input은 예외처리를 깜빡했네요!! 찾아주셔서 감사합니당ㅎㅎㅎ

/>
</form>
<Button
onClick={handleSubmit}
text="Submit"
backgroundColor="#088395"
textColor="white"
buttonSize="large"
/>
</InputForm>
);
}

export default NameInput;
50 changes: 50 additions & 0 deletions src/components/TodoForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import Button from "../utils/Button";
import styled from "styled-components";
//todo Input 받는 부분

const InputForm = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
border-radius: 1rem;
`;
const InputField = styled.input`
flex-grow: 1;
border: none;
padding: 0.3rem;
outline: none;
width: 15rem;
height: 4rem;
font-size: 1.5rem;
border: 0;
border-radius: 1.5rem;
outline: none;
padding-left: 1rem;
background-color: #f0f0f0;
margin-right: 2rem;
`;
function TodoForm({ onSubmit, onChange, value }) {
return (
<InputForm>
<form onSubmit={onSubmit}>
<InputField
autoFocus
type="text"
placeholder="📍Enter your to-do"
onChange={onChange}
value={value}
/>
</form>
<Button
onClick={onSubmit}
text={"Add"}
backgroundColor="#191D88"
textColor="white"
buttonSize="large"
></Button>
</InputForm>
);
}

export default TodoForm;
44 changes: 44 additions & 0 deletions src/components/TodoItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import Button from "../utils/Button";
import styled from "styled-components";

//list에 들어갈 Item Component

const TodoItemWrapper = styled.li`
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.3rem;
margin-bottom: 0.3rem;
font-size: 1.5rem;
font-family: "SUITE-Regular", sans-serif; // 폰트 지정
`;

Choose a reason for hiding this comment

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

전에 제가 리뷰받았던 내용인데, 영어에 대해서는 줄 바꿈 처리가 안되어있어서
word-break: break-all; 를 추가해보시는 것도 좋을 것 같습니다 !

Copy link
Author

Choose a reason for hiding this comment

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

오 그런 방법이 있었군요!!담에 적용해봐여겠어요👍🏻

const ButtonContainer = styled.div`
display: flex;
`;

function TodoItem({ item, index, moveBtn, deleteBtn, isDone }) {
return (
<TodoItemWrapper>
{item}
<ButtonContainer>
<Button
onClick={() => moveBtn(index, isDone)}
text={isDone ? "⬆" : "⬇"} //버튼 위, 아래 나눠서 설정
backgroundColor="#6aafe6"
textColor="white"
buttonSize="small"
/>

Choose a reason for hiding this comment

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

위코드들을 보니 isDone을 사용해 , todo와 done을 구분하는 코드가 훨씬 직관적이고 간단하더라구요 배워갑니다 !

<Button
onClick={() => deleteBtn(index, isDone)}
text="x"
backgroundColor="red"
textColor="white"
buttonSize="small"
/>
</ButtonContainer>
</TodoItemWrapper>
);
}

export default TodoItem;
Loading