From 6e1a5d2ea010527506baed6ae5c45c1a445e6375 Mon Sep 17 00:00:00 2001 From: FiratUsta <67150276+FiratUsta@users.noreply.github.com> Date: Fri, 20 Sep 2024 14:29:00 +0300 Subject: [PATCH] Home page improvements (#1940) * Add feautre group header fragment for homepage. * Add feature group headers to feature groups. * Style feature groups. * Add collapsing/expanding functionality as well as a favorites section. * Cards are now sorted in the order of update link > favorite > alphabetical on the homepage. * Decrease space between section title and cards. * Add filtering buttons and view options to homepage. * Hide list view button in preparation for release. --------- Co-authored-by: FiratUsta --- src/main/resources/static/css/home.css | 63 ++- src/main/resources/static/js/homecard.js | 152 +++++- .../fragments/featureGroupHeader.html | 6 + src/main/resources/templates/home.html | 443 ++++++++++-------- 4 files changed, 456 insertions(+), 208 deletions(-) create mode 100644 src/main/resources/templates/fragments/featureGroupHeader.html diff --git a/src/main/resources/static/css/home.css b/src/main/resources/static/css/home.css index 07a1350f..782e336d 100644 --- a/src/main/resources/static/css/home.css +++ b/src/main/resources/static/css/home.css @@ -10,6 +10,26 @@ outline-color: var(--md-sys-color-outline-variant); } +#filtersContainer { + display: flex; + width: 100%; + align-items: center; + justify-content: center; + gap: 10px; +} + +.filter-button { + color: var(--md-sys-color-secondary); + user-select: none; + cursor: pointer; + transition: transform 0.3s; + transform-origin: center center; +} + +.filter-button:hover { + transform: scale(1.08); +} + .search-icon { position: absolute; margin: 0.75rem 1rem; @@ -17,9 +37,50 @@ } .features-container { + display: flex; + flex-direction: column; + gap: 30px; +} + +.feature-group { + display: flex; + flex-direction: column; +} + +.feature-group-header { + display: flex; + align-items: center; + justify-content: flex-start; + color: var(--md-sys-color-on-surface); + margin-bottom: 15px; + user-select: none; + cursor: pointer; + gap: 10px; +} + +.feature-group-container { display: grid; grid-template-columns: repeat(auto-fill, minmax(15rem, 3fr)); gap: 30px 30px; + transition: 0.5s all; + overflow: hidden; + margin: -20px; + padding: 20px; +} + +.feature-group.collapsed>.feature-group-container { + max-height: 0 !important; + margin: 0; + padding: 0; +} + +.header-expand-button { + transition: 0.5s all; + transform: rotate(90deg); +} + +.header-expand-button.collapsed { + transform: rotate(0deg); } .feature-card { @@ -151,5 +212,5 @@ } .hidden { - visibility: hidden; + visibility: hidden; } diff --git a/src/main/resources/static/js/homecard.js b/src/main/resources/static/js/homecard.js index 818e62e9..4cdecdb7 100644 --- a/src/main/resources/static/js/homecard.js +++ b/src/main/resources/static/js/homecard.js @@ -22,6 +22,26 @@ function filterCards() { } } +function updateFavoritesSection() { + const favoritesContainer = document.getElementById("groupFavorites").querySelector(".feature-group-container"); + favoritesContainer.innerHTML = ""; + const cards = Array.from(document.querySelectorAll(".feature-card")); + let favoritesAmount = 0; + cards.forEach(card => { + if (localStorage.getItem(card.id) === "favorite") { + const duplicate = card.cloneNode(true); + favoritesContainer.appendChild(duplicate); + favoritesAmount++; + } + }); + if (favoritesAmount === 0) { + document.getElementById("groupFavorites").style.display = "none"; + } else { + document.getElementById("groupFavorites").style.display = "flex"; + }; + reorderCards(favoritesContainer); +}; + function toggleFavorite(element) { var span = element.querySelector("span.material-symbols-rounded"); var card = element.closest(".feature-card"); @@ -37,15 +57,17 @@ function toggleFavorite(element) { card.classList.remove("favorite"); localStorage.removeItem(cardId); } - reorderCards(); + reorderCards(card.parentNode); + updateFavoritesSection(); updateFavoritesDropdown(); filterCards(); } - -function reorderCards() { - var container = document.querySelector(".features-container"); - var cards = Array.from(container.getElementsByClassName("feature-card")); +function reorderCards(container) { + var cards = Array.from(container.querySelectorAll(".feature-card")); + cards.forEach(function (card) { + container.removeChild(card); + }); cards.sort(function (a, b) { var aIsFavorite = localStorage.getItem(a.id) === "favorite"; var bIsFavorite = localStorage.getItem(b.id) === "favorite"; @@ -55,19 +77,29 @@ function reorderCards() { if (b.id === "update-link") { return 1; } + if (aIsFavorite && !bIsFavorite) { return -1; } - if (!aIsFavorite && bIsFavorite) { + else if (!aIsFavorite && bIsFavorite) { return 1; } - return 0; + else { + return a.id > b.id; + } }); cards.forEach(function (card) { container.appendChild(card); }); } +function reorderAllCards() { + const containers = Array.from(document.querySelectorAll(".feature-group-container")); + containers.forEach(function (container) { + reorderCards(container); + }) +} + function initializeCards() { var cards = document.querySelectorAll(".feature-card"); cards.forEach(function (card) { @@ -79,21 +111,107 @@ function initializeCards() { card.classList.add("favorite"); } }); - reorderCards(); + reorderAllCards(); + updateFavoritesSection(); updateFavoritesDropdown(); filterCards(); } +function showFavoritesOnly() { + const groups = Array.from(document.querySelectorAll(".feature-group")); + if (localStorage.getItem("favoritesOnly") === "true") { + groups.forEach((group) => { + if (group.id !== "groupFavorites") { + group.style.display = "none"; + }; + }) + } else { + groups.forEach((group) => { + if (group.id !== "groupFavorites") { + group.style.display = "flex"; + }; + }) + }; +} + +function toggleFavoritesOnly() { + if (localStorage.getItem("favoritesOnly") === "true") { + localStorage.setItem("favoritesOnly", "false"); + } else { + localStorage.setItem("favoritesOnly", "true"); + } + showFavoritesOnly(); +} + +// Expands a feature group on true, collapses it on false and toggles state on null. +function expandCollapseToggle(group, expand = null) { + if (expand === null) { + group.classList.toggle("collapsed"); + group.querySelector(".header-expand-button").classList.toggle("collapsed"); + } else if (expand) { + group.classList.remove("collapsed"); + group.querySelector(".header-expand-button").classList.remove("collapsed"); + } else { + group.classList.add("collapsed"); + group.querySelector(".header-expand-button").classList.add("collapsed"); + } + + const collapsed = localStorage.getItem("collapsedGroups") ? JSON.parse(localStorage.getItem("collapsedGroups")) : []; + const groupIndex = collapsed.indexOf(group.id); + + if (group.classList.contains("collapsed")) { + if (groupIndex === -1) { + collapsed.push(group.id); + } + } else { + if (groupIndex !== -1) { + collapsed.splice(groupIndex, 1); + } + } + + localStorage.setItem("collapsedGroups", JSON.stringify(collapsed)); +} + +function expandCollapseAll(expandAll) { + const groups = Array.from(document.querySelectorAll(".feature-group")); + groups.forEach((group) => { + expandCollapseToggle(group, expandAll); + }) +} + window.onload = initializeCards; - document.addEventListener("DOMContentLoaded", function() { - const materialIcons = new FontFaceObserver('Material Symbols Rounded'); - - materialIcons.load().then(() => { - document.querySelectorAll('.feature-card.hidden').forEach(el => { - el.classList.remove('hidden'); - }); - }).catch(() => { - console.error('Material Symbols Rounded font failed to load.'); +document.addEventListener("DOMContentLoaded", function () { + const materialIcons = new FontFaceObserver('Material Symbols Rounded'); + + materialIcons.load().then(() => { + document.querySelectorAll('.feature-card.hidden').forEach(el => { + el.classList.remove('hidden'); }); + }).catch(() => { + console.error('Material Symbols Rounded font failed to load.'); }); + + Array.from(document.querySelectorAll(".feature-group-header")).forEach(header => { + const parent = header.parentNode; + const container = header.parentNode.querySelector(".feature-group-container"); + if (parent.id !== "groupFavorites") { + container.style.maxHeight = container.clientHeight + "px"; + } else { + container.style.maxHeight = "500px"; + } + header.onclick = () => { + expandCollapseToggle(parent); + }; + }) + + const collapsed = localStorage.getItem("collapsedGroups") ? JSON.parse(localStorage.getItem("collapsedGroups")) : []; + + Array.from(document.querySelectorAll(".feature-group")).forEach(group => { + if (collapsed.indexOf(group.id) !== -1) { + expandCollapseToggle(group, false); + } + }) + + showFavoritesOnly(); +}); diff --git a/src/main/resources/templates/fragments/featureGroupHeader.html b/src/main/resources/templates/fragments/featureGroupHeader.html new file mode 100644 index 00000000..0a8f7e9b --- /dev/null +++ b/src/main/resources/templates/fragments/featureGroupHeader.html @@ -0,0 +1,6 @@ +
+ + + chevron_right + +
\ No newline at end of file diff --git a/src/main/resources/templates/home.html b/src/main/resources/templates/home.html index 30c295a5..f8f85440 100644 --- a/src/main/resources/templates/home.html +++ b/src/main/resources/templates/home.html @@ -28,9 +28,26 @@ search + +
+ + star + + + expand_all + + + collapse_all + + +
+
-