├── src ├── index.js ├── utils.js ├── index.html ├── interactive.js ├── style.css └── addRemove.js ├── .gitignore ├── dist ├── main.js.LICENSE.txt └── index.html ├── .hintrc ├── .eslintrc.json ├── webpack.config.js ├── .stylelintrc.json ├── LICENSE ├── package.json ├── .github └── workflows │ └── linters.yml └── README.md /src/index.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /dist/main.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Lodash 4 | * Copyright OpenJS Foundation and other contributors 5 | * Released under MIT license 6 | * Based on Underscore.js 1.8.3 7 | * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 8 | */ 9 | -------------------------------------------------------------------------------- /.hintrc: -------------------------------------------------------------------------------- 1 | { 2 | "connector": { 3 | "name": "local", 4 | "options": { 5 | "pattern": ["**", "!.git/**", "!node_modules/**"] 6 | } 7 | }, 8 | "extends": ["development"], 9 | "formatters": ["stylish"], 10 | "hints": [ 11 | "button-type", 12 | "disown-opener", 13 | "html-checker", 14 | "meta-charset-utf-8", 15 | "meta-viewport", 16 | "no-inline-styles:error" 17 | ] 18 | } -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | "jest": true 6 | }, 7 | "parser": "babel-eslint", 8 | "parserOptions": { 9 | "ecmaVersion": 2018, 10 | "sourceType": "module" 11 | }, 12 | "extends": ["airbnb-base"], 13 | "rules": { 14 | "no-shadow": "off", 15 | "no-param-reassign": "off", 16 | "eol-last": "off", 17 | "import/extensions": [ 1, { 18 | "js": "always", "json": "always" 19 | }] 20 | }, 21 | "ignorePatterns": [ 22 | "dist/", 23 | "build/" 24 | ] 25 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | 4 | module.exports = { 5 | mode: 'development', 6 | entry: './src/addRemove.js', 7 | devServer: { 8 | static: './dist', 9 | }, 10 | plugins: [ 11 | new HtmlWebpackPlugin({ 12 | template: './src/index.html', 13 | }), 14 | ], 15 | output: { 16 | filename: 'main.js', 17 | path: path.resolve(__dirname, 'dist'), 18 | }, 19 | 20 | module: { 21 | rules: [ 22 | { 23 | test: /\.css$/i, 24 | use: ['style-loader', 'css-loader'], 25 | }, 26 | ], 27 | }, 28 | }; -------------------------------------------------------------------------------- /.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["stylelint-config-standard"], 3 | "plugins": ["stylelint-scss", "stylelint-csstree-validator"], 4 | "rules": { 5 | "at-rule-no-unknown": [ 6 | true, 7 | { 8 | "ignoreAtRules": ["tailwind", "apply", "variants", "responsive", "screen"] 9 | } 10 | ], 11 | "scss/at-rule-no-unknown": [ 12 | true, 13 | { 14 | "ignoreAtRules": ["tailwind", "apply", "variants", "responsive", "screen"] 15 | } 16 | ], 17 | "csstree/validator": true 18 | }, 19 | "ignoreFiles": ["build/**", "dist/**", "**/reset*.css", "**/bootstrap*.css", "**/*.js", "**/*.jsx"] 20 | } -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | To Do List 8 | 9 | 10 |

Today's To Do

11 |
12 | 15 | 16 | 17 |
18 | 19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | To Do List 8 | 9 | 10 |

Today's To Do

11 |
12 | 15 | 16 | 17 |
18 | 19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/interactive.js: -------------------------------------------------------------------------------- 1 | const tasks = JSON.parse(localStorage.getItem('tasks')) || []; 2 | 3 | export function updateTaskStatus(task, status) { 4 | task.completed = status; 5 | localStorage.setItem('tasks', JSON.stringify(tasks)); 6 | } 7 | 8 | export function checkAllCompleted() { 9 | const anyCompleted = tasks.some((task) => task.completed); 10 | const clearAllButton = document.getElementById('clear-all'); 11 | clearAllButton.disabled = !anyCompleted; 12 | clearAllButton.addEventListener('click', () => { 13 | const completedTasks = tasks.filter((task) => task.completed); 14 | const remainingTasks = tasks.filter((task) => !task.completed); 15 | localStorage.setItem('tasks', JSON.stringify(remainingTasks)); 16 | completedTasks.forEach((task) => { 17 | const index = tasks.indexOf(task); 18 | tasks.splice(index, 1); 19 | }); 20 | checkAllCompleted(); 21 | window.location.reload(); 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 osugo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "to-do-list", 3 | "version": "1.0.0", 4 | "description": "", 5 | "private": "true", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "watch": "webpack --watch", 9 | "start": "webpack serve --open", 10 | "build": "webpack" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/0sugo/to-do-list.git" 15 | }, 16 | "keywords": [], 17 | "author": "", 18 | "license": "ISC", 19 | "bugs": { 20 | "url": "https://github.com/0sugo/to-do-list/issues" 21 | }, 22 | "homepage": "https://github.com/0sugo/to-do-list#readme", 23 | "devDependencies": { 24 | "babel-eslint": "^10.1.0", 25 | "css-loader": "^6.7.3", 26 | "eslint": "^7.32.0", 27 | "eslint-config-airbnb-base": "^14.2.1", 28 | "eslint-plugin-import": "^2.27.5", 29 | "hint": "^7.1.7", 30 | "html-webpack-plugin": "^5.5.0", 31 | "style-loader": "^3.3.2", 32 | "stylelint": "^13.13.1", 33 | "stylelint-config-standard": "^21.0.0", 34 | "stylelint-csstree-validator": "^1.9.0", 35 | "stylelint-scss": "^3.21.0", 36 | "webpack": "^5.76.3", 37 | "webpack-cli": "^5.0.1", 38 | "webpack-dev-server": "^4.13.1" 39 | }, 40 | "dependencies": { 41 | "lodash": "^4.17.21" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: sans-serif; 3 | margin: 0.5rem auto; 4 | padding: 0; 5 | } 6 | 7 | h1 { 8 | font-size: 3rem; 9 | margin-top: 2rem; 10 | margin-left: 2rem; 11 | } 12 | 13 | p { 14 | margin: 2rem; 15 | font-size: 20px; 16 | } 17 | 18 | #task-list { 19 | margin: 0.5rem auto; 20 | width: 95%; 21 | } 22 | 23 | ul { 24 | list-style-type: none; 25 | margin: 0; 26 | padding: 0; 27 | } 28 | 29 | li { 30 | display: flex; 31 | align-items: center; 32 | background-color: #f5f5f5; 33 | border: 1px solid #ddd; 34 | padding: 1rem; 35 | margin-bottom: 1rem; 36 | } 37 | 38 | input[type="checkbox"] { 39 | margin-right: 1rem; 40 | } 41 | 42 | .completed { 43 | text-decoration: line-through; 44 | } 45 | 46 | .completed2 { 47 | text-decoration: unset; 48 | } 49 | 50 | #clear-all { 51 | text-align: center; 52 | display: block; 53 | font-size: 1.2rem; 54 | padding: 0.5rem 1rem; 55 | background-color: #f5f5f5; 56 | border: 1px solid #ddd; 57 | cursor: pointer; 58 | width: 95%; 59 | margin: 0.5rem auto; 60 | } 61 | 62 | #clear-all:hover { 63 | background-color: #ddd; 64 | } 65 | 66 | #clear-all:disabled { 67 | cursor: default; 68 | background-color: #ddd; 69 | color: #999; 70 | border-color: #999; 71 | } 72 | 73 | #edit { 74 | position: relative; 75 | } 76 | 77 | #delete { 78 | position: relative; 79 | } 80 | 81 | #identifier { 82 | display: flex; 83 | justify-content: space-between; 84 | } 85 | 86 | form { 87 | display: flex; 88 | justify-content: center; 89 | } 90 | 91 | #savg:hover { 92 | cursor: move; 93 | } 94 | -------------------------------------------------------------------------------- /.github/workflows/linters.yml: -------------------------------------------------------------------------------- 1 | name: Linters 2 | 3 | on: pull_request 4 | 5 | env: 6 | FORCE_COLOR: 1 7 | 8 | jobs: 9 | lighthouse: 10 | name: Lighthouse 11 | runs-on: ubuntu-22.04 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: actions/setup-node@v1 15 | with: 16 | node-version: "16.x" 17 | - name: Setup Lighthouse 18 | run: npm install -g @lhci/cli@0.7.x 19 | - name: Lighthouse Report 20 | run: lhci autorun --upload.target=temporary-public-storage --collect.staticDistDir=. 21 | webhint: 22 | name: Webhint 23 | runs-on: ubuntu-22.04 24 | steps: 25 | - uses: actions/checkout@v2 26 | - uses: actions/setup-node@v1 27 | with: 28 | node-version: "16.x" 29 | - name: Setup Webhint 30 | run: | 31 | npm install --save-dev hint@7.x 32 | [ -f .hintrc ] || wget https://raw.githubusercontent.com/microverseinc/linters-config/master/html-css-js/.hintrc 33 | - name: Webhint Report 34 | run: npx hint . 35 | stylelint: 36 | name: Stylelint 37 | runs-on: ubuntu-22.04 38 | steps: 39 | - uses: actions/checkout@v2 40 | - uses: actions/setup-node@v1 41 | with: 42 | node-version: "18.x" 43 | - name: Setup Stylelint 44 | run: | 45 | npm install --save-dev stylelint@13.x stylelint-scss@3.x stylelint-config-standard@21.x stylelint-csstree-validator@1.x 46 | [ -f .stylelintrc.json ] || wget https://raw.githubusercontent.com/microverseinc/linters-config/master/html-css-js/.stylelintrc.json 47 | - name: Stylelint Report 48 | run: npx stylelint "**/*.{css,scss}" 49 | eslint: 50 | name: ESLint 51 | runs-on: ubuntu-22.04 52 | steps: 53 | - uses: actions/checkout@v2 54 | - uses: actions/setup-node@v1 55 | with: 56 | node-version: "18.x" 57 | - name: Setup ESLint 58 | run: | 59 | npm install --save-dev eslint@7.x eslint-config-airbnb-base@14.x eslint-plugin-import@2.x babel-eslint@10.x 60 | [ -f .eslintrc.json ] || wget https://raw.githubusercontent.com/microverseinc/linters-config/master/html-css-js/.eslintrc.json 61 | - name: ESLint Report 62 | run: npx eslint . 63 | nodechecker: 64 | name: node_modules checker 65 | runs-on: ubuntu-22.04 66 | steps: 67 | - uses: actions/checkout@v2 68 | - name: Check node_modules existence 69 | run: | 70 | if [ -d "node_modules/" ]; then echo -e "\e[1;31mThe node_modules/ folder was pushed to the repo. Please remove it from the GitHub repository and try again."; echo -e "\e[1;32mYou can set up a .gitignore file with this folder included on it to prevent this from happening in the future." && exit 1; fi -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # 📗 Table of Contents 5 | 6 | - [📗 Table of Contents](#-table-of-contents) 7 | - [📖 To Do List ](#-to-do-list-) 8 | - [🛠 Built With ](#-built-with-) 9 | - [Tech Stack ](#tech-stack-) 10 | - [Key Features ](#key-features-) 11 | - [🚀 Live Demo ](#-live-demo-) 12 | - [💻 Getting Started ](#-getting-started-) 13 | - [Prerequisites](#prerequisites) 14 | - [Setup](#setup) 15 | - [Install](#install) 16 | - [Usage](#usage) 17 | - [Run tests](#run-tests) 18 | - [Deployment](#deployment) 19 | - [👥 Authors ](#-authors-) 20 | - [🔭 Future Features ](#-future-features-) 21 | - [🤝 Contributing ](#-contributing-) 22 | - [⭐️ Show your support ](#️-show-your-support-) 23 | - [🙏 Acknowledgments ](#-acknowledgments-) 24 | - [📝 LICENSE ](#-license-) 25 | 26 | 27 | 28 | # 📖 To Do List 29 | 30 | 31 | **To Do List** is an application that shows things I intended to do in a day keeping track with a tick for those I have completed. 32 | 33 | ## 🛠 Built With 34 | 35 | ### Tech Stack 36 | 37 |
38 | Client 39 | 43 |
44 | 45 |
46 | Server 47 |
    48 |
  • N/A
  • 49 |
50 |
51 | 52 |
53 | Database 54 |
    55 |
  • N/A
  • 56 |
57 |
58 | 59 | 60 | 61 | ### Key Features 62 | 63 | - **To-do-list** 64 | - **Setup with Webpack** 65 | - **CSS loader Webpack** 66 | - **HTML Webpack Plugin** 67 | - **Source files in src(Folder)** 68 | - **dist (folder)** 69 | 70 | 71 | 72 | 73 | ## 🚀 Live Demo 74 | 75 | 76 | - [Live Demo Link](https://0sugo.github.io/to-do-list/dist/) 77 | 78 | 79 | 80 | 81 | ## 💻 Getting Started 82 | 83 | 84 | To get a local copy up and running, follow these steps. 85 | 86 | ### Prerequisites 87 | 88 | In order to run this project you need: 89 | 90 | - Modern chrome Browser 91 | - visual studio code 92 | 93 | ### Setup 94 | 95 | - Clone this repository to your desired folder with the following commnd 96 | ``` 97 | git clone https://github.com/0sugo/to-do-list.git 98 | ``` 99 | 100 | 101 | ### Install 102 | 103 | - run ```npm i``` to install all dependencies 104 | - run ```npm run build``` to bundle the project with webpack, and 105 | 106 | 107 | ### Usage 108 | 109 | - To run the project, execute the following command: 110 | -- run ```npm start``` to launch the application 111 | 112 | ### Run tests 113 | 114 | - To run tests, run the following command: 115 | - ```npm run tests``` 116 | 117 | ### Deployment 118 | 119 | - You can deploy this project using: 120 | - Type cd ``` To-Do-List ``` 121 | - open index.html file in the browser 122 | 123 | 124 | 125 | ## 👥 Authors 126 | 127 | 👤 **JOSECK OSUGO** 128 | 129 | - GitHub: [0sugo](https://github.com/0sugo) 130 | - Twitter: [@0sugo5](https://twitter.com/osugo5) 131 | - LinkedIn: [Joseck Osugo](https://www.linkedin.com/in/joseck-osugo-873b0618a/) 132 | 133 | 134 | 135 | 136 | 137 | ## 🔭 Future Features 138 | 139 | - [ ] **React** 140 | - [ ] **Ruby** 141 | - [ ] **Java-Script** 142 | 143 | 144 | 145 | 146 | ## 🤝 Contributing 147 | 148 | Contributions, issues, and feature requests are welcome! 149 | 150 | Feel free to check the [issues page]([../../issues/](https://github.com/0sugo/to-do-list/issues)). 151 | 152 | 153 | 154 | 155 | ## ⭐️ Show your support 156 | 157 | 158 | - If you like this project please give it a STAR⭐️ 159 | 160 | 161 | 162 | 163 | ## 🙏 Acknowledgments 164 | 165 | - I would like to thank following 166 | - MicroVerse 167 | - web.archive.org. 168 | 169 | 170 | 171 | ## 📝 LICENSE 172 | 173 | This project is [MIT](./LICENSE) licensed. 174 | 175 |

(back to top)

176 | 177 | -------------------------------------------------------------------------------- /src/addRemove.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | import './style.css'; 3 | import { updateTaskStatus, checkAllCompleted } from './interactive.js'; 4 | 5 | const tasks = JSON.parse(localStorage.getItem('tasks')) || []; 6 | 7 | const taskList = document.getElementById('task-list'); 8 | 9 | function component() { 10 | const element = document.createElement('div'); 11 | 12 | // lodash imported by script 13 | element.innerHTML = _.join(['', ''], ' '); 14 | return element; 15 | } 16 | 17 | document.body.appendChild(component()); 18 | 19 | // able and disable button 20 | 21 | // render infor to html 22 | function renderTaskList() { 23 | taskList.innerHTML = ''; 24 | // const task = JSON.parse(localStorage.getItem('tasks')) || []; 25 | 26 | tasks.forEach((task, index) => { 27 | task.index = index; 28 | const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); 29 | const svgDelete = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); 30 | const listItem = document.createElement('li'); 31 | listItem.id = 'identifier'; 32 | 33 | // function to re assign indexes 34 | function assignIndexesToLocalTasks() { 35 | const tasks = JSON.parse(localStorage.getItem('tasks')); 36 | 37 | tasks.forEach((task, index) => { 38 | task.index = index; 39 | }); 40 | localStorage.setItem('tasks', JSON.stringify(tasks)); 41 | } 42 | 43 | // add a draggable attribute to the li element 44 | listItem.draggable = true; 45 | 46 | // set up the dragstart event listener to store the task index in the dataTransfer object 47 | listItem.addEventListener('dragstart', (event) => { 48 | event.dataTransfer.setData('text/plain', index); 49 | event.dataTransfer.effectAllowed = 'move'; 50 | }); 51 | 52 | // set up the dragover event listener to allow the li element to be dropped on top of it 53 | listItem.addEventListener('dragover', (event) => { 54 | event.preventDefault(); 55 | event.dataTransfer.dropEffect = 'move'; 56 | }); 57 | 58 | // set up the drop event listener to swap the positions of the dragged task and the dropped task 59 | listItem.addEventListener('drop', (event) => { 60 | event.preventDefault(); 61 | const sourceIndex = event.dataTransfer.getData('text/plain'); 62 | if (sourceIndex !== index) { 63 | const sourceTask = tasks[sourceIndex]; 64 | const targetTask = tasks[index]; 65 | tasks[sourceIndex] = targetTask; 66 | tasks[index] = sourceTask; 67 | localStorage.setItem('tasks', JSON.stringify(tasks)); 68 | assignIndexesToLocalTasks(); 69 | renderTaskList(); 70 | } 71 | }); 72 | 73 | // update the indexes of the list items 74 | const listItems = document.querySelectorAll('.task'); 75 | listItems.forEach((listItem) => { 76 | // retrieve the index from the list item's id attribute 77 | const taskIndex = parseInt(listItem.id.split('-')[1], 10); 78 | listItem.dataset.index = taskIndex; 79 | renderTaskList(); 80 | }); 81 | 82 | const checkbox = document.createElement('input'); 83 | checkbox.type = 'checkbox'; 84 | checkbox.checked = task.completed; 85 | checkbox.addEventListener('change', (event) => { 86 | updateTaskStatus(task, event.target.checked); 87 | checkAllCompleted(); 88 | window.location.reload(); 89 | localStorage.setItem('tasks', JSON.stringify(tasks)); 90 | if (event.target.checked) { 91 | listItem.classList.add('completed'); 92 | checkAllCompleted(); 93 | // listItem.style.textDecoration = 'line-through'; 94 | } else { 95 | listItem.classList.remove('completed'); 96 | // listItem.style.textDecoration = 'unset'; 97 | } 98 | }); 99 | listItem.appendChild(checkbox); 100 | 101 | const description = document.createElement('span'); 102 | description.innerText = task.description; 103 | if (task.completed) { 104 | description.classList.add('completed'); 105 | } 106 | listItem.appendChild(description); 107 | 108 | const editButton = document.createElement('button'); 109 | editButton.id = 'edit'; 110 | 111 | listItem.appendChild(editButton); 112 | 113 | const editInput = document.createElement('input'); 114 | editInput.type = 'text'; 115 | editInput.value = task.description; 116 | editInput.style.display = 'none'; 117 | listItem.appendChild(editInput); 118 | 119 | listItem.addEventListener('click', (event) => { 120 | if (event.target.id === 'edit') { 121 | description.style.display = 'none'; 122 | editInput.style.display = 'inline-block'; 123 | editInput.focus(); 124 | listItem.style.backgroundColor = '#326789'; 125 | svg.style.display = 'none'; 126 | } 127 | }); 128 | 129 | editInput.addEventListener('keyup', (event) => { 130 | if (event.key === 'Enter') { 131 | const newDescription = editInput.value.trim(); 132 | if (newDescription !== '') { 133 | task.description = newDescription; 134 | description.innerText = newDescription; 135 | description.style.display = 'inline-block'; 136 | editInput.style.display = 'none'; 137 | svgDelete.style.display = 'none'; 138 | listItem.style.backgroundColor = 'unset'; 139 | svg.style.display = 'unset'; 140 | localStorage.setItem('tasks', JSON.stringify(tasks)); 141 | } 142 | } 143 | }); 144 | 145 | listItem.appendChild(editInput); 146 | 147 | // create a new SVG element 148 | svg.id = 'savg'; 149 | svg.setAttribute('style', 'cursor: default;'); // set default cursor 150 | svg.addEventListener('mouseover', () => { 151 | svg.style.cursor = 'move'; // set move cursor on hover 152 | }); 153 | svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); 154 | svg.setAttribute('fill', 'none'); 155 | svg.setAttribute('viewBox', '0 0 24 24'); 156 | svg.setAttribute('stroke-width', '1.5'); 157 | svg.setAttribute('stroke', 'currentColor'); 158 | svg.setAttribute('class', 'w-6 h-6'); 159 | svg.setAttribute('width', '18'); 160 | svg.setAttribute('height', '32'); 161 | 162 | // create a new path element 163 | const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); 164 | path.setAttribute('stroke-linecap', 'round'); 165 | path.setAttribute('stroke-linejoin', 'round'); 166 | path.setAttribute('d', 'M12 6.75a.75.75 0 110-1.5.75.75 0 010 1.5zM12 12.75a.75.75 0 110-1.5.75.75 0 010 1.5zM12 18.75a.75.75 0 110-1.5.75.75 0 010 1.5z'); 167 | 168 | // add the path element to the SVG element 169 | svg.appendChild(path); 170 | listItem.appendChild(svg); 171 | 172 | // add event listener to SVG element 173 | description.addEventListener('click', () => { 174 | editButton.click(); 175 | svgDelete.style.display = 'block'; 176 | }); 177 | 178 | // add event listener to edit button 179 | editButton.addEventListener('click', () => { 180 | description.style.display = 'none'; 181 | editInput.style.display = 'inline-block'; 182 | editInput.focus(); 183 | }); 184 | 185 | svgDelete.style.display = 'none'; 186 | svgDelete.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); 187 | svgDelete.setAttribute('fill', 'none'); 188 | svgDelete.setAttribute('viewBox', '0 0 24 24'); 189 | svgDelete.setAttribute('stroke-width', '1.5'); 190 | svgDelete.setAttribute('stroke', 'currentColor'); 191 | svgDelete.setAttribute('class', 'w-6 h-6 svgDelete'); // add a new class name "svgDelete" 192 | svgDelete.setAttribute('width', '18'); 193 | svgDelete.setAttribute('height', '32'); 194 | 195 | const pathDelete = document.createElementNS('http://www.w3.org/2000/svg', 'path'); 196 | pathDelete.setAttribute('stroke-linecap', 'round'); 197 | pathDelete.setAttribute('stroke-linejoin', 'round'); 198 | pathDelete.setAttribute('d', 'M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0'); 199 | svgDelete.appendChild(pathDelete); 200 | listItem.appendChild(svgDelete); 201 | 202 | svgDelete.addEventListener('click', () => { 203 | const index = tasks.findIndex((t) => t.index === task.index); 204 | if (index > -1) { 205 | tasks.splice(index, 1); 206 | renderTaskList(); 207 | localStorage.setItem('tasks', JSON.stringify(tasks)); 208 | listItem.remove(); 209 | } 210 | }); 211 | 212 | taskList.appendChild(listItem); 213 | }); 214 | } 215 | window.onload = function onload() { 216 | renderTaskList(); 217 | checkAllCompleted(); 218 | }; 219 | 220 | renderTaskList(); 221 | checkAllCompleted(); 222 | 223 | const myForm = document.getElementById('myform'); 224 | myForm.addEventListener('submit', (event) => { 225 | event.preventDefault(); 226 | 227 | const individualTask = document.getElementById('individualTask').value; 228 | if (individualTask.trim() !== '') { 229 | const newObj = { 230 | description: individualTask, 231 | completed: false, 232 | index: tasks.length, 233 | }; 234 | tasks.push(newObj); 235 | localStorage.setItem('tasks', JSON.stringify(tasks)); 236 | 237 | // clear input field 238 | document.getElementById('individualTask').value = ''; 239 | 240 | renderTaskList(); 241 | checkAllCompleted(); 242 | } 243 | }); 244 | --------------------------------------------------------------------------------