diff --git a/books.json b/books.json new file mode 100644 index 0000000..0a56813 --- /dev/null +++ b/books.json @@ -0,0 +1,91 @@ +[ + { + "title": "The Great Gatsby", + "author": "F. Scott Fitzgerald", + "image": "images/gatsby.png", + "genre": "Tragedy", + "year": 1925, + "language": "English" + }, + { + "title": "To Kill a Mockingbird", + "author": "Harper Lee", + "image": "images/to-kill-a-mockingbird.png", + "genre": "Southern Gothic", + "year": 1960, + "language": "English" + }, + { + "title": "1984", + "author": "George Orwell", + "image": "images/1984.png", + "genre": "Dystopian", + "year": 1949, + "language": "English" + }, + { + "title": "Moby Dick", + "author": "Herman Melville", + "image": "images/moby-dick.png", + "genre": "Adventure", + "year": 1851, + "language": "English" + }, + { + "title": "The Catcher in the Rye", + "author": "J.D. Salinger", + "image": "images/catcher.png", + "genre": "Realistic", + "year": 1951, + "language": "English" + }, + { + "title": "Pride and Prejudice", + "author": "Jane Austen", + "image": "images/pride-and-prejudice.png", + "genre": "Classic Regency", + "year": 1813, + "language": "English" + }, + { + "title": "Hiroshima", + "author": "John Hersey", + "image": "images/hiroshima.jpg", + "genre": "Historic", + "year": 1946, + "language": "English" + }, + { + "title": "The Hobbit", + "author": "J.R.R. Tolkien", + "image": "images/hobbit.png", + "genre": "Fantasy", + "year": 1937, + "language": "English" + }, + { + "title": "Fahrenheit 451", + "author": "Ray Bradbury", + "image": "images/fahrenheit.png", + "genre": "Science Fiction", + "year": 1953, + "language": "English" + }, + { + "title": "Енеїда", + "author": "Іван Котляревський", + "image": "images/eneyida.jpeg", + "genre": "Burlesque", + "year": 1798, + "language": "Українська" + }, + { + "title": "Кайдашева Сім'я", + "author": "Іван Нечуй-Левицький", + "image": "images/kaydasheva-simya.jpg", + "genre": "Social", + "year": 1879, + "language": "Українська" + } + +] \ No newline at end of file diff --git a/favorites.html b/favorites.html new file mode 100644 index 0000000..998f65a --- /dev/null +++ b/favorites.html @@ -0,0 +1,80 @@ + + + + + + Online Book Library + + + + + + + + +
+
+

Online Library

+

ONLINE LIBRARY

+
+ + +
+
+ +
+

Online Book Library

+
+ + +
+
+ + +
+ + +
+
+ + + \ No newline at end of file diff --git a/images/1984.png b/images/1984.png new file mode 100644 index 0000000..1696ad6 Binary files /dev/null and b/images/1984.png differ diff --git a/images/arrow.svg b/images/arrow.svg new file mode 100644 index 0000000..eaa659d --- /dev/null +++ b/images/arrow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/catcher.png b/images/catcher.png new file mode 100644 index 0000000..db61ded Binary files /dev/null and b/images/catcher.png differ diff --git a/images/eneyida.jpeg b/images/eneyida.jpeg new file mode 100644 index 0000000..fdc0a55 Binary files /dev/null and b/images/eneyida.jpeg differ diff --git a/images/fahrenheit.png b/images/fahrenheit.png new file mode 100644 index 0000000..3f2a3ae Binary files /dev/null and b/images/fahrenheit.png differ diff --git a/images/gatsby.png b/images/gatsby.png new file mode 100644 index 0000000..a9e09bd Binary files /dev/null and b/images/gatsby.png differ diff --git a/images/hiroshima.jpg b/images/hiroshima.jpg new file mode 100644 index 0000000..d8ad2af Binary files /dev/null and b/images/hiroshima.jpg differ diff --git a/images/hobbit.png b/images/hobbit.png new file mode 100644 index 0000000..7d2d083 Binary files /dev/null and b/images/hobbit.png differ diff --git a/images/kaydasheva-simya.jpg b/images/kaydasheva-simya.jpg new file mode 100644 index 0000000..15e48e7 Binary files /dev/null and b/images/kaydasheva-simya.jpg differ diff --git a/images/moby-dick.png b/images/moby-dick.png new file mode 100644 index 0000000..83db099 Binary files /dev/null and b/images/moby-dick.png differ diff --git a/images/pride-and-prejudice.png b/images/pride-and-prejudice.png new file mode 100644 index 0000000..15867d2 Binary files /dev/null and b/images/pride-and-prejudice.png differ diff --git a/images/sapiens.png b/images/sapiens.png new file mode 100644 index 0000000..cd8f60f Binary files /dev/null and b/images/sapiens.png differ diff --git a/images/to-kill-a-mockingbird.png b/images/to-kill-a-mockingbird.png new file mode 100644 index 0000000..10eb060 Binary files /dev/null and b/images/to-kill-a-mockingbird.png differ diff --git a/index.html b/index.html index 09c6dc0..fd2936c 100644 --- a/index.html +++ b/index.html @@ -3,11 +3,83 @@ - Simple HTML Page + Online Book Library + + + + +
+
+

Online Library

+

ONLINE LIBRARY

+
+ + +
+
+ +
+

Online Book Library

+
+ + +
+ +
+ + +
+ + +
+
+ \ No newline at end of file diff --git a/src/main.js b/src/main.js index e69de29..11ef32f 100644 --- a/src/main.js +++ b/src/main.js @@ -0,0 +1,292 @@ +let allBooks = []; +let originalBookOrder = []; +let currentSort = "default"; +const isFavoritesPage = window.location.pathname.includes("favorites.html"); + +document.addEventListener("DOMContentLoaded", () => { + fetch('books.json') + .then(response => response.json()) + .then(books => { + const savedBooks = JSON.parse(localStorage.getItem("books")); + allBooks = savedBooks || books; + allBooks.forEach(book => { + if (book.favorited === undefined) book.favorited = false; + }); + renderBooks(allBooks); + setupModal(); + if (!isFavoritesPage) { + generateFilters(allBooks); + addListeners(); + addMobileListener(); + } + if (isFavoritesPage) { + document.querySelector(".filter-panel")?.classList.add("hidden"); + document.querySelector(".filter-button")?.classList.add("hidden"); + document.querySelector(".sort-button")?.classList.add("hidden"); + document.querySelector(".dropdown")?.classList.add("hidden"); + } + + + }) + .catch(error => console.error("Error loading books:", error)); +}); + +function renderBooks(books) { + const bookList = document.getElementById("book-list"); + bookList.innerHTML = ""; + + const cards = books.map(book => { + const bookCard = document.createElement("div"); + bookCard.className = "book-card"; + bookCard.dataset.title = book.title; + bookCard.dataset.genre = book.genre; + bookCard.dataset.year = book.year; + bookCard.dataset.language = book.language; + bookCard.dataset.author = book.author; + + const img = document.createElement("img"); + img.className = "cover"; + img.src = book.image; + img.alt = book.title; + + const info = document.createElement("span"); + info.className = "book-info"; + + const title = document.createElement("h3"); + title.className = "title"; + title.textContent = book.title; + + const author = document.createElement("p"); + author.className = "author"; + author.textContent = book.author; + + info.appendChild(title); + info.appendChild(author); + bookCard.appendChild(img); + bookCard.appendChild(info); + + if (!isFavoritesPage) { + originalBookOrder.push(bookCard); + } + + if (isFavoritesPage && !book.favorited) { + bookCard.classList.add("invisible"); + } + + return {book, card: bookCard}; + }); + + const sortedCards = isFavoritesPage ? cards.filter(c => c.book.favorited).concat(cards.filter(c => !c.book.favorited)) : cards; + + sortedCards.forEach(obj => { + const card = obj.card; + bookList.appendChild(card); + }); +} + +function addFiltersToSection(name, items) { + const section = document.getElementById(`${name}-section`); + if (!section) return; + + items.forEach(item => { + const label = document.createElement("label"); + + const input = document.createElement("input"); + input.className = "checkbox"; + input.type = "checkbox"; + input.name = name; + input.value = item; + + label.appendChild(input); + label.append(` ${item}`); + section.appendChild(label); + }); +} + +function generateFilters(books) { + const genreList = []; + const yearList = []; + const languageList = []; + + books.forEach(book => { + if (!genreList.includes(book.genre)) { + genreList.push(book.genre); + } + if (!yearList.includes(book.year)) { + yearList.push(book.year); + } + if (!languageList.includes(book.language)) { + languageList.push(book.language) + } + }); + + addFiltersToSection("genre", genreList.sort()); + addFiltersToSection("year", yearList.sort((a,b) => a-b)); + addFiltersToSection("language", languageList.sort()); +} + +function getSelectedValues(name) { + return Array.from(document.querySelectorAll(`input[name="${name}"]:checked`)).map(checkbox => checkbox.value); +} + +function addListeners() { + const checkboxes = document.querySelectorAll(".checkbox"); + + checkboxes.forEach(checkbox => { + checkbox.addEventListener("change", applyFilters); + }); + + const authorField = document.querySelector(".author-field"); + authorField.addEventListener("input", applyFilters); + + const sortingDropdowns = document.querySelectorAll(".dropdown"); + sortingDropdowns.forEach(dropdown => { + dropdown.addEventListener("change", (e) => { + currentSort = e.target.value; + sortingDropdowns.forEach(dropdown => { + if (dropdown !== e.target) dropdown.value = currentSort; + }); + + applyFilters(); + }); + }); +} + +function sortCards(cards, sortType) { + if (sortType === "default") { + return originalBookOrder.filter(card => cards.includes(card)); + } + + return [...cards].sort((a, b) => { + if (sortType === "date-newest") { + return parseInt(b.dataset.year) - parseInt(a.dataset.year); + } + else if (sortType === "date-oldest") { + return parseInt(a.dataset.year) - parseInt(b.dataset.year); + } + else if (sortType === "genre") { + return a.dataset.genre.localeCompare(b.dataset.genre); + } + }); +} + +function applyFilters() { + const authorField = document.querySelector(".author-field"); + const selectedGenres = getSelectedValues("genre"); + const selectedYears = getSelectedValues("year"); + const selectedLanguages = getSelectedValues("language"); + const selectedAuthor = authorField.value; + const bookList = document.getElementById("book-list"); + const allCards = Array.from(document.querySelectorAll(".book-card")); + const matchedCards = []; + const unmatchedCards = []; + + const noFilters = + selectedGenres.length === 0 && selectedYears.length === 0 && selectedLanguages.length === 0 && selectedAuthor.length === 0; + + if (noFilters) { + const sortedCards = sortCards(originalBookOrder, currentSort); + sortedCards.forEach(card => { + bookList.appendChild(card); + card.classList.remove("invisible"); + }); + return; + } + + allCards.forEach(card => { + const genre = card.dataset.genre; + const year = card.dataset.year; + const language = card.dataset.language; + const author = card.dataset.author; + + const matchedGenre = selectedGenres.length === 0 || selectedGenres.includes(genre); + const matchedYear = selectedYears.length === 0 || selectedYears.includes(year); + const matchedLanguage = selectedLanguages.length === 0 || selectedLanguages.includes(language); + const matchedAuthor = selectedAuthor.length === 0 || author.toLowerCase().includes(selectedAuthor.toLowerCase()); + + const isMatch = matchedGenre && matchedYear && matchedLanguage && matchedAuthor; + isMatch ? matchedCards.push(card) : unmatchedCards.push(card); + }); + + const sortedMatchedCards = sortCards(matchedCards, currentSort); + + const reordered = [...sortedMatchedCards, ...unmatchedCards]; + reordered.forEach(card => bookList.appendChild(card)); + sortedMatchedCards.forEach(card => card.classList.remove("invisible")); + unmatchedCards.forEach(card => card.classList.add("invisible")); +} + +function setupModal() { + const modalOverlay = document.getElementById("modal-overlay"); + const modalContent = document.getElementById("modal-content"); + const closeButton = document.getElementById("modal-close"); + + document.querySelectorAll(".book-card").forEach((card) => { + card.addEventListener("click", () => { + const title = card.dataset.title; + const book = allBooks.find(book => book.title === title); + if (book) { + showModal(book); + } + }); + }); + + closeButton.addEventListener("click", () => { + modalOverlay.classList.add("hidden"); + document.body.classList.remove("modal-open"); + }); + + modalOverlay.addEventListener("click", e => { + if (e.target === modalOverlay) { + modalOverlay.classList.add("hidden"); + document.body.classList.remove("modal-open"); + } + }); +} +function showModal(book) { + const modalOverlay = document.getElementById("modal-overlay"); + const modalContent = document.getElementById("modal-content"); + + modalContent.innerHTML = ` +

${book.title}

+

Author: ${book.author}

+

Genre: ${book.genre}

+

Year: ${book.year}

+

Language: ${book.language}

+ ${book.title} + `; + + const favoriteBtn = document.getElementById("favorite-btn"); + updateFavoriteBtnStyle(favoriteBtn, book.favorited); + + favoriteBtn.addEventListener("click", () => { + book.favorited = !book.favorited; + updateFavoriteBtnStyle(favoriteBtn, book.favorited); + favoriteBtn.textContent = book.favorited ? "Remove from Favorites" : "Add to Favorites"; + + localStorage.setItem("books", JSON.stringify(allBooks)); + if (isFavoritesPage && !book.favorited) { + const card = [...document.querySelectorAll(".book-card")].find(c => c.dataset.title === book.title); + if (card) card.classList.add("invisible"); + modalOverlay.classList.add("hidden"); + document.body.classList.remove("modal-open"); + location.reload(); + } + }); + + modalOverlay.classList.remove("hidden"); + document.body.classList.add("modal-open"); +} + +function updateFavoriteBtnStyle(btn, isFavorited) { + btn.style.backgroundColor = isFavorited ? "#f9f9f9" : "#395081"; + btn.style.color =isFavorited ? "black" : "white" +} +function addMobileListener() { + const filterPanel = document.querySelector(".filter-panel"); + const filterButton = document.querySelector(".filter-button"); + filterButton.addEventListener("click", () => { + filterPanel.classList.toggle("desktop-display"); + }) + +} \ No newline at end of file diff --git a/style/style.css b/style/style.css index e69de29..0274bd0 100644 --- a/style/style.css +++ b/style/style.css @@ -0,0 +1,475 @@ +body { + font-family: "Inter", sans-serif; + margin: 0; +} +.header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 1rem 1rem 1rem 1.5rem; + border-bottom: 2px solid lightgray; +} +.header-title { + font-weight: 600; +} +.header-actions { + display: flex; + justify-content: space-between; + align-items: center; + min-width: 28%; + margin-right: 17%; + padding-left: 5%; +} +.filter-button, .sort-button { + background-color: white; + border-radius: 0.4rem; + padding-left: 12%; + padding-right: 12%; + padding-top: 8%; + padding-bottom: 8%; + margin-right: 8%; + font-size: 1.1rem; + font-family: 'Inter', sans-serif; + border: 1px solid #8c8c8c; +} + .sort-button { + display: flex; + justify-content: center; + margin-right: 1rem; + appearance: none; + background-color: white; + text-align: center; + } +.featured-books { + padding-left: 1.5rem; +} +.featured-heading { + font-size: 1.9rem; + font-weight: 600; + + margin-bottom: 6%; +} +.mobile-normal-featured-header { + display: flex; + justify-content: space-between; + align-items: center; +} +.favorites-page-button, .back-to-main-button { + background-color: white; + border-radius: 0.4rem; + margin-right: 4%; + padding-left: 4%; + padding-right: 4%; + padding-top: 2%; + padding-bottom: 2%; + margin-right: 1rem; + font-size: 1.1rem; + font-family: 'Inter', sans-serif; + border: 1px solid #8c8c8c; +} +.back-to-main-button { + font-size: 1rem; +} +.book-card { + display: flex; + align-items: center; + padding-bottom: 1rem; +} +.book-card.invisible { + visibility: hidden; + pointer-events: none; + opacity: 0; +} +.book-info { + margin-left: 5%; + margin-bottom: 9%; +} +.title { + font-size: 1.4rem; + font-weight: 600; +} +.author { + font-size: 1.2rem; + margin-top: -10px; +} +.cover { + max-width: 150px; + border-radius: 0.5rem; +} +.mobile-display { + display: auto; +} +.mobile-or-normal-display { + display: auto; +} +.normal-display { + display: none; +} + +.bold-para { + font-weight: bold; +} + +.modal-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.6); + display: flex; + align-items: center; + justify-content: center; + z-index: 9999; +} + +.modal { + background: white; + padding: 2rem; + max-width: 400px; + width: 90%; + max-height: 100%; + height: 95%; + overflow-y: auto; + border-radius: 8px; + position: relative; + box-sizing: border-box; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.modal-close { + position: absolute; + top: 8px; + right: 8px; + background: transparent; + border: none; + font-size: 1.5rem; + cursor: pointer; +} +.modal-content { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} +.modal-content img { + aspect-ratio: 3 / 4; + width: 230px; +} + +.favorite-button { + margin-top: 1.7rem; + color: white; + border: none; + padding: 0.5rem 1rem; + border-radius: 4px; + cursor: pointer; + font-family: "Inter", sans-serif; + font-weight: 500; + font-size: 1rem; + height: 6%; + width: auto; + +} +.hidden { + display: none; +} + +body.modal-open { + overflow: hidden; +} + + +.filter-panel { + width: 100%; + display: flex; + flex-direction: column; + background-color: #f9f9f9; + border: solid 1px #e1e4eb; + padding: 1rem; + border-radius: 0.4rem; + font-size: 0.95rem; + } + .filter-panel h2 { + font-weight: 500; + margin: 0; + } + .filter-group h3 { + margin-bottom: 0.5rem; + font-size: 1.1rem; + font-weight: 600; + } + .checkbox { + appearance: none; + width: 1rem; + height: 1rem; + border: solid 1px #e1e4eb; + border-radius: 0.3rem; + background-color: white; + cursor: pointer; + vertical-align: middle; + position: relative; + margin-bottom: 7px; + } + + .checkbox:checked { + background-color: #243664; + } + .checkbox:checked::after { + content: ""; + position: absolute; + top: 0.16rem; + left: 0.32rem; + width: 0.13rem; + height: 0.4rem; + border: solid white; + border-width: 0 0.13rem 0.13rem 0; + transform: rotate(45deg); + +} + .filter-group label { + display: block; + margin-bottom: 0.5rem; + } + .author-field { + width: 90.5%; + font-family: "Inter", sans-serif; + font-size: 1rem; + border-radius: 0.3rem; + padding: 0.4rem; + border: solid 1px #e1e4eb; + color: #243664; + } + .author-field::placeholder { + color: #243664; + } + .desktop-display { + display: none; +} + + + +@media (min-width: 431px) { + body { + background-color: #f8f6f2; + margin: 0 + + } + .container { + background-color: white; + max-width: 80%; + margin-left: auto; + margin-right: auto; + margin-top: 10%; + margin-bottom: 10%; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.05); + border-radius: 0.75rem; + border: 1px solid #e4e1de; + } + .header-title { + font-size: 1.4rem; + } + .header-actions { + margin-right: 0%; + padding-left: 0%; + } + .header { + border-bottom: 1px solid #e0deda; + margin-left: 1.5rem; + margin-right: 1.5rem; + padding-left: 0; + padding-right: 0; + } + .sort-button, .filter-button { + border: none; + padding-left: 0; + padding-right: 0; + } + .sort-button::after { + content: "⏷"; + font-size: 1rem; + position: relative; + left: 1px; + top: -2px; + } + .filter-panel { + width: 93%; + } + .featured-heading { + font-size: 2rem; + } + .books { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); + gap: 1.5rem; + padding: 0 1.5rem 0 0rem; + } + + .book-card { + display: flex; + flex-direction: column; + align-items: stretch; + width: 100%; + } + + .cover { + width: 100%; + aspect-ratio: 3 / 4; + object-fit: cover; + border-radius: 0.3rem; + display: block; + } + .book-info { + margin-left: 0%; + margin-bottom: 0%; + width: 100%; + } + .title { + font-size: 1.2rem; + font-weight: 600; + } + .author { + font-size: 1rem; + } + + .mobile-display { + display: none; + } + .desktop-display { + display: none; + } + .normal-display { + display: flex; + } + .mobile-or-normal-display { + display: auto; + } +} +@media (min-width: 1023px) { + .desktop-display { + display: flex; + } + .mobile-display { + display: none; + } + .normal-display { + display: none; + } + .mobile-or-normal-display { + display: none; + } + body { + background-color: white; + } + .container { + background-color: white; + max-width: 100%; + height: 100%; + margin-left: auto; + margin-right: auto; + margin-top: 0; + margin-bottom: 0; + box-shadow: none; + border-radius: none; + border: none; + } + + .header-title { + font-size: 2rem; + } + + .header { + border-bottom: 1px solid #e0deda; + margin: 0 0 1.5rem 0; + padding-left: 2rem; + padding-right: 2rem; + padding-top: 0; + padding-bottom: 0; + background-color: #fcfcfd; + } + + .sign-in-button { + font-family: 'Inter', sans-serif; + color: white; + background-color: #395081; + width: 6rem; + height: 2rem; + border-radius: 0.3rem; + border: none; + font-weight: 600; + font-size: 0.85rem; + } + .favorites-page-button, .back-to-main-button { + font-family: 'Inter', sans-serif; + color: black; + background-color: #dbdbdb; + width: 6rem; + height: 2rem; + border-radius: 0.3rem; + border: none; + font-weight: 600; + font-size: 0.85rem; + margin: 0; + padding: 0; + } + .featured-heading { + font-size: 1.6rem; + + } + .featured-header { + display: flex; + justify-content: space-between; + align-items: center; + padding-bottom: 1.3rem; + } + .featured-header h2 { + margin: 0; + } + .dropdown { + font-family: "Inter", sans-serif; + font-size: 1rem; + height: 2.5rem; + border-radius: 0.3rem; + padding-right: 3rem; + padding-left: 1rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + border: solid 1px #e1e4eb; + margin-right: 0.5rem; + + appearance: none; + background-color: white; + + background-image: url("images/arrow.svg"); + background-repeat: no-repeat; + background-position: right 0.2rem center; + background-size: 2rem; + } + + + .main-layout { + display: flex; + flex-direction: row; + gap: 2rem; + padding: 1.5rem; + align-items: flex-start; + } + .filter-panel { + width: 150px; + } + .featured-books { + flex: 1; + } + body.modal-open { + padding-right: 16px; + } + .hidden { + display: none; +} +} + + +