-
Notifications
You must be signed in to change notification settings - Fork 10
[2주차] 신동현 미션 제출합니다. #7
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: master
Are you sure you want to change the base?
Changes from all commits
f0da355
f59ceae
ce3dfa3
644f42d
d1256be
fa9f5e0
5cd0e6e
cc9661e
30949ee
c9fc7cd
5fc9215
b29fc5b
57d0f68
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| 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; | ||
| } |
| 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; // 폰트 지정 | ||
| `; | ||
|
|
||
| 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(() => { | ||
|
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. 저는 보통 바뀌는 요소를 한 배열안에 넣어서 모두 업데이트하는데 이렇게 따로따로 하는게 불필요한 리렌더링을 방지할 수 있을 것 같네요 ! |
||
| 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) { | ||
|
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. isDone이 bool값이라 그냥 |
||
| 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; | ||
| 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"; | ||
|
|
||
|
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. 사용자마다 이름을 추가해 개인 별 투두리스트로 만든 것 멋지네요 |
||
| //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)} | ||
|
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.
Author
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. 아 이름 부분 input은 예외처리를 깜빡했네요!! 찾아주셔서 감사합니당ㅎㅎㅎ |
||
| /> | ||
| </form> | ||
| <Button | ||
| onClick={handleSubmit} | ||
| text="Submit" | ||
| backgroundColor="#088395" | ||
| textColor="white" | ||
| buttonSize="large" | ||
| /> | ||
| </InputForm> | ||
| ); | ||
| } | ||
|
|
||
| export default NameInput; | ||
| 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; |
| 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; // 폰트 지정 | ||
| `; | ||
|
|
||
|
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. 전에 제가 리뷰받았던 내용인데, 영어에 대해서는 줄 바꿈 처리가 안되어있어서
Author
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. 오 그런 방법이 있었군요!!담에 적용해봐여겠어요👍🏻 |
||
| 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" | ||
| /> | ||
|
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. 위코드들을 보니 isDone을 사용해 , todo와 done을 구분하는 코드가 훨씬 직관적이고 간단하더라구요 배워갑니다 ! |
||
| <Button | ||
| onClick={() => deleteBtn(index, isDone)} | ||
| text="x" | ||
| backgroundColor="red" | ||
| textColor="white" | ||
| buttonSize="small" | ||
| /> | ||
| </ButtonContainer> | ||
| </TodoItemWrapper> | ||
| ); | ||
| } | ||
|
|
||
| export default TodoItem; | ||

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.
전체 컨테이너에 vw 와 vh를 사용해서 전체 크기를 조정하니 더 괜찮네요 배워갑니다 !