diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..63b6d3d Binary files /dev/null and b/.DS_Store differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..57395b1 --- /dev/null +++ b/index.html @@ -0,0 +1,35 @@ + + + + + vanilla-todo + + + +
+ +

To Do

+
+ +
+ + + +
+
+ + + + +
+ + + diff --git a/style.css b/style.css new file mode 100644 index 0000000..a84b055 --- /dev/null +++ b/style.css @@ -0,0 +1,241 @@ +@import url("https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css"); + +/* 기본 설정 */ +* { + text-align: center; + font-family: "Pretendard"; + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + min-height: 100vh; + background-color: rgb(133, 173, 198); + color: #000; + + display: flex; + flex-direction: column; + align-items: center; +} + +/* 상단 영역 */ +header { + width: 100%; + padding: 16px 0; + position: relative; + + display: flex; + justify-content: center; + align-items: center; +} + +.title { + font-size: 2rem; + font-weight: 700; + cursor: pointer; +} + +.menuButton { + position: absolute; + left: 1rem; + border: none; + background-color: transparent; + font-size: 2rem; + cursor: pointer; +} + +/* 사이드바 디자인 */ +.menuList { + position: fixed; + top: 0; + left: -100%; + width: 70%; + max-width: 320px; + height: 100vh; + padding: 24px 0; + background-color: #f5f5f5; + transition: left 0.3s ease; + z-index: 1000; + + display: flex; + flex-direction: column; + align-items: center; + gap: 60px; +} + +.closeMenu { + align-self: flex-start; + margin-left: 16px; + font-size: 2rem; + cursor: pointer; + background: transparent; +} + +.week { + border: none; + background: transparent; + font-size: 2rem; + color: #3b1f1f; + cursor: pointer; +} + +#calendar { + border: none; + background: transparent; + font-size: 2rem; + color: #3b1f1f; + text-align: center; +} + +.darkMode { + border: none; + background: transparent; + font-size: 2rem; + cursor: pointer; +} + +/* 날짜 표시 영역 */ +.dateBox { + margin-top: 8px; + + display: flex; + justify-content: center; + align-items: center; + gap: 16px; +} + +.date { + border: none; + background: transparent; + font-size: 1.5rem; + cursor: pointer; +} + +#currentDateText { + font-size: 1.5rem; + font-weight: 600; +} + +/* 메인 영역 */ +main { + width: 100%; + max-width: 600px; + margin-top: 24px; + + display: flex; + flex-direction: column; + align-items: center; + gap: 16px; +} + +.inputArea { + display: flex; + justify-content: center; + align-items: center; + gap: 8px; +} + +.input { + width: 280px; + padding: 10px 12px; + border: 1px solid #ccc; + border-radius: 8px; + background-color: white; + text-align: left; + font-size: 1rem; +} + +.enter { + padding: 10px 16px; + border: none; + border-radius: 8px; + background-color: white; + cursor: pointer; + font-size: 1rem; +} + +#total { + font-size: 1rem; + font-weight: 600; +} + +#todoList { + width: 100%; + list-style: none; + + display: flex; + flex-direction: column; + align-items: center; + gap: 12px; +} + +#todoList li { + width: 100%; + max-width: 500px; + padding: 10px 14px; + border-radius: 10px; + background-color: white; + + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; +} + +#todoList li input[type="checkbox"] { + width: 18px; + height: 18px; + flex-shrink: 0; +} + +#todoList span { + flex: 1; + background: transparent; + font-size: 1.1rem; + word-break: break-word; +} + +#todoList li button { + border: none; + background: transparent; + color: red; + cursor: pointer; + font-size: 0.95rem; + flex-shrink: 0; +} + +/* 다크모드 디자인 */ +body.darkTheme { + background-color: black; + color: white; +} + +body.darkTheme .menuList { + background-color: gray; +} + +body.darkTheme .title, +body.darkTheme .menuButton, +body.darkTheme .closeMenu, +body.darkTheme .week, +body.darkTheme #calendar, +body.darkTheme .darkMode, +body.darkTheme .date, +body.darkTheme #currentDateText, +body.darkTheme #total, +body.darkTheme #todoList span { + color: white; +} + +body.darkTheme .input, +body.darkTheme .enter, +body.darkTheme #todoList li { + background-color: #2d2c2c; + color: white; + border: 1px solid #555; +} + +body.darkTheme .deleteButton { + color: white; +} diff --git a/vanilla.js b/vanilla.js new file mode 100644 index 0000000..b9e46de --- /dev/null +++ b/vanilla.js @@ -0,0 +1,209 @@ +const currentDateText = document.querySelector("#currentDateText"); +const home = document.querySelector(".title"); +const menuButton = document.querySelector(".menuButton"); +const menuList = document.querySelector(".menuList"); +const closeMenu = document.querySelector(".closeMenu"); +const prev7 = document.querySelector("#previousWeek"); +const next7 = document.querySelector("#nextWeek"); +const todoDate = document.querySelector("#calendar"); +const prev1 = document.querySelector("#previousDate"); +const next1 = document.querySelector("#nextDate"); +const todoInput = document.querySelector(".input"); +const enterButton = document.querySelector(".enter"); +const todoList = document.querySelector("#todoList"); +const total = document.querySelector("#total"); +const darkModeButton = document.querySelector(".darkMode"); + +// 0) 메인화면 날짜 표시 +function formatKoreanDate(dateString) { + const [year, month, day] = dateString.split("-"); + return `${Number(year)}년 ${Number(month)}월 ${Number(day)}일`; +} + +function updateCurrentDateText() { + currentDateText.textContent = formatKoreanDate(todoDate.value); +} + +// 공통 날짜 포맷 +function formatInputDate(date) { + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, "0"); + const day = String(date.getDate()).padStart(2, "0"); + return `${year}-${month}-${day}`; +} + +// 1) 제목 누르면 오늘로 돌아가기 +function homeButton() { + todoDate.value = formatInputDate(new Date()); + updateCurrentDateText(); + renderTodoList(); +} + +// 2) 사이드바 열기/닫기 +function openSidebar() { + menuList.style.left = "0"; +} + +function closeSidebar() { + menuList.style.left = "-100%"; +} +// 공통 날짜 변경 포멧 +function formatChangeDate(currentDate) { + todoDate.value = formatInputDate(currentDate); + updateCurrentDateText(); + renderTodoList(); +} +// 2-1) 이전 주 / 다음 주 +function minusWeek() { + const currentDate = new Date(todoDate.value); + currentDate.setDate(currentDate.getDate() - 7); + formatChangeDate(currentDate); +} + +function plusWeek() { + const currentDate = new Date(todoDate.value); + currentDate.setDate(currentDate.getDate() + 7); + formatChangeDate(currentDate); +} + +// 3) 이전 날짜 / 다음 날짜 +function minusDay() { + const currentDate = new Date(todoDate.value); + currentDate.setDate(currentDate.getDate() - 1); + formatChangeDate(currentDate); +} + +function plusDay() { + const currentDate = new Date(todoDate.value); + currentDate.setDate(currentDate.getDate() + 1); + formatChangeDate(currentDate); +} + +// 4) 날짜별 todo 저장/불러오기 +function getDateKey() { + return todoDate.value; +} + +function getTodoListByDate() { + const dateKey = getDateKey(); + return JSON.parse(localStorage.getItem(dateKey)) || []; +} + +function saveTodoListByDate(todoArray) { + const dateKey = getDateKey(); + localStorage.setItem(dateKey, JSON.stringify(todoArray)); +} + +// 4-1) 남은 todo 개수 표시 +function updateTotal() { + const todoArray = getTodoListByDate(); + const remainingCount = todoArray.filter(function (todo) { + return !todo.completed; + }).length; + + total.textContent = `남은 할 일 ${remainingCount}개`; +} + +// 4) todo 목록 화면에 그리기 +function renderTodoList() { + const todoArray = getTodoListByDate(); + todoList.innerHTML = ""; + + todoArray.forEach(function (todo, index) { + const listItem = document.createElement("li"); + const checkbox = document.createElement("input"); + const todoText = document.createElement("span"); + const deleteButton = document.createElement("button"); + + checkbox.type = "checkbox"; + checkbox.checked = todo.completed; + todoText.textContent = todo.text; + deleteButton.textContent = "X"; + + if (todo.completed) { + todoText.style.textDecoration = "line-through"; + } else { + todoText.style.textDecoration = "none"; + } + + checkbox.addEventListener("change", function () { + const currentTodoArray = getTodoListByDate(); + currentTodoArray[index].completed = checkbox.checked; + saveTodoListByDate(currentTodoArray); + renderTodoList(); + }); + + deleteButton.addEventListener("click", function () { + const currentTodoArray = getTodoListByDate(); + currentTodoArray.splice(index, 1); + saveTodoListByDate(currentTodoArray); + renderTodoList(); + }); + + listItem.appendChild(checkbox); + listItem.appendChild(todoText); + listItem.appendChild(deleteButton); + todoList.appendChild(listItem); + }); + + updateTotal(); +} + +// 4) todo 추가 +function addTodo() { + const inputValue = todoInput.value.trim(); + + if (inputValue === "") { + return; + } + + const todoArray = getTodoListByDate(); + + const newTodo = { + text: inputValue, + completed: false, + }; + + todoArray.push(newTodo); + saveTodoListByDate(todoArray); + todoInput.value = ""; + renderTodoList(); +} + +//이벤트 연결 +home.addEventListener("click", homeButton); +menuButton.addEventListener("click", openSidebar); +closeMenu.addEventListener("click", closeSidebar); +prev7.addEventListener("click", minusWeek); +next7.addEventListener("click", plusWeek); +prev1.addEventListener("click", minusDay); +next1.addEventListener("click", plusDay); +enterButton.addEventListener("click", addTodo); + +todoInput.addEventListener("keydown", function (event) { + if (event.key === "Enter") { + addTodo(); + } +}); + +todoDate.addEventListener("change", function () { + updateCurrentDateText(); + renderTodoList(); +}); + +todoDate.value = new Date().toISOString().split("T")[0]; +updateCurrentDateText(); +renderTodoList(); + +// 다크모드 구현 +function toggleDarkMode() { + document.body.classList.toggle("darkTheme"); + + if (document.body.classList.contains("darkTheme")) { + darkModeButton.textContent = "☀️"; + } else { + darkModeButton.textContent = "🌙"; + } +} + +darkModeButton.addEventListener("click", toggleDarkMode);