├── .stylelintrc.json ├── .hintrc ├── modules ├── class.js └── manage.js ├── .eslintrc.json ├── package.json ├── LICENSE ├── index.js ├── index.html ├── .github └── workflows │ └── linters.yml ├── README.md ├── .gitignore └── index.css /.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": ["build/**", "dist/**", "**/reset*.css", "**/bootstrap*.css", "**/*.js", "**/*.jsx"] 10 | } 11 | -------------------------------------------------------------------------------- /.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 | } -------------------------------------------------------------------------------- /modules/class.js: -------------------------------------------------------------------------------- 1 | export default class Books { 2 | constructor() { 3 | this.list = localStorage.getItem('booksList') 4 | ? JSON.parse(localStorage.getItem('booksList')) 5 | : []; 6 | } 7 | 8 | addBook(book) { 9 | this.list.push(book); 10 | 11 | localStorage.setItem('booksList', JSON.stringify(this.list)); 12 | } 13 | 14 | removeBook(title) { 15 | this.list = this.list.filter((book) => book.title !== title); 16 | 17 | localStorage.setItem('booksList', JSON.stringify(this.list)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.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 | } 26 | -------------------------------------------------------------------------------- /modules/manage.js: -------------------------------------------------------------------------------- 1 | const renderBooks = (booksList) => { 2 | const listItem = document.querySelector('.books-list'); 3 | listItem.innerHTML = ''; 4 | 5 | booksList.list.forEach((book) => { 6 | listItem.innerHTML += ` 7 |
  • 8 |

    9 | "${book.title}" by ${book.author} 10 |

    11 | 12 |
  • 13 | 14 | `; 15 | }); 16 | 17 | const remBtns = document.querySelectorAll('.remBtn'); 18 | remBtns.forEach((btn) => { 19 | btn.addEventListener('click', () => { 20 | const elem = btn.parentNode; 21 | const bookTitle = elem.querySelector('.title').textContent; 22 | booksList.removeBook(bookTitle); 23 | elem.remove(); 24 | }); 25 | }); 26 | }; 27 | 28 | export default renderBooks; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "microverse-m2-awbook-es6", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/OybekKayumov/microverse-m2-awbook-es6.git" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "ISC", 16 | "bugs": { 17 | "url": "https://github.com/OybekKayumov/microverse-m2-awbook-es6/issues" 18 | }, 19 | "homepage": "https://github.com/OybekKayumov/microverse-m2-awbook-es6#readme", 20 | "devDependencies": { 21 | "babel-eslint": "^10.1.0", 22 | "eslint": "^7.32.0", 23 | "eslint-config-airbnb-base": "^14.2.1", 24 | "eslint-plugin-import": "^2.25.4", 25 | "hint": "^6.1.10", 26 | "stylelint": "^13.13.1", 27 | "stylelint-config-standard": "^21.0.0", 28 | "stylelint-csstree-validator": "^1.9.0", 29 | "stylelint-scss": "^3.21.0" 30 | }, 31 | "dependencies": { 32 | "luxon": "^2.3.1" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Oybek Kayumov 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 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import { DateTime } from './node_modules/luxon/build/es6/luxon.js'; 2 | import Books from './modules/class.js'; 3 | import renderBooks from './modules/manage.js'; 4 | 5 | const form = document.querySelector('.form'); 6 | 7 | const booksList = new Books(); 8 | 9 | renderBooks(booksList); 10 | 11 | form.addEventListener('submit', (e) => { 12 | e.preventDefault(); 13 | 14 | const title = form.title.value; 15 | const author = form.author.value; 16 | 17 | form.title.value = ''; 18 | form.author.value = ''; 19 | 20 | booksList.addBook({ title, author }); 21 | renderBooks(booksList); 22 | }); 23 | 24 | const timePara = DateTime.now().toFormat('LLL dd yyyy, hh:mm:ss a'); 25 | document.querySelector('.date').textContent = timePara; 26 | 27 | const navLinks = document.querySelectorAll('.nav-links a'); 28 | navLinks.forEach((link) => { 29 | link.addEventListener('click', () => { 30 | if (link.classList.contains('active')) return; 31 | 32 | navLinks.forEach((a) => { 33 | a.classList.remove('active'); 34 | 35 | link.classList.add('active'); 36 | 37 | document.querySelector('.flex').classList.remove('flex'); 38 | 39 | document.querySelector(`.${link.id}`).classList.add('flex'); 40 | }); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 13 | Awesome books 14 | 15 | 16 | 17 |
    18 |

    Awesome books

    19 | 24 |
    25 | 26 |

    27 | 28 |
    29 |

    All Awesome books

    30 | 31 |
    32 | 33 |
    34 |

    Add a new book

    35 |
    36 |

    37 |

    38 |
    39 | 40 |
    41 |
    42 |
    43 | 44 |
    45 |

    Contact information

    46 |

    47 | Do you have any questions or you just want to say "Hello"?
    48 | You can reach out to us! 49 |

    50 | 55 |
    56 | 57 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # microverse-m2-Awesome-books-es6 2 | 3 | > This project is about building Awesome Books using JavaScript and a copy of my awesome books project build by using ES6 syntax and modules. 4 | 5 | ![Awesome Books design](https://user-images.githubusercontent.com/85465559/159246177-d200e21b-2874-40f0-95f1-fd51b31474ea.png) 6 | 7 | ## Built With 8 | 9 | -Lighthouse (An open-source, automated tool for improving the quality of web pages. It has audits for performance, accessibility, progressive web apps, SEO and more). 10 | 11 | -Webhint (A customizable linting tool that helps you improve your site's accessibility, speed, cross-browser compatibility, and more by checking your code for best practices and common errors). 12 | 13 | -Stylelint (A mighty, modern linter that helps you avoid errors and enforce conventions in your styles). 14 | 15 | ### Prerequisites 16 | 17 | ## VScode or other editor 18 | 19 | ### Install 20 | 21 | #### Cloning the project 22 | 23 | git clone https://github.com/OybekKayumov/microverse-m2-awbook-es6.git 24 | 25 | ## Getting packages and debugging with Stylelint 26 | 27 | npm install --save-dev stylelint@13.x stylelint-scss@3.x stylelint-config-standard@21.x stylelint-csstree-validator@1.x 28 | 29 | ##### For validation detection using Stylelint Run 30 | 31 | npx stylelint "\*_/_.{css,scss}" 32 | 33 | ##### from parent source directory 34 | 35 | ## Getting packages and debuging with Webhint 36 | 37 | npm init -y 38 | npm install --save-dev hint@6.x 39 | 40 | ##### For validation detection using Webhint Run 41 | 42 | npx hint . 43 | 44 | ## Author 45 | 46 | 👤 **Oybek Kayumov** 47 | 48 | - GitHub: [OybekKayumov](https://github.com/OybekKayumov) 49 | - LinkedIn: [oybek-kayumov](https://www.linkedin.com/in/oybek-kayumov-54a8485b/) 50 | - Twitter: [@KayumovOybek](https://twitter.com/KayumovOybek) 51 | 52 | ## 🤝 Contributing 53 | 54 | Contributions, issues, and feature requests are welcome! 55 | 56 | Feel free to check the [issues page](https://github.com/OybekKayumov/microverse-m2-awbook-es6/issues/). 57 | 58 | ## Show your support 59 | 60 | Give a ⭐️ if you like this project! 61 | 62 | ## Acknowledgments 63 | 64 | -This project was inspired by [Microverse](https://www.microverse.org) 65 | 66 | ## 📝 License 67 | 68 | This project is [MIT](./MIT.md) licensed.![](https://img.shields.io/badge/Microverse-blueviolet) 69 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # temp.md 10 | temp.md 11 | 12 | # vscode 13 | .vscode/* 14 | !.vscode/settings.json 15 | !.vscode/tasks.json 16 | !.vscode/launch.json 17 | !.vscode/extensions.json 18 | !.vscode/*.code-snippets 19 | 20 | # Local History for Visual Studio Code 21 | .history/ 22 | 23 | # Built Visual Studio Code Extensions 24 | *.vsix 25 | 26 | # Diagnostic reports (https://nodejs.org/api/report.html) 27 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 28 | 29 | # Runtime data 30 | pids 31 | *.pid 32 | *.seed 33 | *.pid.lock 34 | 35 | # Directory for instrumented libs generated by jscoverage/JSCover 36 | lib-cov 37 | 38 | # Coverage directory used by tools like istanbul 39 | coverage 40 | *.lcov 41 | 42 | # nyc test coverage 43 | .nyc_output 44 | 45 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 46 | .grunt 47 | 48 | # Bower dependency directory (https://bower.io/) 49 | bower_components 50 | 51 | # node-waf configuration 52 | .lock-wscript 53 | 54 | # Compiled binary addons (https://nodejs.org/api/addons.html) 55 | build/Release 56 | 57 | # Dependency directories 58 | node_modules/ 59 | jspm_packages/ 60 | 61 | # TypeScript v1 declaration files 62 | typings/ 63 | 64 | # TypeScript cache 65 | *.tsbuildinfo 66 | 67 | # Optional npm cache directory 68 | .npm 69 | 70 | # Optional eslint cache 71 | .eslintcache 72 | 73 | # Microbundle cache 74 | .rpt2_cache/ 75 | .rts2_cache_cjs/ 76 | .rts2_cache_es/ 77 | .rts2_cache_umd/ 78 | 79 | # Optional REPL history 80 | .node_repl_history 81 | 82 | # Output of 'npm pack' 83 | *.tgz 84 | 85 | # Yarn Integrity file 86 | .yarn-integrity 87 | 88 | # dotenv environment variables file 89 | .env 90 | .env.test 91 | 92 | # parcel-bundler cache (https://parceljs.org/) 93 | .cache 94 | 95 | # Next.js build output 96 | .next 97 | 98 | # Nuxt.js build / generate output 99 | .nuxt 100 | dist 101 | 102 | # Gatsby files 103 | .cache/ 104 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 105 | # https://nextjs.org/blog/next-9-1#public-directory-support 106 | # public 107 | 108 | # vuepress build output 109 | .vuepress/dist 110 | 111 | # Serverless directories 112 | .serverless/ 113 | 114 | # FuseBox cache 115 | .fusebox/ 116 | 117 | # DynamoDB Local files 118 | .dynamodb/ 119 | 120 | # TernJS port file 121 | .tern-port 122 | -------------------------------------------------------------------------------- /index.css: -------------------------------------------------------------------------------- 1 | * { 2 | padding: 0; 3 | margin: 0; 4 | box-sizing: border-box; 5 | list-style: none; 6 | } 7 | 8 | body { 9 | font-family: 'Comic Neue', sans-serif; 10 | background-color: #fff; 11 | color: #333; 12 | padding: 20px 10px; 13 | margin: 20px; 14 | } 15 | 16 | a, 17 | a:hover, 18 | a:visited { 19 | text-decoration: none; 20 | color: unset; 21 | } 22 | 23 | header { 24 | display: flex; 25 | justify-content: space-between; 26 | align-items: center; 27 | padding: 10px; 28 | border: 3px solid #333; 29 | border-radius: 4px; 30 | } 31 | 32 | .date { 33 | width: 100%; 34 | margin: 15px 0; 35 | text-align: right; 36 | } 37 | 38 | h1 { 39 | font-size: 24px; 40 | } 41 | 42 | .nav-links { 43 | display: flex; 44 | gap: 12px; 45 | } 46 | 47 | .nav-links .active { 48 | padding-bottom: 5px; 49 | border-right: 2px solid #333; 50 | } 51 | 52 | .nav-links a:not(.active):hover { 53 | border-bottom: 3px solid #333; 54 | } 55 | 56 | .home { 57 | display: none; 58 | flex-direction: column; 59 | align-items: center; 60 | margin: 30px 0; 61 | } 62 | 63 | .books-list { 64 | width: 100%; 65 | border: 2px solid #333; 66 | margin: 18px 0; 67 | border-radius: 4px; 68 | } 69 | 70 | .books-list li { 71 | display: flex; 72 | justify-content: space-between; 73 | align-items: center; 74 | padding: 10px; 75 | font-size: 18px; 76 | } 77 | 78 | .books-list li:nth-child(odd) { 79 | background-color: #d3d3d3; 80 | } 81 | 82 | button { 83 | cursor: pointer; 84 | background-color: #fff; 85 | color: #333; 86 | border: 3px solid #333; 87 | border-radius: 4px; 88 | padding: 10px; 89 | transition: all 0.2s ease-in-out; 90 | } 91 | 92 | button:hover { 93 | transform: scale(1.1); 94 | } 95 | 96 | button:active { 97 | transform: scale(0.9); 98 | } 99 | 100 | .form-container { 101 | display: none; 102 | flex-direction: column; 103 | align-items: center; 104 | margin-top: 30px; 105 | } 106 | 107 | form { 108 | margin: 40px 0 18px 0; 109 | width: 50%; 110 | } 111 | 112 | form input { 113 | font-size: 16px; 114 | background-color: #fff; 115 | color: #333; 116 | width: 100%; 117 | border: 3px solid #333; 118 | border-radius: 4px; 119 | padding: 10px; 120 | } 121 | 122 | .btn-submit { 123 | float: right; 124 | } 125 | 126 | .contact { 127 | display: none; 128 | flex-direction: column; 129 | align-items: center; 130 | margin-top: 40px; 131 | font-size: 18px; 132 | } 133 | 134 | .contact p { 135 | margin: 40px 0 18px 0; 136 | } 137 | 138 | .info { 139 | list-style-type: circle; 140 | } 141 | 142 | .flex { 143 | display: flex; 144 | } 145 | 146 | footer { 147 | position: absolute; 148 | font-size: 22px; 149 | font-weight: bold; 150 | bottom: 0; 151 | right: 0; 152 | left: 0; 153 | height: 60px; 154 | border: 3px solid #333; 155 | border-radius: 4px; 156 | padding: 10px 40px; 157 | margin: 20px; 158 | } 159 | --------------------------------------------------------------------------------