From f68e560695ce157d264db4e9973002a12291ac59 Mon Sep 17 00:00:00 2001 From: Kal-MH Date: Tue, 15 Nov 2022 12:31:38 +0900 Subject: [PATCH 01/19] Add: add config files & index.html, main.js --- .editorconfig | 10 ++++++++++ .gitignore | 1 + .prettierrc | 17 +++++++++++++++++ css/style.css | 32 ++++++++++++++++++++++++++++++++ index.html | 20 ++++++++++++++++++++ src/main.js | 1 + 6 files changed, 81 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 .prettierrc create mode 100644 css/style.css create mode 100644 index.html create mode 100644 src/main.js diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..eb38f73d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = crlf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..3a4c3de1 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +src/api/api_constant.js diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..2c1b8962 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,17 @@ +{ + "arrowParens": "avoid", + "bracketSpacing": true, + "htmlWhitespaceSensitivity": "css", + "insertPragma": false, + "bracketSameLine": false, + "jsxSingleQuote": false, + "printWidth": 120, + "proseWrap": "preserve", + "quoteProps": "as-needed", + "semi": true, + "singleQuote": true, + "useTabs": false, + "tabWidth": 2, + "trailingComma": "all", + "endOfLine": "lf" +} diff --git a/css/style.css b/css/style.css new file mode 100644 index 00000000..96f62d9b --- /dev/null +++ b/css/style.css @@ -0,0 +1,32 @@ +/* +GLOBAL +============================ */ +html { + font-size: 100%; +} + +/* +COMMON +============================ */ +#container { + height: 100vh; + margin-bottom: 4rem; + padding: 1rem 2.5rem 2.5rem; + display: flex; + flex: 1 1 0%; + justify-content: space-between; +} + +#sidebar-container { + width: 240px; + height: 100%; + flex-grow: 0; + flex-shrink: 0; + border: 2px solid black; +} + +#editor-container { + width: 100%; + height: 100%; + border: 2px solid black; +} diff --git a/index.html b/index.html new file mode 100644 index 00000000..53a6b35b --- /dev/null +++ b/index.html @@ -0,0 +1,20 @@ + + + + + + + + + + + Notion + + +
+ +
+
+ + + diff --git a/src/main.js b/src/main.js new file mode 100644 index 00000000..e921523b --- /dev/null +++ b/src/main.js @@ -0,0 +1 @@ +console.log('hello'); From c924bbbe38cb4e6234a8cc47d1613a3f225a007e Mon Sep 17 00:00:00 2001 From: Kal-MH Date: Tue, 15 Nov 2022 12:46:02 +0900 Subject: [PATCH 02/19] feat: Basic Editor component (EditPage, Editor, Header) --- .editorconfig | 2 +- .prettierrc | 24 ++--- src/components/EditPage/EditPage.js | 58 ++++++++++++ src/components/EditPage/Editor.js | 48 ++++++++++ src/components/EditPage/Header.js | 56 +++++++++++ src/fakeDB.js | 138 ++++++++++++++++++++++++++++ src/main.js | 2 +- src/util/constants.js | 5 + src/util/error.js | 20 ++++ 9 files changed, 336 insertions(+), 17 deletions(-) create mode 100644 src/components/EditPage/EditPage.js create mode 100644 src/components/EditPage/Editor.js create mode 100644 src/components/EditPage/Header.js create mode 100644 src/fakeDB.js create mode 100644 src/util/constants.js create mode 100644 src/util/error.js diff --git a/.editorconfig b/.editorconfig index eb38f73d..339284fa 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,4 +7,4 @@ indent_size = 2 end_of_line = crlf charset = utf-8 trim_trailing_whitespace = true -insert_final_newline = true \ No newline at end of file +insert_final_newline = true diff --git a/.prettierrc b/.prettierrc index 2c1b8962..fd0a1df0 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,17 +1,11 @@ { - "arrowParens": "avoid", - "bracketSpacing": true, - "htmlWhitespaceSensitivity": "css", - "insertPragma": false, - "bracketSameLine": false, - "jsxSingleQuote": false, - "printWidth": 120, - "proseWrap": "preserve", - "quoteProps": "as-needed", - "semi": true, - "singleQuote": true, - "useTabs": false, - "tabWidth": 2, - "trailingComma": "all", - "endOfLine": "lf" + "printWidth": 120, + "overrides": [ + { + "files": "*.html", + "options": { + "printWidth": 160 + } + } + ] } diff --git a/src/components/EditPage/EditPage.js b/src/components/EditPage/EditPage.js new file mode 100644 index 00000000..7f4c443e --- /dev/null +++ b/src/components/EditPage/EditPage.js @@ -0,0 +1,58 @@ +import { ERROR_NEW_KEYWORD_MISSING } from "../../utils/constants.js"; +import { hasContent, hasNewTarget, hasTitle, isValidateString } from "../utils/error.js"; +import Editor from "./Editor.js"; +import Header from "./Header.js"; + +export default function EditPage({ $target, initialState }) { + if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); + + //tags + const $page = document.createElement("div"); + $page.setAttribute("id", "editor-container"); + + //method + const isValidateState = (state) => { + if (!state) return false; + if (!hasTitle(state) || !isValidateString(state.title)) return false; + if (!hasContent(state) || !isValidateString(state.content)) return false; + return true; + }; + + //state + this.state = isValidateState(initialState) + ? initialState + : { + title: "", + content: "", + }; + + //components + new Header({ + $target: $page, + initialState: { title: this.state.title }, + }); + + new Editor({ + $target: $page, + initialState: { + content: this.state.content, + }, + }); + + this.setState = (nextState) => { + if (!isValidateState(nextState)) return; + + this.state = nextState; + + //header, editor 변화? + + this.render(); + }; + + this.render = () => { + $target.appendChild($page); + }; + + this.render(); + //event handler +} diff --git a/src/components/EditPage/Editor.js b/src/components/EditPage/Editor.js new file mode 100644 index 00000000..9da3deeb --- /dev/null +++ b/src/components/EditPage/Editor.js @@ -0,0 +1,48 @@ +import { hasContent, isValidateString, hasNewTarget } from "../../utils/error.js"; +import { ERROR_NEW_KEYWORD_MISSING } from "../../utils/constants.js"; + +export default function Editor({ $target, initialState }) { + if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); + + const $editor = document.createElement("div"); + $editor.setAttribute("class", "editor"); + $target.appendChild($editor); + + let isInit = false; + + //method + const isValidateContent = (state) => { + if (hasContent(state) && isValidateString(state.content)) return true; + return false; + }; + + //state + this.state = isValidateContent(initialState) ? initialState : { content: "" }; + + this.setState = (nextState) => { + if (!isValidateContent(nextState)) return; + + this.state = nextState; + $editor.querySelector("textarea").value = this.state.content; + }; + + this.render = () => { + if (!isInit) { + $editor.innerHTML = ` + + `; + } + }; + + this.render(); + + //event handler + $editor.addEventListener("input", (e) => { + const { target } = e; + const { value } = target; + + this.setState({ + content: value, + }); + }); +} diff --git a/src/components/EditPage/Header.js b/src/components/EditPage/Header.js new file mode 100644 index 00000000..da93365f --- /dev/null +++ b/src/components/EditPage/Header.js @@ -0,0 +1,56 @@ +import { ERROR_NEW_KEYWORD_MISSING, HEADER_DEFAULT_TITLE } from "../../utils/constants.js"; +import { hasNewTarget, hasTitle, isValidateString } from "../../utils/error.js"; + +export default function Header({ $target, initialState }) { + if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); + + //tags + const $header = document.createElement("header"); + $target.appendChild($header); + + let isInit = false; + + //method + const isValidateTitle = (state) => { + if (hasTitle(state) && isValidateString(state.title)) return true; + return false; + }; + + //state + this.state = isValidateTitle(initialState) ? initialState : { title: "" }; + + this.setState = (nextState) => { + if (!isValidateTitle(nextState)) return; + + this.state = nextState; + + const { title } = this.state; + if (title.length) { + $header.querySelector("input").value = title; + } else { + $header.querySelector("input").placeholder = HEADER_DEFAULT_TITLE; + } + }; + + this.render = () => { + if (!isInit) { + const { title } = this.state; + $header.innerHTML = ` + + `; + isInit = true; + } + }; + + this.render(); + + //event handler + $header.addEventListener("input", (e) => { + const { target } = e; + + const { value } = target; + this.setState({ + title: value, + }); + }); +} diff --git a/src/fakeDB.js b/src/fakeDB.js new file mode 100644 index 00000000..8afc090b --- /dev/null +++ b/src/fakeDB.js @@ -0,0 +1,138 @@ +//전체 Documents + +export const documentsList = [ + { + id: 1, // Document id + title: "노션을 만들자", // Document title + documents: [ + { + id: 2, + title: "블라블라", + documents: [ + { + id: 3, + title: "함냐함냐", + documents: [], + }, + { + id: 4, + title: "함냐함냐2", + documents: [], + }, + ], + }, + { + id: 5, + title: "함냐함냐5", + documents: [], + }, + ], + }, + { + id: 6, + title: "hello!", + documents: [ + { + id: 7, + title: "함냐함냐7", + documents: [], + }, + ], + }, +]; + +export const documentId1 = { + id: 1, + title: "노션을 만들자", + content: "즐거운 자바스크립트의 세계!", + documents: [ + { + id: 2, + title: "블라블라", + createdAt: "11.11", + updatedAt: "11.11", + }, + { + id: 5, + title: "함냐함냐5", + createdAt: "11.11", + updatedAt: "11.11", + }, + ], + createdAt: "11.10", + updatedAt: "11.10", +}; + +export const documentId2 = { + id: 2, + title: "블라블라", + content: "blahblah~~~", + documents: [ + { + id: 3, + title: "함냐함냐", + createdAt: "11.11", + updatedAt: "11.11", + }, + { + id: 4, + title: "함냐함냐2", + createdAt: "11.11", + updatedAt: "11.11", + }, + ], + createdAt: "11.11", + updatedAt: "11.11", +}; + +export const documentId3 = { + id: 3, + title: "함냐함냐", + content: "함냐함냐함 아~ 맛있다", + documents: [], + createdAt: "11.11", + updatedAt: "11.11", +}; + +export const documentId4 = { + id: 4, + title: "함냐함냐2", + content: "함냐함냐함 함냐함냐함 아~ 맛있다", + documents: [], + createdAt: "11.11", + updatedAt: "11.11", +}; + +export const documentId5 = { + id: 5, + title: "함냐함냐5", + content: "함냐함냐함 아~ 맛있다5555555", + documents: [], + createdAt: "11.11", + updatedAt: "11.11", +}; + +export const documentId6 = { + id: 6, + title: "hello!", + content: "Nice to see you", + documents: [ + { + id: 7, + title: "함냐함냐7", + createdAt: "11.11", + updatedAt: "11.11", + }, + ], + createdAt: "11.11", + updatedAt: "11.11", +}; + +export const documentId7 = { + id: 7, + title: "함냐함냐7", + content: "함냐함냐함 아~ 맛있다777777", + documents: [], + createdAt: "11.11", + updatedAt: "11.11", +}; diff --git a/src/main.js b/src/main.js index e921523b..702f4280 100644 --- a/src/main.js +++ b/src/main.js @@ -1 +1 @@ -console.log('hello'); +console.log("hello"); diff --git a/src/util/constants.js b/src/util/constants.js new file mode 100644 index 00000000..a218021b --- /dev/null +++ b/src/util/constants.js @@ -0,0 +1,5 @@ +//Header +export const HEADER_DEFAULT_TITLE = "제목 없음"; + +//error +export const ERROR_NEW_KEYWORD_MISSING = "Error: missing new keyword"; diff --git a/src/util/error.js b/src/util/error.js new file mode 100644 index 00000000..ca9c4d23 --- /dev/null +++ b/src/util/error.js @@ -0,0 +1,20 @@ +//new target +export const hasNewTarget = (target) => (target ? true : false); + +//validate properties +const properties = { + TITLE: "title", + CONTENT: "content", + DOCUMENTS: "documents", +}; + +export const hasTitle = (state) => state.hasOwnProperty(properties.TITLE); + +export const hasContent = (state) => state.hasOwnProperty(properties.CONTENT); + +export const hasDocuments = (state) => state.hasOwnProperty(properties.DOCUMENTS); + +//string +export const isValidateString = (str) => str !== undefined && typeof str === "string"; + +export const isSpaceString = (str) => !str.replace(/[\s]/g, "").length; From 55334b1dff989384a5097297ff96df7b14792e49 Mon Sep 17 00:00:00 2001 From: Kal-MH Date: Tue, 15 Nov 2022 15:36:11 +0900 Subject: [PATCH 03/19] =?UTF-8?q?Add:=20util=20functions=201.=20constants.?= =?UTF-8?q?js=202.=20error.js=203.=20router.js=20-=20new=20CustomEvent=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=20=20-=20DOCUMENT=20CREATE,=20PUT,=20DELETE?= =?UTF-8?q?=20=EC=A0=9C=EA=B3=B5=20=20-=20ROUTE=5FPUSH=20=20-=20HEADER=5FT?= =?UTF-8?q?ITLE=5FCHANGE(=EC=97=90=EB=94=94=ED=84=B0=20=EC=A0=9C=EB=AA=A9?= =?UTF-8?q?=20=EC=9E=85=EB=A0=A5=EC=B0=BD=EA=B3=BC=20=EC=82=AC=EC=9D=B4?= =?UTF-8?q?=EB=93=9C=EB=B0=94=20=EB=A9=94=EB=89=B4=EC=9D=98=20=EC=A0=9C?= =?UTF-8?q?=EB=AA=A9=20=EB=8F=99=EA=B8=B0=ED=99=94)=204.=20storage.js?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 17 +-- src/api/api.js | 22 ++++ src/components/EditPage/EditPage.js | 58 ----------- src/components/EditPage/Editor.js | 48 --------- src/components/EditPage/Header.js | 56 ---------- src/components/utils/constants.js | 63 ++++++++++++ src/components/utils/error.js | 17 +++ src/components/utils/router.js | 154 ++++++++++++++++++++++++++++ src/components/utils/storage.js | 28 +++++ src/main.js | 30 +++++- src/util/constants.js | 5 - src/util/error.js | 20 ---- 12 files changed, 322 insertions(+), 196 deletions(-) create mode 100644 src/api/api.js delete mode 100644 src/components/EditPage/EditPage.js delete mode 100644 src/components/EditPage/Editor.js delete mode 100644 src/components/EditPage/Header.js create mode 100644 src/components/utils/constants.js create mode 100644 src/components/utils/error.js create mode 100644 src/components/utils/router.js create mode 100644 src/components/utils/storage.js delete mode 100644 src/util/constants.js delete mode 100644 src/util/error.js diff --git a/index.html b/index.html index 53a6b35b..0dc10aa8 100644 --- a/index.html +++ b/index.html @@ -5,16 +5,17 @@ - - - + + + + + + + Notion -
- -
-
- +
+ diff --git a/src/api/api.js b/src/api/api.js new file mode 100644 index 00000000..1028da74 --- /dev/null +++ b/src/api/api.js @@ -0,0 +1,22 @@ +import { ERROR_API_CALL } from "../components/utils/constants.js"; +import { API_END_POINT } from "./api_constant.js"; + +export const request = async (url, options = {}) => { + try { + const res = await fetch(`${API_END_POINT}${url}`, { + ...options, + headers: { + "Content-Type": "application/json", + "x-username": "kal", + }, + }); + + if (res.ok) { + return await res.json(); + } + + throw new Error(ERROR_API_CALL); + } catch (e) { + alert(e.message); + } +}; diff --git a/src/components/EditPage/EditPage.js b/src/components/EditPage/EditPage.js deleted file mode 100644 index 7f4c443e..00000000 --- a/src/components/EditPage/EditPage.js +++ /dev/null @@ -1,58 +0,0 @@ -import { ERROR_NEW_KEYWORD_MISSING } from "../../utils/constants.js"; -import { hasContent, hasNewTarget, hasTitle, isValidateString } from "../utils/error.js"; -import Editor from "./Editor.js"; -import Header from "./Header.js"; - -export default function EditPage({ $target, initialState }) { - if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); - - //tags - const $page = document.createElement("div"); - $page.setAttribute("id", "editor-container"); - - //method - const isValidateState = (state) => { - if (!state) return false; - if (!hasTitle(state) || !isValidateString(state.title)) return false; - if (!hasContent(state) || !isValidateString(state.content)) return false; - return true; - }; - - //state - this.state = isValidateState(initialState) - ? initialState - : { - title: "", - content: "", - }; - - //components - new Header({ - $target: $page, - initialState: { title: this.state.title }, - }); - - new Editor({ - $target: $page, - initialState: { - content: this.state.content, - }, - }); - - this.setState = (nextState) => { - if (!isValidateState(nextState)) return; - - this.state = nextState; - - //header, editor 변화? - - this.render(); - }; - - this.render = () => { - $target.appendChild($page); - }; - - this.render(); - //event handler -} diff --git a/src/components/EditPage/Editor.js b/src/components/EditPage/Editor.js deleted file mode 100644 index 9da3deeb..00000000 --- a/src/components/EditPage/Editor.js +++ /dev/null @@ -1,48 +0,0 @@ -import { hasContent, isValidateString, hasNewTarget } from "../../utils/error.js"; -import { ERROR_NEW_KEYWORD_MISSING } from "../../utils/constants.js"; - -export default function Editor({ $target, initialState }) { - if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); - - const $editor = document.createElement("div"); - $editor.setAttribute("class", "editor"); - $target.appendChild($editor); - - let isInit = false; - - //method - const isValidateContent = (state) => { - if (hasContent(state) && isValidateString(state.content)) return true; - return false; - }; - - //state - this.state = isValidateContent(initialState) ? initialState : { content: "" }; - - this.setState = (nextState) => { - if (!isValidateContent(nextState)) return; - - this.state = nextState; - $editor.querySelector("textarea").value = this.state.content; - }; - - this.render = () => { - if (!isInit) { - $editor.innerHTML = ` - - `; - } - }; - - this.render(); - - //event handler - $editor.addEventListener("input", (e) => { - const { target } = e; - const { value } = target; - - this.setState({ - content: value, - }); - }); -} diff --git a/src/components/EditPage/Header.js b/src/components/EditPage/Header.js deleted file mode 100644 index da93365f..00000000 --- a/src/components/EditPage/Header.js +++ /dev/null @@ -1,56 +0,0 @@ -import { ERROR_NEW_KEYWORD_MISSING, HEADER_DEFAULT_TITLE } from "../../utils/constants.js"; -import { hasNewTarget, hasTitle, isValidateString } from "../../utils/error.js"; - -export default function Header({ $target, initialState }) { - if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); - - //tags - const $header = document.createElement("header"); - $target.appendChild($header); - - let isInit = false; - - //method - const isValidateTitle = (state) => { - if (hasTitle(state) && isValidateString(state.title)) return true; - return false; - }; - - //state - this.state = isValidateTitle(initialState) ? initialState : { title: "" }; - - this.setState = (nextState) => { - if (!isValidateTitle(nextState)) return; - - this.state = nextState; - - const { title } = this.state; - if (title.length) { - $header.querySelector("input").value = title; - } else { - $header.querySelector("input").placeholder = HEADER_DEFAULT_TITLE; - } - }; - - this.render = () => { - if (!isInit) { - const { title } = this.state; - $header.innerHTML = ` - - `; - isInit = true; - } - }; - - this.render(); - - //event handler - $header.addEventListener("input", (e) => { - const { target } = e; - - const { value } = target; - this.setState({ - title: value, - }); - }); -} diff --git a/src/components/utils/constants.js b/src/components/utils/constants.js new file mode 100644 index 00000000..f46d0668 --- /dev/null +++ b/src/components/utils/constants.js @@ -0,0 +1,63 @@ +//validate properties +export const properties = { + ID: "id", + TITLE: "title", + CONTENT: "content", + DOCUMENTS: "documents", +}; + +//Header +export const DEFAULT_TITLE = "제목없음"; +export const ROOT_TITLE = "작업 중... 👌"; + +//Document +export const classNameObj = { + SIDEBAR: "sidebar", + TITLE: "title", + DISPLAY_BTN: "display-btn", + NEW_BTN: "new-btn", + REMOVE_BTN: "remove-btn", + DOCUMENT_BLOCK: "document-block", + DOCUMENT_SECTION: "document-section", + DOCUMENT_LIST_BLOCK: "document-list-block", + DOCUMENT_BLOCK_INNER: "document-block-inner", + SIDEBAR_DOCUMENT_LIST_CONTAINER: "sidebar-document-list-container", +}; + +export const styleObj = { + DEFAULT_PADDING: 10, + PADDING_INCREMENT: 20, +}; + +export const DOCUMENT_HEADER_CONTENT = "워크 스페이스"; +export const DOCUMENT_FOOTER_CONTENT = "새 문서 만들기"; +export const DISABLED_ID = -1; +export const DEFAULT_ID = "new"; + +export const DEFAULT_STATE = { + id: DISABLED_ID, + title: ROOT_TITLE, + content: "", +}; + +//Button +export const DELETE_BUTTON_TEXT = "Delete"; + +//error +export const ERROR_NEW_KEYWORD_MISSING = "Error: missing new keyword"; +export const ERROR_API_CALL = "Error: Api call"; + +//localstorage +export const LOCAL_STORAGE_RECENT_DOCUMENT = "recent-document"; +export const LOCAL_STORAGE_DISPLAY = "display"; +export const LOCAL_IS_GOING_TO_PUT = "is-going-to-put"; +export const LOCAL_IS_PUTTING_OKAY = "is-putting-okay"; +export const LOCAL_IS_WAITING_USER_ANSWER = "is-waiting-user-answer"; + +//route keyword +export const EVENT_ROUTE_PUSH = "route-push"; +export const EVENT_ROUTE_REMOVE = "route-remove-document"; +export const EVENT_ROUTE_CREATE = "route-create-document"; +export const EVENT_ROUTE_PUT = "route-put-document"; + +export const EVENT_HEADER_CHANGE = "header-change"; diff --git a/src/components/utils/error.js b/src/components/utils/error.js new file mode 100644 index 00000000..3b2d816b --- /dev/null +++ b/src/components/utils/error.js @@ -0,0 +1,17 @@ +import { properties } from "./constants.js"; + +const { ID, TITLE, CONTENT, DOCUMENTS } = properties; + +//new target +export const hasNewTarget = (target) => (target ? true : false); + +export const hasId = (state) => state.hasOwnProperty(ID); + +export const hasTitle = (state) => state.hasOwnProperty(TITLE); + +export const hasContent = (state) => state.hasOwnProperty(CONTENT); + +export const hasDocuments = (state) => state.hasOwnProperty(DOCUMENTS); + +//array +export const isValidArray = (arr) => arr && Array.isArray(arr); diff --git a/src/components/utils/router.js b/src/components/utils/router.js new file mode 100644 index 00000000..d36acd75 --- /dev/null +++ b/src/components/utils/router.js @@ -0,0 +1,154 @@ +import { request } from "../../api/api.js"; +import { + DEFAULT_TITLE, + EVENT_HEADER_CHANGE, + EVENT_ROUTE_PUSH, + EVENT_ROUTE_CREATE, + EVENT_ROUTE_PUT, + EVENT_ROUTE_REMOVE, + LOCAL_STORAGE_RECENT_DOCUMENT, +} from "./constants.js"; +import { removeItem, setItem } from "./storage.js"; + +export const initRouter = (onRoute) => { + const removeAllDocument = async (document) => { + for (const subDocument of document.documents) { + removeAllDocument(subDocument); + } + + await request(`/documents/${document.id}`, { + method: "DELETE", + }); + }; + + //event listener + window.addEventListener("popstate", () => onRoute()); + + window.addEventListener(EVENT_ROUTE_PUSH, (e) => { + const { nextUrl } = e.detail; + + if (!nextUrl) return; + + history.replaceState(null, null, nextUrl); + onRoute(); + }); + + window.addEventListener(EVENT_ROUTE_REMOVE, async (e) => { + const { id, parentId } = e.detail; + + if (!id) return; + + const removedDocument = await request(`/documents/${id}`); + + await removeAllDocument(removedDocument); + removeItem(LOCAL_STORAGE_RECENT_DOCUMENT); + + routePush(`${parentId ? `/documents/${parentId}` : "/"}`); + }); + + window.addEventListener(EVENT_ROUTE_CREATE, async (e) => { + const { id } = e.detail; + + const createNewDocument = await request("/documents", { + method: "POST", + body: JSON.stringify({ + title: DEFAULT_TITLE, + content: "", + parent: id, + }), + }); + console.log(createNewDocument); + + routePush(`/documents/${createNewDocument.id}`); + }); + + window.addEventListener(EVENT_ROUTE_PUT, async (e) => { + const { id, title, content } = e.detail; + + const res = await request(`/documents/${id}`, { + method: "PUT", + body: JSON.stringify({ + title, + content, + }), + }); + + setItem(LOCAL_STORAGE_RECENT_DOCUMENT, { + ...res, + tempSaveData: new Date(), + }); + }); + + window.addEventListener(EVENT_HEADER_CHANGE, (e) => { + const { id, title } = e.detail; + + //해당 document-block의 title을 찾아서 innerText를 바꿔주는 거지. + const documentBlockArr = + e.target.document.body.querySelectorAll(".document-block"); + + let currentDocumentBlock = null; + + documentBlockArr.forEach((e) => { + const dataId = e.dataset.id; + + if (parseInt(dataId) === id) { + currentDocumentBlock = e; + } + }); + + const currentTitleBlock = currentDocumentBlock.querySelector(".title"); + currentTitleBlock.innerText = title; + }); +}; + +export const setHeaderChange = ({ id, title }) => { + window.dispatchEvent( + new CustomEvent(EVENT_HEADER_CHANGE, { + detail: { + id, + title, + }, + }) + ); +}; + +export const routePush = (nextUrl) => { + window.dispatchEvent( + new CustomEvent(EVENT_ROUTE_PUSH, { + detail: { nextUrl }, + }) + ); +}; + +export const routeRemoveDocument = ({ id, parentId }) => { + window.dispatchEvent( + new CustomEvent(EVENT_ROUTE_REMOVE, { + detail: { + id, + parentId, + }, + }) + ); +}; + +export const routeCreateDocument = ({ id }) => { + window.dispatchEvent( + new CustomEvent(EVENT_ROUTE_CREATE, { + detail: { + id, + }, + }) + ); +}; + +export const routePutDocument = ({ id, title, content }) => { + window.dispatchEvent( + new CustomEvent(EVENT_ROUTE_PUT, { + detail: { + id, + title, + content, + }, + }) + ); +}; diff --git a/src/components/utils/storage.js b/src/components/utils/storage.js new file mode 100644 index 00000000..0d5860ac --- /dev/null +++ b/src/components/utils/storage.js @@ -0,0 +1,28 @@ +/* + * 모든 데이터가 JSON형식인 상황이기 때문에 + * storage 내부 메서드에서 JSON.stringfy, JSON.parse가 행해진다. + */ + +const storage = window.localStorage; + +export const setItem = (key, value) => { + try { + storage.setItem(key, JSON.stringify(value)); + } catch (e) { + console.log(e.message); + } +}; + +export const getItem = (key, defaultReturnValue) => { + try { + const storedValue = storage.getItem(key); + + return storedValue ? JSON.parse(storedValue) : defaultReturnValue; + } catch (e) { + return defaultReturnValue; + } +}; + +export const removeItem = (key) => { + storage.removeItem(key); +}; diff --git a/src/main.js b/src/main.js index 702f4280..a31baa62 100644 --- a/src/main.js +++ b/src/main.js @@ -1 +1,29 @@ -console.log("hello"); +// import DocumentPage from "./components/DocumentPage/DocumentPage.js"; +import EditPage from "./components/EditPage/EditPage.js"; +import { documentId1, documentsList } from "./fakeDb.js"; + +const $container = document.querySelector("#container"); + +// const documentPage = new DocumentPage({ +// $target: $container, +// initialState: [], +// }); + +// documentPage.setState(documentsList); + +// const editPage = new EditPage({ +// $target: $container, +// initialState: { +// title: "", +// content: "", +// }, +// }); + +//documentId1을 불러왔을 때 +editPage.setState(documentId1); + +//새로운 document를 불러왔을 때 +// editPage.setState({ +// title: "", +// content: "", +// }); diff --git a/src/util/constants.js b/src/util/constants.js deleted file mode 100644 index a218021b..00000000 --- a/src/util/constants.js +++ /dev/null @@ -1,5 +0,0 @@ -//Header -export const HEADER_DEFAULT_TITLE = "제목 없음"; - -//error -export const ERROR_NEW_KEYWORD_MISSING = "Error: missing new keyword"; diff --git a/src/util/error.js b/src/util/error.js deleted file mode 100644 index ca9c4d23..00000000 --- a/src/util/error.js +++ /dev/null @@ -1,20 +0,0 @@ -//new target -export const hasNewTarget = (target) => (target ? true : false); - -//validate properties -const properties = { - TITLE: "title", - CONTENT: "content", - DOCUMENTS: "documents", -}; - -export const hasTitle = (state) => state.hasOwnProperty(properties.TITLE); - -export const hasContent = (state) => state.hasOwnProperty(properties.CONTENT); - -export const hasDocuments = (state) => state.hasOwnProperty(properties.DOCUMENTS); - -//string -export const isValidateString = (str) => str !== undefined && typeof str === "string"; - -export const isSpaceString = (str) => !str.replace(/[\s]/g, "").length; From 465ecc86761e69d819bf68a9e17f69c902b094e8 Mon Sep 17 00:00:00 2001 From: Kal-MH Date: Tue, 15 Nov 2022 15:42:17 +0900 Subject: [PATCH 04/19] =?UTF-8?q?Add=20files:=20Create=20EditPage=201.=20E?= =?UTF-8?q?ditPage=20=20=20-=20Topbar,=20Header,=20Editor=EB=A5=BC=20?= =?UTF-8?q?=EB=A0=8C=EB=8D=94=EB=A7=81=20=20=20-=20autoSaveDocument()?= =?UTF-8?q?=EB=A5=BC=20=ED=98=B8=EC=B6=9C=ED=95=B4=EC=84=9C=20=ED=98=84?= =?UTF-8?q?=EC=9E=AC=20=EC=9E=91=EC=97=85=EC=A4=91=EC=9D=B8=20document=20?= =?UTF-8?q?=EC=9E=90=EB=8F=99=20=EC=A0=80=EC=9E=A5=20=20=20=20=20-=20LOCAL?= =?UTF-8?q?=5FIS=5FGOING=5FTO=5FPUT,=20LOCAL=5FIS=5FWAITING=5FUSER=5FANWER?= =?UTF-8?q?,=20LOCAL=5FIS=5FGOING=5FTO=5FPUT=20=EC=84=B8=EA=B0=80=EC=A7=80?= =?UTF-8?q?=20=ED=94=8C=EB=9E=98=EA=B7=B8=EB=A5=BC=20=EC=9D=B4=EC=9A=A9?= =?UTF-8?q?=ED=95=B4=EC=84=9C=20=20=20=20=20=20=20=EC=9E=84=EA=B3=84?= =?UTF-8?q?=EA=B5=AC=EC=97=AD=20=EC=84=A4=EC=A0=95.=20=20=20=20=20-=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EC=9E=90=EA=B0=80=20=EC=9E=91=EC=84=B1=20?= =?UTF-8?q?=EC=A4=91=20=EC=9E=90=EB=8F=99=EC=A0=80=EC=9E=A5=EC=9D=B4=20?= =?UTF-8?q?=EB=90=98=EA=B8=B0=20=EC=A0=84=EC=97=90=20=EB=8B=A4=EB=A5=B8=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= =?UTF-8?q?=ED=95=A0=20=EC=8B=9C,=20confirm()=EB=A5=BC=20=ED=86=B5?= =?UTF-8?q?=ED=95=B4=20=EC=9D=98=EC=82=AC=EB=A5=BC=20=ED=99=95=EC=9D=B8?= =?UTF-8?q?=ED=95=9C=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- css/style.css | 97 +++++++++++++++++++++ src/components/EditPage/EditPage.js | 125 ++++++++++++++++++++++++++++ src/components/EditPage/Editor.js | 50 +++++++++++ src/components/EditPage/Header.js | 67 +++++++++++++++ src/components/EditPage/Topbar.js | 37 ++++++++ 5 files changed, 376 insertions(+) create mode 100644 src/components/EditPage/EditPage.js create mode 100644 src/components/EditPage/Editor.js create mode 100644 src/components/EditPage/Header.js create mode 100644 src/components/EditPage/Topbar.js diff --git a/css/style.css b/css/style.css index 96f62d9b..b57bf524 100644 --- a/css/style.css +++ b/css/style.css @@ -29,4 +29,101 @@ COMMON width: 100%; height: 100%; border: 2px solid black; + display: flex; + flex-direction: column; +} + +/* +EDITOR-PAGE +============================= */ + +/* HEADER */ +header { + padding: 2.5rem 3rem; + flex-shrink: 0; + flex-grow: 0; +} + +header input { + width: 100%; + font-size: 2.5rem; +} + +/* EDITOR */ +#editor-container .editor { + height: 100%; + padding: 1rem 3rem; +} + +#editor-container .editor textarea { + width: 100%; + height: 100%; +} + +/* +DOCUMENT-PAGE +============================== */ + +/* DOCUMENT BLOCK */ +.document-block { + margin-left: 5px; + margin-right: 5px; + font-size: 0.9rem; +} + +.document-block-inner { + width: 100%; + padding: 2px 10px 2px 5px; + margin-top: 1px; + margin-bottom: 1px; + display: flex; + overflow: hidden; + text-overflow: clip; +} + +.document-block-inner .display-btn::before { + flex-shrink: 0; + flex-grow: 0; + width: 22px; + height: 22px; + margin-right: 3px; + /* Font Awesome */ + font-family: FontAwesome; + content: "\f1b0"; + font-weight: 900; + color: #949087; +} + +.document-block-inner .remove-btn::after { + flex-shrink: 0; + flex-grow: 0; + width: 22px; + height: 22px; + margin-right: 3px; + /* Font Awesome */ + font-family: FontAwesome; + content: "\f1b0"; + font-weight: 900; + color: #949087; +} + +.document-block-inner .new-btn::after { + flex-shrink: 0; + flex-grow: 0; + width: 22px; + height: 22px; + margin-right: 3px; + /* Font Awesome */ + font-family: FontAwesome; + content: "\f1b0"; + font-weight: 900; + color: #949087; +} + +.document-block-inner .title { + align-items: center; + display: flex; + flex: 1 1 auto; + overflow: hidden; + text-overflow: clip; } diff --git a/src/components/EditPage/EditPage.js b/src/components/EditPage/EditPage.js new file mode 100644 index 00000000..609a98b6 --- /dev/null +++ b/src/components/EditPage/EditPage.js @@ -0,0 +1,125 @@ +import { + DEFAULT_TITLE, + DISABLED_ID, + ERROR_NEW_KEYWORD_MISSING, + LOCAL_STORAGE_RECENT_DOCUMENT, + LOCAL_IS_GOING_TO_PUT, + LOCAL_IS_PUTTING_OKAY, + LOCAL_IS_WAITING_USER_ANSWER, +} from "../utils/constants.js"; +import { hasNewTarget } from "../utils/error.js"; +import { routePutDocument, setHeaderChange } from "../utils/router.js"; +import { getItem, setItem } from "../utils/storage.js"; +import Editor from "./Editor.js"; +import Header from "./Header.js"; +import Topbar from "./Topbar.js"; + +export default function EditPage({ $target, initialState }) { + if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); + + //tags + const $page = document.createElement("div"); + $page.setAttribute("id", "editor-container"); + + let isInit = false; + let timer = null; + + //state + this.state = initialState || { + id: DISABLED_ID, + title: "", + content: "", + }; + + //topbar with fetchButton + const topbar = new Topbar({ + $target: $page, + initialState: {}, + }); + + //components + const header = new Header({ + $target: $page, + initialState: { + id: this.state.id, + title: this.state.title, + }, + onEditing: (title) => { + this.setState({ + ...this.state, + title, + }); + + setHeaderChange({ + id: this.state.id, + title: this.state.title, + }); + autoSaveDocument({ delay: 2000 }); + }, + }); + + const editor = new Editor({ + $target: $page, + initialState: { + content: this.state.content, + }, + onEditing: (content) => { + this.setState({ + ...this.state, + content, + }); + + autoSaveDocument({ delay: 2000 }); + }, + }); + + this.setState = (nextState) => { + if (!nextState) return; + + this.state = nextState; + + //header, editor 변화? + const { id, parentId, title, content } = this.state; + + header.setState({ + id, + title: title || DEFAULT_TITLE, + }); + editor.setState({ id, content: content || "" }); + topbar.setState({ id, parentId: parentId || null }); + + //LS 저장 + setItem(LOCAL_STORAGE_RECENT_DOCUMENT, { + ...this.state, + tempSaveDate: new Date(), + }); + + if (!isInit) { + this.render(); + isInit = true; + } + }; + + this.render = () => { + $target.appendChild($page); + }; + + //method + const autoSaveDocument = ({ delay }) => { + if (timer !== null) { + clearTimeout(timer); + } + + setItem(LOCAL_IS_GOING_TO_PUT, true); + timer = setTimeout(async () => { + const { id, title, content } = this.state; + + while (getItem(LOCAL_IS_WAITING_USER_ANSWER, false)); + getItem(LOCAL_IS_PUTTING_OKAY, true) + ? routePutDocument({ id, title, content }) + : ""; + setItem(LOCAL_IS_GOING_TO_PUT, false); + }, delay); + }; + //event handler +} diff --git a/src/components/EditPage/Editor.js b/src/components/EditPage/Editor.js new file mode 100644 index 00000000..22ab338d --- /dev/null +++ b/src/components/EditPage/Editor.js @@ -0,0 +1,50 @@ +import { hasContent, hasNewTarget } from "../utils/error.js"; +import { DISABLED_ID, ERROR_NEW_KEYWORD_MISSING } from "../utils/constants.js"; + +export default function Editor({ $target, initialState, onEditing }) { + if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); + + const $editor = document.createElement("div"); + $editor.setAttribute("class", "editor"); + $target.appendChild($editor); + + let isInit = false; + + //validation + const isValidContent = (state) => { + if (!state || !hasContent(state)) return false; + return true; + }; + + //state + this.state = isValidContent(initialState) ? initialState : { content: "" }; + + this.setState = (nextState) => { + if (!isValidContent(nextState)) return; + + this.state = nextState; + const textarea = $editor.querySelector("textarea"); + textarea.value = this.state.content; + + this.state.id === DISABLED_ID + ? (textarea.disabled = true) + : (textarea.disabled = false); + }; + + this.render = () => { + if (!isInit) { + $editor.innerHTML = ` + + `; + } + }; + + this.render(); + + //event handler + $editor.addEventListener("input", (e) => { + const { value } = e.target; + + onEditing(value); + }); +} diff --git a/src/components/EditPage/Header.js b/src/components/EditPage/Header.js new file mode 100644 index 00000000..caa1cad5 --- /dev/null +++ b/src/components/EditPage/Header.js @@ -0,0 +1,67 @@ +import { + ERROR_NEW_KEYWORD_MISSING, + DEFAULT_TITLE, + DISABLED_ID, + ROOT_TITLE, +} from "../utils/constants.js"; +import { hasId, hasNewTarget, hasTitle } from "../utils/error.js"; + +export default function Header({ $target, initialState, onEditing }) { + if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); + + //tags + const $header = document.createElement("header"); + $target.appendChild($header); + + let isInit = false; + + //validation + const isValidState = (state) => { + if (!state) return false; + if (!hasId(state) || !hasTitle(state)) return false; + return true; + }; + + //state + this.state = isValidState(initialState) ? initialState : { title: "" }; + + this.setState = (nextState) => { + if (!isValidState(nextState)) return; + this.state = nextState; + + const { title } = this.state; + const input = $header.querySelector("input"); + + if (title === DEFAULT_TITLE || title === ROOT_TITLE) { + input.value = ""; + input.placeholder = title; + } else { + input.value = title; + } + + this.state.id === DISABLED_ID + ? (input.disabled = true) + : (input.disabled = false); + }; + + this.render = () => { + if (!isInit) { + const { title } = this.state; + $header.innerHTML = ` + + `; + isInit = true; + } + }; + + this.render(); + + //event handler + $header.addEventListener("input", (e) => { + const { value } = e.target; + + onEditing(value); + }); +} diff --git a/src/components/EditPage/Topbar.js b/src/components/EditPage/Topbar.js new file mode 100644 index 00000000..cf9f981c --- /dev/null +++ b/src/components/EditPage/Topbar.js @@ -0,0 +1,37 @@ +import { hasId, hasNewTarget } from "../utils/error.js"; +import { DISABLED_ID, ERROR_NEW_KEYWORD_MISSING } from "../utils/constants.js"; +import DeleteButton from "../Button/DeleteButton.js"; + +export default function Topbar({ $target, initialState }) { + if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); + + const $topbar = document.createElement("div"); + $topbar.setAttribute("class", "editor-topbar"); + $target.appendChild($topbar); + + const isValidState = (state) => { + if (!state || !hasId(state)) return false; + return true; + }; + + this.state = isValidState(initialState) ? initialState : { id: DISABLED_ID }; + + //topbar with fetchButton + const deleteBtn = new DeleteButton({ + $target: $topbar, + initialState: {}, + }); + + this.setState = (nextState) => { + if (!nextState) return; + + this.state = nextState; + + if (this.state.id === DISABLED_ID) { + $topbar.style.display = "none"; + } else { + $topbar.style.display = "block"; + deleteBtn.setState(this.state); + } + }; +} From 1bc878430885b6f7992ee98af3640ab81bbb5a70 Mon Sep 17 00:00:00 2001 From: Kal-MH Date: Tue, 15 Nov 2022 15:51:25 +0900 Subject: [PATCH 05/19] =?UTF-8?q?Add=20files:=20DocumentFooter=20DocumentP?= =?UTF-8?q?age=20=201.=20DocumentHeader=20:=20=EC=9B=8C=ED=81=AC=20?= =?UTF-8?q?=EC=8A=A4=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EB=AC=B8=EA=B5=AC,=20?= =?UTF-8?q?=EC=83=88=EB=A1=9C=20=EC=83=9D=EC=84=B1=20=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?=202.=20Documents:=20=EC=A0=84=EC=B2=B4=20=EB=AC=B8=EC=84=9C=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=ED=8A=B8=EB=A6=AC=ED=98=95=ED=83=9C?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=B4=EC=97=AC=EC=A4=8C=20=20=20=20=20-=20?= =?UTF-8?q?=ED=8E=BC=EC=B9=A8,=20=EC=82=AD=EC=A0=9C,=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=20=EB=B2=84=ED=8A=BC=EC=9D=B4=20=EA=B0=81=20=EB=AC=B8=EC=84=9C?= =?UTF-8?q?=EB=A7=88=EB=8B=A4=20=EB=8B=AC=EB=A0=A4=20=EC=9E=88=EB=8B=A4.?= =?UTF-8?q?=20=20=20=20=20-=20=EC=83=9D=EC=84=B1=EC=9D=98=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0,=20=EB=82=99=EA=B4=80=EC=A0=81=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8=20=EC=9D=B4=ED=9B=84,=20=EC=A0=84=EC=B2=B4?= =?UTF-8?q?=20=EB=AC=B8=EC=84=9C=20=EB=AA=A9=EB=A1=9D=EC=9D=84=20=EB=8B=A4?= =?UTF-8?q?=EC=8B=9C=20=EB=A0=8C=EB=8D=94=EB=A7=81=20=20=20=20=20-=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C,=20=EC=83=9D=EC=84=B1=EC=9D=80=20routes.js?= =?UTF-8?q?=EC=97=90=20=EC=A0=95=EC=9D=98=EB=90=9C=20=ED=95=A8=EC=88=98?= =?UTF-8?q?=EB=A5=BC=20=ED=98=B8=EC=B6=9C=ED=95=98=EC=97=AC=20=EC=9D=B4?= =?UTF-8?q?=EB=B2=A4=ED=8A=B8=EB=A5=BC=20=EB=B0=9C=EC=83=9D,=20url?= =?UTF-8?q?=EA=B3=BC=20=ED=99=94=EB=A9=B4=EC=9D=84=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=ED=95=9C=EB=8B=A4.=20=203.=20DocumentFooter=20:=20=EC=83=88?= =?UTF-8?q?=EB=A1=9C=EC=9A=B4=20=EB=AC=B8=EC=84=9C=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=ED=95=98=EA=B8=B0=20=EB=AC=B8=EA=B5=AC,=20=EC=83=88=EB=A1=9C?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1=20=EB=B2=84=ED=8A=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/DocumentPage/DocumentFooter.js | 36 ++++ src/components/DocumentPage/DocumentHeader.js | 36 ++++ src/components/DocumentPage/DocumentPage.js | 71 ++++++++ src/components/DocumentPage/Documents.js | 109 +++++++++++++ .../document_util/documentUtil.js | 154 ++++++++++++++++++ 5 files changed, 406 insertions(+) create mode 100644 src/components/DocumentPage/DocumentFooter.js create mode 100644 src/components/DocumentPage/DocumentHeader.js create mode 100644 src/components/DocumentPage/DocumentPage.js create mode 100644 src/components/DocumentPage/Documents.js create mode 100644 src/components/DocumentPage/document_util/documentUtil.js diff --git a/src/components/DocumentPage/DocumentFooter.js b/src/components/DocumentPage/DocumentFooter.js new file mode 100644 index 00000000..bb5fb736 --- /dev/null +++ b/src/components/DocumentPage/DocumentFooter.js @@ -0,0 +1,36 @@ +import { + classNameObj, + DOCUMENT_FOOTER_CONTENT, + ERROR_NEW_KEYWORD_MISSING, + styleObj, +} from "../utils/constants.js"; +import { hasNewTarget } from "../utils/error.js"; +import { routeCreateDocument } from "../utils/router.js"; + +const { NEW_BTN, DOCUMENT_BLOCK_INNER } = classNameObj; + +export default function DocumentFooter({ $target }) { + if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); + + const $footer = document.createElement("div"); + $footer.setAttribute("id", "sidebar-footer"); + $target.appendChild($footer); + + const CONTENT = DOCUMENT_FOOTER_CONTENT; + const DEFAULT_PADDING = styleObj.DEFAULT_PADDING; + + this.render = () => { + $footer.innerHTML = ` +
+
${CONTENT}
+
+
+ `; + }; + + this.render(); + + $footer.addEventListener("click", () => { + routeCreateDocument({ id: null }); + }); +} diff --git a/src/components/DocumentPage/DocumentHeader.js b/src/components/DocumentPage/DocumentHeader.js new file mode 100644 index 00000000..39905845 --- /dev/null +++ b/src/components/DocumentPage/DocumentHeader.js @@ -0,0 +1,36 @@ +import { + classNameObj, + DOCUMENT_HEADER_CONTENT, + ERROR_NEW_KEYWORD_MISSING, + styleObj, +} from "../utils/constants.js"; +import { hasNewTarget } from "../utils/error.js"; +import { routeCreateDocument } from "../utils/router.js"; + +const { DOCUMENT_BLOCK, DOCUMENT_BLOCK_INNER, NEW_BTN } = classNameObj; + +export default function DocumentHeader({ $target }) { + if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); + + const $header = document.createElement("div"); + $header.setAttribute("id", "sidebar-header"); + $target.appendChild($header); + + const CONTENT = DOCUMENT_HEADER_CONTENT; + const DEFAULT_PADDING = styleObj.DEFAULT_PADDING; + + this.render = () => { + $header.innerHTML = ` +
+
${CONTENT}
+
+
+ `; + }; + + this.render(); + + $header.addEventListener("click", () => { + routeCreateDocument({ id: null }); + }); +} diff --git a/src/components/DocumentPage/DocumentPage.js b/src/components/DocumentPage/DocumentPage.js new file mode 100644 index 00000000..118c923a --- /dev/null +++ b/src/components/DocumentPage/DocumentPage.js @@ -0,0 +1,71 @@ +import { + ERROR_NEW_KEYWORD_MISSING, + LOCAL_IS_GOING_TO_PUT, + LOCAL_IS_PUTTING_OKAY, + LOCAL_IS_WAITING_USER_ANSWER, +} from "../utils/constants.js"; +import { hasNewTarget, isValidArray } from "../utils/error.js"; +import { routePush } from "../utils/router.js"; +import { getItem, setItem } from "../utils/storage.js"; +import DocumentFooter from "./DocumentFooter.js"; +import DocumentHeader from "./DocumentHeader.js"; +import Documents from "./Documents.js"; + +export default function DocumentPage({ $target, initialState }) { + if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); + + //tags + const $page = document.createElement("div"); + $page.setAttribute("id", "sidebar-container"); + + let isInit = false; + + //validation + const isValidState = (state) => { + if (!state || !isValidArray(state)) return false; + return true; + }; + + //state + this.state = isValidState(initialState) ? initialState : []; + + //components + new DocumentHeader({ $target: $page }); + + const documents = new Documents({ + $target: $page, + initialState: this.state, + onClick: async ({ id, parentId }) => { + const isGoingToPut = getItem(LOCAL_IS_GOING_TO_PUT, false); + + setItem(LOCAL_IS_WAITING_USER_ANSWER, true); + if (!isGoingToPut || confirm("변경사항이 저장되지 않을 수 있습니다.")) { + setItem(LOCAL_IS_PUTTING_OKAY, !isGoingToPut); + routePush( + `/documents/${id}${parentId ? `?parent.id=${parentId}` : ""}` + ); + } + setItem(LOCAL_IS_WAITING_USER_ANSWER, false); + }, + }); + + new DocumentFooter({ $target: $page }); + + this.setState = (nextState) => { + if (!isValidState(nextState)) return; + + this.state = nextState; + + //document set state + documents.setState(this.state); + + if (!isInit) { + this.render(); + isInit = true; + } + }; + + this.render = () => { + $target.appendChild($page); + }; +} diff --git a/src/components/DocumentPage/Documents.js b/src/components/DocumentPage/Documents.js new file mode 100644 index 00000000..549b766a --- /dev/null +++ b/src/components/DocumentPage/Documents.js @@ -0,0 +1,109 @@ +import { + classNameObj, + ERROR_NEW_KEYWORD_MISSING, + LOCAL_STORAGE_DISPLAY, + styleObj, +} from "../utils/constants.js"; +import { hasNewTarget, isValidArray } from "../utils/error.js"; +import { routeCreateDocument, routeRemoveDocument } from "../utils/router.js"; +import { getItem, setItem } from "../utils/storage.js"; +import { + sidebarNewDocumentBtnClick, + sidebarDisplayBtnClick, + createDocumentSection, +} from "./document_util/documentUtil.js"; + +//constants +const { + TITLE, + DISPLAY_BTN, + NEW_BTN, + REMOVE_BTN, + DOCUMENT_BLOCK, + DOCUMENT_LIST_BLOCK, + DOCUMENT_SECTION, + SIDEBAR_DOCUMENT_LIST_CONTAINER, +} = classNameObj; + +export default function Documents({ $target, initialState, onClick }) { + if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); + + //tags + const $sidebar = document.createElement("div"); + $sidebar.setAttribute("id", SIDEBAR_DOCUMENT_LIST_CONTAINER); + $target.appendChild($sidebar); + + //validation + const isValidState = (state) => { + if (!state || !isValidArray(state)) return false; + return true; + }; + + //state + this.state = isValidState(initialState) ? initialState : []; + + const displayArr = getItem( + LOCAL_STORAGE_DISPLAY, + new Map(this.state.map((document) => [document.id, false])) + ); + + this.setState = (nextState) => { + if (!isValidState(nextState)) return; + + this.state = nextState; + this.render(); + }; + + this.render = () => { + $sidebar.innerHTML = ` + ${this.state + .map((rootDocument) => + createDocumentSection( + rootDocument, + styleObj.DEFAULT_PADDING, + displayArr + ) + ) + .join("")} + `; + }; + + this.render(); + + //event handlers + $sidebar.addEventListener("click", (e) => { + const { target } = e; + const { classList } = target; + const { id } = target.closest(`.${DOCUMENT_BLOCK}`).dataset; + const documentListAddBlock = target.closest(`.${DOCUMENT_SECTION}`); + + if (classList[0] === TITLE) { + const parentId = documentListAddBlock.parentNode.dataset.id; + + onClick({ id, parentId }); + } else if (classList[0] === DISPLAY_BTN) { + sidebarDisplayBtnClick(id, target, displayArr); + } else if (classList[0] === NEW_BTN) { + sidebarNewDocumentBtnClick(id, target); + + //펼침 적용 + if (documentListAddBlock) { + const documentListBlock = documentListAddBlock.querySelector( + `.${DOCUMENT_LIST_BLOCK}` + ); + + displayArr[id] = true; + setItem(LOCAL_STORAGE_DISPLAY, displayArr); + documentListBlock.style.display = "block"; + } + + routeCreateDocument({ id }); + } else if (classList[0] === REMOVE_BTN) { + const parentId = documentListAddBlock.parentNode.dataset.id; + routeRemoveDocument({ + id, + parentId, + }); + } + }); +} diff --git a/src/components/DocumentPage/document_util/documentUtil.js b/src/components/DocumentPage/document_util/documentUtil.js new file mode 100644 index 00000000..2ce32d23 --- /dev/null +++ b/src/components/DocumentPage/document_util/documentUtil.js @@ -0,0 +1,154 @@ +import { setItem } from "../../utils/storage.js"; +import { + classNameObj, + styleObj, + DEFAULT_TITLE, + DEFAULT_ID, + LOCAL_STORAGE_DISPLAY, +} from "../../utils/constants.js"; + +//constants +const { + TITLE, + DISPLAY_BTN, + NEW_BTN, + REMOVE_BTN, + DOCUMENT_BLOCK, + DOCUMENT_BLOCK_INNER, + DOCUMENT_LIST_BLOCK, + DOCUMENT_SECTION, +} = classNameObj; + +const { PADDING_INCREMENT } = styleObj; + +//method +export const createDocumentBlock = (id, title, padding) => { + return ` +
+
+
+
${title}
+
+
+
+
+ `; +}; + +export const createDocumentSection = (parentDocument, padding, displayArr) => { + const { id, title, documents } = parentDocument; + + if (id && title !== undefined) { + return ` +
+ ${createDocumentBlock(id, title, padding)} + ${ + documents.length + ? ` +
+ ${documents + .map((document) => + createDocumentSection( + document, + padding + PADDING_INCREMENT, + displayArr + ) + ) + .join("")} +
+ ` + : "" + } +
+ `; + } +}; + +const createNewHTML = (origin, documentArgs) => { + const { id, title, padding } = documentArgs; + + return ` + ${origin} +
+ ${createDocumentBlock(id, title, padding)} +
+ `; +}; + +export const sidebarDisplayBtnClick = (id, target, displayArr) => { + const documentListBlock = target + .closest(`.${DOCUMENT_SECTION}`) + .querySelector(`.${DOCUMENT_LIST_BLOCK}`); + + if (!documentListBlock) return; + + const { display } = documentListBlock.style; + + if (display === "none") { + displayArr[id] = true; + documentListBlock.style.display = "block"; + } else { + displayArr[id] = false; + documentListBlock.style.display = "none"; + } + setItem(LOCAL_STORAGE_DISPLAY, displayArr); +}; + +const addNewDocumentBlock = (documentListBlock, paddingLeft) => { + //document-list-block 가져와서 맨 마지막에 document-block 삽입하기 + //낙관적 업데이트로 보여주기 + const innerTags = documentListBlock.innerHTML; + + documentListBlock.innerHTML = ` + ${createNewHTML(innerTags.substring(0, innerTags.length - 5), { + id: DEFAULT_ID, + title: DEFAULT_TITLE, + padding: + parseInt(paddingLeft.substring(0, paddingLeft.length - 2)) + + PADDING_INCREMENT, + })} + + `; +}; + +//document-list가 없어서 새로 생성해야 하는 경우 +const addNewDocumentList = (id, documentListAddBlock, paddingLeft) => { + const origin = ` + ${documentListAddBlock.innerHTML} +
+ `; + + documentListAddBlock.innerHTML = ` + ${createNewHTML(origin, { + id: DEFAULT_ID, + title: DEFAULT_TITLE, + padding: + parseInt(paddingLeft.substring(0, paddingLeft.length - 2)) + + PADDING_INCREMENT, + })} +
+ `; +}; + +const sidebarSubDocumentBtnClick = ( + id, + documentListBlock, + paddingLeft, + target +) => { + if (documentListBlock) { + addNewDocumentBlock(documentListBlock, paddingLeft); + } else { + const documentListAddBlock = target.closest(`.${DOCUMENT_SECTION}`); + addNewDocumentList(id, documentListAddBlock, paddingLeft); + } +}; + +export const sidebarNewDocumentBtnClick = (id, target) => { + const documentListBlock = target + .closest(`.${DOCUMENT_SECTION}`) + .querySelector(`.${DOCUMENT_LIST_BLOCK}`); + const { paddingLeft } = target.closest(`.${DOCUMENT_BLOCK_INNER}`).style; + sidebarSubDocumentBtnClick(id, documentListBlock, paddingLeft, target); +}; From a343c419b6b315d03f5e1c58b43fc57a62b4c31f Mon Sep 17 00:00:00 2001 From: Kal-MH Date: Tue, 15 Nov 2022 15:56:23 +0900 Subject: [PATCH 06/19] =?UTF-8?q?Add=20files:=20DeleteButton=20-=20Editor?= =?UTF-8?q?=20Topbar=EC=97=90=20=EB=93=A4=EC=96=B4=EA=B0=80=EB=8A=94=20del?= =?UTF-8?q?eteButton=EC=9C=BC=EB=A1=9C=20=ED=81=B4=EB=A6=AD=EC=8B=9C=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=EA=B0=80=20?= =?UTF-8?q?=EB=B0=9C=EC=83=9D=ED=95=9C=EB=8B=A4.=20=20=20-=20=ED=95=B4?= =?UTF-8?q?=EB=8B=B9=20id=EC=9D=98=20=EB=AC=B8=EC=84=9C=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=20=20-=20=EB=B6=80=EB=AA=A8=20=EB=AC=B8=EC=84=9C?= =?UTF-8?q?=EA=B0=80=20=EC=9E=88=EC=9C=BC=EB=A9=B4=20=ED=95=B4=EB=8B=B9=20?= =?UTF-8?q?=EB=B6=80=EB=AA=A8=20id=EB=A1=9C=20url=20=EB=B0=8F=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EB=B3=80=EA=B2=BD=20=20=20-=20=EB=B6=80=EB=AA=A8?= =?UTF-8?q?=20=EB=AC=B8=EC=84=9C=EA=B0=80=20=EC=97=86=EC=9C=BC=EB=A9=B4(ro?= =?UTF-8?q?ot=20document=EC=9D=B4=EB=A9=B4)=20/=EB=A1=9C=20=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/App.js | 52 ++++++++++ src/components/Button/DeleteButton.js | 40 ++++++++ src/fakeDB.js | 138 -------------------------- src/main.js | 30 +----- 4 files changed, 96 insertions(+), 164 deletions(-) create mode 100644 src/components/App.js create mode 100644 src/components/Button/DeleteButton.js delete mode 100644 src/fakeDB.js diff --git a/src/components/App.js b/src/components/App.js new file mode 100644 index 00000000..f8244cef --- /dev/null +++ b/src/components/App.js @@ -0,0 +1,52 @@ +import { + DEFAULT_STATE, + DEFAULT_TITLE, + DISABLED_ID, + ERROR_NEW_KEYWORD_MISSING, + LOCAL_STORAGE_RECENT_DOCUMENT, + ROOT_TITLE, +} from "./utils/constants.js"; +import { hasNewTarget } from "./utils/error.js"; +import DocumentPage from "./DocumentPage/DocumentPage.js"; +import EditPage from "./EditPage/EditPage.js"; +import { getItem } from "./utils/storage.js"; +import { request } from "../api/api.js"; +import { initRouter } from "./utils/router.js"; + +export default function App({ $target }) { + if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); + + const documentPage = new DocumentPage({ + $target, + initialState: [], + }); + + const editPage = new EditPage({ + $target, + initialState: { + id: DISABLED_ID, + title: DEFAULT_TITLE, + content: "", + }, + }); + + this.route = async () => { + const { pathname, search } = location; + const documentsList = await request("/documents"); + + documentPage.setState(documentsList); + if (pathname === "/") { + editPage.setState(DEFAULT_STATE); + } else if (pathname.indexOf("/documents") === 0) { + const [, , documentId] = pathname.split("/"); + const [, parentId] = search.split("="); + const document = await request(`/documents/${documentId}`); + + editPage.setState({ ...document, parentId }); + } + }; + + this.route(); + + initRouter(() => this.route()); +} diff --git a/src/components/Button/DeleteButton.js b/src/components/Button/DeleteButton.js new file mode 100644 index 00000000..ca9b2383 --- /dev/null +++ b/src/components/Button/DeleteButton.js @@ -0,0 +1,40 @@ +import { hasNewTarget, hasId } from "../utils/error.js"; +import { + DELETE_BUTTON_TEXT, + ERROR_NEW_KEYWORD_MISSING, +} from "../utils/constants.js"; +import { routeRemoveDocument } from "../utils/router.js"; + +export default function DeleteButton({ $target, initialState }) { + if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); + + //tags + const $button = document.createElement("button"); + $target.appendChild($button); + + const isValidState = (state) => { + if (!state || !hasId(state)) return false; + return true; + }; + + this.state = isValidState(initialState) ? initialState : {}; + + this.setState = (initialState) => { + this.state = initialState; + }; + + this.render = () => { + $button.textContent = DELETE_BUTTON_TEXT; + }; + + this.render(); + + //event handler + $button.addEventListener("click", async () => { + const { id, parentId } = this.state; + routeRemoveDocument({ + id, + parentId, + }); + }); +} diff --git a/src/fakeDB.js b/src/fakeDB.js deleted file mode 100644 index 8afc090b..00000000 --- a/src/fakeDB.js +++ /dev/null @@ -1,138 +0,0 @@ -//전체 Documents - -export const documentsList = [ - { - id: 1, // Document id - title: "노션을 만들자", // Document title - documents: [ - { - id: 2, - title: "블라블라", - documents: [ - { - id: 3, - title: "함냐함냐", - documents: [], - }, - { - id: 4, - title: "함냐함냐2", - documents: [], - }, - ], - }, - { - id: 5, - title: "함냐함냐5", - documents: [], - }, - ], - }, - { - id: 6, - title: "hello!", - documents: [ - { - id: 7, - title: "함냐함냐7", - documents: [], - }, - ], - }, -]; - -export const documentId1 = { - id: 1, - title: "노션을 만들자", - content: "즐거운 자바스크립트의 세계!", - documents: [ - { - id: 2, - title: "블라블라", - createdAt: "11.11", - updatedAt: "11.11", - }, - { - id: 5, - title: "함냐함냐5", - createdAt: "11.11", - updatedAt: "11.11", - }, - ], - createdAt: "11.10", - updatedAt: "11.10", -}; - -export const documentId2 = { - id: 2, - title: "블라블라", - content: "blahblah~~~", - documents: [ - { - id: 3, - title: "함냐함냐", - createdAt: "11.11", - updatedAt: "11.11", - }, - { - id: 4, - title: "함냐함냐2", - createdAt: "11.11", - updatedAt: "11.11", - }, - ], - createdAt: "11.11", - updatedAt: "11.11", -}; - -export const documentId3 = { - id: 3, - title: "함냐함냐", - content: "함냐함냐함 아~ 맛있다", - documents: [], - createdAt: "11.11", - updatedAt: "11.11", -}; - -export const documentId4 = { - id: 4, - title: "함냐함냐2", - content: "함냐함냐함 함냐함냐함 아~ 맛있다", - documents: [], - createdAt: "11.11", - updatedAt: "11.11", -}; - -export const documentId5 = { - id: 5, - title: "함냐함냐5", - content: "함냐함냐함 아~ 맛있다5555555", - documents: [], - createdAt: "11.11", - updatedAt: "11.11", -}; - -export const documentId6 = { - id: 6, - title: "hello!", - content: "Nice to see you", - documents: [ - { - id: 7, - title: "함냐함냐7", - createdAt: "11.11", - updatedAt: "11.11", - }, - ], - createdAt: "11.11", - updatedAt: "11.11", -}; - -export const documentId7 = { - id: 7, - title: "함냐함냐7", - content: "함냐함냐함 아~ 맛있다777777", - documents: [], - createdAt: "11.11", - updatedAt: "11.11", -}; diff --git a/src/main.js b/src/main.js index a31baa62..7921075d 100644 --- a/src/main.js +++ b/src/main.js @@ -1,29 +1,7 @@ -// import DocumentPage from "./components/DocumentPage/DocumentPage.js"; -import EditPage from "./components/EditPage/EditPage.js"; -import { documentId1, documentsList } from "./fakeDb.js"; +import App from "./components/App.js"; const $container = document.querySelector("#container"); -// const documentPage = new DocumentPage({ -// $target: $container, -// initialState: [], -// }); - -// documentPage.setState(documentsList); - -// const editPage = new EditPage({ -// $target: $container, -// initialState: { -// title: "", -// content: "", -// }, -// }); - -//documentId1을 불러왔을 때 -editPage.setState(documentId1); - -//새로운 document를 불러왔을 때 -// editPage.setState({ -// title: "", -// content: "", -// }); +new App({ + $target: $container, +}); From 87a2175dd4f9b164febb428c75f68d746406d599 Mon Sep 17 00:00:00 2001 From: Kal-MH Date: Tue, 15 Nov 2022 18:44:21 +0900 Subject: [PATCH 07/19] Sytle: Css styling --- css/style.css | 226 ++++++++++++++---- src/components/DocumentPage/DocumentFooter.js | 11 +- src/components/DocumentPage/DocumentHeader.js | 11 +- .../document_util/documentUtil.js | 43 +--- src/components/utils/constants.js | 4 +- 5 files changed, 204 insertions(+), 91 deletions(-) diff --git a/css/style.css b/css/style.css index b57bf524..8a3df9c4 100644 --- a/css/style.css +++ b/css/style.css @@ -11,10 +11,9 @@ COMMON #container { height: 100vh; margin-bottom: 4rem; - padding: 1rem 2.5rem 2.5rem; display: flex; flex: 1 1 0%; - justify-content: space-between; + background: white; } #sidebar-container { @@ -22,108 +21,255 @@ COMMON height: 100%; flex-grow: 0; flex-shrink: 0; - border: 2px solid black; + background: rgba(251, 251, 250); + box-shadow: rgb(0, 0, 0 / 2%) -1px 0px 0px 0px inset; + color: rgba(25, 23, 17, 0.6); + font-weight: 500; + + display: flex; + flex-direction: column; } #editor-container { width: 100%; height: 100%; - border: 2px solid black; display: flex; flex-direction: column; + overflow: hidden; } /* -EDITOR-PAGE -============================= */ +SIDEBAR_PAGE +=============================*/ -/* HEADER */ -header { - padding: 2.5rem 3rem; +/* Header */ +#sidebar-header { + display: flex; + align-items: center; + height: 45px; + width: 100%; + cursor: pointer; +} + +#sidebar-header:hover { + background: rgba(55, 53, 47, 0.08); +} + +#sidebar-header .document-block-inner { + width: 100%; + min-height: 27px; + display: flex; + justify-content: space-between; + align-items: center; + font-size: 1.25rem; + margin: 1px auto; + overflow: hidden; +} + +#sidebar-header .document-block-inner .title { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +#sidebar-header .document-block-inner .new-btn::after { flex-shrink: 0; flex-grow: 0; + width: 22px; + height: 22px; + margin-right: 3px; + display: flex; + align-items: center; + justify-content: center; + /* Font Awesome */ + font-family: FontAwesome; + content: "\2b"; + font-weight: 900; + color: #949087; } -header input { +/* Documents */ +#sidebar-document-list-container { + padding: 6px 0 20px 0; + box-shadow: rgb(55 53 47 / 9%) 0px 1px 0px inset; + overflow: hidden auto; + display: flex; + flex-direction: column; + margin-bottom: 1.4rem; +} + +#sidebar-document-list-container .document-section .document-block { width: 100%; - font-size: 2.5rem; + color: inherit; + cursor: pointer; + overflow: hidden; } -/* EDITOR */ -#editor-container .editor { - height: 100%; - padding: 1rem 3rem; +#sidebar-document-list-container .document-list-block { + overflow: hidden; } -#editor-container .editor textarea { - width: 100%; - height: 100%; +#sidebar-document-list-container .document-block:hover .remove-btn, +#sidebar-document-list-container .document-block:hover .new-btn { + display: block; } -/* -DOCUMENT-PAGE -============================== */ +#sidebar-document-list-container .document-block:hover .title-wrapper { + padding-right: 30px; + text-overflow: clip; +} -/* DOCUMENT BLOCK */ -.document-block { - margin-left: 5px; - margin-right: 5px; - font-size: 0.9rem; +#sidebar-document-list-container .documen-section { + width: 100%; } -.document-block-inner { +#sidebar-document-list-container .document-block-inner { width: 100%; padding: 2px 10px 2px 5px; margin-top: 1px; margin-bottom: 1px; display: flex; + position: relative; overflow: hidden; - text-overflow: clip; + flex: 1 1 auto; } -.document-block-inner .display-btn::before { +#sidebar-document-list-container .document-block-inner .title-wrapper { + display: flex; + align-items: center; + flex: 1 1 auto; + width: 100%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +#sidebar-document-list-container .document-block-inner .title { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +#sidebar-document-list-container .document-block-inner .display-btn::before { flex-shrink: 0; flex-grow: 0; width: 22px; height: 22px; margin-right: 3px; + display: flex; + align-items: center; + justify-content: center; + /* Font Awesome */ + font-family: FontAwesome; + content: "\f054"; + font-weight: 500; + color: rgba(25, 23, 17, 0.6); + transform: rotateZ(0deg); +} + +#sidebar-document-list-container .document-block-inner .remove-btn::after { + flex-shrink: 0; + flex-grow: 0; + width: 18px; + height: 18px; + display: flex; + margin-right: 3px; + align-items: center; + justify-content: center; /* Font Awesome */ font-family: FontAwesome; - content: "\f1b0"; + content: "\f1f8"; font-weight: 900; color: #949087; } -.document-block-inner .remove-btn::after { +#sidebar-document-list-container .document-block-inner .remove-btn { + display: none; + position: absolute; + right: 22px; +} + +#sidebar-document-list-container .document-block-inner .new-btn::after { flex-shrink: 0; flex-grow: 0; width: 22px; height: 22px; margin-right: 3px; + display: flex; + align-items: center; + justify-content: center; /* Font Awesome */ font-family: FontAwesome; - content: "\f1b0"; + content: "\2b"; font-weight: 900; color: #949087; } -.document-block-inner .new-btn::after { +#sidebar-document-list-container .document-block-inner .new-btn { + display: none; + position: absolute; + right: 0; +} + +/* Document Footer */ +#sidebar-footer { + flex-shrink: 0; + flex-grow: 0; + margin-top: auto; + cursor: pointer; + box-shadow: rgb(55 53 47 / 9%) 0px -1px 0px; +} + +#sidebar-footer .document-block-inner { + width: 100%; + height: 45px; + min-height: 27px; + display: flex; + align-items: center; + font-size: 1.25rem; + margin: 1px auto; + color: rgba(55, 53, 47, 0.65); +} + +#sidebar-footer .document-block-inner .new-btn::after { flex-shrink: 0; flex-grow: 0; width: 22px; height: 22px; margin-right: 3px; + display: flex; + align-items: center; + justify-content: center; /* Font Awesome */ font-family: FontAwesome; - content: "\f1b0"; + content: "\2b"; font-weight: 900; color: #949087; } -.document-block-inner .title { - align-items: center; - display: flex; - flex: 1 1 auto; - overflow: hidden; - text-overflow: clip; +/* +EDITOR-PAGE +============================= */ + +/* HEADER */ +header { + padding: 2.5rem 3rem; + flex-shrink: 0; + flex-grow: 0; +} + +header input { + width: 100%; + font-size: 2.5rem; +} + +/* EDITOR */ +#editor-container .editor { + height: 100%; + padding: 1rem 3rem; +} + +#editor-container .editor textarea { + width: 100%; + height: 100%; } diff --git a/src/components/DocumentPage/DocumentFooter.js b/src/components/DocumentPage/DocumentFooter.js index bb5fb736..7df2d031 100644 --- a/src/components/DocumentPage/DocumentFooter.js +++ b/src/components/DocumentPage/DocumentFooter.js @@ -1,13 +1,8 @@ -import { - classNameObj, - DOCUMENT_FOOTER_CONTENT, - ERROR_NEW_KEYWORD_MISSING, - styleObj, -} from "../utils/constants.js"; +import { classNameObj, DOCUMENT_FOOTER_CONTENT, ERROR_NEW_KEYWORD_MISSING, styleObj } from "../utils/constants.js"; import { hasNewTarget } from "../utils/error.js"; import { routeCreateDocument } from "../utils/router.js"; -const { NEW_BTN, DOCUMENT_BLOCK_INNER } = classNameObj; +const { TITLE, NEW_BTN, DOCUMENT_BLOCK_INNER } = classNameObj; export default function DocumentFooter({ $target }) { if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); @@ -22,8 +17,8 @@ export default function DocumentFooter({ $target }) { this.render = () => { $footer.innerHTML = `
-
${CONTENT}
+
${CONTENT}
`; }; diff --git a/src/components/DocumentPage/DocumentHeader.js b/src/components/DocumentPage/DocumentHeader.js index 39905845..30f73952 100644 --- a/src/components/DocumentPage/DocumentHeader.js +++ b/src/components/DocumentPage/DocumentHeader.js @@ -1,13 +1,8 @@ -import { - classNameObj, - DOCUMENT_HEADER_CONTENT, - ERROR_NEW_KEYWORD_MISSING, - styleObj, -} from "../utils/constants.js"; +import { classNameObj, DOCUMENT_HEADER_CONTENT, ERROR_NEW_KEYWORD_MISSING, styleObj } from "../utils/constants.js"; import { hasNewTarget } from "../utils/error.js"; import { routeCreateDocument } from "../utils/router.js"; -const { DOCUMENT_BLOCK, DOCUMENT_BLOCK_INNER, NEW_BTN } = classNameObj; +const { TITLE, DOCUMENT_BLOCK_INNER, NEW_BTN } = classNameObj; export default function DocumentHeader({ $target }) { if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); @@ -22,7 +17,7 @@ export default function DocumentHeader({ $target }) { this.render = () => { $header.innerHTML = `
-
${CONTENT}
+
${CONTENT}
`; diff --git a/src/components/DocumentPage/document_util/documentUtil.js b/src/components/DocumentPage/document_util/documentUtil.js index 2ce32d23..8c56e2b3 100644 --- a/src/components/DocumentPage/document_util/documentUtil.js +++ b/src/components/DocumentPage/document_util/documentUtil.js @@ -1,11 +1,5 @@ import { setItem } from "../../utils/storage.js"; -import { - classNameObj, - styleObj, - DEFAULT_TITLE, - DEFAULT_ID, - LOCAL_STORAGE_DISPLAY, -} from "../../utils/constants.js"; +import { classNameObj, styleObj, DEFAULT_TITLE, DEFAULT_ID, LOCAL_STORAGE_DISPLAY } from "../../utils/constants.js"; //constants const { @@ -27,7 +21,9 @@ export const createDocumentBlock = (id, title, padding) => {
-
${title}
+
+
${title}
+
@@ -48,13 +44,7 @@ export const createDocumentSection = (parentDocument, padding, displayArr) => {
${documents - .map((document) => - createDocumentSection( - document, - padding + PADDING_INCREMENT, - displayArr - ) - ) + .map((document) => createDocumentSection(document, padding + PADDING_INCREMENT, displayArr)) .join("")}
` @@ -77,9 +67,7 @@ const createNewHTML = (origin, documentArgs) => { }; export const sidebarDisplayBtnClick = (id, target, displayArr) => { - const documentListBlock = target - .closest(`.${DOCUMENT_SECTION}`) - .querySelector(`.${DOCUMENT_LIST_BLOCK}`); + const documentListBlock = target.closest(`.${DOCUMENT_SECTION}`).querySelector(`.${DOCUMENT_LIST_BLOCK}`); if (!documentListBlock) return; @@ -104,9 +92,7 @@ const addNewDocumentBlock = (documentListBlock, paddingLeft) => { ${createNewHTML(innerTags.substring(0, innerTags.length - 5), { id: DEFAULT_ID, title: DEFAULT_TITLE, - padding: - parseInt(paddingLeft.substring(0, paddingLeft.length - 2)) + - PADDING_INCREMENT, + padding: parseInt(paddingLeft.substring(0, paddingLeft.length - 2)) + PADDING_INCREMENT, })}
`; @@ -123,20 +109,13 @@ const addNewDocumentList = (id, documentListAddBlock, paddingLeft) => { ${createNewHTML(origin, { id: DEFAULT_ID, title: DEFAULT_TITLE, - padding: - parseInt(paddingLeft.substring(0, paddingLeft.length - 2)) + - PADDING_INCREMENT, + padding: parseInt(paddingLeft.substring(0, paddingLeft.length - 2)) + PADDING_INCREMENT, })} `; }; -const sidebarSubDocumentBtnClick = ( - id, - documentListBlock, - paddingLeft, - target -) => { +const sidebarSubDocumentBtnClick = (id, documentListBlock, paddingLeft, target) => { if (documentListBlock) { addNewDocumentBlock(documentListBlock, paddingLeft); } else { @@ -146,9 +125,7 @@ const sidebarSubDocumentBtnClick = ( }; export const sidebarNewDocumentBtnClick = (id, target) => { - const documentListBlock = target - .closest(`.${DOCUMENT_SECTION}`) - .querySelector(`.${DOCUMENT_LIST_BLOCK}`); + const documentListBlock = target.closest(`.${DOCUMENT_SECTION}`).querySelector(`.${DOCUMENT_LIST_BLOCK}`); const { paddingLeft } = target.closest(`.${DOCUMENT_BLOCK_INNER}`).style; sidebarSubDocumentBtnClick(id, documentListBlock, paddingLeft, target); }; diff --git a/src/components/utils/constants.js b/src/components/utils/constants.js index f46d0668..90158a0c 100644 --- a/src/components/utils/constants.js +++ b/src/components/utils/constants.js @@ -29,8 +29,8 @@ export const styleObj = { PADDING_INCREMENT: 20, }; -export const DOCUMENT_HEADER_CONTENT = "워크 스페이스"; -export const DOCUMENT_FOOTER_CONTENT = "새 문서 만들기"; +export const DOCUMENT_HEADER_CONTENT = "Notion"; +export const DOCUMENT_FOOTER_CONTENT = "New Doc"; export const DISABLED_ID = -1; export const DEFAULT_ID = "new"; From db430498277a4bc358b709992773dc0f60d14602 Mon Sep 17 00:00:00 2001 From: Kal-MH Date: Tue, 15 Nov 2022 22:57:10 +0900 Subject: [PATCH 08/19] Style: add Css Styling with editor component --- css/style.css | 225 +++++++++++++----- src/api/api.js | 4 +- src/components/Button/DeleteButton.js | 17 +- src/components/DocumentPage/DocumentFooter.js | 10 +- src/components/DocumentPage/DocumentHeader.js | 10 +- src/components/DocumentPage/DocumentPage.js | 23 +- src/components/DocumentPage/Documents.js | 46 ++-- .../document_util/documentUtil.js | 10 +- src/components/EditPage/EditPage.js | 25 +- src/components/EditPage/Editor.js | 16 +- src/components/EditPage/Header.js | 18 +- src/components/EditPage/Topbar.js | 12 +- src/components/utils/constants.js | 19 +- src/components/utils/router.js | 3 +- 14 files changed, 257 insertions(+), 181 deletions(-) diff --git a/css/style.css b/css/style.css index 8a3df9c4..e41fd905 100644 --- a/css/style.css +++ b/css/style.css @@ -1,3 +1,21 @@ +/* +Variables +============================ */ +:root { + --box-shadow: rgb(0 0 0 / 2%) -1px 0px 0px 0px inset; + --box-shadow-inset: rgb(55 53 47 / 9%) 0px 1px 0px inset; + --box-shadow-top: rgb(55 53 47 / 9%) 0px -1px 0px; + --background: rgba(251, 251, 250); + --hover-background: rgba(55, 53, 47, 0.08); + --font-color: rgba(25, 23, 17, 0.6); + --font-color-2: rgba(25, 23, 17, 0.85); + --header-color: rgb(55, 53, 47); + --btn-color: #949087; + --display-icon: "\f054"; + --remove-icon: "\f1f8"; + --new-icon: "\2b"; +} + /* GLOBAL ============================ */ @@ -21,10 +39,10 @@ COMMON height: 100%; flex-grow: 0; flex-shrink: 0; - background: rgba(251, 251, 250); - box-shadow: rgb(0, 0, 0 / 2%) -1px 0px 0px 0px inset; - color: rgba(25, 23, 17, 0.6); - font-weight: 500; + background: var(--background); + box-shadow: var(--box-shadow); + color: var(--font-color); + font-weight: 800; display: flex; flex-direction: column; @@ -52,7 +70,7 @@ SIDEBAR_PAGE } #sidebar-header:hover { - background: rgba(55, 53, 47, 0.08); + background: var(--hover-background); } #sidebar-header .document-block-inner { @@ -83,91 +101,130 @@ SIDEBAR_PAGE justify-content: center; /* Font Awesome */ font-family: FontAwesome; - content: "\2b"; + content: var(--new-icon); font-weight: 900; - color: #949087; + color: var(--btn-color); } /* Documents */ -#sidebar-document-list-container { - padding: 6px 0 20px 0; - box-shadow: rgb(55 53 47 / 9%) 0px 1px 0px inset; +#sidebar-list-container { + padding: 6px 5px 20px 0; + box-shadow: var(--box-shadow-inset); overflow: hidden auto; display: flex; flex-direction: column; margin-bottom: 1.4rem; + font-weight: 500; } -#sidebar-document-list-container .document-section .document-block { - width: 100%; +#sidebar-list-container .document-block { color: inherit; cursor: pointer; overflow: hidden; + margin-left: 5px; + border-radius: 3px; } -#sidebar-document-list-container .document-list-block { - overflow: hidden; +#sidebar-list-container .document-block:hover { + background-color: var(--hover-background); } -#sidebar-document-list-container .document-block:hover .remove-btn, -#sidebar-document-list-container .document-block:hover .new-btn { - display: block; +#sidebar-list-container .document-block:hover .remove-btn, +#sidebar-list-container .document-block:hover .new-btn { + display: flex; + /* align-items: center; */ + /*justify-content: center;*/ } -#sidebar-document-list-container .document-block:hover .title-wrapper { - padding-right: 30px; +#sidebar-list-container .document-block:hover .title-wrapper { + padding-right: 3rem; text-overflow: clip; } -#sidebar-document-list-container .documen-section { +#sidebar-list-container .documen-section { width: 100%; } -#sidebar-document-list-container .document-block-inner { +#sidebar-list-container .document-block-inner { width: 100%; padding: 2px 10px 2px 5px; + min-height: 27px; margin-top: 1px; margin-bottom: 1px; display: flex; - position: relative; overflow: hidden; flex: 1 1 auto; + position: relative; } -#sidebar-document-list-container .document-block-inner .title-wrapper { +#sidebar-list-container .document-block-inner .title-wrapper { + width: 100%; display: flex; align-items: center; - flex: 1 1 auto; - width: 100%; - white-space: nowrap; + flex-grow: 0; + flex-shrink: 0; + padding-right: 10px; overflow: hidden; + font-size: 1rem; + white-space: nowrap; text-overflow: ellipsis; + line-height: 1.4rem; + text-align: center; } -#sidebar-document-list-container .document-block-inner .title { +#sidebar-list-container .document-block-inner .title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } -#sidebar-document-list-container .document-block-inner .display-btn::before { - flex-shrink: 0; - flex-grow: 0; - width: 22px; - height: 22px; - margin-right: 3px; +#sidebar-list-container .document-block-inner .display-btn { display: flex; align-items: center; justify-content: center; + width: 20px; + height: 20px; +} + +#sidebar-list-container .document-block-inner .btn-wrapper { + display: flex; + align-items: center; + justify-content: center; + position: absolute; + right: 0; + padding-top: 4px; +} + +#sidebar-list-container .document-block-inner .remove-btn, +#sidebar-list-container .document-block-inner .new-btn { + display: none; + width: 20px; + height: 20px; + padding-right: 3px; +} +/* +#sidebar-list-container .document-block-inner .remove-btn { + right: 22px; +} + +#sidebar-list-container .document-block-inner .new-btn { + right: 0; +} */ + +#sidebar-list-container .document-block-inner .display-btn::before { + flex-shrink: 0; + flex-grow: 0; + width: 18px; + height: 18px; /* Font Awesome */ font-family: FontAwesome; - content: "\f054"; + content: var(--display-icon); font-weight: 500; - color: rgba(25, 23, 17, 0.6); + color: var(--btn-color); transform: rotateZ(0deg); } -#sidebar-document-list-container .document-block-inner .remove-btn::after { +#sidebar-list-container .document-block-inner .remove-btn::after { flex-shrink: 0; flex-grow: 0; width: 18px; @@ -178,37 +235,25 @@ SIDEBAR_PAGE justify-content: center; /* Font Awesome */ font-family: FontAwesome; - content: "\f1f8"; + content: var(--remove-icon); font-weight: 900; - color: #949087; + color: var(--btn-color); } -#sidebar-document-list-container .document-block-inner .remove-btn { - display: none; - position: absolute; - right: 22px; -} - -#sidebar-document-list-container .document-block-inner .new-btn::after { +#sidebar-list-container .document-block-inner .new-btn::after { flex-shrink: 0; flex-grow: 0; - width: 22px; - height: 22px; + width: 18px; + height: 18px; margin-right: 3px; display: flex; align-items: center; justify-content: center; /* Font Awesome */ font-family: FontAwesome; - content: "\2b"; + content: var(--new-icon); font-weight: 900; - color: #949087; -} - -#sidebar-document-list-container .document-block-inner .new-btn { - display: none; - position: absolute; - right: 0; + color: var(--btn-color); } /* Document Footer */ @@ -217,7 +262,11 @@ SIDEBAR_PAGE flex-grow: 0; margin-top: auto; cursor: pointer; - box-shadow: rgb(55 53 47 / 9%) 0px -1px 0px; + box-shadow: var(--box-shadow-top); +} + +#sidebar-footer:hover { + background-color: var(--hover-background); } #sidebar-footer .document-block-inner { @@ -228,7 +277,7 @@ SIDEBAR_PAGE align-items: center; font-size: 1.25rem; margin: 1px auto; - color: rgba(55, 53, 47, 0.65); + color: var(--font-color); } #sidebar-footer .document-block-inner .new-btn::after { @@ -242,34 +291,84 @@ SIDEBAR_PAGE justify-content: center; /* Font Awesome */ font-family: FontAwesome; - content: "\2b"; + content: var(--new-icon); font-weight: 900; - color: #949087; + color: var(--btn-color); } /* EDITOR-PAGE ============================= */ +/* TOPBAR */ +#editor-container .topbar { + height: 70px; + max-width: 100vw; + background: white; + opacity: 1; + position: relative; + padding: 1rem 3rem; + display: flex; + justify-content: end; +} + +#editor-container .topbar div { + display: block; + padding: 5px 10px; + border-radius: 3px; + border: 1px solid var(--btn-color); + background-color: var(--hover-background); + color: var(--font-color-2); +} + +#editor-container .topbar div:hover { + border: 1px solid var(--hover-background); + background-color: var(--btn-color); + color: var(--font-color-2); +} + +#editor-container .topbar div::after { + /* Font Awesome */ + font-family: FontAwesome; + content: var(--remove-icon); + font-weight: 900; + color: var(--font-color); +} + /* HEADER */ header { - padding: 2.5rem 3rem; + padding: 3rem 8rem 1rem 8rem; + width: 100%; flex-shrink: 0; flex-grow: 0; } +header .input-wrapper { + margin-top: 5rem; +} + header input { - width: 100%; - font-size: 2.5rem; + height: 60px; + width: 900px; + font-size: 40px; + line-height: 1.2; + font-weight: 700; +} + +header input::placeholder { + color: var(--btn-color); } /* EDITOR */ #editor-container .editor { height: 100%; - padding: 1rem 3rem; + padding: 3rem 8rem; + font-size: 1.2rem; } #editor-container .editor textarea { width: 100%; height: 100%; + border: none; + resize: none; } diff --git a/src/api/api.js b/src/api/api.js index 1028da74..6ae5991b 100644 --- a/src/api/api.js +++ b/src/api/api.js @@ -1,5 +1,5 @@ import { ERROR_API_CALL } from "../components/utils/constants.js"; -import { API_END_POINT } from "./api_constant.js"; +import { API_END_POINT, USERNAME } from "./api_constant.js"; export const request = async (url, options = {}) => { try { @@ -7,7 +7,7 @@ export const request = async (url, options = {}) => { ...options, headers: { "Content-Type": "application/json", - "x-username": "kal", + "x-username": `${USERNAME}`, }, }); diff --git a/src/components/Button/DeleteButton.js b/src/components/Button/DeleteButton.js index ca9b2383..6fe17dde 100644 --- a/src/components/Button/DeleteButton.js +++ b/src/components/Button/DeleteButton.js @@ -1,15 +1,12 @@ import { hasNewTarget, hasId } from "../utils/error.js"; -import { - DELETE_BUTTON_TEXT, - ERROR_NEW_KEYWORD_MISSING, -} from "../utils/constants.js"; +import { DELETE_BUTTON_TEXT, DISABLED_ID, ERROR_NEW_KEYWORD_MISSING } from "../utils/constants.js"; import { routeRemoveDocument } from "../utils/router.js"; export default function DeleteButton({ $target, initialState }) { if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); //tags - const $button = document.createElement("button"); + const $button = document.createElement("div"); $target.appendChild($button); const isValidState = (state) => { @@ -21,14 +18,14 @@ export default function DeleteButton({ $target, initialState }) { this.setState = (initialState) => { this.state = initialState; - }; - this.render = () => { - $button.textContent = DELETE_BUTTON_TEXT; + if (this.state.id === DISABLED_ID) { + $button.style.display = "none"; + } else { + $button.style.display = "block"; + } }; - this.render(); - //event handler $button.addEventListener("click", async () => { const { id, parentId } = this.state; diff --git a/src/components/DocumentPage/DocumentFooter.js b/src/components/DocumentPage/DocumentFooter.js index 7df2d031..d4bdcddb 100644 --- a/src/components/DocumentPage/DocumentFooter.js +++ b/src/components/DocumentPage/DocumentFooter.js @@ -1,4 +1,10 @@ -import { classNameObj, DOCUMENT_FOOTER_CONTENT, ERROR_NEW_KEYWORD_MISSING, styleObj } from "../utils/constants.js"; +import { + classNameObj, + styleObj, + idNameObj, + DOCUMENT_FOOTER_CONTENT, + ERROR_NEW_KEYWORD_MISSING, +} from "../utils/constants.js"; import { hasNewTarget } from "../utils/error.js"; import { routeCreateDocument } from "../utils/router.js"; @@ -8,7 +14,7 @@ export default function DocumentFooter({ $target }) { if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); const $footer = document.createElement("div"); - $footer.setAttribute("id", "sidebar-footer"); + $footer.setAttribute("id", idNameObj.SIDEBAR_FOOTER); $target.appendChild($footer); const CONTENT = DOCUMENT_FOOTER_CONTENT; diff --git a/src/components/DocumentPage/DocumentHeader.js b/src/components/DocumentPage/DocumentHeader.js index 30f73952..6828ce79 100644 --- a/src/components/DocumentPage/DocumentHeader.js +++ b/src/components/DocumentPage/DocumentHeader.js @@ -1,4 +1,10 @@ -import { classNameObj, DOCUMENT_HEADER_CONTENT, ERROR_NEW_KEYWORD_MISSING, styleObj } from "../utils/constants.js"; +import { + classNameObj, + styleObj, + idNameObj, + DOCUMENT_HEADER_CONTENT, + ERROR_NEW_KEYWORD_MISSING, +} from "../utils/constants.js"; import { hasNewTarget } from "../utils/error.js"; import { routeCreateDocument } from "../utils/router.js"; @@ -8,7 +14,7 @@ export default function DocumentHeader({ $target }) { if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); const $header = document.createElement("div"); - $header.setAttribute("id", "sidebar-header"); + $header.setAttribute("id", idNameObj.SIDEBAR_HEADER); $target.appendChild($header); const CONTENT = DOCUMENT_HEADER_CONTENT; diff --git a/src/components/DocumentPage/DocumentPage.js b/src/components/DocumentPage/DocumentPage.js index 118c923a..40780540 100644 --- a/src/components/DocumentPage/DocumentPage.js +++ b/src/components/DocumentPage/DocumentPage.js @@ -1,12 +1,5 @@ -import { - ERROR_NEW_KEYWORD_MISSING, - LOCAL_IS_GOING_TO_PUT, - LOCAL_IS_PUTTING_OKAY, - LOCAL_IS_WAITING_USER_ANSWER, -} from "../utils/constants.js"; +import { ERROR_NEW_KEYWORD_MISSING, idNameObj } from "../utils/constants.js"; import { hasNewTarget, isValidArray } from "../utils/error.js"; -import { routePush } from "../utils/router.js"; -import { getItem, setItem } from "../utils/storage.js"; import DocumentFooter from "./DocumentFooter.js"; import DocumentHeader from "./DocumentHeader.js"; import Documents from "./Documents.js"; @@ -16,7 +9,7 @@ export default function DocumentPage({ $target, initialState }) { //tags const $page = document.createElement("div"); - $page.setAttribute("id", "sidebar-container"); + $page.setAttribute("id", idNameObj.SIDEBAR_CONTAINER); let isInit = false; @@ -35,18 +28,6 @@ export default function DocumentPage({ $target, initialState }) { const documents = new Documents({ $target: $page, initialState: this.state, - onClick: async ({ id, parentId }) => { - const isGoingToPut = getItem(LOCAL_IS_GOING_TO_PUT, false); - - setItem(LOCAL_IS_WAITING_USER_ANSWER, true); - if (!isGoingToPut || confirm("변경사항이 저장되지 않을 수 있습니다.")) { - setItem(LOCAL_IS_PUTTING_OKAY, !isGoingToPut); - routePush( - `/documents/${id}${parentId ? `?parent.id=${parentId}` : ""}` - ); - } - setItem(LOCAL_IS_WAITING_USER_ANSWER, false); - }, }); new DocumentFooter({ $target: $page }); diff --git a/src/components/DocumentPage/Documents.js b/src/components/DocumentPage/Documents.js index 549b766a..ac69b128 100644 --- a/src/components/DocumentPage/Documents.js +++ b/src/components/DocumentPage/Documents.js @@ -1,11 +1,6 @@ -import { - classNameObj, - ERROR_NEW_KEYWORD_MISSING, - LOCAL_STORAGE_DISPLAY, - styleObj, -} from "../utils/constants.js"; +import { classNameObj, ERROR_NEW_KEYWORD_MISSING, LOCAL_STORAGE_DISPLAY, styleObj } from "../utils/constants.js"; import { hasNewTarget, isValidArray } from "../utils/error.js"; -import { routeCreateDocument, routeRemoveDocument } from "../utils/router.js"; +import { routeCreateDocument, routePush, routeRemoveDocument } from "../utils/router.js"; import { getItem, setItem } from "../utils/storage.js"; import { sidebarNewDocumentBtnClick, @@ -16,6 +11,7 @@ import { //constants const { TITLE, + TITLE_WRAPPER, DISPLAY_BTN, NEW_BTN, REMOVE_BTN, @@ -25,7 +21,7 @@ const { SIDEBAR_DOCUMENT_LIST_CONTAINER, } = classNameObj; -export default function Documents({ $target, initialState, onClick }) { +export default function Documents({ $target, initialState }) { if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); //tags @@ -42,10 +38,7 @@ export default function Documents({ $target, initialState, onClick }) { //state this.state = isValidState(initialState) ? initialState : []; - const displayArr = getItem( - LOCAL_STORAGE_DISPLAY, - new Map(this.state.map((document) => [document.id, false])) - ); + const displayArr = getItem(LOCAL_STORAGE_DISPLAY, new Map(this.state.map((document) => [document.id, false]))); this.setState = (nextState) => { if (!isValidState(nextState)) return; @@ -57,13 +50,7 @@ export default function Documents({ $target, initialState, onClick }) { this.render = () => { $sidebar.innerHTML = ` ${this.state - .map((rootDocument) => - createDocumentSection( - rootDocument, - styleObj.DEFAULT_PADDING, - displayArr - ) - ) + .map((rootDocument) => createDocumentSection(rootDocument, styleObj.DEFAULT_PADDING, displayArr)) .join("")} `; }; @@ -75,22 +62,20 @@ export default function Documents({ $target, initialState, onClick }) { const { target } = e; const { classList } = target; const { id } = target.closest(`.${DOCUMENT_BLOCK}`).dataset; - const documentListAddBlock = target.closest(`.${DOCUMENT_SECTION}`); + const documentSection = target.closest(`.${DOCUMENT_SECTION}`); - if (classList[0] === TITLE) { - const parentId = documentListAddBlock.parentNode.dataset.id; + if (classList[0] === TITLE || classList[0] === TITLE_WRAPPER) { + const parentId = documentSection.parentNode.dataset.id; - onClick({ id, parentId }); + routePush(`/documents/${id}${parentId ? `?parent.id=${parentId}` : ""}`); } else if (classList[0] === DISPLAY_BTN) { sidebarDisplayBtnClick(id, target, displayArr); } else if (classList[0] === NEW_BTN) { sidebarNewDocumentBtnClick(id, target); //펼침 적용 - if (documentListAddBlock) { - const documentListBlock = documentListAddBlock.querySelector( - `.${DOCUMENT_LIST_BLOCK}` - ); + if (documentSection) { + const documentListBlock = documentSection.querySelector(`.${DOCUMENT_LIST_BLOCK}`); displayArr[id] = true; setItem(LOCAL_STORAGE_DISPLAY, displayArr); @@ -99,11 +84,8 @@ export default function Documents({ $target, initialState, onClick }) { routeCreateDocument({ id }); } else if (classList[0] === REMOVE_BTN) { - const parentId = documentListAddBlock.parentNode.dataset.id; - routeRemoveDocument({ - id, - parentId, - }); + const parentId = documentSection.parentNode.dataset.id; + routeRemoveDocument({ id, parentId }); } }); } diff --git a/src/components/DocumentPage/document_util/documentUtil.js b/src/components/DocumentPage/document_util/documentUtil.js index 8c56e2b3..54b75d4a 100644 --- a/src/components/DocumentPage/document_util/documentUtil.js +++ b/src/components/DocumentPage/document_util/documentUtil.js @@ -4,6 +4,8 @@ import { classNameObj, styleObj, DEFAULT_TITLE, DEFAULT_ID, LOCAL_STORAGE_DISPLA //constants const { TITLE, + TITLE_WRAPPER, + BTN_WRAPPER, DISPLAY_BTN, NEW_BTN, REMOVE_BTN, @@ -21,11 +23,13 @@ export const createDocumentBlock = (id, title, padding) => {
-
+
${title}
-
-
+
+
+
+
`; diff --git a/src/components/EditPage/EditPage.js b/src/components/EditPage/EditPage.js index 609a98b6..a4847899 100644 --- a/src/components/EditPage/EditPage.js +++ b/src/components/EditPage/EditPage.js @@ -3,13 +3,12 @@ import { DISABLED_ID, ERROR_NEW_KEYWORD_MISSING, LOCAL_STORAGE_RECENT_DOCUMENT, - LOCAL_IS_GOING_TO_PUT, - LOCAL_IS_PUTTING_OKAY, - LOCAL_IS_WAITING_USER_ANSWER, + idNameObj, + DEFAULT_CONTENT, } from "../utils/constants.js"; import { hasNewTarget } from "../utils/error.js"; import { routePutDocument, setHeaderChange } from "../utils/router.js"; -import { getItem, setItem } from "../utils/storage.js"; +import { setItem } from "../utils/storage.js"; import Editor from "./Editor.js"; import Header from "./Header.js"; import Topbar from "./Topbar.js"; @@ -19,7 +18,7 @@ export default function EditPage({ $target, initialState }) { //tags const $page = document.createElement("div"); - $page.setAttribute("id", "editor-container"); + $page.setAttribute("id", idNameObj.EDITOR_CONTAINER); let isInit = false; let timer = null; @@ -54,7 +53,7 @@ export default function EditPage({ $target, initialState }) { id: this.state.id, title: this.state.title, }); - autoSaveDocument({ delay: 2000 }); + autoSaveDocument({ delay: 500 }); }, }); @@ -69,7 +68,7 @@ export default function EditPage({ $target, initialState }) { content, }); - autoSaveDocument({ delay: 2000 }); + autoSaveDocument({ delay: 500 }); }, }); @@ -85,7 +84,10 @@ export default function EditPage({ $target, initialState }) { id, title: title || DEFAULT_TITLE, }); - editor.setState({ id, content: content || "" }); + editor.setState({ + id, + content: content || DEFAULT_CONTENT, + }); topbar.setState({ id, parentId: parentId || null }); //LS 저장 @@ -110,15 +112,10 @@ export default function EditPage({ $target, initialState }) { clearTimeout(timer); } - setItem(LOCAL_IS_GOING_TO_PUT, true); timer = setTimeout(async () => { const { id, title, content } = this.state; - while (getItem(LOCAL_IS_WAITING_USER_ANSWER, false)); - getItem(LOCAL_IS_PUTTING_OKAY, true) - ? routePutDocument({ id, title, content }) - : ""; - setItem(LOCAL_IS_GOING_TO_PUT, false); + routePutDocument({ id, title, content }); }, delay); }; //event handler diff --git a/src/components/EditPage/Editor.js b/src/components/EditPage/Editor.js index 22ab338d..38879ec1 100644 --- a/src/components/EditPage/Editor.js +++ b/src/components/EditPage/Editor.js @@ -1,5 +1,5 @@ import { hasContent, hasNewTarget } from "../utils/error.js"; -import { DISABLED_ID, ERROR_NEW_KEYWORD_MISSING } from "../utils/constants.js"; +import { DEFAULT_CONTENT, DISABLED_ID, ERROR_NEW_KEYWORD_MISSING } from "../utils/constants.js"; export default function Editor({ $target, initialState, onEditing }) { if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); @@ -23,12 +23,18 @@ export default function Editor({ $target, initialState, onEditing }) { if (!isValidContent(nextState)) return; this.state = nextState; + + const { content } = this.state; const textarea = $editor.querySelector("textarea"); - textarea.value = this.state.content; - this.state.id === DISABLED_ID - ? (textarea.disabled = true) - : (textarea.disabled = false); + if (content === DEFAULT_CONTENT) { + textarea.value = ""; + textarea.placeholder = DEFAULT_CONTENT; + } else { + textarea.value = this.state.content; + } + + this.state.id === DISABLED_ID ? (textarea.disabled = true) : (textarea.disabled = false); }; this.render = () => { diff --git a/src/components/EditPage/Header.js b/src/components/EditPage/Header.js index caa1cad5..35824550 100644 --- a/src/components/EditPage/Header.js +++ b/src/components/EditPage/Header.js @@ -1,9 +1,4 @@ -import { - ERROR_NEW_KEYWORD_MISSING, - DEFAULT_TITLE, - DISABLED_ID, - ROOT_TITLE, -} from "../utils/constants.js"; +import { ERROR_NEW_KEYWORD_MISSING, DEFAULT_TITLE, DISABLED_ID, ROOT_TITLE } from "../utils/constants.js"; import { hasId, hasNewTarget, hasTitle } from "../utils/error.js"; export default function Header({ $target, initialState, onEditing }) { @@ -27,6 +22,7 @@ export default function Header({ $target, initialState, onEditing }) { this.setState = (nextState) => { if (!isValidState(nextState)) return; + this.state = nextState; const { title } = this.state; @@ -39,18 +35,16 @@ export default function Header({ $target, initialState, onEditing }) { input.value = title; } - this.state.id === DISABLED_ID - ? (input.disabled = true) - : (input.disabled = false); + this.state.id === DISABLED_ID ? (input.disabled = true) : (input.disabled = false); }; this.render = () => { if (!isInit) { const { title } = this.state; $header.innerHTML = ` - +
+ +
`; isInit = true; } diff --git a/src/components/EditPage/Topbar.js b/src/components/EditPage/Topbar.js index cf9f981c..177b6a21 100644 --- a/src/components/EditPage/Topbar.js +++ b/src/components/EditPage/Topbar.js @@ -1,12 +1,12 @@ import { hasId, hasNewTarget } from "../utils/error.js"; -import { DISABLED_ID, ERROR_NEW_KEYWORD_MISSING } from "../utils/constants.js"; +import { DISABLED_ID, ERROR_NEW_KEYWORD_MISSING, classNameObj } from "../utils/constants.js"; import DeleteButton from "../Button/DeleteButton.js"; export default function Topbar({ $target, initialState }) { if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); const $topbar = document.createElement("div"); - $topbar.setAttribute("class", "editor-topbar"); + $topbar.setAttribute("class", classNameObj.EDITOR_TOPBAR); $target.appendChild($topbar); const isValidState = (state) => { @@ -26,12 +26,6 @@ export default function Topbar({ $target, initialState }) { if (!nextState) return; this.state = nextState; - - if (this.state.id === DISABLED_ID) { - $topbar.style.display = "none"; - } else { - $topbar.style.display = "block"; - deleteBtn.setState(this.state); - } + deleteBtn.setState(this.state); }; } diff --git a/src/components/utils/constants.js b/src/components/utils/constants.js index 90158a0c..f8fce4c3 100644 --- a/src/components/utils/constants.js +++ b/src/components/utils/constants.js @@ -7,13 +7,23 @@ export const properties = { }; //Header -export const DEFAULT_TITLE = "제목없음"; +export const DEFAULT_TITLE = "제목 없음"; export const ROOT_TITLE = "작업 중... 👌"; +export const DEFAULT_CONTENT = "내용을 입력해주세요"; //Document +export const idNameObj = { + SIDEBAR_CONTAINER: "sidebar-container", + SIDEBAR_HEADER: "sidebar-header", + SIDEBAR_LIST_CONTAINER: "sidebar-list-container", + SIDEBAR_FOOTER: "sidebar-footer", + EDITOR_CONTAINER: "editor-container", +}; + export const classNameObj = { - SIDEBAR: "sidebar", + TITLE_WRAPPER: "title-wrapper", TITLE: "title", + BTN_WRAPPER: "btn-wrapper", DISPLAY_BTN: "display-btn", NEW_BTN: "new-btn", REMOVE_BTN: "remove-btn", @@ -21,11 +31,12 @@ export const classNameObj = { DOCUMENT_SECTION: "document-section", DOCUMENT_LIST_BLOCK: "document-list-block", DOCUMENT_BLOCK_INNER: "document-block-inner", - SIDEBAR_DOCUMENT_LIST_CONTAINER: "sidebar-document-list-container", + SIDEBAR_DOCUMENT_LIST_CONTAINER: "sidebar-list-container", + EDITOR_TOPBAR: "topbar", }; export const styleObj = { - DEFAULT_PADDING: 10, + DEFAULT_PADDING: 15, PADDING_INCREMENT: 20, }; diff --git a/src/components/utils/router.js b/src/components/utils/router.js index d36acd75..dd150be1 100644 --- a/src/components/utils/router.js +++ b/src/components/utils/router.js @@ -83,8 +83,7 @@ export const initRouter = (onRoute) => { const { id, title } = e.detail; //해당 document-block의 title을 찾아서 innerText를 바꿔주는 거지. - const documentBlockArr = - e.target.document.body.querySelectorAll(".document-block"); + const documentBlockArr = e.target.document.body.querySelectorAll(".document-block"); let currentDocumentBlock = null; From 3d44489b4801a5697c301e643423ecad17c35609 Mon Sep 17 00:00:00 2001 From: Kal-MH Date: Tue, 15 Nov 2022 23:00:09 +0900 Subject: [PATCH 09/19] Style: set padding value with sidebar-container --- css/style.css | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/css/style.css b/css/style.css index e41fd905..435ffbc5 100644 --- a/css/style.css +++ b/css/style.css @@ -67,6 +67,8 @@ SIDEBAR_PAGE height: 45px; width: 100%; cursor: pointer; + margin-top: 10px; + margin-bottom: 8px; } #sidebar-header:hover { @@ -108,7 +110,7 @@ SIDEBAR_PAGE /* Documents */ #sidebar-list-container { - padding: 6px 5px 20px 0; + padding: 15px 5px 20px 0; box-shadow: var(--box-shadow-inset); overflow: hidden auto; display: flex; From 7f99f223a9a28130a02935952e32f7fb0da5828b Mon Sep 17 00:00:00 2001 From: Kal-MH Date: Wed, 16 Nov 2022 13:45:09 +0900 Subject: [PATCH 10/19] style: editor page - remove LS save method & move setHeaderChange into Header component --- css/style.css | 2 +- src/components/App.js | 3 --- src/components/EditPage/EditPage.js | 31 ++++------------------------- src/components/EditPage/Editor.js | 14 ++++++++----- src/components/EditPage/Header.js | 14 +++++++++++-- src/components/utils/constants.js | 3 ++- src/components/utils/router.js | 15 ++++++-------- 7 files changed, 34 insertions(+), 48 deletions(-) diff --git a/css/style.css b/css/style.css index 435ffbc5..b15c2d08 100644 --- a/css/style.css +++ b/css/style.css @@ -351,7 +351,7 @@ header .input-wrapper { header input { height: 60px; - width: 900px; + width: 100%; font-size: 40px; line-height: 1.2; font-weight: 700; diff --git a/src/components/App.js b/src/components/App.js index f8244cef..41ffb1ce 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -3,13 +3,10 @@ import { DEFAULT_TITLE, DISABLED_ID, ERROR_NEW_KEYWORD_MISSING, - LOCAL_STORAGE_RECENT_DOCUMENT, - ROOT_TITLE, } from "./utils/constants.js"; import { hasNewTarget } from "./utils/error.js"; import DocumentPage from "./DocumentPage/DocumentPage.js"; import EditPage from "./EditPage/EditPage.js"; -import { getItem } from "./utils/storage.js"; import { request } from "../api/api.js"; import { initRouter } from "./utils/router.js"; diff --git a/src/components/EditPage/EditPage.js b/src/components/EditPage/EditPage.js index a4847899..37dbad14 100644 --- a/src/components/EditPage/EditPage.js +++ b/src/components/EditPage/EditPage.js @@ -1,14 +1,10 @@ import { - DEFAULT_TITLE, DISABLED_ID, ERROR_NEW_KEYWORD_MISSING, - LOCAL_STORAGE_RECENT_DOCUMENT, idNameObj, - DEFAULT_CONTENT, } from "../utils/constants.js"; import { hasNewTarget } from "../utils/error.js"; -import { routePutDocument, setHeaderChange } from "../utils/router.js"; -import { setItem } from "../utils/storage.js"; +import { routePutDocument } from "../utils/router.js"; import Editor from "./Editor.js"; import Header from "./Header.js"; import Topbar from "./Topbar.js"; @@ -30,13 +26,12 @@ export default function EditPage({ $target, initialState }) { content: "", }; - //topbar with fetchButton + //components const topbar = new Topbar({ $target: $page, initialState: {}, }); - //components const header = new Header({ $target: $page, initialState: { @@ -49,10 +44,6 @@ export default function EditPage({ $target, initialState }) { title, }); - setHeaderChange({ - id: this.state.id, - title: this.state.title, - }); autoSaveDocument({ delay: 500 }); }, }); @@ -77,25 +68,12 @@ export default function EditPage({ $target, initialState }) { this.state = nextState; - //header, editor 변화? const { id, parentId, title, content } = this.state; - header.setState({ - id, - title: title || DEFAULT_TITLE, - }); - editor.setState({ - id, - content: content || DEFAULT_CONTENT, - }); + header.setState({ id, title }); + editor.setState({ id, content }); topbar.setState({ id, parentId: parentId || null }); - //LS 저장 - setItem(LOCAL_STORAGE_RECENT_DOCUMENT, { - ...this.state, - tempSaveDate: new Date(), - }); - if (!isInit) { this.render(); isInit = true; @@ -118,5 +96,4 @@ export default function EditPage({ $target, initialState }) { routePutDocument({ id, title, content }); }, delay); }; - //event handler } diff --git a/src/components/EditPage/Editor.js b/src/components/EditPage/Editor.js index 38879ec1..93b8530d 100644 --- a/src/components/EditPage/Editor.js +++ b/src/components/EditPage/Editor.js @@ -1,11 +1,11 @@ import { hasContent, hasNewTarget } from "../utils/error.js"; -import { DEFAULT_CONTENT, DISABLED_ID, ERROR_NEW_KEYWORD_MISSING } from "../utils/constants.js"; +import { classNameObj, DEFAULT_CONTENT, DISABLED_ID, ERROR_NEW_KEYWORD_MISSING } from "../utils/constants.js"; export default function Editor({ $target, initialState, onEditing }) { if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); const $editor = document.createElement("div"); - $editor.setAttribute("class", "editor"); + $editor.setAttribute("class", classNameObj.EDOTOR_EDITOR); $target.appendChild($editor); let isInit = false; @@ -24,12 +24,12 @@ export default function Editor({ $target, initialState, onEditing }) { this.state = nextState; - const { content } = this.state; + const { id, content } = this.state; const textarea = $editor.querySelector("textarea"); - if (content === DEFAULT_CONTENT) { + if (!content || content === DEFAULT_CONTENT) { textarea.value = ""; - textarea.placeholder = DEFAULT_CONTENT; + textarea.placeholder = id === DISABLED_ID ? "" : DEFAULT_CONTENT; } else { textarea.value = this.state.content; } @@ -53,4 +53,8 @@ export default function Editor({ $target, initialState, onEditing }) { onEditing(value); }); + + $editor.querySelector("textarea").addEventListener("focus", (e) => { + e.target.placeholder = ""; + }) } diff --git a/src/components/EditPage/Header.js b/src/components/EditPage/Header.js index 35824550..098d11dd 100644 --- a/src/components/EditPage/Header.js +++ b/src/components/EditPage/Header.js @@ -1,5 +1,6 @@ import { ERROR_NEW_KEYWORD_MISSING, DEFAULT_TITLE, DISABLED_ID, ROOT_TITLE } from "../utils/constants.js"; import { hasId, hasNewTarget, hasTitle } from "../utils/error.js"; +import { setHeaderChange } from "../utils/router.js"; export default function Header({ $target, initialState, onEditing }) { if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); @@ -28,9 +29,9 @@ export default function Header({ $target, initialState, onEditing }) { const { title } = this.state; const input = $header.querySelector("input"); - if (title === DEFAULT_TITLE || title === ROOT_TITLE) { + if (!title || title === DEFAULT_TITLE || title === ROOT_TITLE) { input.value = ""; - input.placeholder = title; + input.placeholder = !title ? DEFAULT_TITLE : title; } else { input.value = title; } @@ -56,6 +57,15 @@ export default function Header({ $target, initialState, onEditing }) { $header.addEventListener("input", (e) => { const { value } = e.target; + setHeaderChange({ + id: this.state.id, + title: value, + }); + onEditing(value); }); + + $header.querySelector("input").addEventListener("focus", (e) => { + e.target.placeholder = ""; + }) } diff --git a/src/components/utils/constants.js b/src/components/utils/constants.js index f8fce4c3..ebc1d21b 100644 --- a/src/components/utils/constants.js +++ b/src/components/utils/constants.js @@ -8,7 +8,7 @@ export const properties = { //Header export const DEFAULT_TITLE = "제목 없음"; -export const ROOT_TITLE = "작업 중... 👌"; +export const ROOT_TITLE = "작업 중... 💨"; export const DEFAULT_CONTENT = "내용을 입력해주세요"; //Document @@ -33,6 +33,7 @@ export const classNameObj = { DOCUMENT_BLOCK_INNER: "document-block-inner", SIDEBAR_DOCUMENT_LIST_CONTAINER: "sidebar-list-container", EDITOR_TOPBAR: "topbar", + EDOTOR_EDITOR: "editor" }; export const styleObj = { diff --git a/src/components/utils/router.js b/src/components/utils/router.js index dd150be1..30317cfe 100644 --- a/src/components/utils/router.js +++ b/src/components/utils/router.js @@ -1,5 +1,6 @@ import { request } from "../../api/api.js"; import { + classNameObj, DEFAULT_TITLE, EVENT_HEADER_CHANGE, EVENT_ROUTE_PUSH, @@ -8,7 +9,9 @@ import { EVENT_ROUTE_REMOVE, LOCAL_STORAGE_RECENT_DOCUMENT, } from "./constants.js"; -import { removeItem, setItem } from "./storage.js"; +import { removeItem } from "./storage.js"; + +const { DOCUMENT_BLOCK, TITLE } = classNameObj; export const initRouter = (onRoute) => { const removeAllDocument = async (document) => { @@ -73,18 +76,12 @@ export const initRouter = (onRoute) => { }), }); - setItem(LOCAL_STORAGE_RECENT_DOCUMENT, { - ...res, - tempSaveData: new Date(), - }); }); window.addEventListener(EVENT_HEADER_CHANGE, (e) => { const { id, title } = e.detail; - //해당 document-block의 title을 찾아서 innerText를 바꿔주는 거지. - const documentBlockArr = e.target.document.body.querySelectorAll(".document-block"); - + const documentBlockArr = e.target.document.body.querySelectorAll(`.${DOCUMENT_BLOCK}`); let currentDocumentBlock = null; documentBlockArr.forEach((e) => { @@ -95,7 +92,7 @@ export const initRouter = (onRoute) => { } }); - const currentTitleBlock = currentDocumentBlock.querySelector(".title"); + const currentTitleBlock = currentDocumentBlock.querySelector(`.${TITLE}`); currentTitleBlock.innerText = title; }); }; From 0f100d999f2e6ad38bc8736218dca265023bdb48 Mon Sep 17 00:00:00 2001 From: Kal-MH Date: Wed, 16 Nov 2022 14:25:11 +0900 Subject: [PATCH 11/19] style: remove unnecessary Document Util methods & add focus event with Header components --- src/components/Button/DeleteButton.js | 7 +- src/components/DocumentPage/DocumentPage.js | 1 - src/components/DocumentPage/Documents.js | 13 ++- .../document_util/documentUtil.js | 102 +++++++++--------- src/components/EditPage/Header.js | 7 ++ src/components/utils/router.js | 10 +- 6 files changed, 71 insertions(+), 69 deletions(-) diff --git a/src/components/Button/DeleteButton.js b/src/components/Button/DeleteButton.js index 6fe17dde..033bf7fc 100644 --- a/src/components/Button/DeleteButton.js +++ b/src/components/Button/DeleteButton.js @@ -5,7 +5,6 @@ import { routeRemoveDocument } from "../utils/router.js"; export default function DeleteButton({ $target, initialState }) { if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); - //tags const $button = document.createElement("div"); $target.appendChild($button); @@ -29,9 +28,7 @@ export default function DeleteButton({ $target, initialState }) { //event handler $button.addEventListener("click", async () => { const { id, parentId } = this.state; - routeRemoveDocument({ - id, - parentId, - }); + + routeRemoveDocument({ id, parentId }); }); } diff --git a/src/components/DocumentPage/DocumentPage.js b/src/components/DocumentPage/DocumentPage.js index 40780540..f16c179d 100644 --- a/src/components/DocumentPage/DocumentPage.js +++ b/src/components/DocumentPage/DocumentPage.js @@ -37,7 +37,6 @@ export default function DocumentPage({ $target, initialState }) { this.state = nextState; - //document set state documents.setState(this.state); if (!isInit) { diff --git a/src/components/DocumentPage/Documents.js b/src/components/DocumentPage/Documents.js index ac69b128..ca71f161 100644 --- a/src/components/DocumentPage/Documents.js +++ b/src/components/DocumentPage/Documents.js @@ -29,6 +29,7 @@ export default function Documents({ $target, initialState }) { $sidebar.setAttribute("id", SIDEBAR_DOCUMENT_LIST_CONTAINER); $target.appendChild($sidebar); + //validation const isValidState = (state) => { if (!state || !isValidArray(state)) return false; @@ -38,7 +39,7 @@ export default function Documents({ $target, initialState }) { //state this.state = isValidState(initialState) ? initialState : []; - const displayArr = getItem(LOCAL_STORAGE_DISPLAY, new Map(this.state.map((document) => [document.id, false]))); + const displayMap = getItem(LOCAL_STORAGE_DISPLAY, new Map(this.state.map((document) => [document.id, false]))); this.setState = (nextState) => { if (!isValidState(nextState)) return; @@ -50,7 +51,7 @@ export default function Documents({ $target, initialState }) { this.render = () => { $sidebar.innerHTML = ` ${this.state - .map((rootDocument) => createDocumentSection(rootDocument, styleObj.DEFAULT_PADDING, displayArr)) + .map((rootDocument) => createDocumentSection(rootDocument, styleObj.DEFAULT_PADDING, displayMap)) .join("")} `; }; @@ -69,22 +70,24 @@ export default function Documents({ $target, initialState }) { routePush(`/documents/${id}${parentId ? `?parent.id=${parentId}` : ""}`); } else if (classList[0] === DISPLAY_BTN) { - sidebarDisplayBtnClick(id, target, displayArr); + sidebarDisplayBtnClick(id, target, displayMap); } else if (classList[0] === NEW_BTN) { + //낙관적 업데이트 sidebarNewDocumentBtnClick(id, target); //펼침 적용 if (documentSection) { const documentListBlock = documentSection.querySelector(`.${DOCUMENT_LIST_BLOCK}`); - displayArr[id] = true; - setItem(LOCAL_STORAGE_DISPLAY, displayArr); + displayMap[id] = true; + setItem(LOCAL_STORAGE_DISPLAY, displayMap); documentListBlock.style.display = "block"; } routeCreateDocument({ id }); } else if (classList[0] === REMOVE_BTN) { const parentId = documentSection.parentNode.dataset.id; + routeRemoveDocument({ id, parentId }); } }); diff --git a/src/components/DocumentPage/document_util/documentUtil.js b/src/components/DocumentPage/document_util/documentUtil.js index 54b75d4a..00ff9ab9 100644 --- a/src/components/DocumentPage/document_util/documentUtil.js +++ b/src/components/DocumentPage/document_util/documentUtil.js @@ -17,14 +17,14 @@ const { const { PADDING_INCREMENT } = styleObj; -//method -export const createDocumentBlock = (id, title, padding) => { +//private method +const createDocumentBlock = (id, title, padding) => { return `
-
${title}
+
${title || DEFAULT_TITLE}
@@ -35,30 +35,6 @@ export const createDocumentBlock = (id, title, padding) => { `; }; -export const createDocumentSection = (parentDocument, padding, displayArr) => { - const { id, title, documents } = parentDocument; - - if (id && title !== undefined) { - return ` -
- ${createDocumentBlock(id, title, padding)} - ${ - documents.length - ? ` -
- ${documents - .map((document) => createDocumentSection(document, padding + PADDING_INCREMENT, displayArr)) - .join("")} -
- ` - : "" - } -
- `; - } -}; - const createNewHTML = (origin, documentArgs) => { const { id, title, padding } = documentArgs; @@ -70,26 +46,7 @@ const createNewHTML = (origin, documentArgs) => { `; }; -export const sidebarDisplayBtnClick = (id, target, displayArr) => { - const documentListBlock = target.closest(`.${DOCUMENT_SECTION}`).querySelector(`.${DOCUMENT_LIST_BLOCK}`); - - if (!documentListBlock) return; - - const { display } = documentListBlock.style; - - if (display === "none") { - displayArr[id] = true; - documentListBlock.style.display = "block"; - } else { - displayArr[id] = false; - documentListBlock.style.display = "none"; - } - setItem(LOCAL_STORAGE_DISPLAY, displayArr); -}; - const addNewDocumentBlock = (documentListBlock, paddingLeft) => { - //document-list-block 가져와서 맨 마지막에 document-block 삽입하기 - //낙관적 업데이트로 보여주기 const innerTags = documentListBlock.innerHTML; documentListBlock.innerHTML = ` @@ -102,7 +59,6 @@ const addNewDocumentBlock = (documentListBlock, paddingLeft) => { `; }; -//document-list가 없어서 새로 생성해야 하는 경우 const addNewDocumentList = (id, documentListAddBlock, paddingLeft) => { const origin = ` ${documentListAddBlock.innerHTML} @@ -119,17 +75,57 @@ const addNewDocumentList = (id, documentListAddBlock, paddingLeft) => { `; }; -const sidebarSubDocumentBtnClick = (id, documentListBlock, paddingLeft, target) => { - if (documentListBlock) { - addNewDocumentBlock(documentListBlock, paddingLeft); +//public method +export const createDocumentSection = (parentDocument, padding, displayMap) => { + const { id, title, documents } = parentDocument; + + if (id && title !== undefined) { + return ` +
+ ${createDocumentBlock(id, title, padding)} + ${ + documents.length + ? ` +
+ ${documents + .map((document) => createDocumentSection(document, padding + PADDING_INCREMENT, displayMap)) + .join("")} +
+ ` + : "" + } +
+ `; + } +}; + +export const sidebarDisplayBtnClick = (id, target, displayMap) => { + const documentListBlock = target.closest(`.${DOCUMENT_SECTION}`).querySelector(`.${DOCUMENT_LIST_BLOCK}`); + + if (!documentListBlock) return; + + const { display } = documentListBlock.style; + + if (display === "none") { + displayMap[id] = true; + documentListBlock.style.display = "block"; } else { - const documentListAddBlock = target.closest(`.${DOCUMENT_SECTION}`); - addNewDocumentList(id, documentListAddBlock, paddingLeft); + displayMap[id] = false; + documentListBlock.style.display = "none"; } + setItem(LOCAL_STORAGE_DISPLAY, displayMap); }; export const sidebarNewDocumentBtnClick = (id, target) => { const documentListBlock = target.closest(`.${DOCUMENT_SECTION}`).querySelector(`.${DOCUMENT_LIST_BLOCK}`); const { paddingLeft } = target.closest(`.${DOCUMENT_BLOCK_INNER}`).style; - sidebarSubDocumentBtnClick(id, documentListBlock, paddingLeft, target); + + if (documentListBlock) { + addNewDocumentBlock(documentListBlock, paddingLeft); + } else { + const documentListAddBlock = target.closest(`.${DOCUMENT_SECTION}`); + + addNewDocumentList(id, documentListAddBlock, paddingLeft); + } }; diff --git a/src/components/EditPage/Header.js b/src/components/EditPage/Header.js index 098d11dd..7acea75f 100644 --- a/src/components/EditPage/Header.js +++ b/src/components/EditPage/Header.js @@ -66,6 +66,13 @@ export default function Header({ $target, initialState, onEditing }) { }); $header.querySelector("input").addEventListener("focus", (e) => { + const { value } = e.target; + e.target.placeholder = ""; + + setHeaderChange({ + id: this.state.id, + title: value, + }); }) } diff --git a/src/components/utils/router.js b/src/components/utils/router.js index 30317cfe..a1758966 100644 --- a/src/components/utils/router.js +++ b/src/components/utils/router.js @@ -7,7 +7,6 @@ import { EVENT_ROUTE_CREATE, EVENT_ROUTE_PUT, EVENT_ROUTE_REMOVE, - LOCAL_STORAGE_RECENT_DOCUMENT, } from "./constants.js"; import { removeItem } from "./storage.js"; @@ -44,7 +43,6 @@ export const initRouter = (onRoute) => { const removedDocument = await request(`/documents/${id}`); await removeAllDocument(removedDocument); - removeItem(LOCAL_STORAGE_RECENT_DOCUMENT); routePush(`${parentId ? `/documents/${parentId}` : "/"}`); }); @@ -68,7 +66,7 @@ export const initRouter = (onRoute) => { window.addEventListener(EVENT_ROUTE_PUT, async (e) => { const { id, title, content } = e.detail; - const res = await request(`/documents/${id}`, { + await request(`/documents/${id}`, { method: "PUT", body: JSON.stringify({ title, @@ -81,10 +79,10 @@ export const initRouter = (onRoute) => { window.addEventListener(EVENT_HEADER_CHANGE, (e) => { const { id, title } = e.detail; - const documentBlockArr = e.target.document.body.querySelectorAll(`.${DOCUMENT_BLOCK}`); + const documentBlockList = e.target.document.body.querySelectorAll(`.${DOCUMENT_BLOCK}`); let currentDocumentBlock = null; - documentBlockArr.forEach((e) => { + documentBlockList.forEach((e) => { const dataId = e.dataset.id; if (parseInt(dataId) === id) { @@ -92,6 +90,8 @@ export const initRouter = (onRoute) => { } }); + if (!currentDocumentBlock) return; + const currentTitleBlock = currentDocumentBlock.querySelector(`.${TITLE}`); currentTitleBlock.innerText = title; }); From e3a6d54539c1a531548f3290ac1db0af84f8b8ec Mon Sep 17 00:00:00 2001 From: Kal-MH Date: Wed, 16 Nov 2022 15:44:57 +0900 Subject: [PATCH 12/19] =?UTF-8?q?feat:=20=EC=82=AD=EC=A0=9C=EB=90=9C=20?= =?UTF-8?q?=EB=AC=B8=EC=84=9C=EB=A1=9C=20history=20popstate=20=EC=9D=B4?= =?UTF-8?q?=EB=B2=A4=ED=8A=B8=20=EC=9D=BC=EC=96=B4=EB=82=AC=EC=9D=84=20?= =?UTF-8?q?=EB=96=84,=20delete=20page=20=EB=B3=B4=EC=97=AC=EC=A3=BC?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/api.js | 4 ++-- src/components/App.js | 8 +++++++- src/components/utils/constants.js | 7 +++++++ src/components/utils/router.js | 2 +- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/api/api.js b/src/api/api.js index 6ae5991b..8f6a03e7 100644 --- a/src/api/api.js +++ b/src/api/api.js @@ -15,8 +15,8 @@ export const request = async (url, options = {}) => { return await res.json(); } - throw new Error(ERROR_API_CALL); + // throw new Error(ERROR_API_CALL); } catch (e) { - alert(e.message); + // alert(e.message); } }; diff --git a/src/components/App.js b/src/components/App.js index 41ffb1ce..ce91b5ca 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -3,6 +3,7 @@ import { DEFAULT_TITLE, DISABLED_ID, ERROR_NEW_KEYWORD_MISSING, + REMOVED_DOC_STATE, } from "./utils/constants.js"; import { hasNewTarget } from "./utils/error.js"; import DocumentPage from "./DocumentPage/DocumentPage.js"; @@ -39,7 +40,12 @@ export default function App({ $target }) { const [, parentId] = search.split("="); const document = await request(`/documents/${documentId}`); - editPage.setState({ ...document, parentId }); + if (document) { + editPage.setState({ ...document, parentId }); + } else { + editPage.setState({ ...REMOVED_DOC_STATE, parentId }) + } + } }; diff --git a/src/components/utils/constants.js b/src/components/utils/constants.js index ebc1d21b..3b986a9d 100644 --- a/src/components/utils/constants.js +++ b/src/components/utils/constants.js @@ -9,6 +9,7 @@ export const properties = { //Header export const DEFAULT_TITLE = "제목 없음"; export const ROOT_TITLE = "작업 중... 💨"; +export const REMOVED_DOC_TITLE = "삭제된 문서입니다." export const DEFAULT_CONTENT = "내용을 입력해주세요"; //Document @@ -52,6 +53,12 @@ export const DEFAULT_STATE = { content: "", }; +export const REMOVED_DOC_STATE = { + id: DISABLED_ID, + title: REMOVED_DOC_TITLE, + content: "", +}; + //Button export const DELETE_BUTTON_TEXT = "Delete"; diff --git a/src/components/utils/router.js b/src/components/utils/router.js index a1758966..c739abda 100644 --- a/src/components/utils/router.js +++ b/src/components/utils/router.js @@ -31,7 +31,7 @@ export const initRouter = (onRoute) => { if (!nextUrl) return; - history.replaceState(null, null, nextUrl); + history.pushState(null, null, nextUrl); onRoute(); }); From 2abf5ed18d19096c9d56d8ebb5fd09be72a19b3c Mon Sep 17 00:00:00 2001 From: Kal-MH Date: Wed, 16 Nov 2022 16:32:14 +0900 Subject: [PATCH 13/19] =?UTF-8?q?fix:=20=ED=95=98=EB=82=98=EC=9D=98=20docu?= =?UTF-8?q?ment=EC=97=90=20=EB=8C=80=ED=95=B4=EC=84=9C=20url=EC=9D=B4=20?= =?UTF-8?q?=EB=91=90=20=EA=B0=9C=20=EC=83=9D=EA=B8=B0=EB=8A=94=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=ED=95=B4=EA=B2=B0=20->=20/documents/id=EB=A1=9C=20?= =?UTF-8?q?=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/App.js | 5 ++--- src/components/DocumentPage/Documents.js | 2 +- src/components/utils/router.js | 11 +++++------ 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index ce91b5ca..12987cd3 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -28,8 +28,8 @@ export default function App({ $target }) { }, }); - this.route = async () => { - const { pathname, search } = location; + this.route = async (parentId) => { + const { pathname } = location; const documentsList = await request("/documents"); documentPage.setState(documentsList); @@ -37,7 +37,6 @@ export default function App({ $target }) { editPage.setState(DEFAULT_STATE); } else if (pathname.indexOf("/documents") === 0) { const [, , documentId] = pathname.split("/"); - const [, parentId] = search.split("="); const document = await request(`/documents/${documentId}`); if (document) { diff --git a/src/components/DocumentPage/Documents.js b/src/components/DocumentPage/Documents.js index ca71f161..069a0cbd 100644 --- a/src/components/DocumentPage/Documents.js +++ b/src/components/DocumentPage/Documents.js @@ -68,7 +68,7 @@ export default function Documents({ $target, initialState }) { if (classList[0] === TITLE || classList[0] === TITLE_WRAPPER) { const parentId = documentSection.parentNode.dataset.id; - routePush(`/documents/${id}${parentId ? `?parent.id=${parentId}` : ""}`); + routePush(`/documents/${id}`, parentId); } else if (classList[0] === DISPLAY_BTN) { sidebarDisplayBtnClick(id, target, displayMap); } else if (classList[0] === NEW_BTN) { diff --git a/src/components/utils/router.js b/src/components/utils/router.js index c739abda..aa5fc398 100644 --- a/src/components/utils/router.js +++ b/src/components/utils/router.js @@ -8,7 +8,6 @@ import { EVENT_ROUTE_PUT, EVENT_ROUTE_REMOVE, } from "./constants.js"; -import { removeItem } from "./storage.js"; const { DOCUMENT_BLOCK, TITLE } = classNameObj; @@ -27,12 +26,12 @@ export const initRouter = (onRoute) => { window.addEventListener("popstate", () => onRoute()); window.addEventListener(EVENT_ROUTE_PUSH, (e) => { - const { nextUrl } = e.detail; + const { nextUrl, parentId } = e.detail; if (!nextUrl) return; history.pushState(null, null, nextUrl); - onRoute(); + onRoute(parentId); }); window.addEventListener(EVENT_ROUTE_REMOVE, async (e) => { @@ -60,7 +59,7 @@ export const initRouter = (onRoute) => { }); console.log(createNewDocument); - routePush(`/documents/${createNewDocument.id}`); + routePush(`/documents/${createNewDocument.id}`, id); }); window.addEventListener(EVENT_ROUTE_PUT, async (e) => { @@ -108,10 +107,10 @@ export const setHeaderChange = ({ id, title }) => { ); }; -export const routePush = (nextUrl) => { +export const routePush = (nextUrl, parentId) => { window.dispatchEvent( new CustomEvent(EVENT_ROUTE_PUSH, { - detail: { nextUrl }, + detail: { nextUrl, parentId }, }) ); }; From 3342b3682de5e4cadcb6dc5df9d0f81ac39ecbbd Mon Sep 17 00:00:00 2001 From: Kal-MH Date: Wed, 16 Nov 2022 17:02:00 +0900 Subject: [PATCH 14/19] =?UTF-8?q?fix:=20Document=20Header,=20footer?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=EA=B0=80=20?= =?UTF-8?q?=EC=A0=84=EC=B2=B4=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8?= =?UTF-8?q?=EC=97=90=20=EC=A0=81=EC=9A=A9=EB=90=98=EB=8A=94=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/DocumentPage/DocumentFooter.js | 2 +- src/components/DocumentPage/DocumentHeader.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/DocumentPage/DocumentFooter.js b/src/components/DocumentPage/DocumentFooter.js index d4bdcddb..5475e97f 100644 --- a/src/components/DocumentPage/DocumentFooter.js +++ b/src/components/DocumentPage/DocumentFooter.js @@ -31,7 +31,7 @@ export default function DocumentFooter({ $target }) { this.render(); - $footer.addEventListener("click", () => { + $footer.querySelector(`.${NEW_BTN}`).addEventListener("click", () => { routeCreateDocument({ id: null }); }); } diff --git a/src/components/DocumentPage/DocumentHeader.js b/src/components/DocumentPage/DocumentHeader.js index 6828ce79..64c5d668 100644 --- a/src/components/DocumentPage/DocumentHeader.js +++ b/src/components/DocumentPage/DocumentHeader.js @@ -31,7 +31,7 @@ export default function DocumentHeader({ $target }) { this.render(); - $header.addEventListener("click", () => { + $header.querySelector(`.${NEW_BTN}`).addEventListener("click", () => { routeCreateDocument({ id: null }); }); } From 0dd186e8a179662e94c24edd42cc260bf2de8c35 Mon Sep 17 00:00:00 2001 From: Kal-MH Date: Wed, 16 Nov 2022 23:15:32 +0900 Subject: [PATCH 15/19] style: add constant(SCROLLBAR) , change css styling --- css/style.css | 31 ++++++++++++++++-------- src/components/App.js | 13 +++++----- src/components/DocumentPage/Documents.js | 10 ++++---- src/components/EditPage/Editor.js | 2 +- src/components/EditPage/Header.js | 3 ++- src/components/utils/constants.js | 15 ++++++------ src/components/utils/router.js | 20 +++++++++------ 7 files changed, 56 insertions(+), 38 deletions(-) diff --git a/css/style.css b/css/style.css index b15c2d08..407f2121 100644 --- a/css/style.css +++ b/css/style.css @@ -14,6 +14,8 @@ Variables --display-icon: "\f054"; --remove-icon: "\f1f8"; --new-icon: "\2b"; + --scrollbar-thumb: #D3D1CB; + --scrollbar-track: #EDECE9; } /* @@ -23,6 +25,10 @@ html { font-size: 100%; } +body { + overflow: hidden; +} + /* COMMON ============================ */ @@ -56,6 +62,20 @@ COMMON overflow: hidden; } +.scrollbar::-webkit-scrollbar { + width: 10px; + height: 10px; + background: transparent; +} + +.scrollbar::-webkit-scrollbar-thumb { + background: var(--scrollbar-thumb); +} + +.scrollbar::-webkit-scrollbar-track { + background: var(--scrollbar-track); +} + /* SIDEBAR_PAGE =============================*/ @@ -134,8 +154,6 @@ SIDEBAR_PAGE #sidebar-list-container .document-block:hover .remove-btn, #sidebar-list-container .document-block:hover .new-btn { display: flex; - /* align-items: center; */ - /*justify-content: center;*/ } #sidebar-list-container .document-block:hover .title-wrapper { @@ -186,6 +204,7 @@ SIDEBAR_PAGE justify-content: center; width: 20px; height: 20px; + padding-top: 2px; } #sidebar-list-container .document-block-inner .btn-wrapper { @@ -204,14 +223,6 @@ SIDEBAR_PAGE height: 20px; padding-right: 3px; } -/* -#sidebar-list-container .document-block-inner .remove-btn { - right: 22px; -} - -#sidebar-list-container .document-block-inner .new-btn { - right: 0; -} */ #sidebar-list-container .document-block-inner .display-btn::before { flex-shrink: 0; diff --git a/src/components/App.js b/src/components/App.js index 12987cd3..d9415f65 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -4,6 +4,7 @@ import { DISABLED_ID, ERROR_NEW_KEYWORD_MISSING, REMOVED_DOC_STATE, + SLASH_DOCUMENTS, } from "./utils/constants.js"; import { hasNewTarget } from "./utils/error.js"; import DocumentPage from "./DocumentPage/DocumentPage.js"; @@ -28,21 +29,21 @@ export default function App({ $target }) { }, }); - this.route = async (parentId) => { + this.route = async () => { const { pathname } = location; - const documentsList = await request("/documents"); + const documentsList = await request(`${SLASH_DOCUMENTS}`); documentPage.setState(documentsList); if (pathname === "/") { editPage.setState(DEFAULT_STATE); - } else if (pathname.indexOf("/documents") === 0) { + } else if (pathname.indexOf(`${SLASH_DOCUMENTS}`) === 0) { const [, , documentId] = pathname.split("/"); - const document = await request(`/documents/${documentId}`); + const document = await request(`${SLASH_DOCUMENTS}/${documentId}`); if (document) { - editPage.setState({ ...document, parentId }); + editPage.setState({ ...document}); } else { - editPage.setState({ ...REMOVED_DOC_STATE, parentId }) + editPage.setState({ ...REMOVED_DOC_STATE}) } } diff --git a/src/components/DocumentPage/Documents.js b/src/components/DocumentPage/Documents.js index 069a0cbd..f7d1f605 100644 --- a/src/components/DocumentPage/Documents.js +++ b/src/components/DocumentPage/Documents.js @@ -1,4 +1,4 @@ -import { classNameObj, ERROR_NEW_KEYWORD_MISSING, LOCAL_STORAGE_DISPLAY, styleObj } from "../utils/constants.js"; +import { classNameObj, ERROR_NEW_KEYWORD_MISSING, LOCAL_STORAGE_DISPLAY, SLASH_DOCUMENTS, styleObj } from "../utils/constants.js"; import { hasNewTarget, isValidArray } from "../utils/error.js"; import { routeCreateDocument, routePush, routeRemoveDocument } from "../utils/router.js"; import { getItem, setItem } from "../utils/storage.js"; @@ -19,6 +19,7 @@ const { DOCUMENT_LIST_BLOCK, DOCUMENT_SECTION, SIDEBAR_DOCUMENT_LIST_CONTAINER, + SCROLLBAR } = classNameObj; export default function Documents({ $target, initialState }) { @@ -27,6 +28,7 @@ export default function Documents({ $target, initialState }) { //tags const $sidebar = document.createElement("div"); $sidebar.setAttribute("id", SIDEBAR_DOCUMENT_LIST_CONTAINER); + $sidebar.setAttribute("class", SCROLLBAR) $target.appendChild($sidebar); @@ -64,11 +66,11 @@ export default function Documents({ $target, initialState }) { const { classList } = target; const { id } = target.closest(`.${DOCUMENT_BLOCK}`).dataset; const documentSection = target.closest(`.${DOCUMENT_SECTION}`); + const parentId = documentSection.parentNode.dataset.id; if (classList[0] === TITLE || classList[0] === TITLE_WRAPPER) { - const parentId = documentSection.parentNode.dataset.id; - routePush(`/documents/${id}`, parentId); + routePush(`${SLASH_DOCUMENTS}/${id}`, parentId); } else if (classList[0] === DISPLAY_BTN) { sidebarDisplayBtnClick(id, target, displayMap); } else if (classList[0] === NEW_BTN) { @@ -86,8 +88,6 @@ export default function Documents({ $target, initialState }) { routeCreateDocument({ id }); } else if (classList[0] === REMOVE_BTN) { - const parentId = documentSection.parentNode.dataset.id; - routeRemoveDocument({ id, parentId }); } }); diff --git a/src/components/EditPage/Editor.js b/src/components/EditPage/Editor.js index 93b8530d..655b70bb 100644 --- a/src/components/EditPage/Editor.js +++ b/src/components/EditPage/Editor.js @@ -40,7 +40,7 @@ export default function Editor({ $target, initialState, onEditing }) { this.render = () => { if (!isInit) { $editor.innerHTML = ` - + `; } }; diff --git a/src/components/EditPage/Header.js b/src/components/EditPage/Header.js index 7acea75f..bddc23c5 100644 --- a/src/components/EditPage/Header.js +++ b/src/components/EditPage/Header.js @@ -1,6 +1,7 @@ import { ERROR_NEW_KEYWORD_MISSING, DEFAULT_TITLE, DISABLED_ID, ROOT_TITLE } from "../utils/constants.js"; import { hasId, hasNewTarget, hasTitle } from "../utils/error.js"; import { setHeaderChange } from "../utils/router.js"; +import { classNameObj } from "../utils/constants.js"; export default function Header({ $target, initialState, onEditing }) { if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); @@ -43,7 +44,7 @@ export default function Header({ $target, initialState, onEditing }) { if (!isInit) { const { title } = this.state; $header.innerHTML = ` -
+
`; diff --git a/src/components/utils/constants.js b/src/components/utils/constants.js index 3b986a9d..1264cdea 100644 --- a/src/components/utils/constants.js +++ b/src/components/utils/constants.js @@ -24,6 +24,7 @@ export const idNameObj = { export const classNameObj = { TITLE_WRAPPER: "title-wrapper", TITLE: "title", + INPUT_WRAPPER: "input-wrapper", BTN_WRAPPER: "btn-wrapper", DISPLAY_BTN: "display-btn", NEW_BTN: "new-btn", @@ -34,7 +35,8 @@ export const classNameObj = { DOCUMENT_BLOCK_INNER: "document-block-inner", SIDEBAR_DOCUMENT_LIST_CONTAINER: "sidebar-list-container", EDITOR_TOPBAR: "topbar", - EDOTOR_EDITOR: "editor" + EDOTOR_EDITOR: "editor", + SCROLLBAR: "scrollbar" }; export const styleObj = { @@ -47,6 +49,7 @@ export const DOCUMENT_FOOTER_CONTENT = "New Doc"; export const DISABLED_ID = -1; export const DEFAULT_ID = "new"; +//state export const DEFAULT_STATE = { id: DISABLED_ID, title: ROOT_TITLE, @@ -67,16 +70,14 @@ export const ERROR_NEW_KEYWORD_MISSING = "Error: missing new keyword"; export const ERROR_API_CALL = "Error: Api call"; //localstorage -export const LOCAL_STORAGE_RECENT_DOCUMENT = "recent-document"; export const LOCAL_STORAGE_DISPLAY = "display"; -export const LOCAL_IS_GOING_TO_PUT = "is-going-to-put"; -export const LOCAL_IS_PUTTING_OKAY = "is-putting-okay"; -export const LOCAL_IS_WAITING_USER_ANSWER = "is-waiting-user-answer"; -//route keyword +//event keyword export const EVENT_ROUTE_PUSH = "route-push"; export const EVENT_ROUTE_REMOVE = "route-remove-document"; export const EVENT_ROUTE_CREATE = "route-create-document"; export const EVENT_ROUTE_PUT = "route-put-document"; - export const EVENT_HEADER_CHANGE = "header-change"; + +//url +export const SLASH_DOCUMENTS = "/documents" diff --git a/src/components/utils/router.js b/src/components/utils/router.js index aa5fc398..9520b81f 100644 --- a/src/components/utils/router.js +++ b/src/components/utils/router.js @@ -7,6 +7,7 @@ import { EVENT_ROUTE_CREATE, EVENT_ROUTE_PUT, EVENT_ROUTE_REMOVE, + SLASH_DOCUMENTS, } from "./constants.js"; const { DOCUMENT_BLOCK, TITLE } = classNameObj; @@ -17,7 +18,7 @@ export const initRouter = (onRoute) => { removeAllDocument(subDocument); } - await request(`/documents/${document.id}`, { + await request(`${SLASH_DOCUMENTS}/${document.id}`, { method: "DELETE", }); }; @@ -31,7 +32,7 @@ export const initRouter = (onRoute) => { if (!nextUrl) return; history.pushState(null, null, nextUrl); - onRoute(parentId); + onRoute({ parentId }); }); window.addEventListener(EVENT_ROUTE_REMOVE, async (e) => { @@ -39,17 +40,17 @@ export const initRouter = (onRoute) => { if (!id) return; - const removedDocument = await request(`/documents/${id}`); + const removedDocument = await request(`${SLASH_DOCUMENTS}/${id}`); await removeAllDocument(removedDocument); - routePush(`${parentId ? `/documents/${parentId}` : "/"}`); + routePush(`${parentId ? `${SLASH_DOCUMENTS}/${parentId}` : "/"}`); }); window.addEventListener(EVENT_ROUTE_CREATE, async (e) => { const { id } = e.detail; - const createNewDocument = await request("/documents", { + const createNewDocument = await request(`${SLASH_DOCUMENTS}`, { method: "POST", body: JSON.stringify({ title: DEFAULT_TITLE, @@ -59,13 +60,13 @@ export const initRouter = (onRoute) => { }); console.log(createNewDocument); - routePush(`/documents/${createNewDocument.id}`, id); + routePush(`${SLASH_DOCUMENTS}/${createNewDocument.id}`, id); }); window.addEventListener(EVENT_ROUTE_PUT, async (e) => { const { id, title, content } = e.detail; - await request(`/documents/${id}`, { + await request(`${SLASH_DOCUMENTS}/${id}`, { method: "PUT", body: JSON.stringify({ title, @@ -110,7 +111,10 @@ export const setHeaderChange = ({ id, title }) => { export const routePush = (nextUrl, parentId) => { window.dispatchEvent( new CustomEvent(EVENT_ROUTE_PUSH, { - detail: { nextUrl, parentId }, + detail: { + nextUrl, + parentId + }, }) ); }; From ef4b42ef875e1581fe63e0e23fee910aa75dd1da Mon Sep 17 00:00:00 2001 From: Kal-MH Date: Thu, 17 Nov 2022 09:10:48 +0900 Subject: [PATCH 16/19] =?UTF-8?q?Comment:=20=EC=A3=BC=EC=84=9D=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EB=B0=8F=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/App.js | 1 - src/components/Button/DeleteButton.js | 1 - src/components/DocumentPage/DocumentPage.js | 4 -- src/components/DocumentPage/Documents.js | 5 --- src/components/EditPage/EditPage.js | 4 -- src/components/EditPage/Editor.js | 9 ++--- src/components/EditPage/Header.js | 4 -- src/components/EditPage/Topbar.js | 1 - src/components/utils/constants.js | 44 ++++++++++----------- src/components/utils/error.js | 4 +- src/components/utils/router.js | 2 +- src/components/utils/storage.js | 5 --- 12 files changed, 28 insertions(+), 56 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index d9415f65..1e84ca18 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -45,7 +45,6 @@ export default function App({ $target }) { } else { editPage.setState({ ...REMOVED_DOC_STATE}) } - } }; diff --git a/src/components/Button/DeleteButton.js b/src/components/Button/DeleteButton.js index 033bf7fc..eec09bb0 100644 --- a/src/components/Button/DeleteButton.js +++ b/src/components/Button/DeleteButton.js @@ -25,7 +25,6 @@ export default function DeleteButton({ $target, initialState }) { } }; - //event handler $button.addEventListener("click", async () => { const { id, parentId } = this.state; diff --git a/src/components/DocumentPage/DocumentPage.js b/src/components/DocumentPage/DocumentPage.js index f16c179d..0d399ddc 100644 --- a/src/components/DocumentPage/DocumentPage.js +++ b/src/components/DocumentPage/DocumentPage.js @@ -7,22 +7,18 @@ import Documents from "./Documents.js"; export default function DocumentPage({ $target, initialState }) { if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); - //tags const $page = document.createElement("div"); $page.setAttribute("id", idNameObj.SIDEBAR_CONTAINER); let isInit = false; - //validation const isValidState = (state) => { if (!state || !isValidArray(state)) return false; return true; }; - //state this.state = isValidState(initialState) ? initialState : []; - //components new DocumentHeader({ $target: $page }); const documents = new Documents({ diff --git a/src/components/DocumentPage/Documents.js b/src/components/DocumentPage/Documents.js index f7d1f605..ecf10b09 100644 --- a/src/components/DocumentPage/Documents.js +++ b/src/components/DocumentPage/Documents.js @@ -25,20 +25,16 @@ const { export default function Documents({ $target, initialState }) { if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); - //tags const $sidebar = document.createElement("div"); $sidebar.setAttribute("id", SIDEBAR_DOCUMENT_LIST_CONTAINER); $sidebar.setAttribute("class", SCROLLBAR) $target.appendChild($sidebar); - - //validation const isValidState = (state) => { if (!state || !isValidArray(state)) return false; return true; }; - //state this.state = isValidState(initialState) ? initialState : []; const displayMap = getItem(LOCAL_STORAGE_DISPLAY, new Map(this.state.map((document) => [document.id, false]))); @@ -60,7 +56,6 @@ export default function Documents({ $target, initialState }) { this.render(); - //event handlers $sidebar.addEventListener("click", (e) => { const { target } = e; const { classList } = target; diff --git a/src/components/EditPage/EditPage.js b/src/components/EditPage/EditPage.js index 37dbad14..b3bda16c 100644 --- a/src/components/EditPage/EditPage.js +++ b/src/components/EditPage/EditPage.js @@ -12,21 +12,18 @@ import Topbar from "./Topbar.js"; export default function EditPage({ $target, initialState }) { if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); - //tags const $page = document.createElement("div"); $page.setAttribute("id", idNameObj.EDITOR_CONTAINER); let isInit = false; let timer = null; - //state this.state = initialState || { id: DISABLED_ID, title: "", content: "", }; - //components const topbar = new Topbar({ $target: $page, initialState: {}, @@ -84,7 +81,6 @@ export default function EditPage({ $target, initialState }) { $target.appendChild($page); }; - //method const autoSaveDocument = ({ delay }) => { if (timer !== null) { clearTimeout(timer); diff --git a/src/components/EditPage/Editor.js b/src/components/EditPage/Editor.js index 655b70bb..c3fe9894 100644 --- a/src/components/EditPage/Editor.js +++ b/src/components/EditPage/Editor.js @@ -10,17 +10,15 @@ export default function Editor({ $target, initialState, onEditing }) { let isInit = false; - //validation - const isValidContent = (state) => { + const isValidState = (state) => { if (!state || !hasContent(state)) return false; return true; }; - //state - this.state = isValidContent(initialState) ? initialState : { content: "" }; + this.state = isValidState(initialState) ? initialState : { content: "" }; this.setState = (nextState) => { - if (!isValidContent(nextState)) return; + if (!isValidState(nextState)) return; this.state = nextState; @@ -47,7 +45,6 @@ export default function Editor({ $target, initialState, onEditing }) { this.render(); - //event handler $editor.addEventListener("input", (e) => { const { value } = e.target; diff --git a/src/components/EditPage/Header.js b/src/components/EditPage/Header.js index bddc23c5..e8840f0b 100644 --- a/src/components/EditPage/Header.js +++ b/src/components/EditPage/Header.js @@ -6,20 +6,17 @@ import { classNameObj } from "../utils/constants.js"; export default function Header({ $target, initialState, onEditing }) { if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); - //tags const $header = document.createElement("header"); $target.appendChild($header); let isInit = false; - //validation const isValidState = (state) => { if (!state) return false; if (!hasId(state) || !hasTitle(state)) return false; return true; }; - //state this.state = isValidState(initialState) ? initialState : { title: "" }; this.setState = (nextState) => { @@ -54,7 +51,6 @@ export default function Header({ $target, initialState, onEditing }) { this.render(); - //event handler $header.addEventListener("input", (e) => { const { value } = e.target; diff --git a/src/components/EditPage/Topbar.js b/src/components/EditPage/Topbar.js index 177b6a21..f107fff8 100644 --- a/src/components/EditPage/Topbar.js +++ b/src/components/EditPage/Topbar.js @@ -16,7 +16,6 @@ export default function Topbar({ $target, initialState }) { this.state = isValidState(initialState) ? initialState : { id: DISABLED_ID }; - //topbar with fetchButton const deleteBtn = new DeleteButton({ $target: $topbar, initialState: {}, diff --git a/src/components/utils/constants.js b/src/components/utils/constants.js index 1264cdea..2564e537 100644 --- a/src/components/utils/constants.js +++ b/src/components/utils/constants.js @@ -1,4 +1,4 @@ -//validate properties +//state properties export const properties = { ID: "id", TITLE: "title", @@ -6,13 +6,31 @@ export const properties = { DOCUMENTS: "documents", }; -//Header +//Default states +export const DEFAULT_STATE = { + id: DISABLED_ID, + title: ROOT_TITLE, + content: "", +}; + +export const REMOVED_DOC_STATE = { + id: DISABLED_ID, + title: REMOVED_DOC_TITLE, + content: "", +}; + +//Default values export const DEFAULT_TITLE = "제목 없음"; export const ROOT_TITLE = "작업 중... 💨"; export const REMOVED_DOC_TITLE = "삭제된 문서입니다." export const DEFAULT_CONTENT = "내용을 입력해주세요"; -//Document +export const DOCUMENT_HEADER_CONTENT = "Notion"; +export const DOCUMENT_FOOTER_CONTENT = "New Doc"; +export const DISABLED_ID = -1; +export const DEFAULT_ID = "new"; + +//Name Objects export const idNameObj = { SIDEBAR_CONTAINER: "sidebar-container", SIDEBAR_HEADER: "sidebar-header", @@ -44,24 +62,6 @@ export const styleObj = { PADDING_INCREMENT: 20, }; -export const DOCUMENT_HEADER_CONTENT = "Notion"; -export const DOCUMENT_FOOTER_CONTENT = "New Doc"; -export const DISABLED_ID = -1; -export const DEFAULT_ID = "new"; - -//state -export const DEFAULT_STATE = { - id: DISABLED_ID, - title: ROOT_TITLE, - content: "", -}; - -export const REMOVED_DOC_STATE = { - id: DISABLED_ID, - title: REMOVED_DOC_TITLE, - content: "", -}; - //Button export const DELETE_BUTTON_TEXT = "Delete"; @@ -72,7 +72,7 @@ export const ERROR_API_CALL = "Error: Api call"; //localstorage export const LOCAL_STORAGE_DISPLAY = "display"; -//event keyword +//event export const EVENT_ROUTE_PUSH = "route-push"; export const EVENT_ROUTE_REMOVE = "route-remove-document"; export const EVENT_ROUTE_CREATE = "route-create-document"; diff --git a/src/components/utils/error.js b/src/components/utils/error.js index 3b2d816b..56f36098 100644 --- a/src/components/utils/error.js +++ b/src/components/utils/error.js @@ -2,7 +2,7 @@ import { properties } from "./constants.js"; const { ID, TITLE, CONTENT, DOCUMENTS } = properties; -//new target +//properties export const hasNewTarget = (target) => (target ? true : false); export const hasId = (state) => state.hasOwnProperty(ID); @@ -13,5 +13,5 @@ export const hasContent = (state) => state.hasOwnProperty(CONTENT); export const hasDocuments = (state) => state.hasOwnProperty(DOCUMENTS); -//array +//type export const isValidArray = (arr) => arr && Array.isArray(arr); diff --git a/src/components/utils/router.js b/src/components/utils/router.js index 9520b81f..d514c4dd 100644 --- a/src/components/utils/router.js +++ b/src/components/utils/router.js @@ -23,7 +23,7 @@ export const initRouter = (onRoute) => { }); }; - //event listener + //event handlers window.addEventListener("popstate", () => onRoute()); window.addEventListener(EVENT_ROUTE_PUSH, (e) => { diff --git a/src/components/utils/storage.js b/src/components/utils/storage.js index 0d5860ac..e965061a 100644 --- a/src/components/utils/storage.js +++ b/src/components/utils/storage.js @@ -1,8 +1,3 @@ -/* - * 모든 데이터가 JSON형식인 상황이기 때문에 - * storage 내부 메서드에서 JSON.stringfy, JSON.parse가 행해진다. - */ - const storage = window.localStorage; export const setItem = (key, value) => { From ec9e2a6e9ac0812bd4b24d7407cdfec3a0f8a883 Mon Sep 17 00:00:00 2001 From: Kal-MH Date: Fri, 18 Nov 2022 15:54:34 +0900 Subject: [PATCH 17/19] =?UTF-8?q?fix:=20=EC=B4=88=EA=B8=B0=ED=99=94=20?= =?UTF-8?q?=EC=A0=84=EC=97=90=20=EC=82=AC=EC=9A=A9=EB=90=98=EB=8A=94=20con?= =?UTF-8?q?stant=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/utils/constants.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/components/utils/constants.js b/src/components/utils/constants.js index 2564e537..2fe68528 100644 --- a/src/components/utils/constants.js +++ b/src/components/utils/constants.js @@ -6,6 +6,17 @@ export const properties = { DOCUMENTS: "documents", }; +//Default values +export const DEFAULT_TITLE = "제목 없음"; +export const ROOT_TITLE = "작업 중... 💨"; +export const REMOVED_DOC_TITLE = "삭제된 문서입니다."; +export const DEFAULT_CONTENT = "내용을 입력해주세요"; + +export const DOCUMENT_HEADER_CONTENT = "Notion"; +export const DOCUMENT_FOOTER_CONTENT = "New Doc"; +export const DISABLED_ID = -1; +export const DEFAULT_ID = "new"; + //Default states export const DEFAULT_STATE = { id: DISABLED_ID, @@ -19,17 +30,6 @@ export const REMOVED_DOC_STATE = { content: "", }; -//Default values -export const DEFAULT_TITLE = "제목 없음"; -export const ROOT_TITLE = "작업 중... 💨"; -export const REMOVED_DOC_TITLE = "삭제된 문서입니다." -export const DEFAULT_CONTENT = "내용을 입력해주세요"; - -export const DOCUMENT_HEADER_CONTENT = "Notion"; -export const DOCUMENT_FOOTER_CONTENT = "New Doc"; -export const DISABLED_ID = -1; -export const DEFAULT_ID = "new"; - //Name Objects export const idNameObj = { SIDEBAR_CONTAINER: "sidebar-container", @@ -54,7 +54,7 @@ export const classNameObj = { SIDEBAR_DOCUMENT_LIST_CONTAINER: "sidebar-list-container", EDITOR_TOPBAR: "topbar", EDOTOR_EDITOR: "editor", - SCROLLBAR: "scrollbar" + SCROLLBAR: "scrollbar", }; export const styleObj = { @@ -80,4 +80,4 @@ export const EVENT_ROUTE_PUT = "route-put-document"; export const EVENT_HEADER_CHANGE = "header-change"; //url -export const SLASH_DOCUMENTS = "/documents" +export const SLASH_DOCUMENTS = "/documents"; From 38230d37e43d48fcf09f93a954daab3632b8e960 Mon Sep 17 00:00:00 2001 From: Kal-MH Date: Sun, 27 Nov 2022 02:05:05 +0900 Subject: [PATCH 18/19] =?UTF-8?q?modified:=20=ED=94=BC=EB=93=9C=EB=B0=B1?= =?UTF-8?q?=20=EB=B0=98=EC=98=811=20-=20DeleteButton.js,=20documentUtil.js?= =?UTF-8?q?=20js=EB=A5=BC=20=ED=86=B5=ED=95=B4=EC=84=9C=20style=EB=B3=80?= =?UTF-8?q?=ED=99=94=EC=8B=9C=ED=82=A4=EB=8A=94=20=EB=B6=80=EB=B6=84=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20->=20className=EC=9D=84=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20-=20init()=ED=95=A8=EC=88=98=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=20-=20=EC=A1=B0=EA=B1=B4=EB=AC=B8=20=EA=B0=84=EB=9E=B5?= =?UTF-8?q?=ED=99=94(this.state.id=20=3D=3D=3D=20DISABLE=5FID)=20=EB=B0=8F?= =?UTF-8?q?=20=EB=B3=80=EC=88=98=20=EC=9D=B4=EB=A6=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- css/style.css | 13 ++++- index.html | 2 +- src/components/Button/DeleteButton.js | 11 ++-- src/components/DocumentPage/Documents.js | 23 +++++--- .../document_util/documentUtil.js | 27 +++++---- src/components/EditPage/Editor.js | 32 +++++----- src/components/EditPage/Header.js | 58 +++++++++---------- src/components/utils/constants.js | 2 +- src/components/utils/error.js | 4 +- 9 files changed, 94 insertions(+), 78 deletions(-) diff --git a/css/style.css b/css/style.css index 407f2121..bd531873 100644 --- a/css/style.css +++ b/css/style.css @@ -14,8 +14,8 @@ Variables --display-icon: "\f054"; --remove-icon: "\f1f8"; --new-icon: "\2b"; - --scrollbar-thumb: #D3D1CB; - --scrollbar-track: #EDECE9; + --scrollbar-thumb: #d3d1cb; + --scrollbar-track: #edece9; } /* @@ -76,6 +76,14 @@ COMMON background: var(--scrollbar-track); } +.none { + display: none; +} + +.block { + display: block; +} + /* SIDEBAR_PAGE =============================*/ @@ -326,7 +334,6 @@ EDITOR-PAGE } #editor-container .topbar div { - display: block; padding: 5px 10px; border-radius: 3px; border: 1px solid var(--btn-color); diff --git a/index.html b/index.html index 0dc10aa8..39915ccb 100644 --- a/index.html +++ b/index.html @@ -1,5 +1,5 @@ - + diff --git a/src/components/Button/DeleteButton.js b/src/components/Button/DeleteButton.js index eec09bb0..ea007e47 100644 --- a/src/components/Button/DeleteButton.js +++ b/src/components/Button/DeleteButton.js @@ -6,6 +6,7 @@ export default function DeleteButton({ $target, initialState }) { if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); const $button = document.createElement("div"); + $button.setAttribute("class", "none"); $target.appendChild($button); const isValidState = (state) => { @@ -15,13 +16,15 @@ export default function DeleteButton({ $target, initialState }) { this.state = isValidState(initialState) ? initialState : {}; - this.setState = (initialState) => { - this.state = initialState; + this.setState = (nextState) => { + this.state = nextState; if (this.state.id === DISABLED_ID) { - $button.style.display = "none"; + $button.classList.remove("block"); + $button.classList.add("none"); } else { - $button.style.display = "block"; + $button.classList.remove("none"); + $button.classList.add("block"); } }; diff --git a/src/components/DocumentPage/Documents.js b/src/components/DocumentPage/Documents.js index ecf10b09..17373c58 100644 --- a/src/components/DocumentPage/Documents.js +++ b/src/components/DocumentPage/Documents.js @@ -1,4 +1,10 @@ -import { classNameObj, ERROR_NEW_KEYWORD_MISSING, LOCAL_STORAGE_DISPLAY, SLASH_DOCUMENTS, styleObj } from "../utils/constants.js"; +import { + classNameObj, + ERROR_NEW_KEYWORD_MISSING, + LOCAL_STORAGE_DISPLAY, + SLASH_DOCUMENTS, + styleObj, +} from "../utils/constants.js"; import { hasNewTarget, isValidArray } from "../utils/error.js"; import { routeCreateDocument, routePush, routeRemoveDocument } from "../utils/router.js"; import { getItem, setItem } from "../utils/storage.js"; @@ -19,7 +25,7 @@ const { DOCUMENT_LIST_BLOCK, DOCUMENT_SECTION, SIDEBAR_DOCUMENT_LIST_CONTAINER, - SCROLLBAR + SCROLLBAR, } = classNameObj; export default function Documents({ $target, initialState }) { @@ -27,7 +33,7 @@ export default function Documents({ $target, initialState }) { const $sidebar = document.createElement("div"); $sidebar.setAttribute("id", SIDEBAR_DOCUMENT_LIST_CONTAINER); - $sidebar.setAttribute("class", SCROLLBAR) + $sidebar.setAttribute("class", SCROLLBAR); $target.appendChild($sidebar); const isValidState = (state) => { @@ -37,7 +43,7 @@ export default function Documents({ $target, initialState }) { this.state = isValidState(initialState) ? initialState : []; - const displayMap = getItem(LOCAL_STORAGE_DISPLAY, new Map(this.state.map((document) => [document.id, false]))); + const openDocumentsList = getItem(LOCAL_STORAGE_DISPLAY, new Map(this.state.map((document) => [document.id, false]))); this.setState = (nextState) => { if (!isValidState(nextState)) return; @@ -49,7 +55,7 @@ export default function Documents({ $target, initialState }) { this.render = () => { $sidebar.innerHTML = ` ${this.state - .map((rootDocument) => createDocumentSection(rootDocument, styleObj.DEFAULT_PADDING, displayMap)) + .map((rootDocument) => createDocumentSection(rootDocument, styleObj.DEFAULT_PADDING, openDocumentsList)) .join("")} `; }; @@ -64,10 +70,9 @@ export default function Documents({ $target, initialState }) { const parentId = documentSection.parentNode.dataset.id; if (classList[0] === TITLE || classList[0] === TITLE_WRAPPER) { - routePush(`${SLASH_DOCUMENTS}/${id}`, parentId); } else if (classList[0] === DISPLAY_BTN) { - sidebarDisplayBtnClick(id, target, displayMap); + sidebarDisplayBtnClick(id, target, openDocumentsList); } else if (classList[0] === NEW_BTN) { //낙관적 업데이트 sidebarNewDocumentBtnClick(id, target); @@ -76,8 +81,8 @@ export default function Documents({ $target, initialState }) { if (documentSection) { const documentListBlock = documentSection.querySelector(`.${DOCUMENT_LIST_BLOCK}`); - displayMap[id] = true; - setItem(LOCAL_STORAGE_DISPLAY, displayMap); + openDocumentsList[id] = true; + setItem(LOCAL_STORAGE_DISPLAY, openDocumentsList); documentListBlock.style.display = "block"; } diff --git a/src/components/DocumentPage/document_util/documentUtil.js b/src/components/DocumentPage/document_util/documentUtil.js index 00ff9ab9..2ade3e00 100644 --- a/src/components/DocumentPage/document_util/documentUtil.js +++ b/src/components/DocumentPage/document_util/documentUtil.js @@ -76,7 +76,7 @@ const addNewDocumentList = (id, documentListAddBlock, paddingLeft) => { }; //public method -export const createDocumentSection = (parentDocument, padding, displayMap) => { +export const createDocumentSection = (parentDocument, padding, openDocumentsList) => { const { id, title, documents } = parentDocument; if (id && title !== undefined) { @@ -86,10 +86,11 @@ export const createDocumentSection = (parentDocument, padding, displayMap) => { ${ documents.length ? ` -
+
${documents - .map((document) => createDocumentSection(document, padding + PADDING_INCREMENT, displayMap)) + .map((document) => + createDocumentSection(document, padding + PADDING_INCREMENT, openDocumentsList) + ) .join("")}
` @@ -100,21 +101,23 @@ export const createDocumentSection = (parentDocument, padding, displayMap) => { } }; -export const sidebarDisplayBtnClick = (id, target, displayMap) => { +export const sidebarDisplayBtnClick = (id, target, openDocumentsList) => { const documentListBlock = target.closest(`.${DOCUMENT_SECTION}`).querySelector(`.${DOCUMENT_LIST_BLOCK}`); if (!documentListBlock) return; - const { display } = documentListBlock.style; + const { classList } = documentListBlock; - if (display === "none") { - displayMap[id] = true; - documentListBlock.style.display = "block"; + if (classList.contains("none")) { + openDocumentsList[id] = true; + classList.remove("none"); + classList.add("block"); } else { - displayMap[id] = false; - documentListBlock.style.display = "none"; + openDocumentsList[id] = false; + classList.remove("block"); + classList.add("none"); } - setItem(LOCAL_STORAGE_DISPLAY, displayMap); + setItem(LOCAL_STORAGE_DISPLAY, openDocumentsList); }; export const sidebarNewDocumentBtnClick = (id, target) => { diff --git a/src/components/EditPage/Editor.js b/src/components/EditPage/Editor.js index c3fe9894..2c53a243 100644 --- a/src/components/EditPage/Editor.js +++ b/src/components/EditPage/Editor.js @@ -8,8 +8,6 @@ export default function Editor({ $target, initialState, onEditing }) { $editor.setAttribute("class", classNameObj.EDOTOR_EDITOR); $target.appendChild($editor); - let isInit = false; - const isValidState = (state) => { if (!state || !hasContent(state)) return false; return true; @@ -32,26 +30,28 @@ export default function Editor({ $target, initialState, onEditing }) { textarea.value = this.state.content; } - this.state.id === DISABLED_ID ? (textarea.disabled = true) : (textarea.disabled = false); + textarea.disabled = this.state.id === DISABLED_ID; }; this.render = () => { - if (!isInit) { - $editor.innerHTML = ` - - `; - } + $editor.innerHTML = ` + + `; }; - this.render(); + const init = () => { + this.render(); - $editor.addEventListener("input", (e) => { - const { value } = e.target; + $editor.addEventListener("input", (e) => { + const { value } = e.target; - onEditing(value); - }); + onEditing(value); + }); + + $editor.querySelector("textarea").addEventListener("focus", (e) => { + e.target.placeholder = ""; + }); + }; - $editor.querySelector("textarea").addEventListener("focus", (e) => { - e.target.placeholder = ""; - }) + init(); } diff --git a/src/components/EditPage/Header.js b/src/components/EditPage/Header.js index e8840f0b..714cc9f4 100644 --- a/src/components/EditPage/Header.js +++ b/src/components/EditPage/Header.js @@ -9,11 +9,8 @@ export default function Header({ $target, initialState, onEditing }) { const $header = document.createElement("header"); $target.appendChild($header); - let isInit = false; - const isValidState = (state) => { - if (!state) return false; - if (!hasId(state) || !hasTitle(state)) return false; + if (!state || !hasId(state) || !hasTitle(state)) return false; return true; }; @@ -34,42 +31,43 @@ export default function Header({ $target, initialState, onEditing }) { input.value = title; } - this.state.id === DISABLED_ID ? (input.disabled = true) : (input.disabled = false); + input.disabled = this.state.id === DISABLED_ID; }; this.render = () => { - if (!isInit) { - const { title } = this.state; - $header.innerHTML = ` -
- -
- `; - isInit = true; - } + const { title } = this.state; + $header.innerHTML = ` +
+ +
+ `; }; - this.render(); + const init = () => { + this.render(); - $header.addEventListener("input", (e) => { - const { value } = e.target; + $header.addEventListener("input", (e) => { + const { value } = e.target; - setHeaderChange({ - id: this.state.id, - title: value, - }); + setHeaderChange({ + id: this.state.id, + title: value, + }); - onEditing(value); - }); + onEditing(value); + }); - $header.querySelector("input").addEventListener("focus", (e) => { - const { value } = e.target; + $header.querySelector("input").addEventListener("focus", (e) => { + const { value } = e.target; - e.target.placeholder = ""; + e.target.placeholder = ""; - setHeaderChange({ - id: this.state.id, - title: value, + setHeaderChange({ + id: this.state.id, + title: value, + }); }); - }) + }; + + init(); } diff --git a/src/components/utils/constants.js b/src/components/utils/constants.js index 2fe68528..d2254874 100644 --- a/src/components/utils/constants.js +++ b/src/components/utils/constants.js @@ -1,5 +1,5 @@ //state properties -export const properties = { +export const PROPERTIES = { ID: "id", TITLE: "title", CONTENT: "content", diff --git a/src/components/utils/error.js b/src/components/utils/error.js index 56f36098..e8b242ca 100644 --- a/src/components/utils/error.js +++ b/src/components/utils/error.js @@ -1,6 +1,6 @@ -import { properties } from "./constants.js"; +import { PROPERTIES } from "./constants.js"; -const { ID, TITLE, CONTENT, DOCUMENTS } = properties; +const { ID, TITLE, CONTENT, DOCUMENTS } = PROPERTIES; //properties export const hasNewTarget = (target) => (target ? true : false); From 7dda4dffdfecd0d6020c2f89ea1eb26efbb741ab Mon Sep 17 00:00:00 2001 From: Kal-MH Date: Sun, 27 Nov 2022 02:25:28 +0900 Subject: [PATCH 19/19] =?UTF-8?q?modified:=20=ED=95=98=EB=82=98=EC=9D=98?= =?UTF-8?q?=20=EB=AA=A8=EB=93=88=20=EC=95=88=EC=97=90=EC=84=9C=EB=A7=8C=20?= =?UTF-8?q?=EC=93=B0=EC=9D=B4=EB=8A=94=20=EC=83=81=EC=88=98=EA=B0=92?= =?UTF-8?q?=EC=97=90=20=EB=8C=80=ED=95=B4=20=ED=8C=8C=EC=9D=BC=20=EC=95=88?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=9C=84=EC=B9=98=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Button/DeleteButton.js | 2 +- src/components/DocumentPage/DocumentFooter.js | 9 ++------- src/components/DocumentPage/DocumentHeader.js | 9 ++------- src/components/utils/constants.js | 15 +-------------- src/components/utils/error.js | 10 +++++++++- 5 files changed, 15 insertions(+), 30 deletions(-) diff --git a/src/components/Button/DeleteButton.js b/src/components/Button/DeleteButton.js index ea007e47..f6b8abe5 100644 --- a/src/components/Button/DeleteButton.js +++ b/src/components/Button/DeleteButton.js @@ -1,5 +1,5 @@ import { hasNewTarget, hasId } from "../utils/error.js"; -import { DELETE_BUTTON_TEXT, DISABLED_ID, ERROR_NEW_KEYWORD_MISSING } from "../utils/constants.js"; +import { DISABLED_ID, ERROR_NEW_KEYWORD_MISSING } from "../utils/constants.js"; import { routeRemoveDocument } from "../utils/router.js"; export default function DeleteButton({ $target, initialState }) { diff --git a/src/components/DocumentPage/DocumentFooter.js b/src/components/DocumentPage/DocumentFooter.js index 5475e97f..07b5e1af 100644 --- a/src/components/DocumentPage/DocumentFooter.js +++ b/src/components/DocumentPage/DocumentFooter.js @@ -1,14 +1,9 @@ -import { - classNameObj, - styleObj, - idNameObj, - DOCUMENT_FOOTER_CONTENT, - ERROR_NEW_KEYWORD_MISSING, -} from "../utils/constants.js"; +import { classNameObj, styleObj, idNameObj, ERROR_NEW_KEYWORD_MISSING } from "../utils/constants.js"; import { hasNewTarget } from "../utils/error.js"; import { routeCreateDocument } from "../utils/router.js"; const { TITLE, NEW_BTN, DOCUMENT_BLOCK_INNER } = classNameObj; +const DOCUMENT_FOOTER_CONTENT = "New Doc"; export default function DocumentFooter({ $target }) { if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); diff --git a/src/components/DocumentPage/DocumentHeader.js b/src/components/DocumentPage/DocumentHeader.js index 64c5d668..0b6c637f 100644 --- a/src/components/DocumentPage/DocumentHeader.js +++ b/src/components/DocumentPage/DocumentHeader.js @@ -1,14 +1,9 @@ -import { - classNameObj, - styleObj, - idNameObj, - DOCUMENT_HEADER_CONTENT, - ERROR_NEW_KEYWORD_MISSING, -} from "../utils/constants.js"; +import { classNameObj, styleObj, idNameObj, ERROR_NEW_KEYWORD_MISSING } from "../utils/constants.js"; import { hasNewTarget } from "../utils/error.js"; import { routeCreateDocument } from "../utils/router.js"; const { TITLE, DOCUMENT_BLOCK_INNER, NEW_BTN } = classNameObj; +const DOCUMENT_HEADER_CONTENT = "Notion"; export default function DocumentHeader({ $target }) { if (!hasNewTarget(new.target)) throw new Error(ERROR_NEW_KEYWORD_MISSING); diff --git a/src/components/utils/constants.js b/src/components/utils/constants.js index d2254874..3c503869 100644 --- a/src/components/utils/constants.js +++ b/src/components/utils/constants.js @@ -1,19 +1,9 @@ -//state properties -export const PROPERTIES = { - ID: "id", - TITLE: "title", - CONTENT: "content", - DOCUMENTS: "documents", -}; - //Default values export const DEFAULT_TITLE = "제목 없음"; +export const DEFAULT_CONTENT = "내용을 입력해주세요"; export const ROOT_TITLE = "작업 중... 💨"; export const REMOVED_DOC_TITLE = "삭제된 문서입니다."; -export const DEFAULT_CONTENT = "내용을 입력해주세요"; -export const DOCUMENT_HEADER_CONTENT = "Notion"; -export const DOCUMENT_FOOTER_CONTENT = "New Doc"; export const DISABLED_ID = -1; export const DEFAULT_ID = "new"; @@ -62,9 +52,6 @@ export const styleObj = { PADDING_INCREMENT: 20, }; -//Button -export const DELETE_BUTTON_TEXT = "Delete"; - //error export const ERROR_NEW_KEYWORD_MISSING = "Error: missing new keyword"; export const ERROR_API_CALL = "Error: Api call"; diff --git a/src/components/utils/error.js b/src/components/utils/error.js index e8b242ca..75a03316 100644 --- a/src/components/utils/error.js +++ b/src/components/utils/error.js @@ -1,4 +1,12 @@ -import { PROPERTIES } from "./constants.js"; +// import { PROPERTIES } from "./constants.js"; + +//state properties +const PROPERTIES = { + ID: "id", + TITLE: "title", + CONTENT: "content", + DOCUMENTS: "documents", +}; const { ID, TITLE, CONTENT, DOCUMENTS } = PROPERTIES;