-
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
[1주차] 조유담 미션 제출합니다 #11
base: master
Are you sure you want to change the base?
Changes from 12 commits
5a64275
dccc8fa
4323a11
9385d1a
14b669a
3877e78
684c1c4
2adc42f
cbfe627
5d2c623
9a94450
53ebfba
386a861
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,7 @@ | ||
{ | ||
"printWidth": 120, | ||
"tabWidth": 2, | ||
"singleQuote": true, | ||
"trailingComma": "all", | ||
"semi": true | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,196 @@ | ||||||||||||||||||||||
import { $, $all } from './util.js'; | ||||||||||||||||||||||
|
||||||||||||||||||||||
// 오늘 날짜 문자열 반환 | ||||||||||||||||||||||
const getTodayDate = () => { | ||||||||||||||||||||||
const today = new Date(); | ||||||||||||||||||||||
const year = today.getFullYear(); | ||||||||||||||||||||||
const month = (today.getMonth() + 1).toString().padStart(2, '0'); | ||||||||||||||||||||||
const date = today.getDate().toString().padStart(2, '0'); | ||||||||||||||||||||||
|
||||||||||||||||||||||
const dayToString = ['일', '월', '화', '수', '목', '금', '토']; | ||||||||||||||||||||||
const day = dayToString[today.getDay()]; | ||||||||||||||||||||||
|
||||||||||||||||||||||
return `${year}년 ${month}월 ${date}일 ${day}요일`; | ||||||||||||||||||||||
}; | ||||||||||||||||||||||
Comment on lines
+3
to
+14
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. 이렇게 작성해주셨을 때 확실히 세세한 설정을 할 수 있어서 좋지만, 디테일한 부분이 필요하지 않을 때는 JS의 toLocaleDateString 를 써봐도 좋을 것 같아요. 설정을 한국어로 바꿔준 뒤 세부 설정을 long으로 해주면 읽기 편한 방식으로 바꿔주는 편리한 기능이라 참고링크 놓고 갑니다! |
||||||||||||||||||||||
|
||||||||||||||||||||||
// 할 일 아이템 생성 | ||||||||||||||||||||||
const createItem = (text, isDone = false) => { | ||||||||||||||||||||||
const newItem = document.createElement('li'); | ||||||||||||||||||||||
|
||||||||||||||||||||||
// draggable 속성 추가 | ||||||||||||||||||||||
newItem.setAttribute('draggable', true); | ||||||||||||||||||||||
newItem.classList.add('draggable'); | ||||||||||||||||||||||
|
||||||||||||||||||||||
// 동그라미 아이콘 | ||||||||||||||||||||||
const circleIcon = document.createElement('i'); | ||||||||||||||||||||||
circleIcon.classList.add('fa-regular', 'fa-circle', 'cursor-pointer'); | ||||||||||||||||||||||
|
||||||||||||||||||||||
if (isDone) { | ||||||||||||||||||||||
circleIcon.classList.add('fa-check-circle'); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
// 누르면 done으로 이동하는 이벤트 리스너 등록 | ||||||||||||||||||||||
circleIcon.addEventListener('click', moveItem); | ||||||||||||||||||||||
newItem.append(circleIcon); | ||||||||||||||||||||||
|
||||||||||||||||||||||
// 입력받은 Text | ||||||||||||||||||||||
const todoText = document.createTextNode(text); | ||||||||||||||||||||||
newItem.append(todoText); | ||||||||||||||||||||||
|
||||||||||||||||||||||
// 쓰레기통 아이콘 | ||||||||||||||||||||||
const trashIcon = document.createElement('i'); | ||||||||||||||||||||||
trashIcon.classList.add('fa-solid', 'fa-trash-can', 'cursor-pointer'); | ||||||||||||||||||||||
|
||||||||||||||||||||||
// 누르면 삭제 | ||||||||||||||||||||||
trashIcon.addEventListener('click', deleteItem); | ||||||||||||||||||||||
newItem.append(trashIcon); | ||||||||||||||||||||||
|
||||||||||||||||||||||
return newItem; | ||||||||||||||||||||||
}; | ||||||||||||||||||||||
|
||||||||||||||||||||||
// 할 일 추가 이벤트 핸들러 | ||||||||||||||||||||||
const addItem = (e) => { | ||||||||||||||||||||||
e.preventDefault(); | ||||||||||||||||||||||
|
||||||||||||||||||||||
// 빈 값 입력시 에러 처리 | ||||||||||||||||||||||
const inputValue = $('.input').value.trim(); | ||||||||||||||||||||||
if (!inputValue) { | ||||||||||||||||||||||
$('.error').textContent = '내용을 입력해주세요'; | ||||||||||||||||||||||
return; | ||||||||||||||||||||||
} | ||||||||||||||||||||||
$('.error').textContent = ''; | ||||||||||||||||||||||
Comment on lines
+55
to
+60
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. 이부분은
Suggested change
로 하면 더 깔끔할거 같아요! 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. 앗 다시 확인해보니 error을 쓰신 이유가 있었네요! alert 부분은 무시해 주셔도 됩니다! 개인적으로는 inputValue와 같은 변수에는 하나의 클래스만 선택하도록 하는 것이 또 쓰일 경우를 대비해서 더 좋더라구요. if문안에도 직관적인 내용이 들어왔을 때가 유지 보수하기 좋았던 것 같은데 이부분은 개인 취향이 아닐까.. 생각합니다! |
||||||||||||||||||||||
|
||||||||||||||||||||||
// 새로운 list 아이템 만들기 | ||||||||||||||||||||||
const newItem = createItem(inputValue); | ||||||||||||||||||||||
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. form 태그에 이벤트를 걸어둔 거라 입력 버튼을 누르거나 엔터를 눌렀을 때 addItem 함수 내부의 createItem이 실행돼요! 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. 아하! 제 코드에도 달아주신 리뷰도 확인했습니다! 많이 배워가요! |
||||||||||||||||||||||
|
||||||||||||||||||||||
// list에 추가 | ||||||||||||||||||||||
$('.todo-list').append(newItem); | ||||||||||||||||||||||
|
||||||||||||||||||||||
// 인풋 비워주기 | ||||||||||||||||||||||
$('.input').value = ''; | ||||||||||||||||||||||
|
||||||||||||||||||||||
updateListCount(); | ||||||||||||||||||||||
saveToLocalStorage(); | ||||||||||||||||||||||
}; | ||||||||||||||||||||||
|
||||||||||||||||||||||
// 할 일 이동 이벤트 핸들러 | ||||||||||||||||||||||
const moveItem = (e) => { | ||||||||||||||||||||||
const circleIcon = e.target; | ||||||||||||||||||||||
|
||||||||||||||||||||||
// 부모 요소 찾기 (아이콘의 부모 요소가 li) | ||||||||||||||||||||||
const clickedItem = circleIcon.parentNode; | ||||||||||||||||||||||
|
||||||||||||||||||||||
// 이동할 리스트 정하기 | ||||||||||||||||||||||
const targetList = clickedItem.closest('.todo-list') ? '.done-list' : '.todo-list'; | ||||||||||||||||||||||
|
||||||||||||||||||||||
// 동그라미 <-> 체크 동그라미 | ||||||||||||||||||||||
circleIcon.classList.toggle('fa-circle'); | ||||||||||||||||||||||
circleIcon.classList.toggle('fa-check-circle'); | ||||||||||||||||||||||
$(targetList).append(clickedItem); | ||||||||||||||||||||||
Comment on lines
+86
to
+88
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. 저두 todo와 done에 따라 다른 아이콘을 주고 싶어서 css로만 조작을 했었는데, 이렇게 따로 append를 해주니 더 좋은거 같아요! 배워갑니다~ |
||||||||||||||||||||||
|
||||||||||||||||||||||
updateListCount(); | ||||||||||||||||||||||
saveToLocalStorage(); | ||||||||||||||||||||||
}; | ||||||||||||||||||||||
|
||||||||||||||||||||||
// 할 일 삭제 이벤트 핸들러 | ||||||||||||||||||||||
const deleteItem = (e) => { | ||||||||||||||||||||||
// 쓰레기통 아이콘의 부모 요소 찾기 | ||||||||||||||||||||||
const clickedItem = e.target.closest('li'); | ||||||||||||||||||||||
clickedItem.remove(); | ||||||||||||||||||||||
|
||||||||||||||||||||||
updateListCount(); | ||||||||||||||||||||||
saveToLocalStorage(); | ||||||||||||||||||||||
Comment on lines
+100
to
+101
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. list count를 업데이트할 때마다 로컬 스토리지에 저장하다보니, updateListCount 함수에 saveToLocalStorage 함수를 넣으면 타 함수 작성시에 둘 중 한 가지 기능을 빼먹을 가능성이 줄어들 것 같아요! |
||||||||||||||||||||||
}; | ||||||||||||||||||||||
|
||||||||||||||||||||||
// 할 일 리스트 개수 업데이트 | ||||||||||||||||||||||
const updateListCount = () => { | ||||||||||||||||||||||
$('.todo-count').textContent = `/ ${$('.todo-list').childElementCount}개`; | ||||||||||||||||||||||
$('.done-count').textContent = `/ ${$('.done-list').childElementCount}개`; | ||||||||||||||||||||||
}; | ||||||||||||||||||||||
|
||||||||||||||||||||||
// Local Storage에 저장 | ||||||||||||||||||||||
const saveToLocalStorage = () => { | ||||||||||||||||||||||
const todoListItems = Array.from($('.todo-list').children).map((item) => item.textContent); | ||||||||||||||||||||||
const doneListItems = Array.from($('.done-list').children).map((item) => item.textContent); | ||||||||||||||||||||||
|
||||||||||||||||||||||
localStorage.setItem('todoList', JSON.stringify(todoListItems)); | ||||||||||||||||||||||
localStorage.setItem('doneList', JSON.stringify(doneListItems)); | ||||||||||||||||||||||
}; | ||||||||||||||||||||||
|
||||||||||||||||||||||
// Local Storage에서 할 일 목록 불러오기 | ||||||||||||||||||||||
const loadFromLocalStorage = () => { | ||||||||||||||||||||||
const todoList = JSON.parse(localStorage.getItem('todoList')) || []; | ||||||||||||||||||||||
todoList.forEach((item) => $('.todo-list').append(createItem(item))); | ||||||||||||||||||||||
|
||||||||||||||||||||||
const doneList = JSON.parse(localStorage.getItem('doneList')) || []; | ||||||||||||||||||||||
doneList.forEach((item) => $('.done-list').append(createItem(item, true))); | ||||||||||||||||||||||
|
||||||||||||||||||||||
updateListCount(); | ||||||||||||||||||||||
}; | ||||||||||||||||||||||
|
||||||||||||||||||||||
// 사용자 이름 설정 | ||||||||||||||||||||||
const showUserName = () => { | ||||||||||||||||||||||
Comment on lines
+130
to
+131
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. 저도 결과 화면 이용해보면서 이거 보고 재치있고 귀엽다고 생각했어요ㅎㅎ 디테일 굿입니당👍👍 |
||||||||||||||||||||||
const savedUserName = localStorage.getItem('userName'); | ||||||||||||||||||||||
if (savedUserName) { | ||||||||||||||||||||||
$('.userName').textContent = `${savedUserName}의 `; | ||||||||||||||||||||||
return; | ||||||||||||||||||||||
} | ||||||||||||||||||||||
const userName = prompt('이름을 알려주세요!'); | ||||||||||||||||||||||
if (userName) { | ||||||||||||||||||||||
$('.userName').textContent = `${userName}의 `; | ||||||||||||||||||||||
localStorage.setItem('userName', userName); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
}; | ||||||||||||||||||||||
Comment on lines
+132
to
+142
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. 로컬 스토리지에 userName이 있으면 해당 텍스트를 띄우고, 없으면 이름을 받아 그걸 저장한 뒤 띄우는 기능을 하는 함수로 보여요! 디테일이 정말 굿입니다...👍 여기서 else if 문을 따로 쓰지 않으시고 if문을 따로 2개 쓰신 이유가 있는지 궁금해요! 제가 인지하지 못한 다른 이유가 없었다면 else if로 되어있으면 조금 더 가독성이 좋아지지 않을까 조심스럽게 의견 내봅니다 ㅎㅎ 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. else if를 사용하는 것보다 early return을 해주는 걸 선호하는 편입니다! 코드 깊이가 얕아지는 장점이 있어서요 ㅎㅎ 무조건 early return을 하는 게 적절하다고 할 수는 없지만, 저는 유효하지 않은 경우를 앞 부분에서 처리해주는 게 더 가독성 있게 여겨지는 거 같아요~~ |
||||||||||||||||||||||
|
||||||||||||||||||||||
const addDragDropEvents = () => { | ||||||||||||||||||||||
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 문제인지 잘 모르겠어서🥲 혹시 좋은 방안이 있다면 같이 이야기 해봐요! 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에서 처리해주면 됩니다! |
||||||||||||||||||||||
const draggableItems = $all('.draggable'); | ||||||||||||||||||||||
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 containers = $all('.list'); | ||||||||||||||||||||||
|
||||||||||||||||||||||
draggableItems.forEach((el) => { | ||||||||||||||||||||||
el.addEventListener('dragstart', () => { | ||||||||||||||||||||||
el.classList.add('dragging'); | ||||||||||||||||||||||
}); | ||||||||||||||||||||||
|
||||||||||||||||||||||
el.addEventListener('dragend', () => { | ||||||||||||||||||||||
el.classList.remove('dragging'); | ||||||||||||||||||||||
}); | ||||||||||||||||||||||
}); | ||||||||||||||||||||||
|
||||||||||||||||||||||
function getDragAfterElement(container, y) { | ||||||||||||||||||||||
const draggableElements = [...container.querySelectorAll('.draggable:not(.dragging)')]; | ||||||||||||||||||||||
|
||||||||||||||||||||||
return draggableElements.reduce( | ||||||||||||||||||||||
(closest, child) => { | ||||||||||||||||||||||
const box = child.getBoundingClientRect(); | ||||||||||||||||||||||
const offset = y - box.top - box.height / 2; | ||||||||||||||||||||||
if (offset < 0 && offset > closest.offset) { | ||||||||||||||||||||||
return { offset: offset, element: child }; | ||||||||||||||||||||||
} else { | ||||||||||||||||||||||
return closest; | ||||||||||||||||||||||
} | ||||||||||||||||||||||
}, | ||||||||||||||||||||||
{ offset: Number.NEGATIVE_INFINITY }, | ||||||||||||||||||||||
).element; | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
containers.forEach((container) => { | ||||||||||||||||||||||
container.addEventListener('dragover', (e) => { | ||||||||||||||||||||||
e.preventDefault(); | ||||||||||||||||||||||
const afterElement = getDragAfterElement(container, e.clientY); | ||||||||||||||||||||||
const draggable = document.querySelector('.dragging'); | ||||||||||||||||||||||
container.insertBefore(draggable, afterElement); | ||||||||||||||||||||||
}); | ||||||||||||||||||||||
}); | ||||||||||||||||||||||
}; | ||||||||||||||||||||||
|
||||||||||||||||||||||
// 초기 실행 함수 | ||||||||||||||||||||||
const init = () => { | ||||||||||||||||||||||
showUserName(); | ||||||||||||||||||||||
$('.date').textContent = getTodayDate(); | ||||||||||||||||||||||
$('.input-form').addEventListener('submit', addItem); | ||||||||||||||||||||||
|
||||||||||||||||||||||
loadFromLocalStorage(); | ||||||||||||||||||||||
addDragDropEvents(); | ||||||||||||||||||||||
}; | ||||||||||||||||||||||
|
||||||||||||||||||||||
// 초기 실행 | ||||||||||||||||||||||
init(); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
function $(selector) { | ||
return document.querySelector(selector); | ||
} | ||
|
||
function $all(selector) { | ||
return document.querySelectorAll(selector); | ||
} | ||
|
||
export { $, $all }; | ||
Comment on lines
+1
to
+9
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. 'document.querySelectorAll(selector)'를 바로 리턴해서 함수 이름에 $를 붙인 이유가 궁금한데 오늘 이따 만나면 여쭤볼게용!!! |
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
.flex-center { | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
} | ||
|
||
.cursor-pointer { | ||
cursor: pointer; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
@font-face { | ||
font-family: 'Ownglyph_meetme-Rg'; | ||
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/[email protected]/Ownglyph_meetme-Rg.woff2') | ||
format('woff2'); | ||
} | ||
|
||
html { | ||
font-size: 62.5%; | ||
font-family: 'Ownglyph_meetme-Rg'; | ||
} | ||
|
||
html, | ||
body, | ||
button, | ||
input, | ||
h1, | ||
h2 { | ||
line-height: normal; | ||
font-style: normal; | ||
font-weight: 400; | ||
} | ||
|
||
a, | ||
button { | ||
cursor: pointer; | ||
} | ||
|
||
#__next { | ||
height: 100%; | ||
} | ||
|
||
.root { | ||
display: flex; | ||
flex-direction: column; | ||
height: 100%; | ||
} | ||
|
||
.content { | ||
flex: 1; | ||
} |
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.
og 태그 작성해주신 점 좋네요! 미리보기까지 챙겨주신 섬세함이 돋보이는 것 같습니다ㅎㅎ