From c701ea788686f1068525b3b833e561efa0081116 Mon Sep 17 00:00:00 2001 From: baekseungsun Date: Sat, 13 Sep 2025 04:40:22 +0900 Subject: [PATCH 1/5] =?UTF-8?q?=ED=97=A4=EB=8D=94=EC=99=80=20=EB=A9=94?= =?UTF-8?q?=EB=89=B4=20=EC=95=84=EC=9D=B4=EC=BD=98=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 index.html diff --git a/index.html b/index.html new file mode 100644 index 0000000..12a65c3 --- /dev/null +++ b/index.html @@ -0,0 +1,17 @@ + + + + + vanilla-todo 백승선 + + + + + + +
+ +

To Do

+
\ No newline at end of file From 100c5355d6d4998420130ab9d44f7d8c6d63857f Mon Sep 17 00:00:00 2001 From: baekseungsun Date: Sat, 13 Sep 2025 04:48:35 +0900 Subject: [PATCH 2/5] =?UTF-8?q?=EB=A9=94=EB=89=B4=20=ED=83=AD=EC=9D=98=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=EB=93=A4=EA=B3=BC=20=EA=B8=B0=ED=83=80=20ui?= =?UTF-8?q?=20=EB=AA=A8=EB=91=90=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/index.html b/index.html index 12a65c3..0aa31ca 100644 --- a/index.html +++ b/index.html @@ -14,4 +14,34 @@

To Do

- \ No newline at end of file + + + + +
+ + + +
+
+ + +
+
+
+ +
To-do: 0
+
+
    +
+
+ + \ No newline at end of file From 9d66d04eaf539665605ba8d436d7ef8f194c64d3 Mon Sep 17 00:00:00 2001 From: baekseungsun Date: Sat, 13 Sep 2025 04:50:27 +0900 Subject: [PATCH 3/5] =?UTF-8?q?style.css=20=ED=8C=8C=EC=9D=BC=EC=9D=84=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- style.css | 287 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 287 insertions(+) create mode 100644 style.css diff --git a/style.css b/style.css new file mode 100644 index 0000000..dbd106a --- /dev/null +++ b/style.css @@ -0,0 +1,287 @@ + + +/*reset*/ +body { + margin: 0; + font-family: arial, sans-serif; + background: #e2e8f0; +} + +/* Header layout with flexbox */ +.header { + display: flex; + align-items: center; + justify-content: center; + background: #0f172a; + color: white; + padding: 14px 16px; +} + +.menu { + position: absolute; + left: 16px; + background: none; + border: none; + cursor: pointer; + padding: 8px; +} + +.menuIcon, +.menuIcon::before, +.menuIcon::after { + display: block; + width: 24px; + height: 2px; + background: white; + border-radius: 2px; + content: ""; + position: relative; +} + +.menuIcon::before, +.menuIcon::after { + position: absolute; + left: 0; +} + +.menuIcon::before { top: -10px; } +.menuIcon::after { top: 10px; } + +.menuTab { + position: fixed; + background: #e2e8f0; + width: 300px; + top: 0; left: 0; bottom: 0; + transform: translateX(-100%); + transition: transform .25s ease; +} + +.menuTab.active { + transform: translateX(0); +} + +.menuContent { + display: flex; + height: 100%; + flex-direction: column; + gap: 8px; + font-size: 15px; +} + +.options { + display: flex; + flex-direction: column; + justify-content: center; + margin-top: 80px; + align-items: center; + gap: 110px; + padding: 20px; + +} + +.nWeek { + width: 120px; + height: 30px; + font-size: 16px; + border-radius: 10px; +} + +.lWeek { + width: 120px; + height: 30px; + font-size: 16px; + border-radius: 10px; +} + +.menuDatePicker{ + margin-top: 80px; + margin-left: 88px; + font-size: 16px; + width: 120px; + height: 30px; + border-radius: 10px; + +} + +#closeMenu { + position: absolute; + font-size: 30px; + width: 22px; + margin-top: 15px; + margin-left: 20px; + background: none; + border: none; + cursor: pointer; +} +/* Centered title */ +.title { + margin: 0; + font-size: 35px; + font-weight: 600; +} + +.dateSelector { + display: flex; + align-items: center; + justify-content: center; + background: none; + margin: 30px; + gap: 20px; +} + +.date { + font-size: 20px; + background: none; + border: none; +} + +.today { + display: flex; + color: #0f172a; + font-size: 20px; + background: none; + border: none; +} + +.inputContainer { + display: flex; + justify-content: center; + gap: 20px; + margin: 40px auto; + background: whitesmoke; + border-radius: 20px; + padding-left: 10px; + width: 400px; + height: 50px; +} + +.input { + font-size: 23px; + border: none; + outline: none; + background: none; + color: #0f172a; +} + +.register { + font-size: 15px; + width: 60px; + height: 25px; + border-radius: 13px; + border: 2px solid black; + cursor: pointer; + background: #0f172a; + color: white; + margin-right: 1px; + margin-top: auto; + margin-bottom: auto; +} + +.list { + display: flex; + flex-direction: column; + gap: 6px; + height: 425px; + width: 600px; + margin: 0 auto; + background: whitesmoke; + border: 6px solid #0f172a; + border-radius: 20px; +} + +.event { + display: flex; + flex-direction: column; + align-items: center; + gap: 14px; + font-size: 26px; + padding: 0; + margin: 5px; + max-height: 350px; + overflow-y: auto; +} + +.topRowList { + display: flex; +} + +.clearAll{ + color: #0f172a; + font-size: 20px; + border-radius: 20px; + width: 120px; + margin-top: 5px; + margin-left: 10px; + background: #e2e8f0; +} + +.numEvent { + line-height: 1.5; + color: #0f172a; + font-size: 18px; + border-radius: 20px; + width: 120px; + margin-top: 5px; + margin-left: auto; + margin-right: 10px; + background: #e2e8f0; + border: 2px solid #0f172a; + padding-left: 20px; +} +.event li { + line-height: 1.5; + display: flex; + padding: 10px 5px; + border: 3px solid #0f172a; + border-radius: 15px; + height: 30px; + width: 550px; + background: #e2e8f0; +} + +.delEvent { + background: #0f172a; + color: white; + border: none; + font-size: 16px; + border-radius: 20px; +} + +.text { + margin-left: 10px; + margin-right: auto; + font-size: 20px; +} + +.pinEvent { + background: none; + color: white; + border: black solid 1px; + font-size: 15px; + border-radius: 20px; + margin-right: 8px; +} + +.doneEvent { + background: #0f172a; + color: white; + border: none; + font-size: 15px; + border-radius: 20px; +} + +.markDone { + text-decoration: line-through; + text-decoration-color: red; + color: grey; +} + +.markPin { + background: red; +} + +body.invert { + filter: invert(1); +} + + From d6a102c6df8695d43b7210b6561fb0a4cc652858 Mon Sep 17 00:00:00 2001 From: baekseungsun Date: Sat, 13 Sep 2025 04:51:46 +0900 Subject: [PATCH 4/5] =?UTF-8?q?=EB=A9=94=EB=89=B4=20=ED=83=AD=20=EC=97=B4?= =?UTF-8?q?=EA=B3=A0=20=EB=8B=AB=EA=B8=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- todolist.js | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 todolist.js diff --git a/todolist.js b/todolist.js new file mode 100644 index 0000000..94b6088 --- /dev/null +++ b/todolist.js @@ -0,0 +1,31 @@ +document.addEventListener('DOMContentLoaded', () => { + + //menu tab + const menuTab = document.getElementById('menuTab'); + const openBtn = document.querySelector('.menu'); + const closeBtn = document.getElementById('closeMenu'); + + //open and close menu drawer + const openDrawer = () => { + menuTab.classList.add('active'); + }; + const closeDrawer = () => { + menuTab.classList.remove('active'); + }; + + openBtn.addEventListener('click', openDrawer); + closeBtn.addEventListener('click', closeDrawer); + + //close menu when clicked outside of menu tab + document.addEventListener('click', (e) => { + if (menuTab.classList.contains('active') && !menuTab.contains(e.target) && !openBtn.contains(e.target)) { + closeDrawer(); + } + }); + + //close menu when user presses esc + document.addEventListener('keydown', (e) => { + if (e.key === 'Escape' && menuTab.classList.contains('active')) closeDrawer(); + }); + +}); \ No newline at end of file From 3f2b339d0ba174ab1284fab3c6c335854da3f26a Mon Sep 17 00:00:00 2001 From: baekseungsun Date: Sat, 13 Sep 2025 04:54:41 +0900 Subject: [PATCH 5/5] =?UTF-8?q?=EA=B8=B0=ED=83=80=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- todolist.js | 208 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 205 insertions(+), 3 deletions(-) diff --git a/todolist.js b/todolist.js index 94b6088..23641f7 100644 --- a/todolist.js +++ b/todolist.js @@ -1,12 +1,12 @@ document.addEventListener('DOMContentLoaded', () => { //menu tab - const menuTab = document.getElementById('menuTab'); - const openBtn = document.querySelector('.menu'); + const menuTab = document.getElementById('menuTab'); + const openBtn = document.querySelector('.menu'); const closeBtn = document.getElementById('closeMenu'); //open and close menu drawer - const openDrawer = () => { + const openDrawer = () => { menuTab.classList.add('active'); }; const closeDrawer = () => { @@ -28,4 +28,206 @@ document.addEventListener('DOMContentLoaded', () => { if (e.key === 'Escape' && menuTab.classList.contains('active')) closeDrawer(); }); + //define date manipulating buttons + const today = document.querySelector('.today'); + const prevBtn = document.getElementById('yesterday'); + const nextBtn = document.getElementById('tomorrow'); + + //get date + let current = new Date(); + const fmt = { year: 'numeric', month: 'long', day: 'numeric' }; + + //Date manipulation + function render() { + today.textContent = current.toLocaleDateString(undefined, fmt); + } + render(); + + //define next and last week buttons in menu + const nWeek = document.querySelector('.nWeek'); + const lWeek = document.querySelector('.lWeek'); + + nWeek.addEventListener('click', e => { + saveList(current); + current.setDate(current.getDate() + 7); + render(); + loadList(current); + }) + + lWeek.addEventListener('click', e => { + saveList(current); + current.setDate(current.getDate() - 7); + render(); + loadList(current); + }) + + const dateKey = (d) => new Date(d).toLocaleDateString('en-CA'); + // "YYYY-MM-DD" in local time + const storageKey = (d) => `${dateKey(d)}`; + + const numEvent = document.querySelector('.numEvent'); + + //saves the events for a date + function saveList(date) { + sessionStorage.setItem(storageKey(date), event.innerHTML); + } + + //loads the events for a date + function loadList(date) { + event.innerHTML = sessionStorage.getItem(storageKey(date) || ''); + getNumEvent(); + } + + //save current events when prevBtn is pressed + prevBtn.addEventListener('click', () => { + if (event.firstChild){ + saveList(current); + } + current.setDate(current.getDate() - 1); + render(); + loadList(current); + }) + + //save current events when nextBtn is pressed + nextBtn.addEventListener('click', () => { + if (event.firstChild){ + saveList(current); + } + current.setDate(current.getDate() + 1); + render(); + loadList(current); + }) + + //when the date is clicked on, change current date to today + today.addEventListener('click', () => { + current = new Date(); + render(); + loadList(current); + }) + + + + const input = document.querySelector('.input'); + const add = document.querySelector('.register'); + const event = document.querySelector('.event'); + const clearAll = document.querySelector('.clearAll'); + + //event listener that is restored once loaded from storage + event.addEventListener('click', (e) => { + //delete button + if (e.target && e.target.classList.contains('delEvent')) { + e.target.closest('li')?.remove(); + saveList(current); + } + + //done button + if (e.target && e.target.classList.contains('doneEvent')) { + const li = e.target.closest('li'); + const span = li?.querySelector('.text'); // get the text span + span.classList.toggle('markDone'); + saveList(current); + } + + //pin and unpin events + if (e.target && e.target.classList.contains('pinEvent')) { + const li = e.target.closest('li'); + const span = li?.querySelector('.pinEvent'); + if (li.dataset.originalIndex === undefined) { + li.dataset.originalIndex = Array.from(event.children).indexOf(li); + } + //save the original index of event for unpinning + const originalIndex = parseInt(li.dataset.originalIndex, 10); + //unpin + if (li.classList.contains('pinned')) { + li.classList.remove('pinned'); + console.log(originalIndex); + span.classList.toggle('markPin'); + event.insertBefore(li, event.children[originalIndex+1]); + } + //pin + else { + console.log(originalIndex); + span.classList.toggle('markPin'); + event.insertBefore(li, event.firstChild); + li.classList.add('pinned'); + } + } + getNumEvent(); + }); + + //add event using enter and button click + add.addEventListener('click', addToList); + input.addEventListener('keydown', (e) => { + if (e.key === 'Enter') { + addToList(); + } + }); + + //adding event to list + function addToList() { + const li = document.createElement('li'); + + const span = document.createElement('span'); + //event as text + span.textContent = input.value; + span.className = 'text'; + + //delete event button + const delEvent = document.createElement('button'); + delEvent.className = 'delEvent'; + delEvent.textContent = 'Delete'; + //done event button + const doneEvent = document.createElement('button'); + doneEvent.className = 'doneEvent'; + doneEvent.textContent = 'Done'; + //pin event button + const pinEvent = document.createElement('button'); + pinEvent.className = 'pinEvent'; + pinEvent.textContent = 'Pin'; + + //add event to list + if (span && span.textContent !== '') { + li.appendChild(doneEvent); + li.appendChild(span); + li.appendChild(pinEvent); + li.appendChild(delEvent); + event.appendChild(li); + } + + getNumEvent(); + saveList(current); + input.value = ''; + } + + //clear all events for a date + function clearALlEvents() { + while (event.firstChild) { + event.removeChild(event.firstChild); + } + } + clearAll.addEventListener('click', () => { + clearALlEvents(); + getNumEvent(); + }) + + //get the number of Events + function getNumEvent() { + numEvent.textContent = "To-do: " + event.children.length; + } + + //calendar manipulation + const menuContent = document.getElementById('menuContent'); + datePickerEl = document.createElement('input'); + datePickerEl.type = 'date'; + datePickerEl.className = 'menuDatePicker';// make it obvious + menuContent.appendChild(datePickerEl); + datePickerEl.addEventListener('change', () => { + if (!datePickerEl.value) return; + const [y, m, d] = datePickerEl.value.split('-').map(Number); + saveList(current); + current = new Date(y, m - 1, d); + render(); + loadList(current); + + }); }); \ No newline at end of file