-
Notifications
You must be signed in to change notification settings - Fork 28
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 과제 #54
base: 4/#5_kimdaeun
Are you sure you want to change the base?
Changes from all commits
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,13 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<link rel="stylesheet" href="/style.css"> | ||
<title>Document</title> | ||
</head> | ||
<body> | ||
<div class="app"></div> | ||
<script type="module" src="/main.js"></script> | ||
</body> | ||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import App from "./src/App.js" | ||
|
||
const $target = document.querySelector(".app") | ||
|
||
new App({$target}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import DocumentPage from "./pages/DocumentPage.js"; | ||
import EditorPage from "./pages/EditorPage.js"; | ||
import { initRouter } from "./utils/router.js"; | ||
|
||
export default function App({$target}){ | ||
const documentPage = new DocumentPage({ | ||
$target, | ||
}) | ||
|
||
const editorpage = new EditorPage({ | ||
$target, | ||
initialState :{} | ||
}) | ||
|
||
this.route = () =>{ | ||
const {pathname} = window.location | ||
$target.innerHTML = "" | ||
if(pathname === "/"){ | ||
documentPage.render() | ||
}else if(pathname.indexOf("/documents/") === 0){ | ||
const [,,documentId] = pathname.split("/") | ||
documentPage.render() | ||
editorpage.setState({documentId}) | ||
}else{ | ||
$target.innerHTML = "" | ||
documentPage.render() | ||
} | ||
} | ||
documentPage.render() | ||
this.route() | ||
|
||
initRouter(()=>{ | ||
this.route() | ||
}) | ||
|
||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,24 @@ | ||||||
import {getItem, OPENED_DOCUMENTS} from "../utils/storage.js" | ||||||
|
||||||
export const createList = (currentList) => { | ||||||
const openedDocuments = getItem(OPENED_DOCUMENTS,[]) | ||||||
return currentList.map(({title,documents,id})=>{ | ||||||
const isOpen = openedDocuments.includes(String(id)); | ||||||
return`<ul data-id="${id}"> | ||||||
<div class="document__title"> | ||||||
<span> | ||||||
<button class="toggle__button">></button> | ||||||
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
HTML에서는 마크업 문제가 있을 수 있으니 |
||||||
${title === "" ? "제목없음" : title} | ||||||
</span> | ||||||
<div> | ||||||
<button class="create">+</button> | ||||||
<button class="remove">x</button> | ||||||
</div> | ||||||
</div> | ||||||
<div class="${isOpen ? "" : "hide"}"> | ||||||
<li>${documents.length > 0 ? createList(documents) : ""}</li> | ||||||
</div> | ||||||
</ul>` | ||||||
} | ||||||
).join("") | ||||||
} | ||||||
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. DocumentLists 컴포넌트의 렌더링 로직에서 그려야 할 HTML 템플릿을 따로 함수로 구현해주신 점 좋네요! 사실 컴포넌트라기보다는 들어온 인자를 가지고 가공해서 문자열을 반환하는 순수 함수의 형태인 것 같아서 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { push } from "../utils/router.js" | ||
import { updateStorage } from "../utils/storage.js" | ||
import {createList} from "./CreateLists.js" | ||
|
||
export default function DocumentLists({$target, initialState, onRemove, onCreate}){ | ||
|
||
const $documentLists = document.createElement("div") | ||
$target.appendChild($documentLists) | ||
$documentLists.className = "document__lists" | ||
|
||
this.state =initialState | ||
this.setState = nextState =>{ | ||
this.state = nextState | ||
this.render() | ||
} | ||
|
||
this.render = () =>{ | ||
$documentLists.innerHTML = `${createList(this.state)}` | ||
} | ||
|
||
$documentLists.addEventListener("click", (e)=>{ | ||
const {target} = e | ||
const $ul = target.closest("ul") | ||
|
||
if($ul){ | ||
const {id} = $ul.dataset | ||
if(target.className === "remove"){ | ||
onRemove(id) | ||
updateStorage.delete(id) | ||
}else if(target.className === "create"){ | ||
onCreate(id) | ||
updateStorage.add(id) | ||
}else if(target.className === "toggle__button"){ | ||
updateStorage.toggle(id) | ||
this.render() | ||
}else{ | ||
push(`/documents/${id}`) | ||
} | ||
} | ||
|
||
}) | ||
|
||
|
||
this.render() | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
export default function Editor({$target, initialState, onEditing}){ | ||
const $editor = document.createElement("div") | ||
$target.appendChild($editor) | ||
|
||
this.state = initialState; | ||
this.setState = nextState =>{ | ||
this.state = nextState | ||
$editor.querySelector(".title").value = this.state.title | ||
$editor.querySelector(".content").value = this.state.content | ||
this.render() | ||
} | ||
|
||
let init = false | ||
this.render = () =>{ | ||
if(!init){ | ||
$editor.innerHTML=` | ||
<input class="title" style="width:800px;height:70px;" value="${this.state.title}"> | ||
<textarea class="content" style="width:800px;height:500px;">${this.state.content}</textarea> | ||
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. 굳이 인라인 스타일을 쓰지 않아도 될 것 같습니다. |
||
` | ||
} | ||
init =true | ||
|
||
} | ||
|
||
$editor.addEventListener("keyup", (e)=>{ | ||
const {target} = e | ||
const name = target.getAttribute("class") | ||
const isTitle = name === "title" | ||
|
||
if(this.state[name] !== undefined){ | ||
const nextState = { | ||
...this.state, | ||
[name] : target.value | ||
} | ||
this.setState(nextState) | ||
onEditing(this.state,isTitle) | ||
} | ||
}) | ||
|
||
this.render() | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
export default function Footer({$target, onClick}){ | ||
const $button = document.createElement("button") | ||
$target.appendChild($button) | ||
$button.textContent = "페이지 추가" | ||
$button.className = "footer" | ||
|
||
document.querySelector(".footer").addEventListener("click", (e)=>{ | ||
onClick() | ||
}) | ||
} | ||
Comment on lines
+1
to
+10
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. Header와 같은 의미로 <footer> 태그를 이용하시는 편이 위치를 이해하기 좋을 것 같다는 생각이 듭니다. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export default function Header({$target}){ | ||
const $header = document.createElement("h2") | ||
$header.innerHTML = "김다은의 Notion" | ||
$target.appendChild($header) | ||
} | ||
Comment on lines
+1
to
+5
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. 처음에 Header 컴포넌트를 보고 <header>태그를 사용하신 컴포넌트로 이해했습니다. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import DocumentLists from "../components/DocumentLists.js" | ||
import Footer from "../components/Footer.js" | ||
import Header from "../components/Header.js" | ||
import { deleteDocument, postDocument, fetchDocumentLists } from "../utils/requset.js" | ||
import { updateDocumentTitle, push } from "../utils/router.js" | ||
|
||
|
||
export default function DocumentPage({$target}){ | ||
const $page = document.createElement("div") | ||
$target.appendChild($page) | ||
$page.className = "document__page" | ||
|
||
this.render = ()=>{ | ||
$target.appendChild($page) | ||
} | ||
|
||
new Header({ | ||
$target : $page | ||
}) | ||
|
||
const documentLists = new DocumentLists({ | ||
$target : $page, | ||
initialState :[], | ||
onRemove: async(id)=>{ | ||
await deleteDocument(id) | ||
push("/") | ||
updateDocumentList() | ||
}, | ||
onCreate : async(id)=>{ | ||
const newDocument = await postDocument(id) | ||
push(`/documents/${newDocument.id}`) | ||
updateDocumentList() | ||
}, | ||
}) | ||
|
||
new Footer({ | ||
$target : $page, | ||
onClick : async()=>{ | ||
const newDocument = await postDocument() | ||
push(`/documents/${newDocument.id}`) | ||
updateDocumentList() | ||
} | ||
}) | ||
|
||
const updateDocumentList = async()=>{ | ||
documentLists.setState(await fetchDocumentLists()) | ||
} | ||
|
||
updateDocumentTitle(()=>{ | ||
updateDocumentList() | ||
}) | ||
updateDocumentList() | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import Editor from "../components/Editor.js" | ||
import {putDocument ,fetchEditor} from "../utils/requset.js" | ||
import { push, update} from "../utils/router.js" | ||
|
||
export default function EditorPage({$target, initialState}){ | ||
const $page = document.createElement("div") | ||
$page.className = "editor__page" | ||
|
||
this.render = ()=>{ | ||
$target.appendChild($page) | ||
} | ||
|
||
this.state = initialState | ||
this.setState = async nextState =>{ | ||
this.state = nextState | ||
const document = await fetchEditor(this.state.documentId) | ||
editor.setState(document) | ||
this.render() | ||
} | ||
|
||
let timer = null; | ||
const editor = new Editor({ | ||
$target : $page, | ||
initialState :{ | ||
title : "", | ||
content :""}, | ||
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. initialState가 인자로 특별히 필요한지 잘 모르겠습니다. |
||
onEditing : (document,isTitle)=>{ | ||
if(timer !== null){ | ||
clearTimeout(timer) | ||
} | ||
timer = setTimeout(async ()=>{ | ||
await putDocument(document) | ||
if(isTitle){ | ||
update() | ||
} | ||
}, 800) | ||
} | ||
}) | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import {push} from "./router.js" | ||
|
||
export const API_END_POINT = "https://kdt-frontend.programmers.co.kr" | ||
|
||
export const request = async (url, options = {}) => { | ||
try{ | ||
const res = await fetch(`${API_END_POINT}${url}`,{ | ||
...options, | ||
headers :{ | ||
"Content-Type" : "application/json", //데이터 형식을 json으로 명시합니다. | ||
"x-username" : "kimdaeun" | ||
} | ||
}) | ||
if(res.ok){ | ||
return res.json() | ||
} | ||
throw new Error("API 호출 오류") | ||
}catch(e){ | ||
console.log(e.message) | ||
push("/") | ||
} | ||
} | ||
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. 서버에 요청을 보내는 일반화된 함수 request 는 request 함수는 |
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,55 @@ | ||
import { request } from "./api.js" | ||
|
||
//document를 삭제합니다 | ||
export const deleteDocument = async(id)=>{ | ||
await request(`/documents/${id}`, { | ||
method : "DELETE" | ||
}) | ||
|
||
} | ||
|
||
//하위document 또는 상위document를 생성합니다 | ||
export const postDocument = async (id)=>{ | ||
if(id){ | ||
return await request("/documents", { | ||
method : "POST", | ||
body : JSON.stringify({ | ||
title : "제목 없음", | ||
parent : `${id}` | ||
}) | ||
}) | ||
}else{ | ||
return await request("/documents", { | ||
method : "POST", | ||
body : JSON.stringify({ | ||
title : "", | ||
parent : "" | ||
}) | ||
}) | ||
} | ||
} | ||
|
||
//document를 수정합니다. | ||
export const putDocument = async(document)=>{ | ||
return await request(`/documents/${document.id}`, { | ||
method : "PUT", | ||
body : JSON.stringify({ | ||
title : document.title, | ||
content : document.content | ||
}) | ||
}) | ||
} | ||
|
||
//특정 document를 불러옵니다. | ||
export const fetchEditor = async(documentId)=>{ | ||
const document = await request(`/documents/${documentId}`) | ||
return document | ||
} | ||
|
||
|
||
//전체 document를 불러옵니다. | ||
export const fetchDocumentLists = async() =>{ | ||
const lists = await request("/documents") | ||
return lists | ||
} | ||
|
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.
공통된 부분이 많아 이렇게 줄여볼 수 있겠네요~