-
Notifications
You must be signed in to change notification settings - Fork 10
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
base: master
Are you sure you want to change the base?
Changes from all commits
7b70be2
1b90ebb
2f3f0f5
ca8c2a0
31c0cd6
1761c96
25aa120
474f8ac
30ff7ce
12444f3
a904ba5
040a0f2
a90782a
d5f8656
4b077c1
aefd4f1
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 |
---|---|---|
@@ -1 +1 @@ | ||
node_modules | ||
node_modules |
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
@font-face { | ||
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; | ||
} | ||
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. 색상을 따로 지정해두고 사용하니까 코드가 더 깔끔하네요! |
||
.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; | ||
} |
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([ | ||
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. 리스트 만들어질 때마다 다른 색 배경으로 한 거 신박하고 멋있어요~! |
||
"#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() !== "") { | ||
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. trim()으로 처리해주신 디테일 좋아요~~ |
||
const newTodoItem = { | ||
content: newTodo, | ||
checked: false, | ||
color: cardColor[colorCnt % 5], | ||
}; | ||
setTodo([newTodoItem, ...todo]); | ||
setColorCnt((colorCnt + 1) % 5); | ||
setNewTodo(""); | ||
} | ||
}; | ||
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 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; |
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}; | ||
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. 저는 css reset한거 좋은 것 같아요! 저도 다음과제부터 해야겠어요 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. 저도 배워갑니다••• |
||
body{ | ||
background-color: black; | ||
color : #ECECEC; | ||
font-family: 'GmarketSansMedium'; | ||
} | ||
`; | ||
export default GlobalStyle; |
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` | ||
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. 저도 1주차 피드백으로 받았던 부분인데, card container div를 만든 다음 height를 지정한 뒤 overflow-y: scroll 속성을 주면 정해진 크기 안에서 카드들을 볼 수 있어서 좋더라구요. 제일 밑에 있는 항목을 삭제하러 내려갔다가 다시 입력하러 스크롤을 올리지 않아도 되는 방식이라 제안 드려봅니다...ㅎㅎ 그리고 스크롤이 생기면 조금 투박하게 생기는데, 이 역시 따로 스타일을 입힐 수 있어요! 저도 공유받은 링크 공유 드리고 갑니다.. 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. 감사합니다!! 지혜님 투두리스트보면서 저도 다음에 써봐야겠다 생각했는데 링크 읽어보겠습니다 ㅎㅎ |
||
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 { | ||
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. 이렇게 스타일 컴포넌트 안에 props 받아와서 설정해주는 거 정말 좋은 것 같아요! 또 3항 연산자 사용해서 깔끔하게 코드 작성된 것 너무 좋습니다...👍 |
||
opacity: ${(props) => (props.checked ? "0.6" : "1")}; | ||
} | ||
`; | ||
const TodoElem = styled.div` | ||
margin-left: 30px; | ||
text-decoration: ${(props) => (props.checked ? "line-through" : "none")}; | ||
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. 체크 누르면 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; | ||
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. 저는 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; |
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 = { | ||
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. 저는 라이브러리를 사용했는데 이렇게 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); | ||
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. 날짜뿐만 아니라 시,분,초까지 나타내는 것, 그리고 그 형식을 따로 이렇게 지정할 수 있는 것도 좋네요!! |
||
|
||
return <div id="clock-js">{stringTime}</div>; | ||
} | ||
|
||
export default Clock; |
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> | ||
); |
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.
GlobalStyle 따로 정의하신 만큼 전체 컴포넌트에 중복되는 속성의 경우 GlobalStyle 사용해도 좋을 것 같아요!