├── .eslintrc.json ├── .gitignore ├── README.md ├── dist ├── index.html └── main.js ├── package-lock.json ├── package.json ├── src ├── img │ ├── mockup1.png │ ├── mockup2.png │ └── mockup3.png ├── index.js ├── modules │ ├── app.js │ ├── filter.js │ ├── models │ │ ├── projectModel.js │ │ ├── storageModel.js │ │ └── taskModel.js │ └── views │ │ ├── projectView.js │ │ └── taskView.js └── styles.scss └── webpack.config.js /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true 5 | }, 6 | "extends": ["airbnb-base", "plugin:prettier/recommended"], 7 | "overrides": [], 8 | "parserOptions": { 9 | "ecmaVersion": "latest", 10 | "sourceType": "module" 11 | }, 12 | "plugins": ["prettier"], 13 | "rules": { 14 | "prettier/prettier": ["error", { "singleQuote": true }], 15 | "function-paren-newline": "off", 16 | "no-plusplus": ["error", { "allowForLoopAfterthoughts": true }], 17 | "no-param-reassign": 0, 18 | "operator-linebreak": "off", 19 | "eslintprettier/prettier": ["off"] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # To-Do List 2 | 3 | ![app-picture](https://github.com/bartbzd/todo-list/blob/main/src/img/mockup1.png) 4 | 5 | ![app-picture](https://github.com/bartbzd/todo-list/blob/main/src/img/mockup2.png) 6 | 7 | ![app-picture](https://github.com/bartbzd/todo-list/blob/main/src/img/mockup3.png) 8 | 9 | ## [Live Demo](https://bartbzd.github.io/todo-list/) 10 | 11 | ## Built with: 12 | 13 | - Webpack 14 | - HTML,SASS 15 | - JavaScript 16 | - npm, date-fns 17 | - Web Storage, local 18 | 19 | ## Extra features added: 20 | 21 | - Favorite starred tasks 22 | - Mobile sidebar modal 23 | - Sort view by filter 24 | 25 | ## Things I learned: 26 | 27 | - Designing UI with modern app-like approach 28 | - Installing Webpack loaders and plugins 29 | - Building CSS animations and keyframes 30 | - Compiling project files with Webpack 31 | - Configuring eslint with prettier 32 | - Using additional array methods 33 | -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 13 | 15 | 17 | 20 | todo. 21 | 22 | 23 | 24 | 25 |
26 |
27 |

todo.

28 |
29 |
30 | 31 | 32 | 36 |
37 |
38 | 39 |
40 | 70 | 71 |
72 |
73 |
74 |

Tasks

75 | 76 |
77 | 78 |
79 |
80 | 81 |
82 | 83 |
84 |
85 | 88 | 89 | 90 | 93 | 94 | 95 |
96 |
97 | 100 | 102 |
103 |
104 |

Date

105 | 108 |
109 | 110 |
111 | 112 |
113 | 114 | 115 | 116 |
117 |
118 |
119 | 120 |
121 |
122 |
123 |
124 | folder 125 |

126 |
127 |
128 | 129 |
130 |

131 | 132 |
133 |
134 |
135 | 136 |
137 |
138 |

139 |
140 |
141 | 142 |
143 |
144 | 145 |
146 |
147 |

148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 | 156 | 157 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todo-list", 3 | "version": "1.0.0", 4 | "description": "To Do List App", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "build": "webpack", 9 | "start": "webpack serve --open" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/bartbzd/todo-list.git" 14 | }, 15 | "keywords": [], 16 | "author": "Bart Bieszczad", 17 | "license": "ISC", 18 | "bugs": { 19 | "url": "https://github.com/bartbzd/todo-list/issues" 20 | }, 21 | "homepage": "https://github.com/bartbzd/todo-list#readme", 22 | "devDependencies": { 23 | "css-loader": "^6.7.3", 24 | "eslint": "^8.33.0", 25 | "eslint-config-airbnb-base": "^15.0.0", 26 | "eslint-config-prettier": "^8.8.0", 27 | "eslint-plugin-import": "^2.27.5", 28 | "eslint-plugin-prettier": "^4.2.1", 29 | "node-sass": "^8.0.0", 30 | "prettier": "2.8.7", 31 | "sass-loader": "^13.2.0", 32 | "style-loader": "^3.3.1", 33 | "webpack": "^5.75.0", 34 | "webpack-cli": "^5.0.1", 35 | "webpack-dev-server": "^4.11.1" 36 | }, 37 | "dependencies": { 38 | "date-fns": "^2.29.3" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/img/mockup1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartbzd/todo-list/f2daaf0fd95d33a674d2c48c5f7f20ec42586bbc/src/img/mockup1.png -------------------------------------------------------------------------------- /src/img/mockup2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartbzd/todo-list/f2daaf0fd95d33a674d2c48c5f7f20ec42586bbc/src/img/mockup2.png -------------------------------------------------------------------------------- /src/img/mockup3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartbzd/todo-list/f2daaf0fd95d33a674d2c48c5f7f20ec42586bbc/src/img/mockup3.png -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import './styles.scss'; 2 | import appController from './modules/app'; 3 | 4 | appController(); 5 | -------------------------------------------------------------------------------- /src/modules/app.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-use-before-define */ 2 | /* eslint-disable prettier/prettier */ 3 | import { parseISO, isToday, isPast, isThisWeek } from 'date-fns'; 4 | import Task from './models/taskModel'; 5 | import Project from './models/projectModel'; 6 | import storage, { projects, allTasksList } from './models/storageModel'; 7 | import createTask from './views/taskView'; 8 | import createProject from './views/projectView'; 9 | 10 | export default function appController() { 11 | const projectForm = document.querySelector('#project-form'); 12 | const projectInput = document.querySelector('#project-name'); 13 | const taskForm = document.querySelector('.task-form'); 14 | const tasksWrapper = document.querySelector('.t-wrapper'); 15 | const formWrapper = document.querySelector('.f-wrapper'); 16 | const openWrapper = document.querySelector('.o-wrapper'); 17 | const editBtn = document.querySelector('.edit-task-btn'); 18 | const addTaskBtn = document.querySelector('.add-btn'); 19 | const addBtn = document.querySelector('.add-task-btn'); 20 | const addProjectBtn = document.querySelector('.fa-plus'); 21 | const titleInput = document.querySelector('#task'); 22 | const noteInput = document.querySelector('#note'); 23 | const dateInput = document.querySelector('#date'); 24 | const formInput = document.querySelector('#projects'); 25 | const formStar = document.querySelector('.add-star'); 26 | const projectGrp = document.querySelector('.project-grp'); 27 | const input = document.querySelector('#project-name'); 28 | const selectAll = document.querySelector('.all'); 29 | const selectStarred = document.querySelector('.starred'); 30 | const selectToday = document.querySelector('.today'); 31 | const selectWeek = document.querySelector('.week'); 32 | const themeIcon = document.querySelector('.theme'); 33 | const mobileMenu = document.querySelector('.menu-icon'); 34 | const sidebar = document.querySelector('.sidebar'); 35 | const content = document.querySelector('.content'); 36 | const filters = document.querySelector('.filters'); 37 | const logo = document.querySelector('header h1'); 38 | 39 | let componentColor = window 40 | .getComputedStyle(document.documentElement) 41 | .getPropertyValue('--component'); 42 | let primaryColor = window 43 | .getComputedStyle(document.documentElement) 44 | .getPropertyValue('--primary'); 45 | const textColor = window 46 | .getComputedStyle(document.documentElement) 47 | .getPropertyValue('--dk-text'); 48 | const subtextColor = window 49 | .getComputedStyle(document.documentElement) 50 | .getPropertyValue('--dk-subtext'); 51 | 52 | let taskIndex = 0; 53 | let projectIndex; 54 | let currProject; 55 | let lastProject; 56 | let selected = ''; 57 | 58 | const showForm = () => { 59 | formWrapper.style.animation = 'ease-out formRight 0.1s'; 60 | formWrapper.style.display = 'flex'; 61 | setTimeout(() => { 62 | formWrapper.style.animation = ''; 63 | }, 100); 64 | }; 65 | const hideForm = () => { 66 | formWrapper.style.animation = 'ease-out formRight reverse 0.1s'; 67 | setTimeout(() => { 68 | formWrapper.style.display = 'none'; 69 | formWrapper.style.animation = ''; 70 | }, 100); 71 | }; 72 | const showTasksRight = () => { 73 | tasksWrapper.style.display = 'flex'; 74 | tasksWrapper.style.animation = 'ease-out taskRight reverse 0.1s'; 75 | setTimeout(() => { 76 | tasksWrapper.style.animation = ''; 77 | }, 100); 78 | }; 79 | const hideTasksRight = () => { 80 | tasksWrapper.style.animation = 'ease-out taskRight 0.1s'; 81 | setTimeout(() => { 82 | tasksWrapper.style.display = 'none'; 83 | tasksWrapper.style.animation = ''; 84 | }, 100); 85 | }; 86 | const showTasksLeft = () => { 87 | tasksWrapper.style.display = 'flex'; 88 | tasksWrapper.style.animation = 'ease-out formRight 0.1s'; 89 | setTimeout(() => { 90 | tasksWrapper.style.animation = ''; 91 | }, 100); 92 | }; 93 | const hideTasksLeft = () => { 94 | tasksWrapper.style.animation = 'ease-out formRight reverse 0.1s'; 95 | setTimeout(() => { 96 | tasksWrapper.style.display = 'none'; 97 | tasksWrapper.style.animation = ''; 98 | }, 100); 99 | }; 100 | const openTask = () => { 101 | openWrapper.style.display = 'flex'; 102 | openWrapper.style.animation = 'ease-out taskRight reverse 0.1s'; 103 | setTimeout(() => { 104 | openWrapper.style.animation = ''; 105 | }, 100); 106 | }; 107 | const closeTask = () => { 108 | openWrapper.style.animation = 'ease-out taskRight 0.1s'; 109 | setTimeout(() => { 110 | openWrapper.style.display = 'none'; 111 | openWrapper.style.animation = ''; 112 | }, 100); 113 | }; 114 | 115 | function isProjectValid() { 116 | const project = document.querySelector('#project-name'); 117 | if (!project.value) { 118 | project.setCustomValidity('Project cannot be empty'); 119 | project.reportValidity(); 120 | return false; 121 | } 122 | return true; 123 | } 124 | function isTaskValid() { 125 | const task = document.querySelector('#task'); 126 | if (!task.value) { 127 | task.setCustomValidity('Task cannot be empty'); 128 | task.reportValidity(); 129 | return false; 130 | } 131 | return true; 132 | } 133 | 134 | function toggleBtnText() { 135 | const title = document.querySelector('.form-title-header'); 136 | title.textContent = 'Edit Task'; 137 | editBtn.classList.toggle('hidden'); 138 | addBtn.classList.toggle('hidden'); 139 | } 140 | function toggleComplete(e, project) { 141 | const wrapper = e.target.closest('.task'); 142 | taskIndex = e.target.closest('.task').getAttribute('data-id'); 143 | const task = project.getTasks()[taskIndex]; 144 | const selectedTask = e.target; 145 | 146 | if (!task.isComplete) { 147 | wrapper.addEventListener('click', renderTasksOpenView); 148 | } 149 | task.isComplete = !task.isComplete; 150 | if (task.isComplete) { 151 | wrapper.removeEventListener('click', renderTasksOpenView); 152 | } 153 | 154 | const checkmarkClasses = ['fa-regular', 'fa-solid', 'fa-circle', 'fa-circle-check']; 155 | checkmarkClasses.forEach((className) => { 156 | if (selectedTask.classList.contains('check')) { 157 | selectedTask.classList.toggle(className); 158 | } 159 | }); 160 | 161 | const title = selectedTask.closest('.task').querySelector('.task-title'); 162 | const edit = selectedTask.closest('.task').querySelector('.edit'); 163 | const trash = selectedTask.closest('.task').querySelector('.delete'); 164 | const star = selectedTask.closest('.task').querySelector('.fa-star'); 165 | 166 | title.style.transition = '0.2s ease-in-out'; 167 | wrapper.style.transition = '0.2s ease-in-out'; 168 | edit.style.transition = '0.2s ease-in-out'; 169 | trash.style.transition = '0.2s ease-in-out'; 170 | star.style.transition = '0.2s ease-in-out'; 171 | if (title.style.textDecoration === '' && title.style.color !== '#d2d8f7a6') { 172 | wrapper.style.backgroundColor = 'transparent'; 173 | wrapper.style.boxShadow = 'none'; 174 | title.style.textDecoration = 'line-through'; 175 | title.style.color = subtextColor; 176 | edit.style.opacity = '0'; 177 | trash.style.opacity = '1'; 178 | star.style.opacity = '0'; 179 | 180 | setTimeout(() => { 181 | edit.style.display = 'none'; 182 | trash.style.display = 'flex'; 183 | star.style.display = 'none'; 184 | }, 100); 185 | } else { 186 | wrapper.style.backgroundColor = componentColor; 187 | title.style.textDecoration = ''; 188 | title.style.color = textColor; 189 | edit.style.opacity = '0'; 190 | trash.style.opacity = '0'; 191 | star.style.opacity = '1'; 192 | 193 | setTimeout(() => { 194 | renderTasks(currProject); 195 | edit.style.display = 'flex'; 196 | trash.style.display = 'none'; 197 | star.style.display = 'flex'; 198 | }, 100); 199 | } 200 | } 201 | function toggleFormStar() { 202 | formStar.classList.toggle('starred'); 203 | formStar.classList.toggle('fa-regular'); 204 | formStar.classList.toggle('fa-solid'); 205 | } 206 | function togglePlusBtn() { 207 | addProjectBtn.classList.toggle('plus'); 208 | addProjectBtn.classList.toggle('rotated'); 209 | } 210 | function toggleAddProject() { 211 | document.querySelector('form').reset(); 212 | selected = ''; 213 | togglePlusBtn(); 214 | projectForm.hidden = !projectForm.hidden; 215 | 216 | if (!projectForm.hidden) { 217 | projectForm.style.animation = 'ease-out formVertical 0.2s'; 218 | for (let i = 0; i < projectGrp.children.length; i++) { 219 | projectGrp.children[i].style.animation = 'ease-out formVertical 0.2s'; 220 | } 221 | projectGrp.insertBefore(projectForm, projectGrp.firstChild); 222 | input.focus(); 223 | } 224 | 225 | const projectBtns = document.querySelectorAll('.project-btn-grp .options'); 226 | projectBtns.forEach((btn) => { 227 | btn.style.opacity = '0'; 228 | }); 229 | 230 | if (projectForm.hidden) { 231 | filters.classList.remove('filtersHide'); 232 | selected = ''; 233 | resetProjects(); 234 | renderProjects(); 235 | updateSelectedProject(); 236 | updateSelectedFilter(); 237 | } 238 | } 239 | function toggleEditProject(e) { 240 | projectForm.hidden = !projectForm.hidden; 241 | const projectBtns = document.querySelectorAll('.options'); 242 | 243 | projectBtns.forEach((btn) => { 244 | btn.style.display = 'none'; 245 | btn.style.opacity = '0'; 246 | }); 247 | 248 | if (!projectForm.hidden) { 249 | projectForm.style.animation = 'ease-out appearForm 0.2s'; 250 | projectIndex = Number(e.target.closest('.project').getAttribute('data-id')); 251 | const selectedIndex = projectGrp.children.item(projectIndex); 252 | 253 | projectGrp.insertBefore(projectForm, selectedIndex); 254 | input.value = projects[projectIndex].name; 255 | input.focus(); 256 | 257 | selected = e.target.closest('.project'); 258 | selected.classList.toggle('edited'); 259 | selected.style.display = 'none'; 260 | 261 | currProject = projects[projectIndex]; 262 | } 263 | togglePlusBtn(); 264 | } 265 | function toggleOverflow() { 266 | const note = document.querySelector('#open-note'); 267 | const botLine = document.querySelector('.bot-note-line'); 268 | if (note.scrollHeight > note.clientHeight) { 269 | botLine.classList.add('visible'); 270 | } else botLine.classList.remove('visible'); 271 | } 272 | 273 | function resetStar() { 274 | document.querySelector('.add-star').className = 'add-star fa-regular fa-star'; 275 | } 276 | function resetProjects() { 277 | document.querySelector('.project-grp').innerHTML = ''; 278 | document.querySelector('select').innerHTML = ''; 279 | } 280 | function resetTasks() { 281 | document.querySelector('.tasks').innerHTML = ''; 282 | } 283 | function resetForm() { 284 | if (editBtn.classList.contains('hidden')) { 285 | document.querySelector('.task-form').reset(); 286 | document.querySelector('form').reset(); 287 | } 288 | } 289 | function resetFilters() { 290 | const filtersList = document.querySelectorAll('.filter'); 291 | filtersList.forEach((filter) => { 292 | filter.style.backgroundColor = 'transparent'; 293 | }); 294 | } 295 | function resetSelectedProject() { 296 | const folders = document.querySelectorAll('.folder'); 297 | folders.forEach((folder) => { 298 | folder.className = 'folder fa-regular fa-folder'; 299 | }); 300 | const projectsList = document.querySelectorAll('.project'); 301 | projectsList.forEach((project) => { 302 | project.style.backgroundColor = 'transparent'; 303 | }); 304 | } 305 | function resetMobileAnimations() { 306 | content.style.animation = ''; 307 | sidebar.style.animation = ''; 308 | } 309 | 310 | function updateOpenTask(e) { 311 | const project = document.querySelector('#open-project'); 312 | const folder = document.querySelector('.open-folder'); 313 | const title = document.querySelector('#open-title'); 314 | const note = document.querySelector('#open-note'); 315 | const date = document.querySelector('.open-date'); 316 | const star = document.querySelector('.open-star'); 317 | const id = e.target.closest('.task').getAttribute('data-id'); 318 | const isStarred = currProject.tasks[id].getIsStarred(); 319 | 320 | title.textContent = currProject.tasks[id].title; 321 | 322 | if (currProject.name === 'All') { 323 | project.textContent = 'All'; 324 | folder.className = 'material-symbols-rounded open-folder'; 325 | folder.textContent = 'inbox'; 326 | } else if (currProject.name === 'Starred') { 327 | project.textContent = 'Starred'; 328 | folder.className = 'fa-solid fa-star open-folder'; 329 | folder.textContent = ''; 330 | } else if (currProject.name === 'Today') { 331 | project.textContent = 'Today'; 332 | folder.className = 'material-symbols-rounded'; 333 | folder.textContent = 'today'; 334 | } else if (currProject.name === 'Week') { 335 | project.textContent = 'Today'; 336 | folder.className = 'material-symbols-rounded'; 337 | folder.textContent = 'date_range'; 338 | } else { 339 | folder.className = 'material-symbols-rounded open-folder'; 340 | project.textContent = currProject.tasks[id].project; 341 | folder.textContent = 'folder'; 342 | } 343 | 344 | if (isStarred === false) { 345 | star.style.display = 'none'; 346 | } else star.style.display = 'inline-block'; 347 | 348 | if (currProject.tasks[id].note === '') { 349 | note.textContent = 'No note'; 350 | note.style.textAlign = 'center'; 351 | } else { 352 | note.style.textAlign = 'left'; 353 | note.textContent = currProject.tasks[id].note; 354 | } 355 | 356 | const selectedDate = parseISO(currProject.tasks[id].date); 357 | if (currProject.tasks[id].date === '') { 358 | date.textContent = ''; 359 | } else if (isPast(selectedDate) && !isToday(selectedDate)) { 360 | date.textContent = 'Past Due'; 361 | date.style.color = '#E34A4A'; 362 | } else { 363 | date.textContent = selectedDate.toLocaleDateString(); 364 | } 365 | } 366 | function updateProjectsIndex() { 367 | for (let i = 0; i < projects.length; i++) { 368 | projects[i].index = i; 369 | } 370 | } 371 | function updateSelectedProject() { 372 | updateProjectsIndex(); 373 | resetFilters(); 374 | const projectsList = document.querySelectorAll('.project'); 375 | let foundProject = false; 376 | projectsList.forEach((project, index) => { 377 | if (foundProject) return; 378 | const i = project.querySelector('i'); 379 | const p = project.querySelector('p'); 380 | if (p.textContent === currProject.name && index === currProject.index) { 381 | p.closest('.project').style.backgroundColor = componentColor; 382 | i.closest('.folder').className = 'folder fa-solid fa-folder'; 383 | foundProject = true; 384 | } 385 | }); 386 | } 387 | function updateSelectedFilter() { 388 | const filtersList = ['All', 'Starred', 'Today', 'Week']; 389 | const arr = [selectAll, selectStarred, selectToday, selectWeek]; 390 | for (let i = 0; i < filtersList.length; i++) { 391 | if (filtersList[i] === currProject.name) { 392 | arr[i].style.transition = '0.2s ease-out'; 393 | arr[i].style.backgroundColor = componentColor; 394 | } 395 | } 396 | } 397 | function updateColors() { 398 | componentColor = window 399 | .getComputedStyle(document.documentElement) 400 | .getPropertyValue('--component'); 401 | primaryColor = window 402 | .getComputedStyle(document.documentElement) 403 | .getPropertyValue('--primary'); 404 | } 405 | 406 | function renderTasksOpenView(e) { 407 | hideTasksLeft(); 408 | 409 | setTimeout(() => { 410 | openTask(); 411 | updateOpenTask(e); 412 | toggleOverflow(); 413 | }, 100); 414 | } 415 | function renderFormView() { 416 | resetForm(); 417 | resetStar(); 418 | if (!projectForm.hidden) { 419 | toggleAddProject(); 420 | } 421 | document.querySelector('select').value = currProject.name; 422 | document.querySelector('.form-title-header').textContent = 'Add Task'; 423 | 424 | hideTasksRight(); 425 | setTimeout(() => { 426 | showForm(); 427 | titleInput.focus(); 428 | }, 100); 429 | } 430 | function renderEditView(e, project) { 431 | e.stopImmediatePropagation(); 432 | if (!projectForm.hidden) { 433 | toggleAddProject(); 434 | } 435 | taskIndex = e.currentTarget.closest('.task').getAttribute('data-id'); 436 | 437 | titleInput.value = project.getTasks()[taskIndex].title; 438 | noteInput.value = project.getTasks()[taskIndex].note; 439 | dateInput.value = project.getTasks()[taskIndex].date; 440 | 441 | const projectName = e.currentTarget 442 | .closest('.task') 443 | .getAttribute('data-project-name'); 444 | document.querySelector('select').value = projectName; 445 | lastProject = projects.find(({ name }) => name === projectName); 446 | if (project.getTasks()[taskIndex].getIsStarred()) { 447 | formStar.classList.add('starred'); 448 | formStar.classList.remove('fa-regular'); 449 | formStar.classList.add('fa-solid'); 450 | } else { 451 | formStar.classList.remove('starred'); 452 | formStar.classList.add('fa-regular'); 453 | formStar.classList.remove('fa-solid'); 454 | } 455 | 456 | hideTasksRight(); 457 | setTimeout(() => { 458 | showForm(); 459 | titleInput.focus(); 460 | toggleBtnText(); 461 | }, 100); 462 | } 463 | function renderTasksView(e) { 464 | e.preventDefault(); 465 | if (addBtn.classList.contains('hidden')) { 466 | toggleBtnText(); 467 | } 468 | if (formWrapper.style.display === 'flex') { 469 | hideForm(); 470 | setTimeout(() => { 471 | showTasksRight(); 472 | }, 100); 473 | return; 474 | } 475 | if (openWrapper.style.display === 'flex') { 476 | closeTask(); 477 | setTimeout(() => { 478 | showTasksLeft(); 479 | }, 100); 480 | } 481 | } 482 | 483 | function handleProjectClick(e) { 484 | resetFilters(); 485 | resetSelectedProject(); 486 | const projectWrappers = document.querySelectorAll('.project'); 487 | const project = e.currentTarget.closest('.project'); 488 | projectWrappers.forEach((wrapper) => { 489 | wrapper.style.backgroundColor = ''; 490 | }); 491 | project.style.backgroundColor = componentColor; 492 | 493 | const folder = project.querySelector('.folder'); 494 | folder.className = 'folder fa-solid fa-folder'; 495 | 496 | projectIndex = Number(project.getAttribute('data-id')); 497 | currProject = projects[projectIndex]; 498 | 499 | renderTasks(currProject); 500 | renderTasksView(e); 501 | updateSelectedProject(); 502 | 503 | closeSideBarModal(); 504 | } 505 | function handleEditProjectClick(e) { 506 | e.stopImmediatePropagation(); 507 | toggleEditProject(e); 508 | renderTasks(currProject); 509 | renderTasksView(e); 510 | 511 | if (content.style.display === 'none') { 512 | toggleSideBarFocus(); 513 | } 514 | } 515 | function handleDeleteProjectClick(e) { 516 | e.stopPropagation(); 517 | e.target.closest('.project').style.animation = 'ease-in formRight reverse 0.2s'; 518 | e.target.closest('.project').style.opacity = '0.7'; 519 | 520 | setTimeout(() => { 521 | deleteProject(e); 522 | resetProjects(); 523 | renderProjects(); 524 | 525 | if (projects.length === 0 || currProject === allTasksList) { 526 | updateAllTasks(); 527 | currProject = allTasksList; 528 | resetFilters(); 529 | updateSelectedFilter(); 530 | } else if (projects.length > 0) { 531 | updateSelectedProject(); 532 | } else updateSelectedFilter(); 533 | 534 | renderTasks(currProject); 535 | renderTasksView(e); 536 | }, 100); 537 | } 538 | function addProjectHandlers() { 539 | const projectWrappers = document.querySelectorAll('.project'); 540 | const editBtns = document.querySelectorAll('.edit-p'); 541 | const deleteBtns = document.querySelectorAll('.delete-p'); 542 | 543 | projectWrappers.forEach((wrapper) => { 544 | wrapper.addEventListener('click', handleProjectClick); 545 | }); 546 | 547 | editBtns.forEach((btn) => { 548 | btn.addEventListener('click', handleEditProjectClick); 549 | }); 550 | 551 | deleteBtns.forEach((btn) => { 552 | btn.addEventListener('click', handleDeleteProjectClick); 553 | }); 554 | } 555 | function renderProjects() { 556 | projects.forEach((project) => { 557 | createProject(project); 558 | }); 559 | addProjectHandlers(); 560 | } 561 | function storeProject() { 562 | const name = document.querySelector('#project-name').value; 563 | return new Project(name); 564 | } 565 | function addProject() { 566 | if (!isProjectValid()) return; 567 | const newProject = storeProject(); 568 | projects.unshift(newProject); 569 | currProject = newProject; 570 | currProject.index = projects.indexOf(newProject); 571 | updateProjectsIndex(); 572 | 573 | resetForm(); 574 | resetProjects(); 575 | renderProjects(); 576 | toggleAddProject(); 577 | updateSelectedProject(); 578 | closeSideBarModal(); 579 | storage().saveData(); 580 | } 581 | function editProject() { 582 | const name = document.querySelector('#project-name'); 583 | if (!name.value) { 584 | name.setCustomValidity('Task cannot be empty'); 585 | name.reportValidity(); 586 | return; 587 | } 588 | 589 | projects[projectIndex].name = name.value; 590 | currProject.index = projectIndex; 591 | resetForm(); 592 | toggleEditProject(); 593 | resetProjects(); 594 | renderProjects(); 595 | updateSelectedProject(); 596 | closeSideBarModal(); 597 | storage().saveData(); 598 | } 599 | function deleteProject(e) { 600 | projectIndex = Number(e.target.closest('.project').getAttribute('data-id')); 601 | projects.splice(projectIndex, 1); 602 | updateProjectsIndex(); 603 | storage().saveData(); 604 | } 605 | 606 | function addTaskHandlers() { 607 | const taskWrapper = document.querySelectorAll('.task'); 608 | const checkmarks = document.querySelectorAll('.fa-circle, .fa-circle-check'); 609 | const editBtns = document.querySelectorAll('.edit'); 610 | const deleteBtns = document.querySelectorAll('.delete'); 611 | const backBtn = document.querySelectorAll('.back-btn'); 612 | 613 | backBtn.forEach((button) => { 614 | button.addEventListener('click', renderTasksView); 615 | }); 616 | taskWrapper.forEach((task) => { 617 | if (!task.isComplete) { 618 | task.addEventListener('click', renderTasksOpenView); 619 | } 620 | if (task.isComplete) { 621 | task.removeEventListener('click', renderTasksOpenView); 622 | } 623 | }); 624 | checkmarks.forEach((checkmark) => { 625 | checkmark.addEventListener('click', (e) => { 626 | toggleComplete(e, currProject); 627 | }); 628 | }); 629 | editBtns.forEach((button) => { 630 | button.addEventListener('click', (e) => { 631 | renderEditView(e, currProject); 632 | }); 633 | }); 634 | deleteBtns.forEach((btn) => { 635 | btn.addEventListener('click', (e) => { 636 | e.stopPropagation(); 637 | e.target.closest('.task').style.animation = 'ease-in formRight reverse 0.2s'; 638 | e.target.closest('.task').style.opacity = '0'; 639 | 640 | setTimeout(() => { 641 | toggleComplete(e, currProject); 642 | deleteTask(e, currProject); 643 | }, 200); 644 | }); 645 | }); 646 | } 647 | function renderTasks(project, selectedTask) { 648 | resetTasks(); 649 | if (project.getTasks().length === 0) { 650 | document.querySelector('.tasks').appendChild(document.createElement('p')); 651 | document.querySelector('.tasks p').textContent = 'No tasks found'; 652 | document.querySelector('.tasks p').className = 'no-tasks'; 653 | } 654 | 655 | project.getTasks().forEach((task) => { 656 | const taskWrapper = createTask(task, project.getTasks()); 657 | document.querySelector('.tasks').append(taskWrapper); 658 | taskWrapper.setAttribute('data-project-name', task.project); 659 | 660 | if (task.isStarred) { 661 | taskWrapper.querySelector('.fa-star').classList.replace('fa-regular', 'fa-solid'); 662 | } 663 | 664 | if (task.isComplete && task !== selectedTask) { 665 | const wrapper = taskWrapper.closest('.task'); 666 | const title = taskWrapper.closest('.task').querySelector('.task-title'); 667 | const edit = taskWrapper.closest('.task').querySelector('.edit'); 668 | const trash = taskWrapper.closest('.task').querySelector('.delete'); 669 | const star = taskWrapper.closest('.task').querySelector('.fa-star'); 670 | 671 | title.style.textDecoration = 'line-through'; 672 | title.style.color = subtextColor; 673 | 674 | wrapper.style.backgroundColor = 'transparent'; 675 | wrapper.style.boxShadow = 'none'; 676 | wrapper.removeEventListener('click', renderTasksOpenView); 677 | 678 | edit.style.display = 'none'; 679 | trash.style.display = 'flex'; 680 | star.style.display = 'none'; 681 | } 682 | }); 683 | addTaskHandlers(); 684 | } 685 | function storeTask() { 686 | const title = document.querySelector('#task').value; 687 | const note = document.querySelector('#note').value; 688 | const project = document.querySelector('#projects').value; 689 | const date = document.querySelector('#date').value; 690 | const isStarred = formStar.classList.contains('starred'); 691 | 692 | return new Task(title, note, project, date, isStarred); 693 | } 694 | function addTask(e, project) { 695 | if (!isTaskValid()) return; 696 | e.preventDefault(); 697 | 698 | const newTask = storeTask(); 699 | project = projects.find(({ name }) => name === formInput.value); 700 | 701 | if (formInput.value === '') { 702 | allTasksList.getTasks().push(newTask); 703 | currProject = allTasksList; 704 | } else { 705 | project.getTasks().push(newTask); 706 | currProject = project; 707 | } 708 | 709 | if (!projectForm.hidden) { 710 | toggleAddProject(); 711 | } 712 | 713 | renderTasksView(e); 714 | renderTasks(currProject); 715 | resetForm(); 716 | storage().saveData(); 717 | } 718 | function editTask(e, project) { 719 | if (!isTaskValid()) return; 720 | e.preventDefault(); 721 | const editedTask = storeTask(); 722 | const temp = projects.find(({ name }) => name === formInput.value); 723 | 724 | if ( 725 | currProject.name === 'Starred' || 726 | currProject.name === 'Today' || 727 | currProject.name === 'Week' 728 | ) { 729 | currProject = allTasksList; 730 | } 731 | 732 | if ( 733 | formInput.value !== project.name && 734 | formInput.value !== '' && 735 | currProject === allTasksList 736 | ) { 737 | temp.getTasks().push(editedTask); 738 | allTasksList.getTasks().splice(taskIndex, 1); 739 | currProject = temp; 740 | 741 | if (lastProject !== undefined) { 742 | lastProject.getTasks().splice(taskIndex, 1); 743 | lastProject = undefined; 744 | } 745 | } else if (formInput.value !== project.name && formInput.value !== '') { 746 | temp.getTasks().push(editedTask); 747 | project.getTasks().splice(taskIndex, 1); 748 | allTasksList.getTasks().splice(taskIndex, 1); 749 | currProject = temp; 750 | } else { 751 | project.getTasks().splice(taskIndex, 1, editedTask); 752 | } 753 | 754 | if (!projectForm.hidden) { 755 | toggleAddProject(); 756 | } 757 | 758 | resetProjects(); 759 | renderProjects(); 760 | renderTasksView(e); 761 | renderTasks(currProject); 762 | updateProjectsIndex(); 763 | updateSelectedProject(); 764 | storage().saveData(); 765 | } 766 | function deleteTask(e, project) { 767 | e.stopImmediatePropagation(); 768 | taskIndex = e.target.closest('.task').getAttribute('data-id'); 769 | const taskToDelete = project.getTasks()[taskIndex]; 770 | 771 | let projectToDeleteFrom; 772 | for (let i = 0; i < projects.length; i++) { 773 | if (projects[i].getTasks().includes(taskToDelete)) { 774 | projectToDeleteFrom = projects[i]; 775 | break; 776 | } 777 | } 778 | 779 | if (projectToDeleteFrom !== undefined) { 780 | projectToDeleteFrom.removeTask(taskToDelete); 781 | } 782 | 783 | if (projectToDeleteFrom !== currProject) { 784 | allTasksList.removeTask(taskToDelete); 785 | currProject = allTasksList; 786 | } 787 | 788 | renderTasksView(e); 789 | renderTasks(currProject, taskToDelete); 790 | updateSelectedProject(); 791 | updateSelectedFilter(); 792 | storage().saveData(); 793 | } 794 | function updateAllTasks() { 795 | if (allTasksList.getTasks().length === 0 || allTasksList.getTasks() !== currProject) { 796 | const allTasks = projects.flatMap((project) => project.tasks); 797 | const unassignedTasks = allTasksList 798 | .getTasks() 799 | .filter((task) => task.project === ''); 800 | const combinedTasks = allTasks.concat(unassignedTasks); 801 | allTasksList.tasks.length = 0; 802 | allTasksList.tasks.push(...combinedTasks); 803 | } else { 804 | currProject = allTasksList; 805 | } 806 | } 807 | function updateStarredTasks() { 808 | const starredTasks = allTasksList.getTasks().filter((task) => task.isStarred); 809 | currProject = new Project('Starred', starredTasks); 810 | } 811 | function updateTodayTasks() { 812 | const todayTasks = allTasksList 813 | .getTasks() 814 | .filter((task) => isToday(parseISO(task.date))); 815 | 816 | currProject = new Project('Today', todayTasks); 817 | } 818 | function updateWeekTasks() { 819 | const weekTasks = allTasksList 820 | .getTasks() 821 | .filter((task) => isThisWeek(parseISO(task.date))); 822 | 823 | currProject = new Project('Week', weekTasks); 824 | } 825 | function showAll(e) { 826 | resetFilters(); 827 | updateAllTasks(); 828 | currProject = allTasksList; 829 | 830 | resetSelectedProject(); 831 | resetProjects(); 832 | 833 | renderProjects(); 834 | renderTasksView(e); 835 | renderTasks(currProject); 836 | updateSelectedFilter(); 837 | closeSideBarModal(); 838 | } 839 | function showStarred(e) { 840 | resetFilters(); 841 | updateAllTasks(); 842 | updateStarredTasks(); 843 | const starredProject = currProject; 844 | 845 | resetSelectedProject(); 846 | resetProjects(); 847 | 848 | renderProjects(); 849 | renderTasksView(e); 850 | renderTasks(starredProject); 851 | currProject = starredProject; 852 | updateSelectedFilter(); 853 | closeSideBarModal(); 854 | } 855 | function showToday(e) { 856 | resetFilters(); 857 | updateAllTasks(); 858 | updateTodayTasks(); 859 | const todayProject = currProject; 860 | 861 | resetSelectedProject(); 862 | resetProjects(); 863 | 864 | renderProjects(); 865 | renderTasksView(e); 866 | renderTasks(todayProject); 867 | currProject = todayProject; 868 | updateSelectedFilter(); 869 | closeSideBarModal(); 870 | } 871 | function showWeek(e) { 872 | resetFilters(); 873 | updateAllTasks(); 874 | updateWeekTasks(); 875 | const weekProject = currProject; 876 | 877 | resetSelectedProject(); 878 | resetProjects(); 879 | 880 | renderProjects(); 881 | renderTasksView(e); 882 | renderTasks(weekProject); 883 | currProject = weekProject; 884 | updateSelectedFilter(); 885 | closeSideBarModal(); 886 | } 887 | 888 | function toggleMobileFocus() { 889 | const header = document.querySelector('header'); 890 | content.style.transition = '0.2s ease-out'; 891 | 892 | if (titleInput.matches(':focus') || noteInput.matches(':focus')) { 893 | header.classList.add('header'); 894 | content.classList.add('mobile-stretch'); 895 | content.style.transition = ''; 896 | } else { 897 | header.classList.remove('header'); 898 | content.classList.remove('mobile-stretch'); 899 | content.style.transition = '0.2s ease-out'; 900 | } 901 | } 902 | function toggleSideBarFocus() { 903 | if (projectInput.matches(':focus')) { 904 | filters.classList.add('filtersHide'); 905 | resetFilters(); 906 | } else if (!addProjectBtn.matches(':active')) { 907 | document.querySelector('form').reset(); 908 | projectForm.hidden = true; 909 | togglePlusBtn(); 910 | resetProjects(); 911 | renderProjects(); 912 | 913 | const filtersArr = ['All', 'Starred', 'Today', 'Week']; 914 | for (let i = 0; i < filtersArr.length; i++) { 915 | if (currProject.name === filtersArr[i]) { 916 | updateSelectedFilter(); 917 | break; 918 | } else { 919 | updateSelectedProject(); 920 | } 921 | } 922 | filters.classList.remove('filtersHide'); 923 | } 924 | } 925 | function toggleSideBarModal() { 926 | mobileMenu.classList.toggle('active'); 927 | 928 | if (mobileMenu.classList.contains('active')) { 929 | sidebar.style.animation = '0.2s formRight ease-out'; 930 | sidebar.style.display = 'flex'; 931 | content.style.display = 'none'; 932 | logo.classList.add('blurred'); 933 | updateSelectedFilter(); 934 | 935 | setTimeout(() => { 936 | resetMobileAnimations(); 937 | }, 200); 938 | } else { 939 | sidebar.style.animation = '0.2s reverse formRight ease-out'; 940 | logo.classList.remove('blurred'); 941 | 942 | setTimeout(() => { 943 | content.style.display = 'block'; 944 | sidebar.style.display = 'none'; 945 | resetMobileAnimations(); 946 | mobileMenu.checked = false; 947 | }, 100); 948 | } 949 | } 950 | function closeSideBarModal() { 951 | if (mobileMenu.classList.contains('active')) { 952 | toggleSideBarModal(); 953 | } 954 | } 955 | function isMobileView() { 956 | if (window.clientWidth >= 480) { 957 | sidebar.style.display = 'flex'; 958 | content.style.display = 'block'; 959 | } else if (window.clientWidth < 480) { 960 | sidebar.style.display = 'none'; 961 | content.style.display = 'block'; 962 | mobileMenu.classList.remove('active'); 963 | logo.classList.remove('blurred'); 964 | mobileMenu.checked = false; 965 | } 966 | } 967 | 968 | function toggleTheme() { 969 | const theme = document.documentElement.getAttribute('data-theme'); 970 | const newTheme = theme === 'dark' ? 'light' : 'dark'; 971 | document.documentElement.setAttribute('data-theme', newTheme); 972 | 973 | const temp = themeIcon.textContent; 974 | themeIcon.textContent = temp === 'toggle_on' ? 'toggle_off' : 'toggle_on'; 975 | 976 | updateColors(); 977 | const date = document.querySelector('.open-date'); 978 | if (date.textContent === 'Past Due') { 979 | date.style.color = '#E34A4A'; 980 | } else { 981 | date.style.color = primaryColor; 982 | } 983 | 984 | updateSelectedProject(); 985 | updateSelectedFilter(); 986 | } 987 | function initIntro() { 988 | const introTask = new Task( 989 | 'Click me to learn more!', 990 | ' - Expand tasks to view additional details about them. \n\n - Write notes, add dates and star tasks from the form pane. \n\n - Thank you for checking out my project!', 991 | 'Default', 992 | '', 993 | true 994 | ); 995 | const introTaskTwo = new Task( 996 | 'Sidebar Info', 997 | ' - Filter created tasks by All, Starred, Today or Week. \n\n - Add projects by clicking (+) and pressing Enter. \n\n - Hover over existing projects to edit or delete them.', 998 | 'Default', 999 | '', 1000 | true 1001 | ); 1002 | const introProject = new Project('Default'); 1003 | introProject.index = 0; 1004 | currProject = introProject; 1005 | projects.push(introProject); 1006 | introProject.getTasks().push(introTask); 1007 | introProject.getTasks().push(introTaskTwo); 1008 | } 1009 | function findProjects() { 1010 | if (!localStorage.getItem('data')) { 1011 | initIntro(); 1012 | } else { 1013 | storage().getData(); 1014 | } 1015 | 1016 | updateAllTasks(); 1017 | currProject = allTasksList; 1018 | renderTasks(currProject); 1019 | updateSelectedFilter(); 1020 | } 1021 | window.addEventListener('resize', isMobileView); 1022 | projectInput.addEventListener('focus', toggleSideBarFocus); 1023 | projectInput.addEventListener('blur', toggleSideBarFocus); 1024 | projectInput.addEventListener('touchend', toggleSideBarFocus); 1025 | mobileMenu.addEventListener('click', toggleSideBarModal); 1026 | titleInput.addEventListener('focus', toggleMobileFocus); 1027 | titleInput.addEventListener('blur', toggleMobileFocus); 1028 | titleInput.addEventListener('touchend', toggleMobileFocus); 1029 | noteInput.addEventListener('focus', toggleMobileFocus); 1030 | noteInput.addEventListener('blur', toggleMobileFocus); 1031 | noteInput.addEventListener('touchend', toggleMobileFocus); 1032 | themeIcon.addEventListener('click', toggleTheme); 1033 | selectAll.addEventListener('click', showAll); 1034 | selectStarred.addEventListener('click', showStarred); 1035 | selectToday.addEventListener('click', showToday); 1036 | selectWeek.addEventListener('click', showWeek); 1037 | addProjectBtn.addEventListener('click', toggleAddProject); 1038 | formStar.addEventListener('click', toggleFormStar); 1039 | addTaskBtn.addEventListener('click', renderFormView); 1040 | addBtn.addEventListener('click', (e) => { 1041 | addTask(e, currProject); 1042 | }); 1043 | editBtn.addEventListener('click', (e) => { 1044 | editTask(e, currProject); 1045 | }); 1046 | projectForm.addEventListener('keydown', (e) => { 1047 | if (e.key === 'Enter') { 1048 | e.preventDefault(); 1049 | if (isProjectValid()) { 1050 | if (selected === '') { 1051 | addProject(); 1052 | projectForm.hidden = true; 1053 | } else { 1054 | editProject(); 1055 | togglePlusBtn(); 1056 | selected.classList.toggle('edited'); 1057 | selected = ''; 1058 | } 1059 | } 1060 | 1061 | updateSelectedProject(); 1062 | renderTasksView(e); 1063 | renderTasks(currProject); 1064 | resetForm(); 1065 | } 1066 | }); 1067 | projectForm.addEventListener('submit', (e) => { 1068 | e.preventDefault(); 1069 | }); 1070 | taskForm.addEventListener('keydown', (e) => { 1071 | if (e.key === 'Enter' && !e.shiftKey) { 1072 | e.preventDefault(); 1073 | } 1074 | }); 1075 | 1076 | document.addEventListener('DOMContentLoaded', (e) => { 1077 | findProjects(); 1078 | renderProjects(); 1079 | resetTasks(); 1080 | renderTasks(currProject); 1081 | renderTasksView(e); 1082 | }); 1083 | } 1084 | -------------------------------------------------------------------------------- /src/modules/filter.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartbzd/todo-list/f2daaf0fd95d33a674d2c48c5f7f20ec42586bbc/src/modules/filter.js -------------------------------------------------------------------------------- /src/modules/models/projectModel.js: -------------------------------------------------------------------------------- 1 | export default class Project { 2 | constructor(name, tasks = []) { 3 | this.name = name; 4 | this.tasks = tasks; 5 | } 6 | 7 | getName() { 8 | return this.name; 9 | } 10 | 11 | getTasks() { 12 | return this.tasks; 13 | } 14 | 15 | removeTask(selectedTask) { 16 | // prettier-ignore 17 | const index = this.tasks.findIndex( 18 | (task) => task.title === selectedTask.title 19 | ); 20 | if (index !== -1) { 21 | this.tasks.splice(index, 1); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/modules/models/storageModel.js: -------------------------------------------------------------------------------- 1 | import Task from './taskModel'; 2 | import Project from './projectModel'; 3 | 4 | export const projects = []; 5 | export const allTasksList = new Project('All'); 6 | 7 | export default function storage() { 8 | let data = { 9 | projects, 10 | tasks: projects.flatMap((project) => project.getTasks()), 11 | all: allTasksList.getTasks(), 12 | }; 13 | 14 | function saveData() { 15 | localStorage.setItem('data', JSON.stringify(data)); 16 | } 17 | 18 | function getData() { 19 | const storedData = localStorage.getItem('data'); 20 | data = JSON.parse(storedData); 21 | 22 | const storedProjects = data.projects.map((project) => { 23 | const storedTasks = project.tasks.map( 24 | (task) => 25 | new Task( 26 | task.title, 27 | task.note, 28 | task.project, 29 | task.date, 30 | task.isStarred, 31 | task.isComplete 32 | ) 33 | ); 34 | return new Project(project.name, storedTasks); 35 | }); 36 | 37 | const allTasks = data.all.map( 38 | (task) => 39 | new Task( 40 | task.title, 41 | task.note, 42 | task.project, 43 | task.date, 44 | task.isStarred, 45 | task.isComplete 46 | ) 47 | ); 48 | data.projects = storedProjects; 49 | data.tasks = storedProjects.flatMap((project) => project.getTasks()); 50 | data.all = allTasks; 51 | 52 | projects.length = 0; 53 | projects.push(...storedProjects); 54 | allTasksList.tasks.length = 0; 55 | allTasksList.tasks.push(...allTasks); 56 | } 57 | 58 | return { 59 | getData, 60 | saveData, 61 | get projects() { 62 | return projects; 63 | }, 64 | get allTasksList() { 65 | return allTasksList; 66 | }, 67 | }; 68 | } 69 | -------------------------------------------------------------------------------- /src/modules/models/taskModel.js: -------------------------------------------------------------------------------- 1 | export default class Task { 2 | constructor(title, note, project, date, isStarred = false, isComplete = false) { 3 | this.title = title; 4 | this.note = note; 5 | this.project = project; 6 | this.date = date; 7 | this.isStarred = isStarred; 8 | this.isComplete = isComplete; 9 | } 10 | 11 | getTitle() { 12 | return this.title; 13 | } 14 | 15 | getProject() { 16 | return this.project; 17 | } 18 | 19 | getIsStarred() { 20 | return this.isStarred; 21 | } 22 | 23 | getIsComplete() { 24 | return this.isComplete; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/modules/views/projectView.js: -------------------------------------------------------------------------------- 1 | import { projects } from '../models/storageModel'; 2 | 3 | export default function createProject(project) { 4 | const div = () => document.createElement('div'); 5 | const text = document.createElement('p'); 6 | const icon = () => document.createElement('i'); 7 | 8 | const wrapper = div(); 9 | wrapper.className = 'project'; 10 | wrapper.setAttribute('data-id', projects.indexOf(project)); 11 | text.className = 'project-name'; 12 | text.textContent = project.name; 13 | const folder = icon(); 14 | folder.classList.add('folder', 'fa-regular', 'fa-folder'); 15 | const group = div(); 16 | group.className = 'project-btn-grp'; 17 | const edit = icon(); 18 | edit.classList.add('options', 'edit-p', 'material-symbols-rounded'); 19 | edit.textContent = 'edit'; 20 | const trash = icon(); 21 | trash.classList.add('options', 'delete-p', 'material-symbols-rounded'); 22 | trash.textContent = 'delete'; 23 | 24 | const option = () => document.createElement('option'); 25 | const pick = option(); 26 | pick.value = project.name; 27 | pick.textContent = project.name; 28 | 29 | document.querySelector('select').appendChild(pick); 30 | document.querySelector('.project-grp').appendChild(wrapper); 31 | wrapper.append(folder, text, group); 32 | group.append(edit, trash); 33 | } 34 | -------------------------------------------------------------------------------- /src/modules/views/taskView.js: -------------------------------------------------------------------------------- 1 | export default function createTask(task, project) { 2 | const taskWrapper = document.createElement('div'); 3 | const checkWrapper = document.createElement('div'); 4 | const input = document.createElement('input'); 5 | const checkmark = document.createElement('i'); 6 | const title = document.createElement('p'); 7 | 8 | const actions = document.createElement('div'); 9 | const edit = document.createElement('i'); 10 | const trash = document.createElement('i'); 11 | const star = document.createElement('i'); 12 | 13 | taskWrapper.setAttribute('data-id', project.indexOf(task)); 14 | taskWrapper.classList.add('task'); 15 | checkWrapper.classList.add('checkmark'); 16 | input.type = 'checkbox'; 17 | input.classList.add('hide-check'); 18 | if (task.isComplete) { 19 | checkmark.classList.add('fa-solid', 'fa-circle-check'); 20 | } else checkmark.classList.add('fa-regular', 'fa-circle'); 21 | checkmark.classList.add('check'); 22 | title.classList.add('task-title'); 23 | title.textContent = task.title; 24 | actions.classList.add('actions'); 25 | edit.classList.add('options', 'edit', 'material-symbols-rounded'); 26 | edit.textContent = 'edit'; 27 | trash.classList.add('options', 'delete', 'material-symbols-rounded'); 28 | trash.textContent = 'delete'; 29 | trash.style.display = 'none'; 30 | star.classList.add('fa-regular', 'fa-star'); 31 | 32 | taskWrapper.append(checkWrapper, title, actions); 33 | checkWrapper.append(input, checkmark); 34 | actions.append(edit, trash, star); 35 | 36 | return taskWrapper; 37 | } 38 | -------------------------------------------------------------------------------- /src/styles.scss: -------------------------------------------------------------------------------- 1 | html[data-theme='light'] { 2 | --bg-color: #e4f0fa; 3 | --primary: #ff7c7c; 4 | --dk-text: #2b3c5b; 5 | --dk-subtext: #7a8aa3; 6 | --card: #e6eef8; 7 | --component-s: #c4cdd1; 8 | --component: #d3dee3; 9 | --grad-base: rgb(250, 177, 135); 10 | --gradient-1: rgb(250, 177, 135); 11 | --gradient-2: rgb(246, 117, 117); 12 | --cal-indicator: invert(72%) sepia(40%) saturate(5103%) hue-rotate(314deg) 13 | brightness(110%) contrast(105%); 14 | } 15 | html[data-theme='dark'] { 16 | --bg-color: #121215; 17 | --primary: #8d8fd2; 18 | --dk-text: #dfe0fb; 19 | --dk-subtext: #84849d; 20 | --card: #18181c; 21 | --component-s: #454254; 22 | --component: #2f2d36; 23 | --grad-base: rgb(125, 90, 242); 24 | --gradient-1: rgb(145, 114, 247); 25 | --gradient-2: rgb(104, 61, 247); 26 | --cal-indicator: invert(57%) sepia(96%) saturate(184%) hue-rotate(200deg) 27 | brightness(85%) contrast(93%); 28 | } 29 | 30 | $bg-color: var(--bg-color); 31 | $primary: var(--primary); 32 | $dk-text: var(--dk-text); 33 | $dk-subtext: var(--dk-subtext); 34 | $card: var(--card); 35 | $component-s: var(--component-s); 36 | $component: var(--component); 37 | $grad-base: var(--grad-base); 38 | $gradient-1: var(--gradient-1); 39 | $gradient-2: var(--gradient-2); 40 | $cal-indicator: var(--cal-indicator); 41 | $transition: 0.2s ease-out; 42 | 43 | * { 44 | margin: 0; 45 | padding: 0; 46 | box-sizing: border-box; 47 | font-family: 'Lexend Deca', sans-serif; 48 | font-weight: 400; 49 | } 50 | html { 51 | height: -webkit-fill-available; 52 | } 53 | body { 54 | background-color: $bg-color; 55 | transition: $transition; 56 | // height: 100vh; 57 | min-height: 100vh; 58 | /* mobile viewport bug fix */ 59 | min-height: -webkit-fill-available; 60 | h2, 61 | h3 { 62 | color: $primary; 63 | } 64 | h2 { 65 | font-size: 20px; 66 | } 67 | h3 { 68 | font-size: 16px; 69 | color: $dk-subtext; 70 | display: flex; 71 | align-items: end; 72 | height: 25px; 73 | } 74 | p { 75 | display: flex; 76 | } 77 | p, 78 | span, 79 | i, 80 | q { 81 | color: $dk-subtext; 82 | } 83 | input, 84 | textarea, 85 | select { 86 | border: none; 87 | border-radius: 10px; 88 | padding: 0 10px; 89 | outline-width: 0; 90 | background-color: $component; 91 | color: $dk-text; 92 | resize: none; 93 | transition: $transition; 94 | } 95 | input { 96 | height: 35px; 97 | &:focus { 98 | outline: none; 99 | } 100 | } 101 | textarea { 102 | height: 120px; 103 | padding-top: 10px; 104 | } 105 | select { 106 | height: 35px; 107 | width: 100%; 108 | padding: 0 10px; 109 | -webkit-appearance: none; 110 | -moz-appearance: none; 111 | appearance: none; 112 | user-select: none; 113 | } 114 | button { 115 | transition: $transition; 116 | } 117 | .material-symbols-rounded, 118 | .material-symbols-outlined { 119 | font-size: 18px; 120 | } 121 | .material-symbols-rounded { 122 | font-variation-settings: 'FILL' 1, 'wght' 300, 'GRAD' 200, 'opsz' 48; 123 | } 124 | } 125 | .container { 126 | min-height: 100vh; 127 | } 128 | header { 129 | height: 130px; 130 | display: flex; 131 | align-items: center; 132 | justify-content: center; 133 | padding: 0 10vw; 134 | transition: $transition; 135 | user-select: none; 136 | h1 { 137 | color: $dk-text; 138 | font-size: 48px; 139 | width: 285px; 140 | transition: $transition; 141 | } 142 | span { 143 | background: $grad-base; 144 | background: linear-gradient(130deg, $gradient-1 0%, $gradient-2 100%); 145 | background-clip: text; 146 | -webkit-background-clip: text; 147 | -webkit-text-fill-color: transparent; 148 | transition: $transition; 149 | } 150 | div { 151 | width: 510px; 152 | } 153 | } 154 | .hidden { 155 | display: none; 156 | } 157 | .side-menu { 158 | display: none; 159 | } 160 | .cards { 161 | display: flex; 162 | flex-wrap: nowrap; 163 | justify-content: center; 164 | gap: 24px; 165 | padding: 0 8vw; 166 | .options { 167 | &:hover { 168 | color: $primary; 169 | transform: scale(1.2); 170 | transition: $transition; 171 | } 172 | &:active { 173 | transform: scale(0.9); 174 | } 175 | } 176 | .filter, 177 | .project, 178 | .task, 179 | button, 180 | .fa-plus, 181 | .theme, 182 | .add-star { 183 | cursor: pointer; 184 | } 185 | .sidebar, 186 | .content { 187 | background-color: $card; 188 | border-radius: 24px; 189 | padding: 24px; 190 | height: 50vh; 191 | transition: 0.2s ease-out; 192 | box-shadow: rgba(0, 0, 0, 0.2) 0px 12px 28px 0px, rgba(0, 0, 0, 0.1) 0px 2px 4px 0px; 193 | } 194 | .section-header { 195 | margin-bottom: 16px; 196 | user-select: none; 197 | transition: $transition; 198 | } 199 | .sidebar { 200 | background-color: $card; 201 | width: 270px; 202 | display: flex; 203 | flex-direction: column; 204 | .filters { 205 | margin-bottom: 40px; 206 | } 207 | .filters-title-grp { 208 | display: flex; 209 | justify-content: space-between; 210 | } 211 | .theme { 212 | height: 26px; 213 | font-size: 24px; 214 | color: $component-s; 215 | user-select: none; 216 | transition: $transition; 217 | &:hover { 218 | transform: scale(1.03); 219 | color: $primary; 220 | } 221 | &:active { 222 | transform: scale(0.97); 223 | } 224 | } 225 | .filter { 226 | height: 32px; 227 | .fa-star { 228 | padding: 1px; 229 | width: 18px; 230 | } 231 | } 232 | .filter, 233 | .project { 234 | padding: 8px; 235 | margin-bottom: 8px; 236 | border-radius: 8px; 237 | user-select: none; 238 | i { 239 | margin-right: 6px; 240 | } 241 | p { 242 | user-select: none; 243 | pointer-events: none; 244 | } 245 | } 246 | .filter, 247 | .project-name { 248 | font-size: 14px; 249 | } 250 | .projects-header { 251 | display: flex; 252 | justify-content: space-between; 253 | overflow: hidden; 254 | i { 255 | height: 24px; 256 | line-height: 24px; 257 | font-size: 24px; 258 | transition: $transition; 259 | color: $component-s; 260 | } 261 | .plus:hover { 262 | color: $primary; 263 | transform: scale(1.1); 264 | } 265 | .rotated { 266 | transform: rotate(45deg); 267 | transition: $transition; 268 | &:hover { 269 | color: $primary; 270 | } 271 | } 272 | } 273 | .projects { 274 | flex: 1; 275 | display: flex; 276 | flex-direction: column; 277 | overflow: auto; 278 | } 279 | .project-grp { 280 | overflow-x: hidden; 281 | margin-bottom: 16px; 282 | width: 100%; 283 | i { 284 | font-size: 14px; 285 | } 286 | } 287 | .project { 288 | display: flex; 289 | height: 32px; 290 | padding: 8px; 291 | overflow: hidden; 292 | align-items: center; 293 | opacity: 1; 294 | transition: $transition; 295 | .folder { 296 | pointer-events: none; 297 | } 298 | .project-name { 299 | width: 10vw; 300 | overflow-x: hidden; 301 | } 302 | .project-btn-grp { 303 | margin-left: auto; 304 | display: flex; 305 | i { 306 | font-size: 16px; 307 | } 308 | .options { 309 | opacity: 0; 310 | } 311 | .edit-p { 312 | margin-right: 8px; 313 | } 314 | .delete-p { 315 | margin-right: 0px; 316 | } 317 | } 318 | &:hover { 319 | .project-btn-grp { 320 | .options { 321 | opacity: 1; 322 | } 323 | } 324 | } 325 | } 326 | #project-name { 327 | width: 100%; 328 | height: 32px; 329 | margin-bottom: 8px; 330 | } 331 | .github { 332 | height: 24px; 333 | display: flex; 334 | justify-content: center; 335 | margin-top: auto; 336 | .fa-github { 337 | font-size: 25px; 338 | transition: $transition; 339 | &:hover { 340 | color: $primary; 341 | } 342 | &:active { 343 | transform: scale(0.95); 344 | } 345 | } 346 | } 347 | } 348 | 349 | .content { 350 | width: 510px; 351 | overflow-x: hidden; 352 | .t-wrapper, 353 | .f-wrapper, 354 | .o-wrapper { 355 | height: 100%; 356 | } 357 | .t-wrapper { 358 | display: flex; 359 | flex-direction: column; 360 | .title-grp { 361 | display: flex; 362 | justify-content: space-between; 363 | .current-title { 364 | margin-top: 6px; 365 | margin-right: 12px; 366 | font-size: 14px; 367 | color: #454254; 368 | } 369 | } 370 | 371 | .no-tasks { 372 | color: $dk-subtext; 373 | display: flex; 374 | justify-content: center; 375 | margin-top: 16px; 376 | font-size: 18px; 377 | user-select: none; 378 | } 379 | .tasks { 380 | overflow-x: hidden; 381 | height: 35vh; 382 | padding: 2px; 383 | } 384 | .task { 385 | user-select: none; 386 | z-index: 0; 387 | border-radius: 10px; 388 | height: 35px; 389 | width: 99.5%; 390 | margin-bottom: 8px; 391 | display: flex; 392 | min-width: 0; 393 | padding-right: 10px; 394 | background-color: $component; 395 | box-shadow: rgba(0, 0, 0, 0.05) 0px 4px 4px 0px, 396 | rgba(0, 0, 0, 0.08) 0px 0px 0px 1px; 397 | transition: $transition; 398 | .hide-check { 399 | display: none; 400 | } 401 | .checkmark { 402 | margin: auto 0; 403 | height: 32px; 404 | transition: $transition; 405 | &:active { 406 | transform: scale(0.8); 407 | } 408 | } 409 | .fa-circle, 410 | .fa-circle-check { 411 | height: 32px; 412 | padding: 0 14px; 413 | display: flex; 414 | align-items: center; 415 | } 416 | .task-title { 417 | display: block; 418 | height: 35px; 419 | width: 75%; 420 | padding-right: 8px; 421 | border: none; 422 | line-height: 35px; 423 | font-size: 14px; 424 | font-weight: 300; 425 | user-select: none; 426 | pointer-events: none; 427 | color: $dk-text; 428 | overflow: hidden; 429 | white-space: nowrap; 430 | text-overflow: ellipsis; 431 | } 432 | 433 | & input[type='checkbox'] { 434 | margin-right: 1vw; 435 | } 436 | div:last-of-type { 437 | margin-left: auto; 438 | } 439 | &:hover { 440 | background-color: $component-s; 441 | .actions { 442 | .edit, 443 | .fa-regular { 444 | transition: $transition; 445 | opacity: 1; 446 | } 447 | } 448 | } 449 | .actions { 450 | z-index: 2; 451 | display: flex; 452 | align-items: center; 453 | transition: $transition; 454 | gap: 4px; 455 | .options { 456 | height: 100%; 457 | display: flex; 458 | align-items: center; 459 | } 460 | i { 461 | transition: $transition; 462 | } 463 | .edit { 464 | opacity: 0; 465 | width: 30px; 466 | display: flex; 467 | justify-content: center; 468 | } 469 | span { 470 | display: flex; 471 | } 472 | .fa-regular { 473 | opacity: 0; 474 | } 475 | 476 | .fa-solid { 477 | color: $dk-subtext; 478 | } 479 | } 480 | } 481 | } 482 | .f-wrapper { 483 | display: none; 484 | .task-form { 485 | border-radius: 20px; 486 | width: 100%; 487 | display: flex; 488 | flex-direction: column; 489 | .form-header { 490 | margin-top: 24px; 491 | } 492 | #task { 493 | height: 36px; 494 | } 495 | #note { 496 | height: 15vh; 497 | padding: 10px; 498 | } 499 | .extras-wrapper { 500 | display: flex; 501 | justify-content: space-between; 502 | align-items: center; 503 | gap: 16px; 504 | } 505 | .extras { 506 | width: 45%; 507 | } 508 | 509 | input[type='date'] { 510 | width: 100%; 511 | padding: 0 5px 0 10px; 512 | user-select: none; 513 | &::-webkit-calendar-picker-indicator { 514 | font-size: 16px; 515 | transition: $transition; 516 | filter: $cal-indicator; 517 | } 518 | &::-webkit-calendar-picker-indicator:hover { 519 | scale: (1.1); 520 | filter: $cal-indicator; 521 | } 522 | } 523 | 524 | .fa-star { 525 | font-size: 24px; 526 | margin-top: 70px; 527 | margin-bottom: 6px; 528 | width: 30px; 529 | display: flex; 530 | justify-content: center; 531 | transition: $transition; 532 | color: $primary; 533 | &:hover { 534 | transform: scale(1.1); 535 | } 536 | &:active { 537 | transform: scale(0.9) rotate(72deg); 538 | } 539 | } 540 | .fa-regular { 541 | font-size: 20px; 542 | } 543 | .starred { 544 | transition: $transition; 545 | } 546 | 547 | .btn-group { 548 | margin-top: auto; 549 | display: flex; 550 | } 551 | .back-btn { 552 | background-color: $card; 553 | border: none; 554 | color: $dk-text; 555 | font-size: 30px; 556 | } 557 | 558 | .submit-btn { 559 | width: 130px; 560 | height: 40px; 561 | border-radius: 30px; 562 | border: none; 563 | margin-left: auto; 564 | font-size: 16px; 565 | color: $dk-text; 566 | background: $grad-base; 567 | background: linear-gradient(160deg, $gradient-1 0%, $gradient-2 100%); 568 | box-shadow: rgba(0, 0, 0, 0.15) 0px 5px 15px 0px; 569 | transition: $transition; 570 | user-select: none; 571 | &:hover { 572 | transform: scale(1.02); 573 | } 574 | &:active { 575 | transform: scale(0.97); 576 | } 577 | } 578 | } 579 | } 580 | .o-wrapper { 581 | display: none; 582 | 583 | .expand-view { 584 | display: flex; 585 | flex-direction: column; 586 | width: 100%; 587 | user-select: none; 588 | .expand-header { 589 | margin-bottom: 24px; 590 | 591 | .project-grp { 592 | display: flex; 593 | margin-top: 2px; 594 | i { 595 | font-size: 20px; 596 | margin-right: 6px; 597 | } 598 | p { 599 | font-size: 16px; 600 | } 601 | } 602 | } 603 | 604 | .open-title-header { 605 | display: flex; 606 | justify-content: space-between; 607 | margin: 8px 16px; 608 | transition: $transition; 609 | .fa-star { 610 | font-size: 24px; 611 | color: $primary; 612 | } 613 | } 614 | .note-wrapper { 615 | margin: 8px 16px 0 16px; 616 | } 617 | hr { 618 | height: 4px; 619 | background: $primary; 620 | 621 | border: none; 622 | border-radius: 2px; 623 | } 624 | #open-title { 625 | font-size: 18px; 626 | color: $dk-text; 627 | width: 90%; 628 | white-space: nowrap; 629 | overflow: hidden; 630 | text-overflow: ellipsis; 631 | } 632 | #open-note { 633 | color: $dk-subtext; 634 | display: block; 635 | font-size: 16px; 636 | white-space: pre-line; 637 | height: 27vh; 638 | overflow: auto; 639 | padding-top: 8px; 640 | &::-webkit-scrollbar-corner { 641 | color: transparent; 642 | } 643 | } 644 | .bot-note-line { 645 | display: none; 646 | width: 20px; 647 | margin-left: auto; 648 | margin-right: auto; 649 | margin-top: 14px; 650 | margin-bottom: 0; 651 | } 652 | .visible { 653 | display: block; 654 | } 655 | .extras-wrapper { 656 | margin-top: auto; 657 | padding: 0 16px 8px 8px; 658 | display: flex; 659 | justify-content: space-between; 660 | } 661 | .extras { 662 | display: flex; 663 | align-items: center; 664 | .open-date { 665 | color: $primary; 666 | font-weight: 500; 667 | } 668 | } 669 | .btn-group { 670 | margin-top: auto; 671 | .back-btn { 672 | background-color: $card; 673 | border: none; 674 | color: $dk-text; 675 | font-size: 30px; 676 | } 677 | } 678 | } 679 | } 680 | .add-btn { 681 | margin-top: auto; 682 | width: 130px; 683 | height: 40px; 684 | border-radius: 24px; 685 | border: none; 686 | margin-left: auto; 687 | font-size: 16px; 688 | color: $dk-text; 689 | background: $grad-base; 690 | background: linear-gradient(160deg, $gradient-1 0%, $gradient-2 100%); 691 | transition: $transition; 692 | box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 15px 0px; 693 | user-select: none; 694 | &:hover { 695 | transform: scale(1.02); 696 | } 697 | &:active { 698 | transform: scale(0.98); 699 | } 700 | } 701 | } 702 | } 703 | 704 | .slide-tasks-in { 705 | animation: ease-out taskRight reverse 0.1s; 706 | } 707 | .slide-tasks-out { 708 | animation: ease-out taskRight 0.1s; 709 | } 710 | @keyframes taskRight { 711 | 0% { 712 | transform: translateX(0); 713 | } 714 | 100% { 715 | transform: translateX(120%); 716 | } 717 | } 718 | .slide-form-in { 719 | animation: ease-out formRight 0.1s; 720 | } 721 | .slide-form-out { 722 | animation: ease-out formRight reverse 0.1s; 723 | } 724 | @keyframes formRight { 725 | 0% { 726 | transform: translateX(-100%); 727 | } 728 | 100% { 729 | transform: translateX(0); 730 | } 731 | } 732 | @keyframes formVertical { 733 | 0% { 734 | transform: translateY(-80%); 735 | } 736 | 100% { 737 | transform: translateY(0); 738 | } 739 | } 740 | @keyframes formVerticall { 741 | 0% { 742 | transform: translateY(20%); 743 | } 744 | 100% { 745 | transform: translateY(0); 746 | } 747 | } 748 | @keyframes appearForm { 749 | 0% { 750 | transform: scale(0.95); 751 | opacity: 0; 752 | } 753 | 100% { 754 | transform: scale(1); 755 | opacity: 1; 756 | } 757 | } 758 | 759 | @media screen and (max-width: 480px) { 760 | .side-menu { 761 | display: flex; 762 | justify-content: flex-end; 763 | position: relative; 764 | width: 80px; 765 | height: 45px; 766 | cursor: pointer; 767 | 768 | .menu-icon { 769 | width: 40px; 770 | height: 45px; 771 | position: relative; 772 | cursor: pointer; 773 | z-index: 2; 774 | -webkit-touch-callout: none; 775 | position: absolute; 776 | opacity: 0; 777 | } 778 | .menu-grp { 779 | margin: auto; 780 | position: absolute; 781 | top: 0; 782 | right: 0; 783 | left: 0; 784 | bottom: 0; 785 | width: 25px; 786 | height: 13px; 787 | .menu-line { 788 | position: absolute; 789 | display: block; 790 | width: 25px; 791 | height: 3px; 792 | background: $component; 793 | background-color: $primary; 794 | border-radius: 1px; 795 | transition: all 0.2s cubic-bezier(0.1, 0.82, 0.76, 0.965); 796 | &:first-of-type { 797 | top: 0; 798 | } 799 | &:last-of-type { 800 | bottom: 0; 801 | } 802 | } 803 | } 804 | &.active, 805 | .menu-icon:checked + div { 806 | .menu-line { 807 | &:first-of-type { 808 | transform: rotate(45deg); 809 | top: 5px; 810 | } 811 | &:last-of-type { 812 | transform: rotate(-45deg); 813 | bottom: 5px; 814 | } 815 | } 816 | } 817 | } 818 | .blurred { 819 | filter: blur(4px); 820 | } 821 | html, 822 | body, 823 | .container { 824 | overflow: hidden; 825 | min-height: -webkit-fill-available; 826 | } 827 | header { 828 | height: 110px; 829 | } 830 | select { 831 | font-size: 11px; 832 | } 833 | input[type='date'] { 834 | font-size: 11px; 835 | } 836 | .cards .content .f-wrapper .task-form #task, 837 | select#projects, 838 | input[type='date'] { 839 | height: 32px; 840 | font-size: 12px; 841 | } 842 | .section-header { 843 | font-size: 18px; 844 | } 845 | .form-header, 846 | .form-title-header { 847 | font-size: 16px; 848 | } 849 | 850 | .cards { 851 | .sidebar { 852 | .filter, 853 | .project { 854 | font-size: 13px; 855 | } 856 | .projects-header { 857 | overflow-x: hidden; 858 | } 859 | .project { 860 | .project-name { 861 | font-size: 13px; 862 | width: 140px; 863 | } 864 | } 865 | } 866 | .content { 867 | .add-btn { 868 | font-size: 14px; 869 | } 870 | .f-wrapper { 871 | .task-form { 872 | .form-header { 873 | margin-top: 14px; 874 | margin-bottom: 10px; 875 | } 876 | .fa-star { 877 | margin-top: 50px; 878 | } 879 | #note { 880 | height: 20vh; 881 | font-size: 12px; 882 | } 883 | .extras-wrapper { 884 | margin-bottom: 20px; 885 | } 886 | .fa-regular { 887 | font-size: 24px; 888 | } 889 | } 890 | .expand-view { 891 | .expand-header { 892 | margin-bottom: 8px; 893 | .project-grp p { 894 | font-size: 14px; 895 | margin-top: 1px; 896 | } 897 | .project-grp i { 898 | font-size: 18px; 899 | } 900 | } 901 | } 902 | } 903 | .t-wrapper { 904 | .task { 905 | .task-title { 906 | font-size: 11px; 907 | } 908 | } 909 | 910 | .no-tasks { 911 | font-size: 16px; 912 | } 913 | } 914 | .o-wrapper { 915 | .expand-view { 916 | #open-title { 917 | width: 215px; 918 | font-size: 16px; 919 | margin-top: 4px; 920 | } 921 | #open-note { 922 | font-size: 14px; 923 | height: 35vh; 924 | } 925 | } 926 | } 927 | } 928 | .sidebar, 929 | .content { 930 | height: 70vh; 931 | } 932 | .mobile-stretch { 933 | transition: 0s; 934 | margin-top: 5vh; 935 | height: 60vh; 936 | } 937 | .sidebar { 938 | display: none; 939 | } 940 | } 941 | .header { 942 | opacity: 0; 943 | height: 0; 944 | } 945 | .filtersHide { 946 | display: none; 947 | } 948 | } 949 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: './src/index.js', 5 | output: { 6 | filename: 'main.js', 7 | path: path.resolve(__dirname, 'dist'), 8 | }, 9 | module: { 10 | rules: [ 11 | { 12 | test: /\.css$/i, 13 | use: ['style-loader', 'css-loader'], 14 | }, 15 | { 16 | test: /\.s[ac]ss$/i, 17 | use: ['style-loader', 'css-loader', 'sass-loader'], 18 | }, 19 | { 20 | test: /\.(png|svg|jpg|jpeg|gif)$/i, 21 | type: 'asset/resource', 22 | }, 23 | ], 24 | }, 25 | mode: 'development', 26 | devtool: 'inline-source-map', 27 | devServer: { 28 | static: './dist', 29 | }, 30 | }; 31 | --------------------------------------------------------------------------------