├── .eslintrc.json ├── .github └── workflows │ └── linters.yml ├── .gitignore ├── .hintrc ├── .stylelintrc.json ├── README.md ├── dist ├── index.html └── main.js ├── package-lock.json ├── package.json ├── src ├── index.html ├── index.js ├── modules │ ├── Task.js │ └── completed.js └── style.css └── webpack.config.js /.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": [ 18 | 1, 19 | { 20 | "js": "always", 21 | "json": "always" 22 | } 23 | ] 24 | }, 25 | "ignorePatterns": ["dist/", "build/"] 26 | } 27 | -------------------------------------------------------------------------------- /.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-18.04 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: actions/setup-node@v1 15 | with: 16 | node-version: '12.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-18.04 24 | steps: 25 | - uses: actions/checkout@v2 26 | - uses: actions/setup-node@v1 27 | with: 28 | node-version: '12.x' 29 | - name: Setup Webhint 30 | run: | 31 | npm install --save-dev hint@6.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-18.04 38 | steps: 39 | - uses: actions/checkout@v2 40 | - uses: actions/setup-node@v1 41 | with: 42 | node-version: '12.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-18.04 52 | steps: 53 | - uses: actions/checkout@v2 54 | - uses: actions/setup-node@v1 55 | with: 56 | node-version: '12.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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.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 | } 19 | -------------------------------------------------------------------------------- /.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["stylelint-config-standard"], 3 | "plugins": ["stylelint-scss", "stylelint-csstree-validator"], 4 | "rules": { 5 | "at-rule-no-unknown": null, 6 | "scss/at-rule-no-unknown": true, 7 | "csstree/validator": true 8 | }, 9 | "ignoreFiles": [ 10 | "build/**", 11 | "dist/**", 12 | "**/reset*.css", 13 | "**/bootstrap*.css", 14 | "**/*.js", 15 | "**/*.jsx" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://img.shields.io/badge/Microverse-blueviolet) 2 | # To Do List 3 | 4 | Application that record the tasks to remind you and got many features that you might need. 5 | 6 | ## This is an image for it 7 | 8 | ## Live Demo 9 | [Live Demo Here](https://omar25ahmed.github.io/ToDo-List/) 10 | 11 | 12 | 13 | 14 | ## Built With 15 | 16 | - HTML 17 | - CSS 18 | - Markdown 19 | - JavaScript 20 | 21 | ## Prerequisites 22 | 23 | - text-editor 24 | - git 25 | - github 26 | 27 | ## Getting Started 28 | 29 | **To Create A Portfolio from this Repository feel free to contact me.** 30 | 31 | **To get a local copy up and running follow these simple steps.** 32 | - you can clone this repo by typing `git clone git@github.com:omar25ahmed/Omar-Ragheb-ToDo-List`. 33 | - type `cd ToDo List` to access the project on terminal. 34 | 35 | ## Authors 36 | 37 | 👤 **Omar Ragheb** 38 | 39 | - - GitHub: [@omar25ahmed](https://github.com/omar25ahmed) 40 | 41 | 42 | ## 🤝 Contributing 43 | 44 | Contributions, issues, and feature requests are welcome! 45 | 46 | Feel free to check the [issues page](https://github.com/omar25ahmed/Portfolio-setup-and-mobile-version-skeleton/issues). 47 | 48 | ## Show your support 49 | 50 | Give a ⭐️ if you like this project! 51 | 52 | ## Acknowledgments 53 | 54 | - Cindy Shin's design 55 | - SciComm [Link](https://www.scicommcon.org/) 56 | - Aspen Global Congress on Scientific Thinking & Action [Link](https://www.aspeninstitute.org/programs/science-society/global-science-congress/) 57 | - Microverse Team 58 | 59 | ## 📝 License 60 | 61 | This project is [MIT](./MIT.md) licensed. 62 | 63 | - GitHub: [@omar25ahmed](https://github.com/omar25ahmed) 64 | -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | To Do List 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |

Today's To Do

17 | 18 |
19 |
20 |
21 | 22 | 23 |
24 | 26 | 29 |
30 | 31 | 32 | -------------------------------------------------------------------------------- /dist/main.js: -------------------------------------------------------------------------------- 1 | /******/ (() => { // webpackBootstrap 2 | /******/ "use strict"; 3 | /******/ var __webpack_modules__ = ([ 4 | /* 0 */, 5 | /* 1 */ 6 | /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 7 | 8 | __webpack_require__.r(__webpack_exports__); 9 | /* harmony export */ __webpack_require__.d(__webpack_exports__, { 10 | /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) 11 | /* harmony export */ }); 12 | /* harmony import */ var _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2); 13 | /* harmony import */ var _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__); 14 | /* harmony import */ var _node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3); 15 | /* harmony import */ var _node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1__); 16 | /* harmony import */ var _node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4); 17 | /* harmony import */ var _node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2__); 18 | /* harmony import */ var _node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(5); 19 | /* harmony import */ var _node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3__); 20 | /* harmony import */ var _node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(6); 21 | /* harmony import */ var _node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4__); 22 | /* harmony import */ var _node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(7); 23 | /* harmony import */ var _node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5__); 24 | /* harmony import */ var _node_modules_css_loader_dist_cjs_js_style_css__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(8); 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | var options = {}; 37 | 38 | options.styleTagTransform = (_node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5___default()); 39 | options.setAttributes = (_node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3___default()); 40 | 41 | options.insert = _node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2___default().bind(null, "head"); 42 | 43 | options.domAPI = (_node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1___default()); 44 | options.insertStyleElement = (_node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4___default()); 45 | 46 | var update = _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default()(_node_modules_css_loader_dist_cjs_js_style_css__WEBPACK_IMPORTED_MODULE_6__["default"], options); 47 | 48 | 49 | 50 | 51 | /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (_node_modules_css_loader_dist_cjs_js_style_css__WEBPACK_IMPORTED_MODULE_6__["default"] && _node_modules_css_loader_dist_cjs_js_style_css__WEBPACK_IMPORTED_MODULE_6__["default"].locals ? _node_modules_css_loader_dist_cjs_js_style_css__WEBPACK_IMPORTED_MODULE_6__["default"].locals : undefined); 52 | 53 | 54 | /***/ }), 55 | /* 2 */ 56 | /***/ ((module) => { 57 | 58 | 59 | 60 | var stylesInDOM = []; 61 | 62 | function getIndexByIdentifier(identifier) { 63 | var result = -1; 64 | 65 | for (var i = 0; i < stylesInDOM.length; i++) { 66 | if (stylesInDOM[i].identifier === identifier) { 67 | result = i; 68 | break; 69 | } 70 | } 71 | 72 | return result; 73 | } 74 | 75 | function modulesToDom(list, options) { 76 | var idCountMap = {}; 77 | var identifiers = []; 78 | 79 | for (var i = 0; i < list.length; i++) { 80 | var item = list[i]; 81 | var id = options.base ? item[0] + options.base : item[0]; 82 | var count = idCountMap[id] || 0; 83 | var identifier = "".concat(id, " ").concat(count); 84 | idCountMap[id] = count + 1; 85 | var indexByIdentifier = getIndexByIdentifier(identifier); 86 | var obj = { 87 | css: item[1], 88 | media: item[2], 89 | sourceMap: item[3], 90 | supports: item[4], 91 | layer: item[5] 92 | }; 93 | 94 | if (indexByIdentifier !== -1) { 95 | stylesInDOM[indexByIdentifier].references++; 96 | stylesInDOM[indexByIdentifier].updater(obj); 97 | } else { 98 | var updater = addElementStyle(obj, options); 99 | options.byIndex = i; 100 | stylesInDOM.splice(i, 0, { 101 | identifier: identifier, 102 | updater: updater, 103 | references: 1 104 | }); 105 | } 106 | 107 | identifiers.push(identifier); 108 | } 109 | 110 | return identifiers; 111 | } 112 | 113 | function addElementStyle(obj, options) { 114 | var api = options.domAPI(options); 115 | api.update(obj); 116 | 117 | var updater = function updater(newObj) { 118 | if (newObj) { 119 | if (newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap && newObj.supports === obj.supports && newObj.layer === obj.layer) { 120 | return; 121 | } 122 | 123 | api.update(obj = newObj); 124 | } else { 125 | api.remove(); 126 | } 127 | }; 128 | 129 | return updater; 130 | } 131 | 132 | module.exports = function (list, options) { 133 | options = options || {}; 134 | list = list || []; 135 | var lastIdentifiers = modulesToDom(list, options); 136 | return function update(newList) { 137 | newList = newList || []; 138 | 139 | for (var i = 0; i < lastIdentifiers.length; i++) { 140 | var identifier = lastIdentifiers[i]; 141 | var index = getIndexByIdentifier(identifier); 142 | stylesInDOM[index].references--; 143 | } 144 | 145 | var newLastIdentifiers = modulesToDom(newList, options); 146 | 147 | for (var _i = 0; _i < lastIdentifiers.length; _i++) { 148 | var _identifier = lastIdentifiers[_i]; 149 | 150 | var _index = getIndexByIdentifier(_identifier); 151 | 152 | if (stylesInDOM[_index].references === 0) { 153 | stylesInDOM[_index].updater(); 154 | 155 | stylesInDOM.splice(_index, 1); 156 | } 157 | } 158 | 159 | lastIdentifiers = newLastIdentifiers; 160 | }; 161 | }; 162 | 163 | /***/ }), 164 | /* 3 */ 165 | /***/ ((module) => { 166 | 167 | 168 | 169 | /* istanbul ignore next */ 170 | function apply(styleElement, options, obj) { 171 | var css = ""; 172 | 173 | if (obj.supports) { 174 | css += "@supports (".concat(obj.supports, ") {"); 175 | } 176 | 177 | if (obj.media) { 178 | css += "@media ".concat(obj.media, " {"); 179 | } 180 | 181 | var needLayer = typeof obj.layer !== "undefined"; 182 | 183 | if (needLayer) { 184 | css += "@layer".concat(obj.layer.length > 0 ? " ".concat(obj.layer) : "", " {"); 185 | } 186 | 187 | css += obj.css; 188 | 189 | if (needLayer) { 190 | css += "}"; 191 | } 192 | 193 | if (obj.media) { 194 | css += "}"; 195 | } 196 | 197 | if (obj.supports) { 198 | css += "}"; 199 | } 200 | 201 | var sourceMap = obj.sourceMap; 202 | 203 | if (sourceMap && typeof btoa !== "undefined") { 204 | css += "\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))), " */"); 205 | } // For old IE 206 | 207 | /* istanbul ignore if */ 208 | 209 | 210 | options.styleTagTransform(css, styleElement, options.options); 211 | } 212 | 213 | function removeStyleElement(styleElement) { 214 | // istanbul ignore if 215 | if (styleElement.parentNode === null) { 216 | return false; 217 | } 218 | 219 | styleElement.parentNode.removeChild(styleElement); 220 | } 221 | /* istanbul ignore next */ 222 | 223 | 224 | function domAPI(options) { 225 | var styleElement = options.insertStyleElement(options); 226 | return { 227 | update: function update(obj) { 228 | apply(styleElement, options, obj); 229 | }, 230 | remove: function remove() { 231 | removeStyleElement(styleElement); 232 | } 233 | }; 234 | } 235 | 236 | module.exports = domAPI; 237 | 238 | /***/ }), 239 | /* 4 */ 240 | /***/ ((module) => { 241 | 242 | 243 | 244 | var memo = {}; 245 | /* istanbul ignore next */ 246 | 247 | function getTarget(target) { 248 | if (typeof memo[target] === "undefined") { 249 | var styleTarget = document.querySelector(target); // Special case to return head of iframe instead of iframe itself 250 | 251 | if (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) { 252 | try { 253 | // This will throw an exception if access to iframe is blocked 254 | // due to cross-origin restrictions 255 | styleTarget = styleTarget.contentDocument.head; 256 | } catch (e) { 257 | // istanbul ignore next 258 | styleTarget = null; 259 | } 260 | } 261 | 262 | memo[target] = styleTarget; 263 | } 264 | 265 | return memo[target]; 266 | } 267 | /* istanbul ignore next */ 268 | 269 | 270 | function insertBySelector(insert, style) { 271 | var target = getTarget(insert); 272 | 273 | if (!target) { 274 | throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid."); 275 | } 276 | 277 | target.appendChild(style); 278 | } 279 | 280 | module.exports = insertBySelector; 281 | 282 | /***/ }), 283 | /* 5 */ 284 | /***/ ((module, __unused_webpack_exports, __webpack_require__) => { 285 | 286 | 287 | 288 | /* istanbul ignore next */ 289 | function setAttributesWithoutAttributes(styleElement) { 290 | var nonce = true ? __webpack_require__.nc : 0; 291 | 292 | if (nonce) { 293 | styleElement.setAttribute("nonce", nonce); 294 | } 295 | } 296 | 297 | module.exports = setAttributesWithoutAttributes; 298 | 299 | /***/ }), 300 | /* 6 */ 301 | /***/ ((module) => { 302 | 303 | 304 | 305 | /* istanbul ignore next */ 306 | function insertStyleElement(options) { 307 | var element = document.createElement("style"); 308 | options.setAttributes(element, options.attributes); 309 | options.insert(element, options.options); 310 | return element; 311 | } 312 | 313 | module.exports = insertStyleElement; 314 | 315 | /***/ }), 316 | /* 7 */ 317 | /***/ ((module) => { 318 | 319 | 320 | 321 | /* istanbul ignore next */ 322 | function styleTagTransform(css, styleElement) { 323 | if (styleElement.styleSheet) { 324 | styleElement.styleSheet.cssText = css; 325 | } else { 326 | while (styleElement.firstChild) { 327 | styleElement.removeChild(styleElement.firstChild); 328 | } 329 | 330 | styleElement.appendChild(document.createTextNode(css)); 331 | } 332 | } 333 | 334 | module.exports = styleTagTransform; 335 | 336 | /***/ }), 337 | /* 8 */ 338 | /***/ ((module, __webpack_exports__, __webpack_require__) => { 339 | 340 | __webpack_require__.r(__webpack_exports__); 341 | /* harmony export */ __webpack_require__.d(__webpack_exports__, { 342 | /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) 343 | /* harmony export */ }); 344 | /* harmony import */ var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(9); 345 | /* harmony import */ var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__); 346 | /* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(10); 347 | /* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__); 348 | // Imports 349 | 350 | 351 | var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default())); 352 | // Module 353 | ___CSS_LOADER_EXPORT___.push([module.id, "body {\n background-color: #f6f6f6;\n}\n\n.container {\n border: 1px solid;\n background-color: #fff;\n width: 50%;\n margin: auto;\n}\n\n.header {\n display: flex;\n justify-content: space-between;\n padding: 10px;\n}\n\n.header button {\n background-color: transparent;\n border: none;\n cursor: pointer;\n}\n\n.todo-add {\n display: flex;\n padding: 10px;\n}\n\n.todo-add button {\n background-color: transparent;\n border: none;\n cursor: pointer;\n}\n\n.form-field {\n font-family: inherit;\n width: 100%;\n border: 0;\n border-bottom: 2px solid #9b9b9b;\n outline: 0;\n font-size: 1.3rem;\n color: black;\n padding: 7px 0;\n background: transparent;\n transition: border-color 0.2s;\n}\n\nul {\n list-style: none;\n padding: 10px;\n}\n\n.task {\n display: flex;\n align-items: center;\n justify-content: flex-start;\n gap: 10px;\n}\n\n.btns {\n margin-left: auto;\n display: flex;\n gap: 40px;\n}\n\n.close-button {\n transform: rotate(45deg);\n cursor: pointer;\n font-size: 30px;\n color: red;\n}\n\n.edit {\n border-color: transparent;\n height: 40px;\n}\n\n.edit:focus-visible {\n outline: green auto 1px;\n font-size: 15px;\n}\n\n.done {\n text-decoration: line-through;\n opacity: 0.5;\n}\n\n.scroll {\n background-color: transparent;\n border: none;\n cursor: pointer;\n margin-left: auto;\n}\n\n.footer {\n background-color: #f6f6f6;\n height: 50px;\n border-top: 1px solid;\n display: flex;\n justify-content: center;\n opacity: 0.5;\n}\n\n.footer button {\n background-color: transparent;\n border: none;\n cursor: pointer;\n font-size: 20px;\n}\n", ""]); 354 | // Exports 355 | /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); 356 | 357 | 358 | /***/ }), 359 | /* 9 */ 360 | /***/ ((module) => { 361 | 362 | 363 | 364 | module.exports = function (i) { 365 | return i[1]; 366 | }; 367 | 368 | /***/ }), 369 | /* 10 */ 370 | /***/ ((module) => { 371 | 372 | 373 | 374 | /* 375 | MIT License http://www.opensource.org/licenses/mit-license.php 376 | Author Tobias Koppers @sokra 377 | */ 378 | module.exports = function (cssWithMappingToString) { 379 | var list = []; // return the list of modules as css string 380 | 381 | list.toString = function toString() { 382 | return this.map(function (item) { 383 | var content = ""; 384 | var needLayer = typeof item[5] !== "undefined"; 385 | 386 | if (item[4]) { 387 | content += "@supports (".concat(item[4], ") {"); 388 | } 389 | 390 | if (item[2]) { 391 | content += "@media ".concat(item[2], " {"); 392 | } 393 | 394 | if (needLayer) { 395 | content += "@layer".concat(item[5].length > 0 ? " ".concat(item[5]) : "", " {"); 396 | } 397 | 398 | content += cssWithMappingToString(item); 399 | 400 | if (needLayer) { 401 | content += "}"; 402 | } 403 | 404 | if (item[2]) { 405 | content += "}"; 406 | } 407 | 408 | if (item[4]) { 409 | content += "}"; 410 | } 411 | 412 | return content; 413 | }).join(""); 414 | }; // import a list of modules into the list 415 | 416 | 417 | list.i = function i(modules, media, dedupe, supports, layer) { 418 | if (typeof modules === "string") { 419 | modules = [[null, modules, undefined]]; 420 | } 421 | 422 | var alreadyImportedModules = {}; 423 | 424 | if (dedupe) { 425 | for (var k = 0; k < this.length; k++) { 426 | var id = this[k][0]; 427 | 428 | if (id != null) { 429 | alreadyImportedModules[id] = true; 430 | } 431 | } 432 | } 433 | 434 | for (var _k = 0; _k < modules.length; _k++) { 435 | var item = [].concat(modules[_k]); 436 | 437 | if (dedupe && alreadyImportedModules[item[0]]) { 438 | continue; 439 | } 440 | 441 | if (typeof layer !== "undefined") { 442 | if (typeof item[5] === "undefined") { 443 | item[5] = layer; 444 | } else { 445 | item[1] = "@layer".concat(item[5].length > 0 ? " ".concat(item[5]) : "", " {").concat(item[1], "}"); 446 | item[5] = layer; 447 | } 448 | } 449 | 450 | if (media) { 451 | if (!item[2]) { 452 | item[2] = media; 453 | } else { 454 | item[1] = "@media ".concat(item[2], " {").concat(item[1], "}"); 455 | item[2] = media; 456 | } 457 | } 458 | 459 | if (supports) { 460 | if (!item[4]) { 461 | item[4] = "".concat(supports); 462 | } else { 463 | item[1] = "@supports (".concat(item[4], ") {").concat(item[1], "}"); 464 | item[4] = supports; 465 | } 466 | } 467 | 468 | list.push(item); 469 | } 470 | }; 471 | 472 | return list; 473 | }; 474 | 475 | /***/ }), 476 | /* 11 */ 477 | /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 478 | 479 | __webpack_require__.r(__webpack_exports__); 480 | /* harmony export */ __webpack_require__.d(__webpack_exports__, { 481 | /* harmony export */ "default": () => (/* binding */ Task) 482 | /* harmony export */ }); 483 | class Task { 484 | constructor(description, index, completed = false) { 485 | this.description = description; 486 | this.completed = completed; 487 | this.index = index; 488 | } 489 | } 490 | 491 | 492 | /***/ }), 493 | /* 12 */ 494 | /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 495 | 496 | __webpack_require__.r(__webpack_exports__); 497 | /* harmony export */ __webpack_require__.d(__webpack_exports__, { 498 | /* harmony export */ "markAsCompleted": () => (/* binding */ markAsCompleted), 499 | /* harmony export */ "clearAllCompletedTasks": () => (/* binding */ clearAllCompletedTasks) 500 | /* harmony export */ }); 501 | function markAsCompleted(e, tasks) { 502 | const completedIndex = e.target.parentElement.dataset.index - 1; 503 | if (e.target.checked) { 504 | e.target.nextElementSibling.classList.add('done'); 505 | tasks[completedIndex].completed = true; 506 | } else { 507 | e.target.nextElementSibling.classList.remove('done'); 508 | tasks[completedIndex].completed = false; 509 | } 510 | localStorage.setItem('tasks', JSON.stringify(tasks)); 511 | } 512 | 513 | function clearAllCompletedTasks(tasks) { 514 | document.querySelectorAll('.done').forEach((task) => { 515 | const deletedIndex = task.parentElement.dataset.index; 516 | const toBeUpdatedTasks = tasks.slice(deletedIndex, tasks.length); 517 | toBeUpdatedTasks.forEach((task) => { 518 | const el = document.querySelector(`[data-index="${task.index}"]`); 519 | el.dataset.index = task.index - 1; 520 | task.index -= 1; 521 | }); 522 | tasks.splice(deletedIndex - 1, 1); 523 | task.parentElement.parentElement.remove(); 524 | }); 525 | localStorage.setItem('tasks', JSON.stringify(tasks)); 526 | } 527 | 528 | /***/ }) 529 | /******/ ]); 530 | /************************************************************************/ 531 | /******/ // The module cache 532 | /******/ var __webpack_module_cache__ = {}; 533 | /******/ 534 | /******/ // The require function 535 | /******/ function __webpack_require__(moduleId) { 536 | /******/ // Check if module is in cache 537 | /******/ var cachedModule = __webpack_module_cache__[moduleId]; 538 | /******/ if (cachedModule !== undefined) { 539 | /******/ return cachedModule.exports; 540 | /******/ } 541 | /******/ // Create a new module (and put it into the cache) 542 | /******/ var module = __webpack_module_cache__[moduleId] = { 543 | /******/ id: moduleId, 544 | /******/ // no module.loaded needed 545 | /******/ exports: {} 546 | /******/ }; 547 | /******/ 548 | /******/ // Execute the module function 549 | /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); 550 | /******/ 551 | /******/ // Return the exports of the module 552 | /******/ return module.exports; 553 | /******/ } 554 | /******/ 555 | /************************************************************************/ 556 | /******/ /* webpack/runtime/compat get default export */ 557 | /******/ (() => { 558 | /******/ // getDefaultExport function for compatibility with non-harmony modules 559 | /******/ __webpack_require__.n = (module) => { 560 | /******/ var getter = module && module.__esModule ? 561 | /******/ () => (module['default']) : 562 | /******/ () => (module); 563 | /******/ __webpack_require__.d(getter, { a: getter }); 564 | /******/ return getter; 565 | /******/ }; 566 | /******/ })(); 567 | /******/ 568 | /******/ /* webpack/runtime/define property getters */ 569 | /******/ (() => { 570 | /******/ // define getter functions for harmony exports 571 | /******/ __webpack_require__.d = (exports, definition) => { 572 | /******/ for(var key in definition) { 573 | /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { 574 | /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); 575 | /******/ } 576 | /******/ } 577 | /******/ }; 578 | /******/ })(); 579 | /******/ 580 | /******/ /* webpack/runtime/hasOwnProperty shorthand */ 581 | /******/ (() => { 582 | /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) 583 | /******/ })(); 584 | /******/ 585 | /******/ /* webpack/runtime/make namespace object */ 586 | /******/ (() => { 587 | /******/ // define __esModule on exports 588 | /******/ __webpack_require__.r = (exports) => { 589 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 590 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 591 | /******/ } 592 | /******/ Object.defineProperty(exports, '__esModule', { value: true }); 593 | /******/ }; 594 | /******/ })(); 595 | /******/ 596 | /************************************************************************/ 597 | var __webpack_exports__ = {}; 598 | // This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. 599 | (() => { 600 | __webpack_require__.r(__webpack_exports__); 601 | /* harmony import */ var _style_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); 602 | /* harmony import */ var _modules_Task_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); 603 | /* harmony import */ var _modules_completed_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(12); 604 | 605 | 606 | 607 | 608 | const form = document.querySelector('.todo-add'); 609 | const input = document.querySelector('.form-field'); 610 | const ul = document.getElementById('todo-list'); 611 | let li; 612 | const clearBtn = document.querySelector('.todo-clear'); 613 | 614 | let tasks; 615 | 616 | if (localStorage.getItem('tasks') === null) { 617 | tasks = []; 618 | } else { 619 | tasks = []; 620 | Array.from(JSON.parse(localStorage.getItem('tasks'))).forEach((task) => { 621 | tasks.push(new _modules_Task_js__WEBPACK_IMPORTED_MODULE_1__["default"](task.description, task.index, task.completed)); 622 | }); 623 | } 624 | 625 | function prepareEdit(task, btn) { 626 | btn.addEventListener('click', () => { 627 | const el = document.querySelector(`[data-index="${task.index}"]`); 628 | const p = el.children[1]; 629 | const input = document.createElement('input'); 630 | input.type = 'text'; 631 | input.value = p.textContent; 632 | input.id = task.index; 633 | el.insertBefore(input, p); 634 | el.removeChild(p); 635 | input.focus(); 636 | input.select(); 637 | input.classList.add('edit'); 638 | }); 639 | } 640 | 641 | function createTaskHtml(task) { 642 | li = document.createElement('li'); 643 | li.classList.add('task-li'); 644 | li.innerHTML = ` 645 |
646 | 647 |

${task.description}

648 |
649 | 650 | 651 |
652 |
653 | `; 654 | ul.appendChild(li); 655 | const btn = document.getElementById(`edit-${task.index}`); 656 | prepareEdit(task, btn); 657 | } 658 | 659 | function loadTasks() { 660 | tasks.forEach((task) => { 661 | createTaskHtml(task); 662 | }); 663 | } 664 | 665 | loadTasks(); 666 | 667 | function addTask() { 668 | ul.innerHTML = ''; 669 | tasks.forEach((task) => { 670 | createTaskHtml(task); 671 | }); 672 | } 673 | 674 | function removeTask(e) { 675 | if (e.target.classList.contains('close-button')) { 676 | const deletedIndex = e.target.parentElement.parentElement.dataset.index; 677 | const toBeUpdatedTasks = tasks.slice(deletedIndex, tasks.length); 678 | toBeUpdatedTasks.forEach((task) => { 679 | const el = document.querySelector(`[data-index="${task.index}"]`); 680 | el.dataset.index = task.index - 1; 681 | task.index -= 1; 682 | }); 683 | e.target.parentElement.parentElement.remove(); 684 | tasks.splice(deletedIndex - 1, 1); 685 | localStorage.setItem('tasks', JSON.stringify(tasks)); 686 | } 687 | } 688 | 689 | form.addEventListener('submit', (e) => { 690 | e.preventDefault(); 691 | const task = new _modules_Task_js__WEBPACK_IMPORTED_MODULE_1__["default"](input.value, tasks.length + 1); 692 | tasks.push(task); 693 | localStorage.setItem('tasks', JSON.stringify(tasks)); 694 | addTask(); 695 | input.value = ''; 696 | }); 697 | 698 | ul.addEventListener('click', removeTask); 699 | 700 | ul.addEventListener('keypress', (e) => { 701 | if (e.key === 'Enter') { 702 | const task = tasks.find((t) => t.index === parseInt(e.target.id, 10)); 703 | task.description = e.target.value; 704 | localStorage.setItem('tasks', JSON.stringify(tasks)); 705 | const p = document.createElement('p'); 706 | p.textContent = e.target.value; 707 | const parent = document.querySelector(`[data-index="${e.target.id}"]`); 708 | parent.insertBefore(p, e.target); 709 | parent.removeChild(e.target); 710 | } 711 | }); 712 | 713 | function wrapMarkAsCompleted(e) { 714 | (0,_modules_completed_js__WEBPACK_IMPORTED_MODULE_2__.markAsCompleted)(e, tasks); 715 | } 716 | 717 | ul.addEventListener('click', wrapMarkAsCompleted); 718 | 719 | function wrapclearAllCompletedTasks() { 720 | (0,_modules_completed_js__WEBPACK_IMPORTED_MODULE_2__.clearAllCompletedTasks)(tasks); 721 | } 722 | 723 | clearBtn.addEventListener('click', wrapclearAllCompletedTasks); 724 | 725 | })(); 726 | 727 | /******/ })() 728 | ; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webpack-setup", 3 | "version": "1.0.0", 4 | "description": "", 5 | "private": true, 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "webpack serve --open", 9 | "build": "webpack" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/ahmedtaa/webpack.git" 14 | }, 15 | "keywords": [], 16 | "author": "", 17 | "license": "ISC", 18 | "bugs": { 19 | "url": "https://github.com/ahmedtaa/webpack/issues" 20 | }, 21 | "homepage": "https://github.com/ahmedtaa/webpack#readme", 22 | "devDependencies": { 23 | "babel-eslint": "^10.1.0", 24 | "css-loader": "^6.5.1", 25 | "eslint": "^7.32.0", 26 | "eslint-config-airbnb-base": "^14.2.1", 27 | "eslint-plugin-import": "^2.25.4", 28 | "hint": "^6.1.9", 29 | "html-webpack-plugin": "^5.5.0", 30 | "style-loader": "^3.3.1", 31 | "stylelint": "^13.13.1", 32 | "stylelint-config-standard": "^21.0.0", 33 | "stylelint-csstree-validator": "^1.9.0", 34 | "stylelint-scss": "^3.21.0", 35 | "webpack": "^5.65.0", 36 | "webpack-cli": "^4.9.1", 37 | "webpack-dev-server": "^4.7.2" 38 | }, 39 | "dependencies": { 40 | "lodash": "^4.17.21" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | To Do List 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |

Today's To Do

17 | 18 |
19 |
20 |
21 | 22 | 23 |
24 | 26 | 29 |
30 | 31 | 32 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import './style.css'; 2 | import Task from './modules/Task.js'; 3 | import { clearAllCompletedTasks, markAsCompleted } from './modules/completed.js'; 4 | 5 | const form = document.querySelector('.todo-add'); 6 | const input = document.querySelector('.form-field'); 7 | const ul = document.getElementById('todo-list'); 8 | let li; 9 | const clearBtn = document.querySelector('.todo-clear'); 10 | 11 | let tasks; 12 | 13 | if (localStorage.getItem('tasks') === null) { 14 | tasks = []; 15 | } else { 16 | tasks = []; 17 | Array.from(JSON.parse(localStorage.getItem('tasks'))).forEach((task) => { 18 | tasks.push(new Task(task.description, task.index, task.completed)); 19 | }); 20 | } 21 | 22 | function prepareEdit(task, btn) { 23 | btn.addEventListener('click', () => { 24 | const el = document.querySelector(`[data-index="${task.index}"]`); 25 | const p = el.children[1]; 26 | const input = document.createElement('input'); 27 | input.type = 'text'; 28 | input.value = p.textContent; 29 | input.id = task.index; 30 | el.insertBefore(input, p); 31 | el.removeChild(p); 32 | input.focus(); 33 | input.select(); 34 | input.classList.add('edit'); 35 | }); 36 | } 37 | 38 | function createTaskHtml(task) { 39 | li = document.createElement('li'); 40 | li.classList.add('task-li'); 41 | li.innerHTML = ` 42 |
43 | 44 |

${task.description}

45 |
46 | 47 | 48 |
49 |
50 | `; 51 | ul.appendChild(li); 52 | const btn = document.getElementById(`edit-${task.index}`); 53 | prepareEdit(task, btn); 54 | } 55 | 56 | function loadTasks() { 57 | tasks.forEach((task) => { 58 | createTaskHtml(task); 59 | }); 60 | } 61 | 62 | loadTasks(); 63 | 64 | function addTask() { 65 | ul.innerHTML = ''; 66 | tasks.forEach((task) => { 67 | createTaskHtml(task); 68 | }); 69 | } 70 | 71 | function removeTask(e) { 72 | if (e.target.classList.contains('close-button')) { 73 | const deletedIndex = e.target.parentElement.parentElement.dataset.index; 74 | const toBeUpdatedTasks = tasks.slice(deletedIndex, tasks.length); 75 | toBeUpdatedTasks.forEach((task) => { 76 | const el = document.querySelector(`[data-index="${task.index}"]`); 77 | el.dataset.index = task.index - 1; 78 | task.index -= 1; 79 | }); 80 | e.target.parentElement.parentElement.remove(); 81 | tasks.splice(deletedIndex - 1, 1); 82 | localStorage.setItem('tasks', JSON.stringify(tasks)); 83 | } 84 | } 85 | 86 | form.addEventListener('submit', (e) => { 87 | e.preventDefault(); 88 | const task = new Task(input.value, tasks.length + 1); 89 | tasks.push(task); 90 | localStorage.setItem('tasks', JSON.stringify(tasks)); 91 | addTask(); 92 | input.value = ''; 93 | }); 94 | 95 | ul.addEventListener('click', removeTask); 96 | 97 | ul.addEventListener('keypress', (e) => { 98 | if (e.key === 'Enter') { 99 | const task = tasks.find((t) => t.index === parseInt(e.target.id, 10)); 100 | task.description = e.target.value; 101 | localStorage.setItem('tasks', JSON.stringify(tasks)); 102 | const p = document.createElement('p'); 103 | p.textContent = e.target.value; 104 | const parent = document.querySelector(`[data-index="${e.target.id}"]`); 105 | parent.insertBefore(p, e.target); 106 | parent.removeChild(e.target); 107 | } 108 | }); 109 | 110 | function wrapMarkAsCompleted(e) { 111 | markAsCompleted(e, tasks); 112 | } 113 | 114 | ul.addEventListener('click', wrapMarkAsCompleted); 115 | 116 | function wrapclearAllCompletedTasks() { 117 | clearAllCompletedTasks(tasks); 118 | } 119 | 120 | clearBtn.addEventListener('click', wrapclearAllCompletedTasks); 121 | -------------------------------------------------------------------------------- /src/modules/Task.js: -------------------------------------------------------------------------------- 1 | export default class Task { 2 | constructor(description, index, completed = false) { 3 | this.description = description; 4 | this.completed = completed; 5 | this.index = index; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/modules/completed.js: -------------------------------------------------------------------------------- 1 | export function markAsCompleted(e, tasks) { 2 | const completedIndex = e.target.parentElement.dataset.index - 1; 3 | if (e.target.checked) { 4 | e.target.nextElementSibling.classList.add('done'); 5 | tasks[completedIndex].completed = true; 6 | } else { 7 | e.target.nextElementSibling.classList.remove('done'); 8 | tasks[completedIndex].completed = false; 9 | } 10 | localStorage.setItem('tasks', JSON.stringify(tasks)); 11 | } 12 | 13 | export function clearAllCompletedTasks(tasks) { 14 | document.querySelectorAll('.done').forEach((task) => { 15 | const deletedIndex = task.parentElement.dataset.index; 16 | const toBeUpdatedTasks = tasks.slice(deletedIndex, tasks.length); 17 | toBeUpdatedTasks.forEach((task) => { 18 | const el = document.querySelector(`[data-index="${task.index}"]`); 19 | el.dataset.index = task.index - 1; 20 | task.index -= 1; 21 | }); 22 | tasks.splice(deletedIndex - 1, 1); 23 | task.parentElement.parentElement.remove(); 24 | }); 25 | localStorage.setItem('tasks', JSON.stringify(tasks)); 26 | } -------------------------------------------------------------------------------- /src/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #f6f6f6; 3 | } 4 | 5 | .container { 6 | border: 1px solid; 7 | background-color: #fff; 8 | width: 50%; 9 | margin: auto; 10 | } 11 | 12 | .header { 13 | display: flex; 14 | justify-content: space-between; 15 | padding: 10px; 16 | } 17 | 18 | .header button { 19 | background-color: transparent; 20 | border: none; 21 | cursor: pointer; 22 | } 23 | 24 | .todo-add { 25 | display: flex; 26 | padding: 10px; 27 | } 28 | 29 | .todo-add button { 30 | background-color: transparent; 31 | border: none; 32 | cursor: pointer; 33 | } 34 | 35 | .form-field { 36 | font-family: inherit; 37 | width: 100%; 38 | border: 0; 39 | border-bottom: 2px solid #9b9b9b; 40 | outline: 0; 41 | font-size: 1.3rem; 42 | color: black; 43 | padding: 7px 0; 44 | background: transparent; 45 | transition: border-color 0.2s; 46 | } 47 | 48 | ul { 49 | list-style: none; 50 | padding: 10px; 51 | } 52 | 53 | .task { 54 | display: flex; 55 | align-items: center; 56 | justify-content: flex-start; 57 | gap: 10px; 58 | } 59 | 60 | .btns { 61 | margin-left: auto; 62 | display: flex; 63 | gap: 40px; 64 | } 65 | 66 | .close-button { 67 | transform: rotate(45deg); 68 | cursor: pointer; 69 | font-size: 30px; 70 | color: red; 71 | } 72 | 73 | .edit { 74 | border-color: transparent; 75 | height: 40px; 76 | } 77 | 78 | .edit:focus-visible { 79 | outline: green auto 1px; 80 | font-size: 15px; 81 | } 82 | 83 | .done { 84 | text-decoration: line-through; 85 | opacity: 0.5; 86 | } 87 | 88 | .scroll { 89 | background-color: transparent; 90 | border: none; 91 | cursor: pointer; 92 | margin-left: auto; 93 | } 94 | 95 | .footer { 96 | background-color: #f6f6f6; 97 | height: 50px; 98 | border-top: 1px solid; 99 | display: flex; 100 | justify-content: center; 101 | opacity: 0.5; 102 | } 103 | 104 | .footer button { 105 | background-color: transparent; 106 | border: none; 107 | cursor: pointer; 108 | font-size: 20px; 109 | } 110 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | 4 | module.exports = { 5 | mode: 'none', 6 | entry: './src/index.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 | clean: true, 19 | }, 20 | module: { 21 | rules: [ 22 | { 23 | test: /\.css$/i, 24 | use: ['style-loader', 'css-loader'], 25 | }, 26 | ], 27 | }, 28 | }; 29 | --------------------------------------------------------------------------------