-
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 과제 #38
base: 4/#5_jgjgill
Are you sure you want to change the base?
Changes from 39 commits
bc9b44a
f60c9cb
4edc593
78870f8
d0db2fd
7d7660f
134fb2c
5f3005e
1357324
64a6d5c
6d490c6
502b74d
5e81344
ac3e47b
66716bc
b989893
7e5e929
c51e413
a522eb2
7809c08
9d47d3a
3c38e75
0778f63
6918089
97c35be
f619831
0ba048e
931b895
8872665
13cb534
0979e02
6428239
6bc441b
f281d38
d5f507a
c1870b6
f29f3a1
6100dc0
00d57ac
f229c64
b93a98a
6c2a351
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 @@ | ||
node_modules/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<!DOCTYPE html> | ||
<html lang="ko"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<title>Jongtion</title> | ||
<link rel="stylesheet" href="/src/styles/styles.css" /> | ||
</head> | ||
<body> | ||
<main id="app"></main> | ||
<script src="/src/main.js" type="module"></script> | ||
</body> | ||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"name": "FEDC4-5_Project_Notion_VanillaJS", | ||
"version": "1.0.0", | ||
"main": "index.js", | ||
"repository": "https://github.com/prgrms-fe-devcourse/FEDC4-5_Project_Notion_VanillaJS.git", | ||
"author": "jgjgill <[email protected]>", | ||
"license": "MIT", | ||
"scripts": { | ||
"dev": "yarn serve -s" | ||
}, | ||
"devDependencies": { | ||
"serve": "^14.2.0" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
import { getDocuments, putDocument } from "./api/document.js"; | ||
import Layout from "./components/common/Layout.js"; | ||
import { PATH } from "./constants/path.js"; | ||
import { initRouter, push } from "./utils/route.js"; | ||
import { | ||
TrieDocument, | ||
addChildDocument, | ||
editTitleDocument, | ||
insertAllDocument, | ||
findChildDocuments, | ||
removeDocument, | ||
} from "./utils/document.js"; | ||
import NotFound from "./components/common/NotFound.js"; | ||
import { debounce } from "./utils/debounce.js"; | ||
import DocumentList from "./components/domain/document/DocumentList.js"; | ||
import DocumentEditor from "./components/domain/document/DocumentEditor.js"; | ||
import Home from "./components/domain/home/Home.js"; | ||
import RecurDocumentList from "./components/domain/document/template/RecurDocumentList.js"; | ||
|
||
/** | ||
* @param {{appElement: Element | null}} | ||
*/ | ||
|
||
export default function App({ appElement }) { | ||
if (!new.target) return new App(...arguments); | ||
|
||
const wrapperContainer = document.createElement("div"); | ||
const leftContainerElement = document.createElement("div"); | ||
const rightContainerEleement = document.createElement("div"); | ||
const leftListElement = document.createElement("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. TMI 저도 실제로 left와 right를 자주 사용하긴 하는데. 바로 언어 호환 때문인데요! 중동어의 경우 시작이 오른쪽에서 왼쪽으로 흘러가기 때문에 여기서 strat, end를 쓰라는 뜻은 아니였고 멀티 랭기지를 챙기게 되면 이런 혼선이 있을 수 있다!! 정도로 다만 여기서는 left, right 보다는 sidebar, contents(or main) 등 시멘틱한 용어를 써서 구분을 지어도 좋을 것 같아요! 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. 오오..! 전혀 생각지도 못한 부분이였네요..! 😂 |
||
|
||
wrapperContainer.className = "wrapper-container"; | ||
leftContainerElement.className = "left-container"; | ||
rightContainerEleement.className = "right-container"; | ||
leftListElement.className = "left-list-container"; | ||
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. 클래스명을 이렇게도 줄 수 있군요! 이렇게 주면 어떤점이 좋다고 생각하시나요?! innerHTML을 안써도 되는 점일까요?? 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 trie = new TrieDocument(); | ||
|
||
const processEdit = debounce(async (documentId, docunemt) => { | ||
await putDocument({ documentId, data: docunemt }); | ||
}, 1000); | ||
|
||
this.state = []; | ||
|
||
this.setState = (nextState) => { | ||
this.state = nextState; | ||
documentListComponent.render(); | ||
}; | ||
|
||
this.editorSetState = (nextState) => { | ||
this.state = nextState; | ||
documentEditorComponent.render(); | ||
}; | ||
|
||
const layoutComponent = new Layout({ parentElement: leftContainerElement }); | ||
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. layout Component를 따로 분리하셨는데 의도가 궁금합니다! 다른 컴포넌트에서도 사용될 것을 염두해서 분리하신건가요?🤔 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 documentListComponent = new DocumentList({ | ||
parentElement: leftListElement, | ||
renderItemComponent: (parentElement) => { | ||
RecurDocumentList({ | ||
rootDocuments: this.state, | ||
parentElement, | ||
childRender: (parentId, newDocument) => { | ||
const nextState = addChildDocument(parentId, this.state, newDocument); | ||
this.setState(nextState); | ||
}, | ||
removeRender: (documentId) => { | ||
const newState = removeDocument(documentId, this.state); | ||
const stringDocumentId = window.location.pathname.split("/")[2]; | ||
|
||
Number(stringDocumentId) !== documentId | ||
? this.editorSetState(newState) | ||
: push(PATH.HOME); | ||
|
||
this.setState(newState); | ||
}, | ||
depthCount: 1, | ||
}); | ||
}, | ||
onAddButtonClick: (newDocument) => { | ||
const nextState = [...this.state, newDocument]; | ||
this.setState(nextState); | ||
}, | ||
}); | ||
|
||
const homeComponent = new Home({ | ||
parentElement: rightContainerEleement, | ||
search: (text) => trie.search(text), | ||
}); | ||
|
||
const documentEditorComponent = new DocumentEditor({ | ||
parentElement: rightContainerEleement, | ||
onEditing: (document) => { | ||
const { documentId, title, isChangeTitle } = document; | ||
|
||
if (isChangeTitle) { | ||
const newState = editTitleDocument(documentId, this.state, title); | ||
this.setState(newState); | ||
} | ||
|
||
processEdit(documentId, document); | ||
}, | ||
getChildDocuments: (documentId) => | ||
findChildDocuments(this.state, documentId), | ||
}); | ||
|
||
const notFoundComponent = new NotFound({ | ||
parentCompoent: rightContainerEleement, | ||
}); | ||
|
||
window.addEventListener("popstate", () => { | ||
this.route(); | ||
}); | ||
|
||
initRouter(() => this.route()); | ||
|
||
this.init = async () => { | ||
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 함수를 만든 것 너무 좋네요~~ 👍 |
||
appElement.append(wrapperContainer); | ||
wrapperContainer.append(leftContainerElement, rightContainerEleement); | ||
|
||
layoutComponent.render(); | ||
leftContainerElement.append(leftListElement); | ||
|
||
const newState = await getDocuments(); | ||
this.setState(newState); | ||
|
||
insertAllDocument(newState, (title, id) => trie.insert(title, id)); | ||
|
||
this.route(); | ||
}; | ||
|
||
this.route = () => { | ||
const { pathname } = window.location; | ||
rightContainerEleement.innerHTML = ``; | ||
|
||
if (pathname === PATH.HOME) { | ||
trie.reset(); | ||
insertAllDocument(this.state, (title, id) => trie.insert(title, id)); | ||
|
||
homeComponent.render(); | ||
} else if (pathname.split("/")[1] === "documents") { | ||
documentEditorComponent.render(); | ||
} else { | ||
notFoundComponent.render(); | ||
} | ||
Comment on lines
+138
to
+147
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. P2; 추후 서비스가 확장된다면 다양한 url이 추가될 것 같은데 그때 else if 문이 반복된다면 코드를 파악하는데 오랜 시간을 써야할 것 같다는 생각이 들었어요. switch 문을 써보는 것은 어떨까요? 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,30 @@ | ||
import { PATH } from "../constants/path.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. 오 이렇게 관심사 별로 fetch 함수들을 따로 빼는 것 너무 좋네요!! 👍 중복되는 값들은 별도의 상수로 관리하는 것도 너무 좋구요!! 👍👍 |
||
import { request } from "./request.js"; | ||
|
||
export const postDocument = async (data) => { | ||
return await request(PATH.DOCUMENTS, { | ||
method: "POST", | ||
body: JSON.stringify(data), | ||
}); | ||
}; | ||
|
||
export const getDocuments = async () => { | ||
return await request(PATH.DOCUMENTS); | ||
}; | ||
|
||
export const getDocument = async (documentId) => { | ||
return await request(`${PATH.DOCUMENTS}/${documentId}`); | ||
}; | ||
|
||
export const putDocument = async ({ documentId, data }) => { | ||
return await request(`${PATH.DOCUMENTS}/${documentId}`, { | ||
method: "PUT", | ||
body: JSON.stringify(data), | ||
}); | ||
}; | ||
|
||
export const deleteDocument = async (documentId) => { | ||
return await request(`${PATH.DOCUMENTS}/${documentId}`, { | ||
method: "DELETE", | ||
}); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { PATH } from "../constants/path.js"; | ||
import { push } from "../utils/route.js"; | ||
|
||
const API_END_POINT = "https://kdt-frontend.programmers.co.kr"; | ||
|
||
export const request = async (url, 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. 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. 추가로 요런 request 함수를 어떻게 말아서 쓰면 좋을까? 고민이 되신다면 axios 또는 ky 라이브러리는 어떻게 쓰라고 되어있는지? 쓰윽 고민해보시면 좋을 것 같아요! TMI
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 { | ||
const res = await fetch(`${API_END_POINT}${url}`, { | ||
...options, | ||
headers: { | ||
"Content-Type": "application/json", | ||
"x-username": "jgjgill", | ||
}, | ||
}); | ||
|
||
if (res.ok) { | ||
return await res.json(); | ||
} | ||
|
||
throw new Error("API 에러가 발생했습니다!"); | ||
Comment on lines
+16
to
+20
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. P3; res.ok가 아닌 경우에는 어떻게 핸들링 하나요? ask; 퀴즈 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. 에러에 대해서 깊게 생각하지 않고 하나로 퉁쳤네요. 😂 |
||
} catch (err) { | ||
alert(err.message); | ||
push(PATH.HOME); | ||
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. P1; request라는 함수의 역할을 생각해 보았을 때 request 함수 이름으로는 error가 발생 헀을 때 UI상의 변경(alert가 뜬다던지, 특정 페이지로 리다이렉트 된다던지) 하는 것을 유추하기 힘들 뿐 아니라. 여기서는 딱 request 함수 이름에서 나타내는 하나의 역할에만 집중하고, 에러 핸들링(특히 UI)의 경우 UI 관련된 곳에서 처리하게 하는 것이 좋을 것 같은데 어떤가요? 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,36 @@ | ||
import Tooltip from "./Tooltip.js"; | ||
|
||
export default function AddButton({ | ||
parentElement, | ||
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. parentElement라는 변수명 이해하기 정말 쉽네요! 부모 element에 들어가는 컴포넌트들에게 target 대신 parentElement 변수명을 사용하니까 직관적이고 더 명확하게 의미를 알게되네요😮 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. 근데 장단점이 있는 것 같습니다..! 😂 |
||
onClick, | ||
text, | ||
tooltipText, | ||
}) { | ||
const buttonElement = document.createElement("button"); | ||
buttonElement.className = "add-button"; | ||
|
||
const tooltipElement = new Tooltip({ text: tooltipText }); | ||
|
||
buttonElement.addEventListener("click", () => { | ||
onClick(); | ||
}); | ||
|
||
buttonElement.addEventListener("mouseover", (e) => { | ||
if (!e.target.closest(".text")) return; | ||
|
||
tooltipElement.toggle(e.target); | ||
}); | ||
|
||
buttonElement.addEventListener("mouseout", (e) => { | ||
if (!e.target.closest(".text")) return; | ||
|
||
tooltipElement.toggle(e.target); | ||
}); | ||
|
||
this.render = () => { | ||
parentElement.append(buttonElement); | ||
buttonElement.innerHTML = tooltipElement.render( | ||
`<div class="text">${text}</div>` | ||
); | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { PATH } from "../../constants/path.js"; | ||
import { push } from "../../utils/route.js"; | ||
|
||
export default function Layout({ parentElement }) { | ||
if (!new.target) return new Layout(...arguments); | ||
|
||
const containerElement = document.createElement("div"); | ||
|
||
containerElement.addEventListener("click", (e) => { | ||
if (e.target.closest("h1")) { | ||
push(PATH.HOME); | ||
} | ||
}); | ||
|
||
this.render = () => { | ||
parentElement.append(containerElement); | ||
containerElement.innerHTML = ` | ||
<h1 class="logo">Jongtion</h1> | ||
`; | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { PATH } from "../../constants/path.js"; | ||
import { push } from "../../utils/route.js"; | ||
|
||
export default function NotFound({ parentCompoent }) { | ||
const containerElement = document.createElement("div"); | ||
containerElement.className = "not-found-container"; | ||
|
||
containerElement.addEventListener("click", (e) => { | ||
if (!e.target.closest(".home-button")) return; | ||
|
||
push(PATH.HOME); | ||
}); | ||
|
||
this.render = () => { | ||
parentCompoent.append(containerElement); | ||
|
||
containerElement.innerHTML = ` | ||
<h2 class="not-found-title">잘못된 경로입니다!</h2> | ||
<button class="home-button">홈으로 이동하기</button> | ||
`; | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
export default function Tooltip({ text }) { | ||
this.toggle = (targetElement) => { | ||
targetElement.nextElementSibling.classList.toggle("toggle"); | ||
}; | ||
Comment on lines
+1
to
+4
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. p5 : Tooltip이라는게 hover시에 표시를 해주는 뜻이라는 걸 처음 알게 되었습니다! tooltip이라는 단어를 잘몰라서 그럴수도 있는데 toggle 보다는 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. 코드를 작성할 때는 |
||
|
||
this.render = (content) => { | ||
return ` | ||
<div class="tooltip"> | ||
${content} | ||
<div class="tooltip-text">${text}</div> | ||
</div> | ||
`; | ||
}; | ||
Comment on lines
+6
to
+13
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 comment
The 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 comment
The 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 comment
The reason will be displayed to describe this comment to others. Learn more.
저도 모든 컴포넌트에는 추가하지 못했습니다.. 😂