-
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
[Mission4/김순요] - Project_Notion_VanillaJS #9
base: 3/#4_soonyo
Are you sure you want to change the base?
Changes from all commits
f2ca14d
08fe4e8
ce1764d
ce79ece
27a4a6f
5f444bf
343e978
836d170
4d44505
16432a4
5fe73ca
b4dad78
b620b0a
8aa0494
594a36f
d2105e5
ec61631
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 @@ | ||
API_END_POINT.js |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<title>KDT</title> | ||
<link rel="stylesheet" href="/style.css" /> | ||
</head> | ||
<body> | ||
<div id="app"></div> | ||
<script src="/index.js" type="module"></script> | ||
</body> | ||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import App from "./src/App.js"; | ||
|
||
const $app = document.querySelector("#app"); | ||
|
||
new App({ | ||
$target: $app, | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import DocsContainer from "./components/Documents/DocsContainer.js"; | ||
import EditorContainer from "./components/Editor/EditorContainer.js"; | ||
import { init } from "./components/utils/router/router.js"; | ||
|
||
export default function App({ $target }) { | ||
const docsContainer = new DocsContainer({ | ||
$target, | ||
}); | ||
|
||
const editorContainer = new EditorContainer({ | ||
$target, | ||
onChange: () => { | ||
docsContainer.setState(); | ||
}, | ||
}); | ||
|
||
this.route = async () => { | ||
const { pathname } = window.location; | ||
|
||
docsContainer.setState(); | ||
if (pathname === "/") { | ||
editorContainer.setState(); | ||
} else if (pathname.indexOf("/documents/") === 0) { | ||
const [, , docId] = pathname.split("/"); | ||
editorContainer.setState({ id: docId }); | ||
} | ||
}; | ||
|
||
this.route(); | ||
|
||
init(this.route); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { API_END_POINT } from "./API_END_POINT.js"; | ||
|
||
export const request = async (url, options = {}) => { | ||
try { | ||
const res = await fetch(`${API_END_POINT}${url}`, { | ||
...options, | ||
headers: { | ||
"x-username": "DevSoonyo", | ||
"Content-type": "application/json", | ||
}, | ||
}); | ||
if (res.ok) { | ||
return await res.json(); | ||
} | ||
throw new Error("API query error"); | ||
} catch (e) {} | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { request } from "../../api/request.js"; | ||
import { push } from "../utils/router/router.js"; | ||
import DocsList from "./DocsList.js"; | ||
|
||
export default function DocsContainer({ $target }) { | ||
const $docsContainer = document.createElement("aside"); | ||
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. aside element는 처음 보는데, 순요님 덕분에 공부하고 지나갑니다~ :) |
||
$docsContainer.className = "docs-container"; | ||
$target.appendChild($docsContainer); | ||
|
||
const docsList = new DocsList({ | ||
$target: $docsContainer, | ||
initialState: { docs: [], selectedDocs: new Set() }, | ||
onAdd: async ({ parent, title }) => { | ||
await request("/documents", { | ||
method: "POST", | ||
body: JSON.stringify({ parent, title }), | ||
}); | ||
this.setState(); | ||
Comment on lines
+13
to
+18
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. 실행해보니까 +버튼으로 새로운 글을 추가했을 때 추가한 글이 편집기에 바로 안 뜨더라구요!! 추가된 문서의 id를 받아서 해당 id의 주소로 이동해주면 좋을 것 같아요 😊 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. 새 문서 만들기 버튼도 같은 함수가 사용되고 있어서 편집기에 바로 뜨지 않고 있어요! 바로 이동되면 좋을 것 같아요 :) |
||
}, | ||
onDelete: async ({ id }) => { | ||
await request(`/documents/${id}`, { | ||
method: "DELETE", | ||
}); | ||
push("/"); | ||
}, | ||
}); | ||
|
||
this.setState = async () => { | ||
const documents = await request("/documents"); | ||
docsList.setState({ docs: documents }); | ||
Comment on lines
+29
to
+30
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. 받는쪽에서도 state를 documents로 정의했으면 좋았을 것 같네요ㅎㅎ |
||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import { push } from "../utils/router/router.js"; | ||
|
||
export default function DocsList({ $target, initialState, onAdd, onDelete }) { | ||
const $docsList = document.createElement("div"); | ||
|
||
$docsList.className = "docs-list"; | ||
|
||
$target.appendChild($docsList); | ||
|
||
this.state = initialState; | ||
|
||
this.setState = (nextState) => { | ||
this.state = { | ||
...this.state, | ||
...nextState, | ||
}; | ||
this.render(); | ||
}; | ||
|
||
this.setMarkup = (docsList, hide = false) => { | ||
const markup = ` | ||
<ul class=${hide ? "hidden" : ""} > | ||
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.
|
||
${docsList | ||
.map((doc) => { | ||
return `<li class="item" data-id=${doc.id}> | ||
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. 여기도 double quote가 빠진 것 같습니다 |
||
<span name="item-content"> ${ | ||
doc.title.trim() === "" ? "빈 제목" : doc.title | ||
} </span> | ||
<div data-group-id=${doc.id} class="button-group""> | ||
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. 전반적으로 따옴표가 많이 빠져있는 것 같은데, jsx가 아니기 때문에 마크업에 대한 체크가 필요해보입니다~! |
||
<button name="add"> + </button> | ||
<button name="delete"> - </button> | ||
</div> | ||
</li> | ||
${ | ||
doc.documents && doc.documents.length > 0 | ||
? this.setMarkup( | ||
doc.documents, | ||
!this.state.selectedDocs.has(`${doc.id}`) | ||
) | ||
: "" | ||
} | ||
`; | ||
}) | ||
.join("")} | ||
</ul> | ||
`; | ||
return markup; | ||
}; | ||
|
||
this.render = () => { | ||
$docsList.innerHTML = ""; | ||
if (this.state.docs && this.state.docs.length > 0) { | ||
$docsList.innerHTML = | ||
this.setMarkup(this.state.docs) + | ||
`<button name="add" > + 새 문서 만들기 </button>`; | ||
} else { | ||
$docsList.innerHTML = ` | ||
<span> No documents </span> | ||
<button name="add" > + 새 문서 만들기 </button> | ||
Comment on lines
+58
to
+59
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. 같은 줄에 No documents와 새 문서 버튼이 출력돼서 span 태그 보다는 p 태그를 사용하거나 css를 통해 다른 줄로 만드는 게 조금 더 보기 좋을 것 같아요! |
||
`; | ||
} | ||
}; | ||
|
||
$docsList.addEventListener("click", (e) => { | ||
const { target } = e; | ||
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 tempTitle = "빈 제목"; | ||
const $li = target.closest("li"); | ||
if (target.tagName === "BUTTON") { | ||
const id = $li?.dataset.id; | ||
if (target.name === "add") { | ||
this.setState({ selectedDocs: this.state.selectedDocs.add(id) }); | ||
onAdd({ parent: id || null, title: tempTitle }); | ||
} else if (target.name === "delete") { | ||
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. if문의 분리가 필요할것 같습니다! |
||
onDelete({ id }); | ||
} | ||
} else if (target.getAttribute("name") === "item-content") { | ||
const id = $li?.dataset.id; | ||
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. id를 if문 안에서 반복해서 변수로 설정하고 있으니 if문 밖에서 선언해주는 건 어떨까요?! 😊 |
||
const selectedDocs = this.state.selectedDocs; | ||
|
||
// subList toggle | ||
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.
멘토님께서 이전 과제 때 주석은위와 같은 형식으로 다는 것을 권장한다고 하시더라구요! 😊 //대신 위와 같은 형태로 주석을 추가해주시면 좋을 것 같아요! :) |
||
if (selectedDocs.has(id)) { | ||
selectedDocs.delete(id); | ||
} else { | ||
selectedDocs.add(id); | ||
} | ||
Comment on lines
+81
to
+85
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({ selectedDocs }); | ||
push(`/documents/${id}`); | ||
} | ||
}); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import EditorPage from "./EditorPage.js"; | ||
import { request } from "../../api/request.js"; | ||
import { push } from "../utils/router/router.js"; | ||
|
||
export default function EditorContainer({ $target, onChange }) { | ||
const $editorContainer = document.createElement("main"); | ||
$editorContainer.className = "editor-container"; | ||
$target.appendChild($editorContainer); | ||
Comment on lines
+5
to
+8
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. new 키워드 생성 방어코드를 추가하시면 좋을 것 같습니다 :) |
||
|
||
let timer = null; | ||
|
||
this.state = { | ||
id: null, | ||
}; | ||
|
||
const $ediotr = new EditorPage({ | ||
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. 가벼운 실수겠지만, editor 오타가 난 것 같습니다 :) |
||
$target: $editorContainer, | ||
initialState: { title: "", content: "" }, | ||
// debounce | ||
onEditing: ({ title, content }) => { | ||
if (timer) { | ||
clearTimeout(timer); | ||
} | ||
|
||
timer = setTimeout(async () => { | ||
if (this.state.id === "new") { | ||
// 새로운 문서 작성 | ||
const res = await request("/documents", { | ||
method: "POST", | ||
body: JSON.stringify({ title, parent: null }), | ||
}); | ||
if (res) { | ||
this.state = res; | ||
push(`/documents/${this.state.id}`); | ||
} else { | ||
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. try catch를 이용헤 res.ok가 아닐경우 에러를 발생시키는 방향도 좋을것 같아요~! |
||
throw new Error("Post method failed"); | ||
} | ||
Comment on lines
+28
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. 이부분은 res의 유무가 아닌 trycatch로 처리를 해야하지 않을까요? |
||
} else { | ||
// 기존 문서 수정 | ||
await request(`/documents/${this.state.id}`, { | ||
method: "PUT", | ||
body: JSON.stringify({ title, content }), | ||
}); | ||
onChange(); | ||
} | ||
}, 1500); | ||
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 = async (selectedDoc) => { | ||
const id = selectedDoc?.id; | ||
if (!id) { | ||
this.state = { id: "new" }; | ||
$ediotr.setState({ title: "", content: "" }); | ||
} else { | ||
this.state = selectedDoc; | ||
const doc = await request(`/documents/${id}`); | ||
$ediotr.setState(doc); | ||
} | ||
Comment on lines
+51
to
+59
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. if else로 구성한다면 굳이 if (!id) 부터 시작하지 않아도 될 것 같습니다 |
||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
export default function EditorPage({ $target, initialState, onEditing }) { | ||
const $editorPage = document.createElement("div"); | ||
$target.appendChild($editorPage); | ||
|
||
this.state = initialState; | ||
|
||
this.setState = (nextState) => { | ||
this.state = nextState; | ||
$editorPage.querySelector("[name=title]").value = this.state.title; | ||
$editorPage.querySelector("[name=content]").value = this.state.content; | ||
}; | ||
|
||
this.render = () => { | ||
$editorPage.innerHTML = ` | ||
<input type="text" name="title" maxlength="30" placeholder="제목을 입력해주세요." ></input> | ||
<textarea name="content" placeholder="내용을 입력해주세요."></textarea> | ||
`; | ||
}; | ||
this.render(); | ||
$editorPage.addEventListener("keyup", (e) => { | ||
const { target } = e; | ||
const { name } = target; | ||
|
||
const editedDoc = { | ||
...this.state, | ||
[name]: target.value, | ||
}; | ||
this.setState(editedDoc); | ||
onEditing(editedDoc); | ||
}); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
const ROUTE_CHANGE_EVENT = "route-change"; | ||
|
||
export const init = (onRoute) => { | ||
window.addEventListener(ROUTE_CHANGE_EVENT, (e) => { | ||
const { nextUrl } = e.detail; | ||
|
||
if (nextUrl) { | ||
history.pushState(null, null, nextUrl); | ||
onRoute(); | ||
} | ||
}); | ||
}; | ||
|
||
export const push = (nextUrl) => { | ||
window.dispatchEvent( | ||
new CustomEvent("route-change", { | ||
detail: { | ||
nextUrl, | ||
}, | ||
}) | ||
); | ||
}; | ||
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,67 @@ | ||
body { | ||
padding: 0 0; | ||
margin: 0 0; | ||
} | ||
|
||
.hidden { | ||
display: none; | ||
} | ||
|
||
li { | ||
margin-top: 30px; | ||
font-weight: bold; | ||
} | ||
|
||
.item { | ||
list-style: none; | ||
cursor: pointer; | ||
} | ||
|
||
.item span:hover { | ||
opacity: 50%; | ||
} | ||
|
||
#app { | ||
display: flex; | ||
} | ||
|
||
.docs-container { | ||
min-width: 15%; | ||
height: 100vh; | ||
background-color: lightgray; | ||
} | ||
|
||
.editor-container div { | ||
height: 100vh; | ||
margin-left: 100px; | ||
} | ||
.button-group { | ||
display: inline-block; | ||
} | ||
|
||
[name="title"], | ||
[name="content"] { | ||
display: block; | ||
width: 800px; | ||
border-width: 0px; | ||
} | ||
|
||
[name="title"] { | ||
height: 5%; | ||
font-size: 1.5rem; | ||
} | ||
|
||
[name="content"] { | ||
height: 90%; | ||
font-size: 1.3rem; | ||
} | ||
|
||
[name="title"]:focus, | ||
[name="content"]:focus { | ||
outline: none; | ||
} | ||
|
||
[name="title"]:hover, | ||
[name="content"]:hover { | ||
border: 0.5px gray dotted; | ||
} |
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.
SEO를 위해 ko로 변경하는건 어떨까요~