diff --git a/index.html b/index.html index d241b1b..88d1e23 100644 --- a/index.html +++ b/index.html @@ -3,12 +3,53 @@ - Vanilla Todo + To Do List-yyj0917 -
+
+
+ +
+

+ +

Today

+ +
+
+ + + +
+ + +
+
diff --git a/script.js b/script.js index 355dcc2..2a321e8 100644 --- a/script.js +++ b/script.js @@ -1 +1,206 @@ //๐Ÿ˜CEOS 20๊ธฐ ํ”„๋ก ํŠธ์—”๋“œ ํŒŒ์ดํŒ…๐Ÿ˜ + +// DOM Load์‹œ ์‹คํ–‰๋˜๋Š” ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ +document.addEventListener('DOMContentLoaded', function () { + const currentDate = new Date(); + const today = document.querySelector('.today'); + const weekDays = document.querySelector('.week-days'); + const taskList = document.querySelector('.task-list'); + const modalBtn = document.querySelector('.btn-modal'); + const taskAddForm = document.querySelector('.task-form'); + const backBtn = document.querySelector('.back'); + const datePicker = document.getElementById('date-picker'); + const ulElement = document.querySelector('.week-days'); + + + function updateDisplayDate(date) { + today.innerText = date.toLocaleString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }); + } + + // input type=date์˜ ์ดˆ๊ธฐ๊ฐ’ ์„ค์ • + function getFormattedDate(date) { + const year = date.getFullYear(); + const month = ('0' + (date.getMonth() + 1)).slice(-2); + const day = ('0' + date.getDate()).slice(-2); + return `${year}-${month}-${day}`; + } + + let selectedDate = getFormattedDate(currentDate); // selectedDate ์ดˆ๊ธฐ๊ฐ’์€ ์˜ค๋Š˜ ๋‚ ์งœ + + // ์ƒ๋‹จ ๋‚ ์งœ ์—…๋ฐ์ดํŠธ ํ•จ์ˆ˜ + + // input์˜ ์ดˆ๊ธฐ ๊ฐ’์„ ์˜ค๋Š˜ ๋‚ ์งœ๋กœ ์„ค์ • + datePicker.value = selectedDate; + + // ๋‚ ์งœ ์„ ํƒ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ + datePicker.addEventListener('change', function(event) { + const selectedDateObj = new Date(event.target.value); // ์‚ฌ์šฉ์ž๊ฐ€ ์„ ํƒํ•œ ๋‚ ์งœ + selectedDate = selectedDateObj.toISOString().split('T')[0]; // ์„ ํƒ๋œ ๋‚ ์งœ์˜ 'YYYY-MM-DD' ํ˜•์‹์œผ๋กœ ์„ค์ • + updateDisplayDate(selectedDateObj); // ์ƒ๋‹จ ๋‚ ์งœ ์—…๋ฐ์ดํŠธ + loadWeekDays(selectedDateObj); // ์„ ํƒํ•œ ๋‚ ์งœ์˜ ์ฃผ๊ฐ„ ํ‘œ์‹œ + loadTasks(selectedDate); // ์„ ํƒ๋œ ๋‚ ์งœ์˜ ํ•  ์ผ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ + }); + + + + + // ์ผ์ฃผ์ผ ๋‚ ์งœ ์ƒ์„ฑ, ์˜ค๋Š˜ ๋‚ ์งœ ํ‘œ์‹œ + function loadWeekDays(selectedDateObj = currentDate) { + weekDays.innerHTML = ''; + updateDisplayDate(selectedDateObj); + + // ๋‚ ์งœ ๊ณ„์‚ฐ ์ฃผ๊ฐ„ ์ฒซ๋ฒˆ์งธ ๋‚  ์›ก์š”์ผ ์„ค์ •๋กœ์ง + const firstDayOfWeek = new Date(selectedDateObj); + const dayOfWeek = selectedDateObj.getDay(); + + // ์ผ์š”์ผ์ธ ๊ฒฝ์šฐ ํŠน๋ณ„ ์ฒ˜๋ฆฌ + if (dayOfWeek === 0) { + firstDayOfWeek.setDate(selectedDateObj.getDate() - 6); // ์ผ์š”์ผ์ผ ๊ฒฝ์šฐ ์ „์ฃผ์˜ ์›”์š”์ผ์„ ์„ค์ • + } else { + firstDayOfWeek.setDate(selectedDateObj.getDate() - dayOfWeek + 1); // ์›”์š”์ผ ์„ค์ • + } + + + for (let i = 0; i < 7; i++) { + const day = new Date(firstDayOfWeek); + day.setDate(firstDayOfWeek.getDate() + i); + const dayElement = document.createElement('li'); + const dateKey = day.toISOString().split('T')[0]; + + // week set + const weekSpan = document.createElement('span'); + weekSpan.className = 'week'; + weekSpan.innerText = day.toLocaleString('en-US', { weekday: 'short' }); + + // day set + const daySpan = document.createElement('span'); + daySpan.className = 'day'; + daySpan.innerText = day.getDate(); + + // dateKey ์„ค์ • + dayElement.dataset.dateKey = dateKey; + + // Today selected set + if (dateKey === selectedDateObj.toISOString().split('T')[0]) { + dayElement.classList.add('selected'); + loadTasks(selectedDate); + } + + // ๊ฐœ๋ณ„ ์Šคํƒ€์ผ์„ ์œ„ํ•œ span tag ์‚ฝ์ž… + dayElement.appendChild(weekSpan); + dayElement.appendChild(daySpan); + weekDays.appendChild(dayElement); + } + // UL ์š”์†Œ์— ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ๋“ฑ๋ก (์ด๋ฒคํŠธ ์œ„์ž„) + ulElement.addEventListener('click', (event) => { + const dayElement = event.target.closest('li'); // ํด๋ฆญ๋œ ์š”์†Œ ์ค‘ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด li ์š”์†Œ ์ฐพ๊ธฐ + if (dayElement) { // li ์š”์†Œ๊ฐ€ ํด๋ฆญ๋œ ๊ฒฝ์šฐ๋งŒ ์‹คํ–‰ + const dateKey = dayElement.dataset.dateKey; // li์˜ ๋ฐ์ดํ„ฐ ์†์„ฑ์—์„œ dateKey ๊ฐ€์ ธ์˜ค๊ธฐ + selectDate(dayElement); // ๋‚ ์งœ ์„ ํƒ ์ฒ˜๋ฆฌ + selectedDate = dateKey; // ์„ ํƒ๋œ ๋‚ ์งœ ์„ค์ • + datePicker.value = dateKey; // ๋‚ ์งœ ์„ ํƒ๊ธฐ๋ฅผ ์„ ํƒํ•œ ๋‚ ์งœ๋กœ ์„ค์ • + loadTasks(dateKey); // ์„ ํƒํ•œ ๋‚ ์งœ์˜ ํ•  ์ผ ๋กœ๋“œ + } + }); + } + // ์„ ํƒ๋œ ๋‚ ์งœ ํ‘œ์‹œ + function selectDate(dayElement) { + const selected = document.querySelector('.selected'); + if (selected) { + selected.classList.remove('selected'); + } + dayElement.classList.add('selected'); + } + + // load To Do List + function loadTasks(dateKey) { + taskList.innerHTML = ''; // ๊ธฐ์กด ํ•  ์ผ ๋ชฉ๋ก ์ง€์šฐ๊ธฐ + const tasks = JSON.parse(localStorage.getItem(dateKey)) || []; // LocalStorage์—์„œ ํ•ด๋‹น ๋‚ ์งœ์˜ ํ•  ์ผ ๊ฐ€์ ธ์˜ค๊ธฐ + + tasks.forEach((task, index) => { + addTaskToDOM(task, index); + }); + } + + + // Add To Do List -> DOM์— ์ถ”๊ฐ€ -> ํ™”๋ฉด์— ๋„์šฐ๊ธฐ์šฉ + function addTaskToDOM(task, index) { + const taskElement = document.createElement('article'); + taskElement.className = 'task-item'; + taskElement.innerHTML = ` +
+ ${task.time} +

${task.title}

+

${task.desc}

+ +
+ `; + taskElement.querySelector('.task-delete').addEventListener('click', function () { + const confirm = window.confirm('์‚ญ์ œํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?'); + if (confirm) { + deleteTask(selectedDate, index); // ์‚ญ์ œ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ํ•  ์ผ ์‚ญ์ œ + } + }); + taskList.appendChild(taskElement); + } + + // Add Task -> LocalStorage์— ์ถ”๊ฐ€ + function addTaskToLocal(dateKey, task) { + console.log(dateKey); + const tasks = JSON.parse(localStorage.getItem(dateKey)) || []; + + // ์ž‘์„ฑ์‹œ๊ฐ„ + task.createdTime = new Date().toLocaleString('en-US', { year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric', second: 'numeric' }); + + tasks.push(task); + localStorage.setItem(dateKey, JSON.stringify(tasks)); + } + + // Delete Task -> LocalStorage์—์„œ ์‚ญ์ œ -> ๊ธฐ์กด ๋ชฉ๋ก ๋ถˆ๋Ÿฌ์˜ค๊ณ , ๋ชฉ๋ก์—์„œ ์‚ญ์ œ ํ›„ ์‚ญ์ œ๋œ ๋ชฉ๋ก ์ €์žฅ...ํ  + function deleteTask(dateKey, taskIndex) { + const tasks = JSON.parse(localStorage.getItem(dateKey)) || []; + tasks.splice(taskIndex, 1); + localStorage.setItem(dateKey, JSON.stringify(tasks)); + loadTasks(dateKey); + } + + // Modal Button + modalBtn.addEventListener('click', () => { + modalBtn.style.display = 'none'; + taskAddForm.style.display = 'flex'; + }); + + // back navigation + backBtn.addEventListener('click', () => { + modalBtn.style.display = 'inline-block'; + taskAddForm.style.display = 'none'; + }); + + // Save Task -> submit ์ด๋ฒคํŠธ๊ฐ€ ํผ์„ ์ž๋™์œผ๋กœ ์ œ์ถœํ•˜๋ฉด์„œ ํŽ˜์ด์ง€๊ฐ€ ์ƒˆ๋กœ๊ณ ์นจ๋จ -> ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— save button์ด ์•„๋‹Œ + // form์— ์ด๋ฒคํŠธ๋ฆฌ์Šค๋„ˆ๋ฅผ ๋“ฑ๋กํ•ด์„œ ๊ธฐ๋ณธ๋™์ž‘์„ ๋ง‰์•„์•ผ ํ•จ. + taskAddForm.addEventListener('submit', function (event) { + event.preventDefault(); + + const taskTitle = document.getElementById('task-title').value.trim(); + const taskDesc = document.getElementById('task-desc').value.trim(); + + const newTask = { + title: taskTitle, + desc: taskDesc, + time: new Date().toLocaleString('en-US', { hour: 'numeric', minute: 'numeric', second: 'numeric' }) + }; + + // input field init ๋ชจ๋“  ํผ ์š”์†Œ๋ฅผ ์ดˆ๊ธฐํ™”ํ•  ๋•Œ ์‚ฌ์šฉ๊ฐ€๋Šฅ + taskAddForm.reset(); + + + addTaskToLocal(selectedDate, newTask); + loadTasks(selectedDate); + modalBtn.style.display = 'block'; + taskAddForm.style.display = 'none'; + }); + + loadWeekDays(currentDate); + loadTasks(selectedDate); + +}); \ No newline at end of file diff --git a/style.css b/style.css index 599136a..659faf5 100644 --- a/style.css +++ b/style.css @@ -1 +1,271 @@ /* ๋ณธ์ธ์˜ ๋””์ž์ธ ๊ฐ๊ฐ์„ ์ตœ๋Œ€ํ•œ ๋ฐœํœ˜ํ•ด์ฃผ์„ธ์š”! */ +@font-face { + font-family: 'Pretendard-Regular'; + src: url('https://fastly.jsdelivr.net/gh/Project-Noonnu/noonfonts_2107@1.1/Pretendard-Regular.woff') format('woff'); + font-weight: 400; + font-style: normal; +} +@font-face { + font-family: 'SUIT-Regular'; + src: url('https://fastly.jsdelivr.net/gh/projectnoonnu/noonfonts_suit@1.0/SUIT-Regular.woff2') format('woff2'); + font-weight: normal; + font-style: normal; +} + +* { + font-family: 'SUIT-Regular', sans-serif; + margin: 0; + padding: 0; + box-sizing: border-box; +} +.wrapper { + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + background-color: #dfe2e5; +} +.container { + width: 360px; + height: 80vh; + margin: 0 auto; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + border-radius: 28px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); + background-color: #fff; + padding: 20px 10px; +} + +/* header */ + +header { + width: 100%; + height: 15%; + padding: 10px; + display: flex; + flex-direction: column; + justify-content: center; + gap: 5px; +} +header span { + display: flex; + justify-content: space-between; + align-items: center; + input[type="date"] { + -webkit-appearance: none; + -moz-appearance: textfield; + appearance: none; + background-color: transparent; + border: none; + box-shadow: inset 0px -1px 0px 0px rgba(0, 0, 0, 1); + padding: 0 4px; + font-size: 14px; + } +} +header h1 { + font-size: 32px; + font-weight: 1200; + color: #333; +} +header h2 { + font-size: 18px; + font-weight: 700; + color: #c0c0c0; +} + +/* nav */ + +nav { + width: 100%; + height: 10%; + display: flex; + align-items: center; +} +nav ul { + padding: 0 10px; + width: 100%; + display: flex; + justify-content: space-between; + gap: 10px; + list-style-type: none; + +} +nav ul li { + width: 14%; + border-radius: 10px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 5px; + cursor: pointer; +} +nav .week { + font-size: 16px; + font-weight: 800; + color: #c0c0c0; +} +nav .day { + font-size: 14px; + font-weight: 900; + color: #333; +} + +/* ์„ ํƒ๋œ ๋‚ ์งœ task-item์— ์ ์šฉ๋˜๋Š” ์Šคํƒ€์ผ */ +nav ul li.selected { + border-radius: 0; + color: #89bfeb; + border-bottom: 2px solid #89bfeb; + span { + color: #89bfeb; + } +} + +/* main */ + +main { + margin: 10px 0; + padding: 10px; + width: 100%; + height: 65%; + display: flex; + flex-direction: column; + gap: 10px; + overflow: auto; + border-top: 2px solid #f1f1f1; + border-bottom: 2px solid #f1f1f1; +} +/* scroll bar hide */ +.task-list { + -ms-overflow-style: none; /* IE ๋ฐ Edge์—์„œ ์Šคํฌ๋กค๋ฐ” ์ˆจ๊น€ */ + scrollbar-width: none; /* Firefox์—์„œ ์Šคํฌ๋กค๋ฐ” ์ˆจ๊น€ */ +} +/* Webkit ๊ธฐ๋ฐ˜ ๋ธŒ๋ผ์šฐ์ €(Chrome, Safari ๋“ฑ)์—์„œ ์Šคํฌ๋กค๋ฐ” ์ˆจ๊น€ */ +.task-list::-webkit-scrollbar { + display: none; +} + +main article { + width: 100%; + height: auto; + padding: 0 10px; + display: flex; + justify-content: space-between; + align-items: center; + background-color: #f8f8ff; + border-radius: 10px; + + .task-detail { + position: relative; + width: 100%; + padding: 20px; + display: flex; + flex-direction: column; + gap: 5px; + overflow-wrap: break-word; + .task-time { + position: absolute; + top: 5px; + right: 5px; + font-size: 12px; + font-weight: 700; + color: gray; + text-decoration: underline; + } + .task-title { + font-size: 16px; + font-weight: 800; + color: #333; + } + .task-desc { + font-size: 14px; + font-weight: 700; + color: gray; + } + .task-delete { + position: absolute; + bottom: 10px; + right: 5px; + background-color: white; + color: black; + border-radius: 8px; + padding: 2px; + border: none; + cursor: pointer; + } + .task-delete:hover { + background-color: lightgray; + } + } +} + +/* footer */ + +footer { + width: 100%; + height: 10%; + display: flex; + justify-content: space-around; + align-items: center; + gap: 20px; +} +footer button.btn-modal{ + border: none; + width: 50px; + height: 50px; + border-radius: 50%; + background-color: #89bfeb; + color: #fff; + font-size: 18px; + font-weight: 800; + cursor: pointer; + font-size: 32px; + text-align: center; + box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1); +} +footer button.btn-modal:hover { + background-color: #509ddc; +} +footer form { + width: 100%; + display: flex; + justify-content: center; + align-items: center; + gap: 20px; +} +footer form div { + width: auto; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 5px; +} +footer input { + width: 200px; + height: 30px; + border: none; + border-radius: 8px; + padding: 0 10px; + font-size: 14px; + font-weight: 800; + color: #333; + box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1); +} +footer button.btn { + padding: 4px; + width: auto; + height: 30px; + border: none; + border-radius: 8px; + background-color: #f8f8ff; + color: #333; + font-size: 16px; + font-weight: 800; + text-align: center; + cursor: pointer; + box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1); +}