From cc803fcf76f2f46d2269e352735567fb908a8985 Mon Sep 17 00:00:00 2001 From: ak Date: Thu, 4 May 2023 12:18:30 -0700 Subject: [PATCH] final --- README.md | 3 + assets/css/style.css | 79 +++++++ assets/images/trash-can-outline.png | Bin 0 -> 361 bytes assets/images/trash-can-outline.svg | 1 + assets/js/main.js | 307 ++++++++++++++++++++++++++++ index.html | 30 +++ 6 files changed, 420 insertions(+) create mode 100644 README.md create mode 100644 assets/css/style.css create mode 100644 assets/images/trash-can-outline.png create mode 100644 assets/images/trash-can-outline.svg create mode 100644 assets/js/main.js create mode 100644 index.html diff --git a/README.md b/README.md new file mode 100644 index 0000000..29001ac --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# js-library + +Proof-of-concept library/book manager written in ES6 JS diff --git a/assets/css/style.css b/assets/css/style.css new file mode 100644 index 0000000..7d9d5fc --- /dev/null +++ b/assets/css/style.css @@ -0,0 +1,79 @@ +.row { + --bs-gutter-x: 0; + row-gap: 1.5rem; + margin-top: 1.5rem; + margin-left: 1.5rem; +} + +.col-sm-6 { + flex: 1; + width: auto; +} + +.card, header { + box-shadow: 0.5px 0.5px 5px black; +} + +header { + background-color: white; +} + +.nav-link.active { + margin-right: 1.5rem; +} + +.delete-button, .read-button { + border-radius: .25rem; + color: white; + border: 0; + padding: .5rem 1rem; + text-decoration: none; + display: flex; + align-items: center; + column-gap: .25rem; + height: 100%; + line-height: 16px; + justify-content: center; +} + +.read-button { + background-color: green; + line-height: 24px; +} + +.delete-button { + background-color: darkred; +} + +.card-link { + margin-left: 0px; +} + +.read-checkbox { + width: 24px; + height: 24px; +} + +.buttons-flex { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: space-around; +} + +#newbookform { + right: 1.5rem; + max-width: 500px; + position: absolute; + z-index: 2; + padding: 1rem 1rem; +} + +.form-control { + color: #6c757d !important; +} + +#closebutton { + display: flex; + justify-content: flex-end; +} \ No newline at end of file diff --git a/assets/images/trash-can-outline.png b/assets/images/trash-can-outline.png new file mode 100644 index 0000000000000000000000000000000000000000..3d7a2de63c9841b42fbe602335597f03bcdae918 GIT binary patch literal 361 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjEa{HEjtmSN`?>!lvI6-E$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZKG?d}4kf#9d}?s_1_ zS>O>_45Sml_(QhSc_1U()5S5w \ No newline at end of file diff --git a/assets/js/main.js b/assets/js/main.js new file mode 100644 index 0000000..03c33e3 --- /dev/null +++ b/assets/js/main.js @@ -0,0 +1,307 @@ +let myLibrary = []; +const row = document.querySelector(".row"); +const newButton = document.querySelector("#newbutton"); + +// for setting multiple CSS attributes on an element +Element.prototype.setAttributes = function (attributes) { + Object.keys(attributes).forEach(key => this.setAttribute(key, attributes[key])); +} + +// Book object constructor +class Book { + constructor (author, title, pages, read, comment) { + this.author = author; + this.title = title; + this.pages = pages; + this.read = read; + this.comment = comment; + } +} + +// pushes completed book (from makeForm() input) into array and adds it to the DOM +function addBookToLibrary(book, readBool) { + // do stuff here + myLibrary.push(book); + // create our divs + // column container + const columnContainer = document.createElement('div'); + columnContainer.setAttribute('class', "col-sm-6"); + // card + const card = document.createElement('div'); + card.setAttributes({class: 'card', style: "width: 18rem"}); + // card body + const cardBody = document.createElement('div'); + cardBody.setAttribute('class', "card-body"); + // book title + const bookTitle = document.createElement('h5'); + bookTitle.setAttribute('class', "card-title"); + bookTitle.textContent = book.title; + // book author + const bookAuthor = document.createElement('h6'); + bookAuthor.setAttribute('class', "card-subtitle mb-2 text-muted"); + bookAuthor.textContent = book.author; + // number of pages in book + const bookPages = document.createElement('h6'); + bookPages.setAttribute('class', "card-subtitle mb-2 text-muted book-pages"); + bookPages.textContent = book.pages + " pages"; + // comment, if any + const bookComment = document.createElement('p'); + bookComment.setAttribute('class', "card-text"); + bookComment.textContent = book.comment; + // create button container + const buttonsFlex = document.createElement('div'); + buttonsFlex.setAttribute('class', "buttons-flex"); + // delete button + const deleteButton = document.createElement('button'); + deleteButton.setAttributes({class: "delete-button", onclick: "removeBookFromLibrary(this)"}); + const deleteButtonText = document.createElement('div'); + deleteButtonText.textContent = "Delete"; + // svg stuff + const trashcanSVGContainer = document.createElementNS("http://www.w3.org/2000/svg","svg"); + trashcanSVGContainer.setAttributes({style: "width:24px;height:24px", "viewBox": "0 0 24 24"}); + const trashcanSVGPath = document.createElementNS("http://www.w3.org/2000/svg","path"); + trashcanSVGPath.setAttributes({fill: "currentColor", d: "M9,3V4H4V6H5V19A2,2 0 0,0 7,21H17A2,2 0 0,0 19,19V6H20V4H15V3H9M7,6H17V19H7V6M9,8V17H11V8H9M13,8V17H15V8H13Z"}); + // read button + const readButton = document.createElement('button'); + readButton.setAttributes({class: "read-button", onclick: "changeBookReadStatus(this)"}); + const readButtonText = document.createElement('div'); + readButtonText.textContent = "Read?"; + // svg stuff + const checkboxSVGContainer = document.createElementNS("http://www.w3.org/2000/svg","svg"); + checkboxSVGContainer.setAttributes({style: "width:24px;height:24px", "viewBox": "0 0 24 24"}); + const checkboxSVGPath = document.createElementNS("http://www.w3.org/2000/svg","path"); + if (readBool === true) { + checkboxSVGPath.setAttributes({fill: "currentColor", d: "M19,3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5C21,3.89 20.1,3 19,3Z"}); + } + else { + checkboxSVGPath.setAttributes({fill: "currentColor", d: "M19,3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5C21,3.89 20.1,3 19,3M19,5V19H5V5H19Z", class: "unchecked"}); + } + // append to DOM + row.appendChild(columnContainer); + columnContainer.appendChild(card); + card.appendChild(cardBody); + cardBody.appendChild(bookTitle); + cardBody.appendChild(bookAuthor); + cardBody.appendChild(bookPages); + cardBody.appendChild(bookComment); + cardBody.appendChild(buttonsFlex); + buttonsFlex.appendChild(deleteButton); + deleteButton.appendChild(deleteButtonText); + deleteButton.appendChild(trashcanSVGContainer); + trashcanSVGContainer.appendChild(trashcanSVGPath); + buttonsFlex.appendChild(readButton); + readButton.appendChild(readButtonText); + readButton.appendChild(checkboxSVGContainer); + checkboxSVGContainer.appendChild(checkboxSVGPath); +} + +// removes book from array and updates DOM +function removeBookFromLibrary(element) { + // go up a level + const buttonsFlex = element.parentElement; + const cardBody = buttonsFlex.parentElement; + // get the book name + const bookTitle = cardBody.firstChild.innerHTML; + // remove from array + const index = myLibrary.findIndex(item => item.title == bookTitle); + myLibrary.splice(index, 1); + // go up a few more levels + const card = cardBody.parentElement; + const columnContainer = card.parentElement; + // remove card + row.removeChild(columnContainer); +} + +function SVGSwitcheroo (oldpath, container) { + const newSVGPath = document.createElementNS("http://www.w3.org/2000/svg","path"); + if (oldpath.getAttribute('class') == "unchecked") { + newSVGPath.setAttributes({fill: "currentColor", d: "M19,3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5C21,3.89 20.1,3 19,3Z"}); + } + else { + newSVGPath.setAttributes({fill: "currentColor", d: "M19,3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5C21,3.89 20.1,3 19,3M19,5V19H5V5H19Z", class: "unchecked"}); + } + container.removeChild(oldpath); + container.appendChild(newSVGPath); +} + +// flip the checkmark in DOM and update object in array +function changeBookReadStatus(element) { + // DOM manipulation + // find the checkbox + const checkboxSVGContainer = element.lastChild; + const oldSVGPath = checkboxSVGContainer.firstChild; + // svg switcheroo + SVGSwitcheroo(oldSVGPath, checkboxSVGContainer); + // update read status in internal array + // go up a couple elements + const buttonsFlex = element.parentElement; + const cardBody = buttonsFlex.parentElement; + // get the book name + const bookTitle = cardBody.firstChild.innerHTML; + // find it in array + const index = myLibrary.findIndex(item => item.title == bookTitle); + // toggle read boolean + myLibrary[index].read = myLibrary[index].read ? myLibrary[index].read = false : myLibrary[index].read = true; +} + +function makeForm() { + // create our divs + // form body + const newBookForm = document.createElement('form'); + newBookForm.setAttributes({class: "card", id: "newbookform"}); + // close button (SVG) + const closeButton = document.createElement('div'); + closeButton.setAttributes({class: "mb-3", id: "closebutton"}); + const closeButtonContainer = document.createElementNS("http://www.w3.org/2000/svg","svg"); + closeButtonContainer.setAttributes({style: "width:24px;height:24px", "viewBox": "0 0 24 24"}); + const closeButtonSVGPath = document.createElementNS("http://www.w3.org/2000/svg","path"); + closeButtonSVGPath.setAttributes({fill: "#6c757d", d: "M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"}); + // close the form if button is clicked + function closeForm() { + newBookForm.parentElement.removeChild(newBookForm); + } + closeButtonContainer.addEventListener('click', closeForm); + // set properties for close button + // make divs and input fields + // name field + const bookNameDiv = document.createElement('div'); + bookNameDiv.setAttribute('class', "mb-3"); + const bookNameInput = document.createElement('input'); + bookNameInput.setAttributes({type: "text", class: "form-control", value: "Book Name"}); + // author field + const bookAuthorDiv = document.createElement('div'); + bookAuthorDiv.setAttribute('class', "mb-3"); + const bookAuthorInput = document.createElement('input'); + bookAuthorInput.setAttributes({type: "text", class: "form-control", value: "Book Author"}); + // page count field + const pageCountDiv = document.createElement('div'); + pageCountDiv.setAttribute('class', "mb-3"); + const pageCountInput = document.createElement('input'); + pageCountInput.setAttributes({type: "text", class: "form-control", value: "Number of Pages"}); + // quick number validation because not all browsers support number input fields + function numberValidation (e) { + const numericValue = this.value.replace(new RegExp(/[^\d]/,'ig'), ""); + this.value = numericValue; + } + pageCountInput.addEventListener('input', numberValidation); + // comments field + const commentsDiv = document.createElement('div'); + commentsDiv.setAttribute('class', "mb-3"); + const commentsInput = document.createElement('textarea'); + commentsInput.setAttributes({rows: "3", class: "form-control"}); + commentsInput.textContent = "Additional Comments"; + // create button container + const buttonsFlex = document.createElement('div'); + buttonsFlex.setAttribute('class', "buttons-flex"); + // read button + const readButton = document.createElement('button'); + const readButtonText = document.createElement('div'); + readButtonText.textContent = "Read?"; + // svg stuff + const checkboxSVGContainer = document.createElementNS("http://www.w3.org/2000/svg","svg"); + checkboxSVGContainer.setAttributes({style: "width:24px;height:24px", "viewBox": "0 0 24 24"}); + const checkboxSVGPath = document.createElementNS("http://www.w3.org/2000/svg","path"); + checkboxSVGPath.setAttributes({fill: "currentColor", d: "M19,3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5C21,3.89 20.1,3 19,3M19,5V19H5V5H19Z", class: "unchecked"}); + // assign read button to switch SVGs based on click + readButton.setAttribute('class', "read-button"); + readButton.addEventListener('click', function(event) { + event.preventDefault(); + SVGSwitcheroo(readButton.lastChild.firstChild, readButton.lastChild); + }); + // eye candy + // book name + bookNameInput.onfocus = function () { + bookNameInput.setAttributes({value: "", style: "color: black !important"}); + } + bookNameInput.onblur = function () { + bookNameInput.setAttributes({value: "Book Name", style: "color: #6c757d !important;"}); + } + // book author + bookAuthorInput.onfocus = function () { + bookAuthorInput.setAttributes({value: "", style: "color: black !important"}); + } + bookAuthorInput.onblur = function () { + bookAuthorInput.setAttributes({value: "Book Author", style: "color: #6c757d !important;"}); + } + // pages + pageCountInput.onfocus = function () { + pageCountInput.setAttributes({value: "", style: "color: black !important"}); + } + pageCountInput.onblur = function () { + pageCountInput.setAttributes({value: "Number of Pages", style: "color: #6c757d !important;"}); + } + // comments box + commentsInput.onfocus = function () { + commentsInput.textContent = ""; + commentsInput.setAttribute('style', "color: black !important"); + } + commentsInput.onblur = function () { + commentsInput.textContent = "Additional Comments"; + commentsInput.setAttribute('style', "color: #6c757d !important;"); + } + // submit button + const submitButton = document.createElement('button'); + submitButton.setAttributes({type: "submit", class: "btn btn-primary"}); + submitButton.textContent = "Submit"; + // prevent default and make new Book with submitted values, then close form + submitButton.addEventListener('click', function(event) { + event.preventDefault(); + // some form validation + if (bookNameInput.value === "" || bookNameInput.value === "Book Name") { + alert("Please enter a book name"); + } + if (bookAuthorInput.value === "" || bookAuthorInput.value === "Book Author") { + alert("Please enter an author"); + } + if (pageCountInput.value === "" || pageCountInput.value === "Number of Pages") { + alert("Please enter a page count"); + } + + else { + if (commentsInput.value === "" || commentsInput.value === "Additional Comments") { + if (readButton.lastChild.firstChild.getAttribute("class") === "unchecked") { + const book = new Book(bookAuthorInput.value, bookNameInput.value, pageCountInput.value, false, ""); + addBookToLibrary(book, false); + } + else { + const book = new Book(bookAuthorInput.value, bookNameInput.value, pageCountInput.value, true, ""); + addBookToLibrary(book, true); + } + } + else { + if (readButton.lastChild.firstChild.getAttribute("class") === "unchecked") { + const book = new Book(bookAuthorInput.value, bookNameInput.value, pageCountInput.value, false, commentsInput.value); + addBookToLibrary(book, false); + } + else { + const book = new Book(bookAuthorInput.value, bookNameInput.value, pageCountInput.value, true, commentsInput.value); + addBookToLibrary(book, true); + } + } + closeForm(); + } + }); + //append to DOM + const pageBody = row.parentElement; + pageBody.insertBefore(newBookForm, row); + newBookForm.appendChild(closeButton); + closeButton.appendChild(closeButtonContainer); + closeButtonContainer.appendChild(closeButtonSVGPath); + newBookForm.appendChild(bookNameDiv); + bookNameDiv.appendChild(bookNameInput); + newBookForm.appendChild(bookAuthorDiv); + bookAuthorDiv.appendChild(bookAuthorInput); + newBookForm.appendChild(pageCountDiv); + pageCountDiv.appendChild(pageCountInput); + newBookForm.appendChild(commentsDiv); + commentsDiv.appendChild(commentsInput); + newBookForm.appendChild(buttonsFlex); + buttonsFlex.appendChild(readButton); + readButton.appendChild(readButtonText); + readButton.appendChild(checkboxSVGContainer); + checkboxSVGContainer.appendChild(checkboxSVGPath); + buttonsFlex.appendChild(submitButton); +} + +// calls makeForm() to bring up form for user input of new book +newButton.addEventListener('click', makeForm); \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..68674c4 --- /dev/null +++ b/index.html @@ -0,0 +1,30 @@ + + + + + + + + + + +Title + + +
+ + + Books + + +
+
+
+ + + + + \ No newline at end of file