Skip to content
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

Open
wants to merge 17 commits into
base: 3/#4_soonyo
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
API_END_POINT.js
14 changes: 14 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SEO를 위해 ko로 변경하는건 어떨까요~

<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>
7 changes: 7 additions & 0 deletions index.js
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,
});
32 changes: 32 additions & 0 deletions src/App.js
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);
}
17 changes: 17 additions & 0 deletions src/api/request.js
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) {}
};
32 changes: 32 additions & 0 deletions src/components/Documents/DocsContainer.js
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");

Choose a reason for hiding this comment

The 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

실행해보니까 +버튼으로 새로운 글을 추가했을 때 추가한 글이 편집기에 바로 안 뜨더라구요!! 추가된 문서의 id를 받아서 해당 id의 주소로 이동해주면 좋을 것 같아요 😊

Choose a reason for hiding this comment

The 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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

받는쪽에서도 state를 documents로 정의했으면 좋았을 것 같네요ㅎㅎ

};
}
90 changes: 90 additions & 0 deletions src/components/Documents/DocsList.js
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" : ""} >
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<ul class="${hide ? "hidden" : ""}">
doublequote가 빠진 것 같네요

${docsList
.map((doc) => {
return `<li class="item" data-id=${doc.id}>
Copy link

Choose a reason for hiding this comment

The 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"">
Copy link

Choose a reason for hiding this comment

The 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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

같은 줄에 No documents와 새 문서 버튼이 출력돼서 span 태그 보다는 p 태그를 사용하거나 css를 통해 다른 줄로 만드는 게 조금 더 보기 좋을 것 같아요!

`;
}
};

$docsList.addEventListener("click", (e) => {
const { target } = e;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

구조분해 할당은 매개변수 쪽에서 바로 하는것도 좋을것 같아요~!~!
({target} =>

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") {

Choose a reason for hiding this comment

The 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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

id를 if문 안에서 반복해서 변수로 설정하고 있으니 if문 밖에서 선언해주는 건 어떨까요?! 😊

const selectedDocs = this.state.selectedDocs;

// subList toggle
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

함수에 대한 주석을 추가할때는

/**
*
*/
로 추가하는 것을 권장합니다.
그래야 ide에서 hover를 했을때 주석까지 출력이 되기 때문입니다

멘토님께서 이전 과제 때 주석은위와 같은 형식으로 다는 것을 권장한다고 하시더라구요! 😊 //대신 위와 같은 형태로 주석을 추가해주시면 좋을 것 같아요! :)

if (selectedDocs.has(id)) {
selectedDocs.delete(id);
} else {
selectedDocs.add(id);
}
Comment on lines +81 to +85
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 토글 부분을 구현하고 싶었는데 순요님 코드 보고 배워갑니다 :)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

토글 기능 너무 좋은 것 같아요👍
그런데 현재 모습에서는 하위 리스트가 있는지 알 수 없어서 나중에 토글 관련 화면이나 css를 추가해 주시면 더 좋을 것 같아요!

this.setState({ selectedDocs });
push(`/documents/${id}`);
}
});
}
61 changes: 61 additions & 0 deletions src/components/Editor/EditorContainer.js
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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new 키워드 생성 방어코드를 추가하시면 좋을 것 같습니다 :)


let timer = null;

this.state = {
id: null,
};

const $ediotr = new EditorPage({

Choose a reason for hiding this comment

The 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 {

Choose a reason for hiding this comment

The 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
Copy link

Choose a reason for hiding this comment

The 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);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

처음 시작하는 화면에서 제목과 내용을 입력하면 새로운 페이지로 추가될 때 내용이 사라지는 현상이 있더라고요!
아마 처음에는 id가 new로 되어있어서 POST 날릴 때 제목만 넣어줘서 그런 것 같은데
제목 입력이 끝난 것을 감지해서 바로 POST를 한다거나 임시로 1.5초로 설정되어 있는 부분을 조금 짧게 설정해 주는 방법도 있을 것 같아요!

},
});

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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if else로 구성한다면 굳이 if (!id) 부터 시작하지 않아도 될 것 같습니다
if (id) else 로 작성하면 될 것 같네요~

};
}
31 changes: 31 additions & 0 deletions src/components/Editor/EditorPage.js
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);
});
}
22 changes: 22 additions & 0 deletions src/components/utils/router/router.js
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,
},
})
);
};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

컴포넌트 폴더 안에 유틸 폴더가 있는건 무언가 네이밍과 맞지 않은것 같아요~!

67 changes: 67 additions & 0 deletions style.css
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;
}