-
Notifications
You must be signed in to change notification settings - Fork 29
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
[Mission5/김유진] Project_Notion_VanillaJs 과제 #44
base: 4/#5_eugene028
Are you sure you want to change the base?
Changes from all commits
7be2c7e
c97b4c1
6c85df6
21b4681
39c765b
b3905cb
1bf31db
6d68453
171b951
8be4f47
db8eae9
2627c94
8b859e9
039776e
807c087
4b9c83c
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,18 @@ | ||
<!DOCTYPE html> | ||
<html lang="ko"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<link rel="icon" href="asset/notion.png"/> | ||
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. ㅎㅎ 감사합니다! |
||
<link rel="stylesheet" href="/style/global.css"> | ||
<link rel="stylesheet" href="/style/DocumentList.css"> | ||
<link rel="stylesheet" href="/style/DocumentModal.css"> | ||
<link rel="stylesheet" href="/style/DocumentEdit.css"> | ||
<link rel="stylesheet" href="/style/DocumentDelete.css"> | ||
<title>유진의 Notion</title> | ||
</head> | ||
<body> | ||
<main class ="app"></main> | ||
<script src="/src/main.js" type ="module"></script> | ||
</body> | ||
</html> | ||
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. html파일까지 미처 신경 못썼네요!! 수정하도록 하겠습니다 🫡 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { EditorPage } from "./EditorPage.js"; | ||
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. 헉 감사합니다 !! 🥹 |
||
import { DocumentPage } from "./DocumentPage.js"; | ||
import { initRouter } from "./router.js"; | ||
|
||
export default function App ($target) { | ||
const $app = document.createElement('div'); | ||
$app.className = 'mainApp' | ||
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 editorPage = new EditorPage($app); | ||
const documentPage = new DocumentPage($app); | ||
|
||
$target.appendChild($app); | ||
|
||
this.route = () => { | ||
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. EditorPage 를 먼저 선언해놓고 route 할 때마다 setState 를 바꿔주는 방식으로 구현하셨군요...! 이 방법으로 하니까 바로바로 읽히네요...!! 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 { pathname } = window.location; | ||
if(pathname.includes('/documents/')){ | ||
const [, , documentId] = pathname.split('/'); | ||
editorPage.setState({...editorPage.state, id : documentId}); | ||
} | ||
} | ||
this.render = () => { | ||
documentPage.render(); | ||
const { pathname } = window.location; | ||
const [, , documentId] = pathname.split('/'); | ||
if(documentId){ | ||
editorPage.setState({...editorPage.state, id: documentId}) | ||
} | ||
} | ||
this.render() | ||
initRouter({onRoute : this.route}); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { routerNav } from '../router.js'; | ||
|
||
export default function ChildDocument ({$target, $document}){ | ||
const $childDocument = document.createElement('div'); | ||
$childDocument.className = 'childLink'; | ||
$target.append($childDocument); | ||
|
||
this.state = { | ||
data : $document | ||
} | ||
this.setState = nextState => { | ||
this.state = nextState | ||
this.render(); | ||
} | ||
this.render = () => { | ||
if(this.state.data){ | ||
const { documents } = this.state.data | ||
if(documents){ | ||
$childDocument.innerHTML = `${ | ||
documents.map(({id, title}) => | ||
`<li class="linkDoc" data-id=${id}>${title}</li>` | ||
).join('')}` | ||
} | ||
} | ||
} | ||
|
||
this.render(); | ||
$childDocument.addEventListener('click', (e) => { | ||
const $link = e.target.closest('li'); | ||
const { id } = $link.dataset; | ||
routerNav(`/documents/${id}`) | ||
}) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { DocumentModal } from "./DocumentModal.js"; | ||
|
||
export function DocumentCreate({$target, parentId, onSubmit}){ | ||
this.state = { | ||
parentId :null, | ||
} | ||
this.setState = (nextState) => { | ||
this.state = nextState; | ||
} | ||
this.render = () => { | ||
const $createBtn = document.createElement('button'); | ||
$createBtn.className = 'createDoc'; | ||
if (parentId === null){ | ||
$createBtn.className = 'rootCreate' | ||
} | ||
$createBtn.textContent = '+'; | ||
$target.append($createBtn); | ||
} | ||
$target.addEventListener('click', (e) => { | ||
const $createBtn = e.target.closest('button'); | ||
if($createBtn){ | ||
if($createBtn.classList.contains('rootCreate')){ | ||
e.stopImmediatePropagation(); | ||
const modal = new DocumentModal(null , onSubmit) | ||
modal.modalOpen(); | ||
return | ||
} | ||
const { id } = $createBtn.nextElementSibling.dataset | ||
const modal = new DocumentModal(id , onSubmit) | ||
modal.modalOpen(); | ||
} | ||
}) | ||
this.render() | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { deleteDocuments } from "../api.js"; | ||
import { routerNav } from "../router.js"; | ||
|
||
export default function DocumentDelete ({$target, id}){ | ||
const $deleteBtn = document.createElement('button'); | ||
$deleteBtn.className= 'delete-btn' | ||
$target.appendChild($deleteBtn) | ||
$deleteBtn.textContent = '삭제하기' | ||
|
||
this.state = { id } | ||
this.setState = nextState => { | ||
this.state = nextState | ||
} | ||
$deleteBtn.addEventListener('click', async (e) => { | ||
await deleteDocuments(this.state.id) | ||
alert("삭제 완료"); | ||
routerNav('/'); | ||
location.reload(); | ||
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. 오 reload 메소드를 통해서 바로 reload하는게 유저 입장에서 좋은것 같습니다! 하나 배워갑니다! 👍 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. 사실 |
||
}) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import { DocumentCreate } from "./DocumentCreate.js" | ||
import { routerNav } from '../router.js'; | ||
|
||
export function DocumentList({$target, data =[], initialState, onSubmit}) { | ||
let init = false; | ||
this.state = initialState | ||
this.setState = (nextState) => { | ||
this.state = nextState | ||
} | ||
this.render = ($renderDOM = $target) => { | ||
const $parentNode = document.createElement('div') | ||
$parentNode.className = `doc-${this.state.selectedNode}` | ||
$parentNode.style.marginLeft = `${this.state.depth * 10}px`; | ||
const createBtn = new DocumentCreate({ | ||
$target: $parentNode, | ||
parentId: this.state.parent, | ||
onSubmit: onSubmit | ||
}) | ||
const doc = document.createElement('li'); | ||
doc.setAttribute("data-id", `${data[0].id}`); | ||
doc.setAttribute("class", 'doc'); | ||
doc.textContent =`${data[0].title}` | ||
$parentNode.append(doc) | ||
console.log(this.state.isOpen) | ||
if(data[0].documents.length === 0){ | ||
const $haveNothing = document.createElement('div'); | ||
$haveNothing.classList.add('nothing') | ||
$haveNothing.textContent = '하위 페이지 없음' | ||
doc.append($haveNothing) | ||
} | ||
else { | ||
data[0].documents.forEach((data => { | ||
const documentList = new DocumentList({ | ||
$target: doc, | ||
data: [data], | ||
initialState: {parent: this.state.selectedNode, selectedNode: data.id, depth: this.state.depth + 1, isOpen: false}, | ||
onSubmit: onSubmit | ||
}) | ||
documentList.render() | ||
})) | ||
} | ||
init= true | ||
$renderDOM.append($parentNode) | ||
|
||
} | ||
|
||
$target.addEventListener('click', (e) => { | ||
e.stopPropagation(); | ||
if($target.classList.contains('documentPage') && !e.target.classList.contains('doc')) | ||
return | ||
else if (e.target.classList.contains('createDoc')){ | ||
return | ||
} | ||
else if (e.target.classList.contains('nothing')){ | ||
return | ||
} | ||
Comment on lines
+49
to
+56
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. 일관성을 지켜서 쓸거면 쓰고, 안쓸거면 아예 쓰지를 말아야겠군요..!!! |
||
console.log(e.target) | ||
console.log(this.state) | ||
if(this.state.isOpen){ | ||
while(e.target.querySelector('div')){ | ||
e.target.classList.remove("open"); | ||
const $removeTarget = e.target.querySelector('div') | ||
e.target.removeChild($removeTarget); | ||
} | ||
this.setState({ | ||
...this.state, | ||
isOpen: !this.state.isOpen, | ||
}) | ||
return | ||
} | ||
Comment on lines
+59
to
+70
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. 의도와는 조금 다른 방식으로 동작하는 것 같습니다. 루트 컴포넌트에서 하위 컴포넌트를 보여줄 때에는 정상적으로 동작하지만 하위 컴포넌트의 하위 컴포넌트를 보여줄 때에는 이미 isOpen이 true인 상태여서 두 번 클릭해야 합니다. isOpen을 각각 적용 시키는 방법은 어떨지 생각해봤습니다! 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 $li = e.target | ||
if($li){ | ||
const { id } = $li.dataset; | ||
if(data){ | ||
const childrenData = data.map(data => data.documents) | ||
$li.classList.add('open') | ||
if(childrenData[0].length > 0){ | ||
this.setState({ | ||
parent: this.state.depth === 0 ? parseInt(id, 10) : parseInt(this.state.parent, 10), | ||
selectedNode: parseInt(id), | ||
isOpen: !this.state.isOpen, | ||
depth: this.state.depth | ||
}) | ||
} | ||
else { | ||
this.setState({ | ||
...this.state, | ||
isOpen: !this.state.isOpen, | ||
}) | ||
} | ||
routerNav(`/documents/${id}`); | ||
} | ||
} | ||
}) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
export function DocumentModal(id , onSubmit){ | ||
const $app = document.querySelector('.app'); | ||
const $modalContainer = document.createElement('div') | ||
$modalContainer.className = 'modal'; | ||
$app.appendChild($modalContainer); | ||
this.render = () => { | ||
$modalContainer.innerHTML = ` | ||
<div class="modal-content"> | ||
<form> | ||
<input class ="modalText" type="text" placeholder='문서의 제목을 작성해주세요'/> | ||
</form> | ||
<button class="closeBtn" id="close-modal">❌</button> | ||
</div> | ||
` | ||
} | ||
|
||
$modalContainer.addEventListener('submit', async (e) => { | ||
e.preventDefault(); | ||
const $input = $modalContainer.querySelector('.modalText'); | ||
let content = $input.value; | ||
if(content.length === 0) { | ||
content = '제목 없음' | ||
} | ||
await onSubmit(content, id); | ||
$input.value ='' | ||
alert('문서 생성이 완료되었습니다') | ||
const modal = document.querySelector('.modal'); | ||
modal.remove() | ||
}) | ||
|
||
$modalContainer.addEventListener('click', (e) => { | ||
const $closeBtn = e.target.closest('button') | ||
if(!$closeBtn) return | ||
if ($closeBtn.classList.contains('closeBtn')){ | ||
const modal = document.querySelector('.modal'); | ||
modal.remove() | ||
} | ||
Comment on lines
+34
to
+37
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. 하위 페이지를 생성할 때 입력하는 부분에 클릭을 하게 되었는데 오류가 발생했습니다. $closeBtn의 존재에 대한 조건도 추가하면 좋을 것 같습니다! 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. 현재 클릭 이벤트가 |
||
}) | ||
|
||
this.modalOpen = () => { | ||
const modal = document.querySelector('.modal'); | ||
modal.style.display = "block"; | ||
document.body.style.overflow = "hidden"; | ||
} | ||
this.render() | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
export default function Editor({$target, initialState = { | ||
title: '', | ||
content: '', | ||
}, onEditing}){ | ||
const $editor = document.createElement('div'); | ||
$editor.className = 'editor' | ||
$target.appendChild($editor); | ||
this.state = initialState; | ||
let isinitialize = false | ||
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.
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. 사실 |
||
|
||
this.setState = (nextState) => { | ||
this.state = nextState | ||
$editor.querySelector('[name=content]').value = this.state.content; | ||
$editor.querySelector('[name=title]').value = this.state.title; | ||
this.render(); | ||
} | ||
this.render = () => { | ||
if(!isinitialize){ | ||
$editor.innerHTML = ` | ||
<input type="text" class="titleInput" name="title"/> | ||
<textarea name="content" class="contentInput"/> | ||
` | ||
isinitialize = true | ||
} | ||
} | ||
|
||
this.render(); | ||
$editor.addEventListener('keyup', e => { | ||
const { target } = e | ||
const name = target.getAttribute('name') | ||
if(this.state[name] !== undefined) { | ||
const nextState = { | ||
...this.state, | ||
[name]: target.value | ||
} | ||
this.setState(nextState) | ||
onEditing(this.state) | ||
} | ||
}) | ||
} |
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.
🇰🇷