diff --git a/asset/notion.png b/asset/notion.png new file mode 100644 index 00000000..adcfb253 Binary files /dev/null and b/asset/notion.png differ diff --git a/index.html b/index.html new file mode 100644 index 00000000..036211d8 --- /dev/null +++ b/index.html @@ -0,0 +1,18 @@ + + + + + + + + + + + + 유진의 Notion + + +
+ + + \ No newline at end of file diff --git a/src/App.js b/src/App.js new file mode 100644 index 00000000..c930e0c9 --- /dev/null +++ b/src/App.js @@ -0,0 +1,30 @@ +import { EditorPage } from "./EditorPage.js"; +import { DocumentPage } from "./DocumentPage.js"; +import { initRouter } from "./router.js"; + +export default function App ($target) { + const $app = document.createElement('div'); + $app.className = 'mainApp' + const editorPage = new EditorPage($app); + const documentPage = new DocumentPage($app); + + $target.appendChild($app); + + this.route = () => { + const { pathname } = window.location; + if(pathname.includes('/documents/')){ + const [, , documentId] = pathname.split('/'); + editorPage.setState({...editorPage.state, id : documentId}); + } + } + this.render = () => { + documentPage.render(); + const { pathname } = window.location; + const [, , documentId] = pathname.split('/'); + if(documentId){ + editorPage.setState({...editorPage.state, id: documentId}) + } + } + this.render() + initRouter({onRoute : this.route}); +} \ No newline at end of file diff --git a/src/Components/ChildDocuments.js b/src/Components/ChildDocuments.js new file mode 100644 index 00000000..d113bb31 --- /dev/null +++ b/src/Components/ChildDocuments.js @@ -0,0 +1,33 @@ +import { routerNav } from '../router.js'; + +export default function ChildDocument ({$target, $document}){ + const $childDocument = document.createElement('div'); + $childDocument.className = 'childLink'; + $target.append($childDocument); + + this.state = { + data : $document + } + this.setState = nextState => { + this.state = nextState + this.render(); + } + this.render = () => { + if(this.state.data){ + const { documents } = this.state.data + if(documents){ + $childDocument.innerHTML = `${ + documents.map(({id, title}) => + `
  • ${title}
  • ` + ).join('')}` + } + } + } + + this.render(); + $childDocument.addEventListener('click', (e) => { + const $link = e.target.closest('li'); + const { id } = $link.dataset; + routerNav(`/documents/${id}`) + }) +} \ No newline at end of file diff --git a/src/Components/DocumentCreate.js b/src/Components/DocumentCreate.js new file mode 100644 index 00000000..ee68f616 --- /dev/null +++ b/src/Components/DocumentCreate.js @@ -0,0 +1,34 @@ +import { DocumentModal } from "./DocumentModal.js"; + +export function DocumentCreate({$target, parentId, onSubmit}){ + this.state = { + parentId :null, + } + this.setState = (nextState) => { + this.state = nextState; + } + this.render = () => { + const $createBtn = document.createElement('button'); + $createBtn.className = 'createDoc'; + if (parentId === null){ + $createBtn.className = 'rootCreate' + } + $createBtn.textContent = '+'; + $target.append($createBtn); + } + $target.addEventListener('click', (e) => { + const $createBtn = e.target.closest('button'); + if($createBtn){ + if($createBtn.classList.contains('rootCreate')){ + e.stopImmediatePropagation(); + const modal = new DocumentModal(null , onSubmit) + modal.modalOpen(); + return + } + const { id } = $createBtn.nextElementSibling.dataset + const modal = new DocumentModal(id , onSubmit) + modal.modalOpen(); + } + }) + this.render() +} \ No newline at end of file diff --git a/src/Components/DocumentDelete.js b/src/Components/DocumentDelete.js new file mode 100644 index 00000000..a79c1ad0 --- /dev/null +++ b/src/Components/DocumentDelete.js @@ -0,0 +1,20 @@ +import { deleteDocuments } from "../api.js"; +import { routerNav } from "../router.js"; + +export default function DocumentDelete ({$target, id}){ + const $deleteBtn = document.createElement('button'); + $deleteBtn.className= 'delete-btn' + $target.appendChild($deleteBtn) + $deleteBtn.textContent = '삭제하기' + + this.state = { id } + this.setState = nextState => { + this.state = nextState + } + $deleteBtn.addEventListener('click', async (e) => { + await deleteDocuments(this.state.id) + alert("삭제 완료"); + routerNav('/'); + location.reload(); + }) +} \ No newline at end of file diff --git a/src/Components/DocumentList.js b/src/Components/DocumentList.js new file mode 100644 index 00000000..4434f766 --- /dev/null +++ b/src/Components/DocumentList.js @@ -0,0 +1,95 @@ +import { DocumentCreate } from "./DocumentCreate.js" +import { routerNav } from '../router.js'; + +export function DocumentList({$target, data =[], initialState, onSubmit}) { + let init = false; + this.state = initialState + this.setState = (nextState) => { + this.state = nextState + } + this.render = ($renderDOM = $target) => { + const $parentNode = document.createElement('div') + $parentNode.className = `doc-${this.state.selectedNode}` + $parentNode.style.marginLeft = `${this.state.depth * 10}px`; + const createBtn = new DocumentCreate({ + $target: $parentNode, + parentId: this.state.parent, + onSubmit: onSubmit + }) + const doc = document.createElement('li'); + doc.setAttribute("data-id", `${data[0].id}`); + doc.setAttribute("class", 'doc'); + doc.textContent =`${data[0].title}` + $parentNode.append(doc) + console.log(this.state.isOpen) + if(data[0].documents.length === 0){ + const $haveNothing = document.createElement('div'); + $haveNothing.classList.add('nothing') + $haveNothing.textContent = '하위 페이지 없음' + doc.append($haveNothing) + } + else { + data[0].documents.forEach((data => { + const documentList = new DocumentList({ + $target: doc, + data: [data], + initialState: {parent: this.state.selectedNode, selectedNode: data.id, depth: this.state.depth + 1, isOpen: false}, + onSubmit: onSubmit + }) + documentList.render() + })) + } + init= true + $renderDOM.append($parentNode) + + } + + $target.addEventListener('click', (e) => { + e.stopPropagation(); + if($target.classList.contains('documentPage') && !e.target.classList.contains('doc')) + return + else if (e.target.classList.contains('createDoc')){ + return + } + else if (e.target.classList.contains('nothing')){ + return + } + console.log(e.target) + console.log(this.state) + if(this.state.isOpen){ + while(e.target.querySelector('div')){ + e.target.classList.remove("open"); + const $removeTarget = e.target.querySelector('div') + e.target.removeChild($removeTarget); + } + this.setState({ + ...this.state, + isOpen: !this.state.isOpen, + }) + return + } + const $li = e.target + if($li){ + const { id } = $li.dataset; + if(data){ + const childrenData = data.map(data => data.documents) + $li.classList.add('open') + if(childrenData[0].length > 0){ + this.setState({ + parent: this.state.depth === 0 ? parseInt(id, 10) : parseInt(this.state.parent, 10), + selectedNode: parseInt(id), + isOpen: !this.state.isOpen, + depth: this.state.depth + }) + } + else { + this.setState({ + ...this.state, + isOpen: !this.state.isOpen, + }) + } + routerNav(`/documents/${id}`); + } + } + }) +} \ No newline at end of file diff --git a/src/Components/DocumentModal.js b/src/Components/DocumentModal.js new file mode 100644 index 00000000..b8bb4e0a --- /dev/null +++ b/src/Components/DocumentModal.js @@ -0,0 +1,46 @@ +export function DocumentModal(id , onSubmit){ + const $app = document.querySelector('.app'); + const $modalContainer = document.createElement('div') + $modalContainer.className = 'modal'; + $app.appendChild($modalContainer); + this.render = () => { + $modalContainer.innerHTML = ` + + ` + } + + $modalContainer.addEventListener('submit', async (e) => { + e.preventDefault(); + const $input = $modalContainer.querySelector('.modalText'); + let content = $input.value; + if(content.length === 0) { + content = '제목 없음' + } + await onSubmit(content, id); + $input.value ='' + alert('문서 생성이 완료되었습니다') + const modal = document.querySelector('.modal'); + modal.remove() + }) + + $modalContainer.addEventListener('click', (e) => { + const $closeBtn = e.target.closest('button') + if(!$closeBtn) return + if ($closeBtn.classList.contains('closeBtn')){ + const modal = document.querySelector('.modal'); + modal.remove() + } + }) + + this.modalOpen = () => { + const modal = document.querySelector('.modal'); + modal.style.display = "block"; + document.body.style.overflow = "hidden"; + } + this.render() +} \ No newline at end of file diff --git a/src/Components/Editor.js b/src/Components/Editor.js new file mode 100644 index 00000000..533e7e02 --- /dev/null +++ b/src/Components/Editor.js @@ -0,0 +1,40 @@ +export default function Editor({$target, initialState = { + title: '', + content: '', +}, onEditing}){ + const $editor = document.createElement('div'); + $editor.className = 'editor' + $target.appendChild($editor); + this.state = initialState; + let isinitialize = false + + this.setState = (nextState) => { + this.state = nextState + $editor.querySelector('[name=content]').value = this.state.content; + $editor.querySelector('[name=title]').value = this.state.title; + this.render(); + } + this.render = () => { + if(!isinitialize){ + $editor.innerHTML = ` + +