├── .gitignore ├── .DS_Store ├── modules ├── .DS_Store ├── book.js ├── dateManager.js ├── bookManager.js └── bookListView.js ├── .hintrc ├── .eslintrc.json ├── .stylelintrc.json ├── package.json ├── LICENSE.md ├── index.js ├── index.html ├── .github └── workflows │ └── linters.yml ├── index.css └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClaudiaRojasSoto/Awesome_Books_ES6/HEAD/.DS_Store -------------------------------------------------------------------------------- /modules/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClaudiaRojasSoto/Awesome_Books_ES6/HEAD/modules/.DS_Store -------------------------------------------------------------------------------- /modules/book.js: -------------------------------------------------------------------------------- 1 | export default class Book { 2 | constructor(id, title, author) { 3 | this.author = author; 4 | this.title = title; 5 | this.id = id; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.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/dateManager.js: -------------------------------------------------------------------------------- 1 | import { DateTime } from './luxon.js'; 2 | 3 | const displayDate = (elementId) => { 4 | const dateElement = document.getElementById(elementId); 5 | 6 | const updateTime = () => { 7 | const today = DateTime.now(); 8 | const format = today.toLocaleString(DateTime.DATETIME_FULL_WITH_SECONDS); 9 | dateElement.textContent = format; 10 | }; 11 | 12 | updateTime(); 13 | setInterval(updateTime, 1000); 14 | }; 15 | 16 | export default displayDate; 17 | -------------------------------------------------------------------------------- /modules/bookManager.js: -------------------------------------------------------------------------------- 1 | export default class BookManager { 2 | constructor() { 3 | this.books = JSON.parse(localStorage.getItem('book')) || []; 4 | } 5 | 6 | addBook(book) { 7 | this.books = [book, ...this.books]; 8 | localStorage.setItem('book', JSON.stringify(this.books)); 9 | } 10 | 11 | removeBook(bookIndex) { 12 | this.books.splice(bookIndex, 1); 13 | localStorage.setItem('book', JSON.stringify(this.books)); 14 | } 15 | 16 | getBooks() { 17 | return this.books; 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 | "no-use-before-define": "off", 17 | "eol-last": "off", 18 | "import/extensions": [ 1, { 19 | "js": "always", "json": "always" 20 | }] 21 | }, 22 | "ignorePatterns": [ 23 | "dist/", 24 | "build/" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /.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 | } 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "awesome_books_es6", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "babel-eslint": "^10.1.0", 15 | "eslint": "^7.32.0", 16 | "eslint-config-airbnb-base": "^14.2.1", 17 | "eslint-plugin-import": "^2.27.5", 18 | "hint": "^7.1.8", 19 | "stylelint": "^13.13.1", 20 | "stylelint-config-standard": "^21.0.0", 21 | "stylelint-csstree-validator": "^1.9.0", 22 | "stylelint-scss": "^3.21.0" 23 | }, 24 | "dependencies": { 25 | "luxon": "^3.3.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | ## Copyright 2021, [Claudia Rojas] 2 | 3 | ###### Please delete this line and the next one 4 | ###### Awesome_Books_ES6 can be a webpage/website, a web app, a software and so on 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this [Awesome_Books_ES6] and associated documentation files, to deal in the [Awesome_Books_ES6] without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the [Awesome_Books_ES6], and to permit persons to whom the [Awesome_Books_ES6] is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the [Awesome_Books_ES6]. 9 | 10 | THE Awesome_Books_ES6 IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE [Awesome_Books_ES6] OR THE USE OR OTHER DEALINGS IN THE [Awesome_Books_ES6]. 11 | -------------------------------------------------------------------------------- /modules/bookListView.js: -------------------------------------------------------------------------------- 1 | export function displayBooks(bookListElement, bookManager) { 2 | const books = bookManager.getBooks(); 3 | 4 | bookListElement.innerHTML = ''; 5 | const list = document.createElement('ul'); 6 | list.classList.add('list-container'); 7 | 8 | books.forEach((book, index) => { 9 | const listItem = document.createElement('li'); 10 | 11 | const booksInfo = document.createElement('span'); 12 | booksInfo.textContent = `${book.title} by ${book.author}`; 13 | listItem.classList.add(index % 2 === 0 ? 'even' : 'odd'); 14 | listItem.appendChild(booksInfo); 15 | const removeButton = document.createElement('button'); 16 | removeButton.id = `remove-button-${book.id}`; 17 | 18 | removeButton.textContent = 'remove'; 19 | removeButton.setAttribute('data-index', index); 20 | 21 | listItem.appendChild(removeButton); 22 | list.appendChild(listItem); 23 | }); 24 | 25 | bookListElement.appendChild(list); 26 | attachRemoveButtonListeners(bookListElement, bookManager); 27 | } 28 | 29 | export function attachRemoveButtonListeners(bookListElement, bookManager) { 30 | const removeButtons = bookListElement.querySelectorAll("button[id^='remove-button-']"); 31 | removeButtons.forEach((button) => { 32 | button.addEventListener('click', (event) => { 33 | const bookIndex = event.target.dataset.index; 34 | bookManager.removeBook(bookIndex); 35 | displayBooks(bookListElement, bookManager); 36 | }); 37 | }); 38 | } 39 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import Book from './modules/book.js'; 2 | import BookManager from './modules/bookManager.js'; 3 | import displayDate from './modules/dateManager.js'; 4 | import { displayBooks } from './modules/bookListView.js'; 5 | 6 | class BookList { 7 | constructor() { 8 | this.bookList = document.getElementById('book-list'); 9 | this.inputTitle = document.getElementById('title'); 10 | this.inputAuthor = document.getElementById('author'); 11 | this.inputButton = document.getElementById('add-button'); 12 | this.listButton = document.getElementById('contact-section'); 13 | this.newButton = document.getElementById('form-section'); 14 | this.books = []; 15 | this.bookManager = new BookManager(); 16 | this.inputButton.addEventListener('click', (event) => this.addBooks(event)); 17 | // Use the method through the instance itself 18 | this.displayBooks(); 19 | 20 | this.listLink = document.getElementById('list'); 21 | this.addLink = document.getElementById('new'); 22 | this.contactLink = document.getElementById('contact'); 23 | 24 | this.listLink.addEventListener('click', () => this.showSection('div-list')); 25 | this.addLink.addEventListener('click', () => this.showSection('form-section')); 26 | this.contactLink.addEventListener('click', () => this.showSection('contact-section')); 27 | 28 | // Use the method through the instance itself 29 | this.displayDate(); 30 | } 31 | 32 | addBooks(event) { 33 | event.preventDefault(); 34 | const title = this.inputTitle.value; 35 | const author = this.inputAuthor.value; 36 | if (title !== '' && author !== '') { 37 | const book = new Book(Date.now(), title, author); 38 | this.bookManager.addBook(book); 39 | } 40 | 41 | this.displayBooks(); 42 | this.inputAuthor.value = ''; 43 | this.inputTitle.value = ''; 44 | } 45 | 46 | showSection = (sectionId) => { 47 | const sections = document.getElementsByClassName('list-section'); 48 | 49 | for (let i = 0; i < sections.length; i += 1) { 50 | const content = sections[i]; 51 | if (content.id === sectionId) { 52 | content.classList.remove('hidden'); 53 | } else { 54 | content.classList.add('hidden'); 55 | } 56 | } 57 | } 58 | 59 | displayDate = () => { 60 | displayDate('date'); 61 | } 62 | 63 | displayBooks = () => { 64 | displayBooks(this.bookList, this.bookManager); 65 | } 66 | } 67 | 68 | const bookList = new BookList(); 69 | bookList.displayBooks(); 70 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |