├── .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 |
43 |
44 |
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 | 
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.
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 |
--------------------------------------------------------------------------------