From 4ba9f04c15c25ad52ee918704d4eb85d984bcc6d Mon Sep 17 00:00:00 2001 From: Reece Browne Date: Mon, 30 Dec 2024 23:18:45 +0000 Subject: [PATCH 01/20] Fix file drag and drop --- .../static/js/multitool/PdfContainer.js | 15 ++++++ .../static/js/multitool/fileInput.js | 48 +++++++++---------- src/main/resources/templates/multi-tool.html | 2 +- 3 files changed, 40 insertions(+), 25 deletions(-) diff --git a/src/main/resources/static/js/multitool/PdfContainer.js b/src/main/resources/static/js/multitool/PdfContainer.js index c060203d1b0..61548200009 100644 --- a/src/main/resources/static/js/multitool/PdfContainer.js +++ b/src/main/resources/static/js/multitool/PdfContainer.js @@ -168,6 +168,21 @@ class PdfContainer { input.click(); }); } + + async handleDroppedFiles(files, nextSiblingElement = null) { + if (files.length > 0) { + const pages = await this.addFilesFromFiles(files, nextSiblingElement, []); + this.updateFilename(files[0]?.name || 'untitled'); + + const selectAll = document.getElementById('select-pages-container'); + if (selectAll) { + selectAll.classList.remove('hidden'); + } + + return pages; + } + } + async addFilesFromFiles(files, nextSiblingElement, pages) { this.fileName = files[0].name; for (var i = 0; i < files.length; i++) { diff --git a/src/main/resources/static/js/multitool/fileInput.js b/src/main/resources/static/js/multitool/fileInput.js index 9efa8eb2a9e..ec7fa4c1e3d 100644 --- a/src/main/resources/static/js/multitool/fileInput.js +++ b/src/main/resources/static/js/multitool/fileInput.js @@ -8,7 +8,7 @@ class FileDragManager { this.setCallback(cb); // Prevent default behavior for drag events - ["dragenter", "dragover", "dragleave", "drop"].forEach((eventName) => { + ['dragenter', 'dragover', 'dragleave', 'drop'].forEach((eventName) => { document.body.addEventListener(eventName, preventDefaults, false); }); @@ -21,13 +21,13 @@ class FileDragManager { this.dragleaveListener = this.dragleaveListener.bind(this); this.dropListener = this.dropListener.bind(this); - document.body.addEventListener("dragenter", this.dragenterListener); - document.body.addEventListener("dragleave", this.dragleaveListener); + document.body.addEventListener('dragenter', this.dragenterListener); + document.body.addEventListener('dragleave', this.dragleaveListener); // Add drop event listener - document.body.addEventListener("drop", this.dropListener); + document.body.addEventListener('drop', this.dropListener); } - setActions({ updateFilename }) { + setActions({updateFilename}) { this.updateFilename = updateFilename; } @@ -35,7 +35,7 @@ class FileDragManager { if (cb) { this.callback = cb; } else { - this.callback = (files) => console.warn("FileDragManager not set"); + this.callback = (files) => console.warn('FileDragManager not set'); } } @@ -43,21 +43,21 @@ class FileDragManager { this.dragCounter++; if (!this.overlay) { // Create and show the overlay - this.overlay = document.createElement("div"); - this.overlay.style.position = "fixed"; + this.overlay = document.createElement('div'); + this.overlay.style.position = 'fixed'; this.overlay.style.top = 0; this.overlay.style.left = 0; - this.overlay.style.width = "100%"; - this.overlay.style.height = "100%"; - this.overlay.style.background = "rgba(0, 0, 0, 0.5)"; - this.overlay.style.color = "#fff"; - this.overlay.style.zIndex = "1000"; - this.overlay.style.display = "flex"; - this.overlay.style.alignItems = "center"; - this.overlay.style.justifyContent = "center"; - this.overlay.style.pointerEvents = "none"; - this.overlay.innerHTML = "

Drop files anywhere to upload

"; - document.getElementById("content-wrap").appendChild(this.overlay); + this.overlay.style.width = '100%'; + this.overlay.style.height = '100%'; + this.overlay.style.background = 'rgba(0, 0, 0, 0.5)'; + this.overlay.style.color = '#fff'; + this.overlay.style.zIndex = '1000'; + this.overlay.style.display = 'flex'; + this.overlay.style.alignItems = 'center'; + this.overlay.style.justifyContent = 'center'; + this.overlay.style.pointerEvents = 'none'; + this.overlay.innerHTML = '

Drop files anywhere to upload

'; + document.getElementById('content-wrap').appendChild(this.overlay); } } @@ -87,16 +87,16 @@ class FileDragManager { this.overlay = null; } - this.updateFilename(files ? files[0].name : ""); + this.updateFilename(files ? files[0].name : ''); }); } async addImageFile(file, nextSiblingElement) { - const div = document.createElement("div"); - div.classList.add("page-container"); + const div = document.createElement('div'); + div.classList.add('page-container'); - var img = document.createElement("img"); - img.classList.add("page-image"); + var img = document.createElement('img'); + img.classList.add('page-image'); img.src = URL.createObjectURL(file); div.appendChild(img); diff --git a/src/main/resources/templates/multi-tool.html b/src/main/resources/templates/multi-tool.html index d785c34858e..d45160f27fb 100644 --- a/src/main/resources/templates/multi-tool.html +++ b/src/main/resources/templates/multi-tool.html @@ -216,7 +216,7 @@
-1) { - card.style.display = ""; + card.style.display = ''; groupMatchesFilter = true; } else { - card.style.display = "none"; + card.style.display = 'none'; } } if (!groupMatchesFilter) { - featureGroup.style.display = "none"; + featureGroup.style.display = 'none'; } else { - featureGroup.style.display = ""; + featureGroup.style.display = ''; resetOrTemporarilyExpandGroup(featureGroup, filter, collapsedGroups); } } } function getCollapsedGroups() { - return localStorage.getItem("collapsedGroups") ? JSON.parse(localStorage.getItem("collapsedGroups")) : []; + return localStorage.getItem('collapsedGroups') ? JSON.parse(localStorage.getItem('collapsedGroups')) : []; } -function resetOrTemporarilyExpandGroup(featureGroup, filterKeywords = "", collapsedGroups = []) { - const shouldResetCollapse = filterKeywords.trim() === ""; +function resetOrTemporarilyExpandGroup(featureGroup, filterKeywords = '', collapsedGroups = []) { + const shouldResetCollapse = filterKeywords.trim() === ''; if (shouldResetCollapse) { // Resetting the group's expand/collapse to its original state (as in collapsed groups) const isCollapsed = collapsedGroups.indexOf(featureGroup.id) != -1; expandCollapseToggle(featureGroup, !isCollapsed); } else { // Temporarily expands feature group without affecting the actual/stored collapsed groups - featureGroup.classList.remove("collapsed"); - featureGroup.querySelector(".header-expand-button").classList.remove("collapsed"); + featureGroup.classList.remove('collapsed'); + featureGroup.querySelector('.header-expand-button').classList.remove('collapsed'); } } function updateFavoritesSection() { - const favoritesContainer = document.getElementById("groupFavorites").querySelector(".feature-group-container"); - favoritesContainer.style.maxHeight = "none"; - favoritesContainer.innerHTML = ""; // Clear the container first - const cards = Array.from(document.querySelectorAll(".feature-card:not(.duplicate)")); + const favoritesContainer = document.getElementById('groupFavorites').querySelector('.feature-group-container'); + favoritesContainer.style.maxHeight = 'none'; + favoritesContainer.innerHTML = ''; // Clear the container first + const cards = Array.from(document.querySelectorAll('.feature-card:not(.duplicate)')); const addedCardIds = new Set(); // To keep track of added card IDs let favoritesAmount = 0; - cards.forEach(card => { - if (localStorage.getItem(card.id) === "favorite" && !addedCardIds.has(card.id)) { + cards.forEach((card) => { + if (localStorage.getItem(card.id) === 'favorite' && !addedCardIds.has(card.id)) { const duplicate = card.cloneNode(true); - duplicate.classList.add("duplicate"); + duplicate.classList.add('duplicate'); favoritesContainer.appendChild(duplicate); addedCardIds.add(card.id); // Mark this card as added favoritesAmount++; @@ -73,31 +73,31 @@ function updateFavoritesSection() { }); if (favoritesAmount === 0) { - document.getElementById("groupFavorites").style.display = "none"; + document.getElementById('groupFavorites').style.display = 'none'; } else { - document.getElementById("groupFavorites").style.display = "flex"; + document.getElementById('groupFavorites').style.display = 'flex'; } reorderCards(favoritesContainer); - favoritesContainer.style.maxHeight = favoritesContainer.scrollHeight + "px"; + favoritesContainer.style.maxHeight = favoritesContainer.scrollHeight + 'px'; } function toggleFavorite(element) { - var span = element.querySelector("span.material-symbols-rounded"); - var card = element.closest(".feature-card"); + var span = element.querySelector('span.material-symbols-rounded'); + var card = element.closest('.dropdown-item'); var cardId = card.id; // Prevent the event from bubbling up to parent elements event.stopPropagation(); - if (span.classList.contains("no-fill")) { - span.classList.remove("no-fill"); - span.classList.add("fill"); - card.classList.add("favorite"); - localStorage.setItem(cardId, "favorite"); + if (span.classList.contains('no-fill')) { + span.classList.remove('no-fill'); + span.classList.add('fill'); + card.classList.add('favorite'); + localStorage.setItem(cardId, 'favorite'); } else { - span.classList.remove("fill"); - span.classList.add("no-fill"); - card.classList.remove("favorite"); + span.classList.remove('fill'); + span.classList.add('no-fill'); + card.classList.remove('favorite'); localStorage.removeItem(cardId); } @@ -111,19 +111,19 @@ function toggleFavorite(element) { } function syncFavorites() { - const cards = Array.from(document.querySelectorAll(".feature-card")); - cards.forEach(card => { - const isFavorite = localStorage.getItem(card.id) === "favorite"; - const starIcon = card.querySelector(".favorite-icon span.material-symbols-rounded"); + const cards = Array.from(document.querySelectorAll('.dropdown-item')); + cards.forEach((card) => { + const isFavorite = localStorage.getItem(card.id) === 'favorite'; + const starIcon = card.querySelector('.favorite-icon span.material-symbols-rounded'); if (starIcon) { if (isFavorite) { - starIcon.classList.remove("no-fill"); - starIcon.classList.add("fill"); - card.classList.add("favorite"); + starIcon.classList.remove('no-fill'); + starIcon.classList.add('fill'); + card.classList.add('favorite'); } else { - starIcon.classList.remove("fill"); - starIcon.classList.add("no-fill"); - card.classList.remove("favorite"); + starIcon.classList.remove('fill'); + starIcon.classList.add('no-fill'); + card.classList.remove('favorite'); } } }); @@ -133,27 +133,25 @@ function syncFavorites() { } function reorderCards(container) { - var cards = Array.from(container.querySelectorAll(".feature-card")); + var cards = Array.from(container.querySelectorAll('.dropdown-item')); 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"; - if (a.id === "update-link") { + var aIsFavorite = localStorage.getItem(a.id) === 'favorite'; + var bIsFavorite = localStorage.getItem(b.id) === 'favorite'; + if (a.id === 'update-link') { return -1; } - if (b.id === "update-link") { + if (b.id === 'update-link') { return 1; } if (aIsFavorite && !bIsFavorite) { return -1; - } - else if (!aIsFavorite && bIsFavorite) { + } else if (!aIsFavorite && bIsFavorite) { return 1; - } - else { + } else { return a.id > b.id; } }); @@ -163,21 +161,21 @@ function reorderCards(container) { } function reorderAllCards() { - const containers = Array.from(document.querySelectorAll(".feature-group-container")); + const containers = Array.from(document.querySelectorAll('.feature-group-container')); containers.forEach(function (container) { reorderCards(container); - }) + }); } function initializeCards() { - var cards = document.querySelectorAll(".feature-card"); + var cards = document.querySelectorAll('.dropdown-item'); cards.forEach(function (card) { var cardId = card.id; - var span = card.querySelector(".favorite-icon span.material-symbols-rounded"); - if (localStorage.getItem(cardId) === "favorite") { - span.classList.remove("no-fill"); - span.classList.add("fill"); - card.classList.add("favorite"); + var span = card.querySelector('.favorite-icon span.material-symbols-rounded'); + if (localStorage.getItem(cardId) === 'favorite') { + span.classList.remove('no-fill'); + span.classList.add('fill'); + card.classList.add('favorite'); } }); reorderAllCards(); @@ -187,27 +185,27 @@ function initializeCards() { } function showFavoritesOnly() { - const groups = Array.from(document.querySelectorAll(".feature-group")); - if (localStorage.getItem("favoritesOnly") === "true") { + const groups = Array.from(document.querySelectorAll('.feature-group')); + if (localStorage.getItem('favoritesOnly') === 'true') { groups.forEach((group) => { - if (group.id !== "groupFavorites") { - group.style.display = "none"; - }; - }) + if (group.id !== 'groupFavorites') { + group.style.display = 'none'; + } + }); } else { groups.forEach((group) => { - if (group.id !== "groupFavorites") { - group.style.display = "flex"; - }; - }) - }; + if (group.id !== 'groupFavorites') { + group.style.display = 'flex'; + } + }); + } } function toggleFavoritesOnly() { - if (localStorage.getItem("favoritesOnly") === "true") { - localStorage.setItem("favoritesOnly", "false"); + if (localStorage.getItem('favoritesOnly') === 'true') { + localStorage.setItem('favoritesOnly', 'false'); } else { - localStorage.setItem("favoritesOnly", "true"); + localStorage.setItem('favoritesOnly', 'true'); } showFavoritesOnly(); } @@ -215,20 +213,20 @@ function toggleFavoritesOnly() { // 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"); + 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"); + 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"); + group.classList.add('collapsed'); + group.querySelector('.header-expand-button').classList.add('collapsed'); } - const collapsed = localStorage.getItem("collapsedGroups") ? JSON.parse(localStorage.getItem("collapsedGroups")) : []; + const collapsed = localStorage.getItem('collapsedGroups') ? JSON.parse(localStorage.getItem('collapsedGroups')) : []; const groupIndex = collapsed.indexOf(group.id); - if (group.classList.contains("collapsed")) { + if (group.classList.contains('collapsed')) { if (groupIndex === -1) { collapsed.push(group.id); } @@ -238,59 +236,62 @@ function expandCollapseToggle(group, expand = null) { } } - localStorage.setItem("collapsedGroups", JSON.stringify(collapsed)); + localStorage.setItem('collapsedGroups', JSON.stringify(collapsed)); } function expandCollapseAll(expandAll) { - const groups = Array.from(document.querySelectorAll(".feature-group")); + const groups = Array.from(document.querySelectorAll('.feature-group')); groups.forEach((group) => { expandCollapseToggle(group, expandAll); - }) + }); } -window.onload = function() { +window.onload = function () { initializeCards(); syncFavorites(); // Ensure everything is in sync on page load }; -document.addEventListener("DOMContentLoaded", function () { +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'); + materialIcons + .load() + .then(() => { + document.querySelectorAll('.dropdown-item.hidden').forEach((el) => { + el.classList.remove('hidden'); + }); + }) + .catch(() => { + console.error('Material Symbols Rounded font failed to load.'); }); - }).catch(() => { - console.error('Material Symbols Rounded font failed to load.'); - }); - Array.from(document.querySelectorAll(".feature-group-header")).forEach(header => { + 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.scrollHeight + "px"; + const container = header.parentNode.querySelector('.feature-group-container'); + if (parent.id !== 'groupFavorites') { + container.style.maxHeight = container.scrollHeight + 'px'; } header.onclick = () => { expandCollapseToggle(parent); }; - }) + }); - const collapsed = localStorage.getItem("collapsedGroups") ? JSON.parse(localStorage.getItem("collapsedGroups")) : []; - const groupsArray = Array.from(document.querySelectorAll(".feature-group")); + const collapsed = localStorage.getItem('collapsedGroups') ? JSON.parse(localStorage.getItem('collapsedGroups')) : []; + const groupsArray = Array.from(document.querySelectorAll('.feature-group')); - groupsArray.forEach(group => { + groupsArray.forEach((group) => { if (collapsed.indexOf(group.id) !== -1) { expandCollapseToggle(group, false); } - }) + }); // Necessary in order to not fire the transition animation on page load, which looks wrong. // The timeout isn't doing anything visible to the user, so it's not making the page load look slower. setTimeout(() => { - groupsArray.forEach(group => { - const container = group.querySelector(".feature-group-container"); - container.classList.add("animated-group"); - }) + groupsArray.forEach((group) => { + const container = group.querySelector('.feature-group-container'); + container.classList.add('animated-group'); + }); }, 500); showFavoritesOnly(); diff --git a/src/main/resources/templates/fragments/featureGroupHeader.html b/src/main/resources/templates/fragments/featureGroupHeader.html index 0a8f7e9b12f..7cdcb833923 100644 --- a/src/main/resources/templates/fragments/featureGroupHeader.html +++ b/src/main/resources/templates/fragments/featureGroupHeader.html @@ -1,6 +1,3 @@
- - - 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 02b6699c7c7..fce55e055ef 100644 --- a/src/main/resources/templates/home.html +++ b/src/main/resources/templates/home.html @@ -11,7 +11,7 @@
-
+

-
- - star - - - expand_all - - - collapse_all - - -
-
- +
-
+ th:replace="~{fragments/navbarEntry :: navbarEntry('view-pdf', 'menu_book', 'home.viewPdf.title', 'home.viewPdf.desc', 'viewPdf.tags', 'other')}"> +
+
+
+
+
+
+
-
-
-
-
-
-
@@ -98,65 +86,64 @@
th:replace="~{fragments/featureGroupHeader :: featureGroupHeader(groupTitle=#{navbar.sections.organize})}">
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ th:replace="~{fragments/navbarEntry :: navbarEntry ('multi-tool', 'construction', 'home.multiTool.title', 'home.multiTool.desc', 'multiTool.tags', 'organize')}">
+ th:replace="~{fragments/navbarEntry :: navbarEntry ('merge-pdfs', 'add_to_photos', 'home.merge.title', 'home.merge.desc', 'merge.tags', 'organize')}">
-
+ th:replace="~{fragments/navbarEntry :: navbarEntry ('split-pdfs', 'cut', 'home.split.title', 'home.split.desc', 'split.tags', 'organize')}">
-
+ th:replace="~{fragments/navbarEntry :: navbarEntry('rotate-pdf', 'rotate_right', 'home.rotate.title', 'home.rotate.desc', 'rotate.tags', 'organize')}"> + +
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+ th:replace="~{fragments/navbarEntry :: navbarEntry('img-to-pdf', 'image', 'home.imageToPdf.title', 'home.imageToPdf.desc', 'imageToPdf.tags', 'image')}"> +
+
+
+
+
+
+
+
+
+
+
@@ -166,73 +153,74 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ th:replace="~{fragments/navbarEntry :: navbarEntry('pdf-to-img', 'image', 'home.pdfToImage.title', 'home.pdfToImage.desc', 'pdfToImage.tags', 'image')}"> +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ th:replace="~{fragments/navbarEntry :: navbarEntry('add-password', 'lock', 'home.addPassword.title', 'home.addPassword.desc', 'addPassword.tags', 'security')}"> +
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+
@@ -241,46 +229,44 @@
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- + th:replace="~{fragments/navbarEntry :: navbarEntry('view-pdf', 'menu_book', 'home.viewPdf.title', 'home.viewPdf.desc', 'viewPdf.tags', 'other')}"> +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -290,44 +276,45 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ th:replace="~{fragments/navbarEntry :: navbarEntry('pipeline', 'family_history', 'home.pipeline.title', 'home.pipeline.desc', 'pipeline.tags', 'advance')}"> +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ From 7c2ef7ef1aa84086edc62cd5cc1e967c73335d37 Mon Sep 17 00:00:00 2001 From: Reece Browne Date: Fri, 10 Jan 2025 17:53:21 +0000 Subject: [PATCH 03/20] Homepage overhaul --- src/main/resources/static/css/general.css | 21 ++++++ src/main/resources/static/css/home.css | 19 +++--- src/main/resources/static/js/homecard.js | 4 -- .../templates/fragments/navbarEntry.html | 7 +- src/main/resources/templates/home.html | 65 ++++++++++--------- 5 files changed, 68 insertions(+), 48 deletions(-) diff --git a/src/main/resources/static/css/general.css b/src/main/resources/static/css/general.css index d5948f99b5d..70115561506 100644 --- a/src/main/resources/static/css/general.css +++ b/src/main/resources/static/css/general.css @@ -114,6 +114,7 @@ input[data-autocompleted] { color: whitesmoke; animation: fadeup 0.15s linear; } + @keyframes fadeup { 0% { transform: translateY(10px); @@ -127,3 +128,23 @@ input[data-autocompleted] { .btn:hover .btn-tooltip { display: block; } + + .nav-tooltip { + position: absolute; + display: none; + bottom: 3.2rem; + white-space: nowrap; + flex-wrap: nowrap; + padding: 7px; + background-color: rgb(0, 29, 41); + border-radius: 3px; + font-size: 12px; + color: whitesmoke; + animation: fadeup 0.15s linear; + z-index: 20; + width:15rem; + text-wrap: auto; + } + .dropdown-item:hover .nav-tooltip { + display: block; + } diff --git a/src/main/resources/static/css/home.css b/src/main/resources/static/css/home.css index 21faabc743c..8c6eb0a7dce 100644 --- a/src/main/resources/static/css/home.css +++ b/src/main/resources/static/css/home.css @@ -45,26 +45,27 @@ .feature-group { display: flex; flex-direction: column; + width: 14rem; +} + +.feature-rows{ + display: flex; + flex-direction: row; + width: 120%; + flex-wrap: wrap; } .feature-group-header { display: flex; - align-items: center; justify-content: flex-start; color: var(--md-sys-color-on-surface); - margin-bottom: 15px; + margin-top: 15px; user-select: none; cursor: pointer; gap: 10px; - +} .feature-group-container { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(12rem, 3fr)); - gap: 10px 0px; - overflow: hidden; - margin: -20px; padding: 10px; - box-sizing:content-box; } .feature-group-container.animated-group { diff --git a/src/main/resources/static/js/homecard.js b/src/main/resources/static/js/homecard.js index c3ea9c5b720..b92e093382a 100644 --- a/src/main/resources/static/js/homecard.js +++ b/src/main/resources/static/js/homecard.js @@ -50,7 +50,6 @@ function resetOrTemporarilyExpandGroup(featureGroup, filterKeywords = '', collap } else { // Temporarily expands feature group without affecting the actual/stored collapsed groups featureGroup.classList.remove('collapsed'); - featureGroup.querySelector('.header-expand-button').classList.remove('collapsed'); } } @@ -214,13 +213,10 @@ function toggleFavoritesOnly() { 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')) : []; diff --git a/src/main/resources/templates/fragments/navbarEntry.html b/src/main/resources/templates/fragments/navbarEntry.html index ad117ed6f1a..f0ab7b496b1 100644 --- a/src/main/resources/templates/fragments/navbarEntry.html +++ b/src/main/resources/templates/fragments/navbarEntry.html @@ -1,9 +1,10 @@ - -
+ +
diff --git a/src/main/resources/templates/home.html b/src/main/resources/templates/home.html index fce55e055ef..0ee407e3289 100644 --- a/src/main/resources/templates/home.html +++ b/src/main/resources/templates/home.html @@ -23,12 +23,35 @@

+

search +
+
+ +
-
+ +
@@ -121,7 +117,12 @@
-
+
+
@@ -146,7 +147,6 @@
-
@@ -181,6 +181,7 @@
+
From d06b3740dbc0bdd1936210268f8c2eacb45c2f4d Mon Sep 17 00:00:00 2001 From: Reece Browne Date: Fri, 10 Jan 2025 20:37:11 +0000 Subject: [PATCH 04/20] full width and bug fixes --- src/main/resources/static/css/home.css | 19 +++++++++------ .../static/css/theme/componentes.css | 3 ++- .../templates/fragments/navbarEntry.html | 6 ++--- src/main/resources/templates/home.html | 23 ++++++++++--------- 4 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/main/resources/static/css/home.css b/src/main/resources/static/css/home.css index 8c6eb0a7dce..33f3d8820d3 100644 --- a/src/main/resources/static/css/home.css +++ b/src/main/resources/static/css/home.css @@ -38,21 +38,25 @@ .features-container { display: flex; - flex-direction: column; gap: 30px; + justify-content: center; } .feature-group { display: flex; - flex-direction: column; - width: 14rem; + flex-direction: column; + min-width: 10rem; + flex: 1 1 auto; + box-sizing: border-box; } .feature-rows{ - display: flex; - flex-direction: row; - width: 120%; - flex-wrap: wrap; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); + grid-auto-flow: dense; + gap: 1rem; +width:100%; + } .feature-group-header { @@ -64,6 +68,7 @@ cursor: pointer; gap: 10px; } + .feature-group-container { padding: 10px; } diff --git a/src/main/resources/static/css/theme/componentes.css b/src/main/resources/static/css/theme/componentes.css index d2c700cea79..890944c8436 100644 --- a/src/main/resources/static/css/theme/componentes.css +++ b/src/main/resources/static/css/theme/componentes.css @@ -874,6 +874,8 @@ textarea.form-control { .dropdown-item { color: var(--md-sys-color-on-surface); padding: 0.25rem 1rem; + border-radius: 3rem; + } .dropdown-item:focus, @@ -881,7 +883,6 @@ textarea.form-control { color: var(--md-sys-color-on-surface); background-color: var(--md-sys-color-surface-5); border-radius: 3rem; - font-weight: 500; font-variation-settings: var(--md-sys-icon-fill-1); } diff --git a/src/main/resources/templates/fragments/navbarEntry.html b/src/main/resources/templates/fragments/navbarEntry.html index f0ab7b496b1..321a3b7ccf8 100644 --- a/src/main/resources/templates/fragments/navbarEntry.html +++ b/src/main/resources/templates/fragments/navbarEntry.html @@ -1,12 +1,12 @@ +th:if="${@endpointConfiguration.isEndpointEnabled(endpoint)}"> -
+
- +
\ No newline at end of file diff --git a/src/main/resources/templates/home.html b/src/main/resources/templates/home.html index 611ea20f056..59049cfbd93 100644 --- a/src/main/resources/templates/home.html +++ b/src/main/resources/templates/home.html @@ -22,13 +22,20 @@


-
+

- - search - +
+
+ + search + +
+
-
-
+
@@ -179,7 +181,6 @@
-
From e69329ae85feb4142b44ee58924c6140d77b21fa Mon Sep 17 00:00:00 2001 From: Reece Browne Date: Fri, 10 Jan 2025 21:30:09 +0000 Subject: [PATCH 05/20] correct resize and wrap --- src/main/resources/static/css/home.css | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/resources/static/css/home.css b/src/main/resources/static/css/home.css index 33f3d8820d3..d705b33c7ff 100644 --- a/src/main/resources/static/css/home.css +++ b/src/main/resources/static/css/home.css @@ -46,17 +46,17 @@ display: flex; flex-direction: column; min-width: 10rem; - flex: 1 1 auto; + flex: 1 1 min(12rem, 100%); box-sizing: border-box; } .feature-rows{ - display: grid; - grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); - grid-auto-flow: dense; - gap: 1rem; -width:100%; - + display: flex; + flex-wrap: wrap; + justify-content: flex-start; + gap: 1rem; + padding: 1rem; + box-sizing: border-box; } .feature-group-header { From 0831787508238014ed9ad56e8913aa7c94f095f3 Mon Sep 17 00:00:00 2001 From: Reece Browne Date: Fri, 10 Jan 2025 21:52:55 +0000 Subject: [PATCH 06/20] improve search --- src/main/resources/static/js/homecard.js | 14 ++++++++++---- .../resources/templates/fragments/navbarEntry.html | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main/resources/static/js/homecard.js b/src/main/resources/static/js/homecard.js index b92e093382a..60c1aede30e 100644 --- a/src/main/resources/static/js/homecard.js +++ b/src/main/resources/static/js/homecard.js @@ -1,6 +1,9 @@ function filterCards() { var input = document.getElementById('searchBar'); - var filter = input.value.toUpperCase(); + var filter = input.value.toUpperCase().trim(); + + // Split the input filter into individual words for multi-word matching + var filterWords = filter.split(/[\s,;.\-]+/); let featureGroups = document.querySelectorAll('.feature-group'); const collapsedGroups = getCollapsedGroups(); @@ -12,15 +15,18 @@ function filterCards() { for (var i = 0; i < cards.length; i++) { var card = cards[i]; var title = card.getAttribute('title') || ''; - //var text = card.querySelector('p.card-text').innerText; // Get the navbar tags associated with the card var navbarItem = document.querySelector(`a.dropdown-item[href="${card.id}"]`); var navbarTags = navbarItem ? navbarItem.getAttribute('data-bs-tags') : ''; + var navbarTags = navbarItem ? navbarTags + ',' + navbarItem.getAttribute('data-bs-title') : ''; + + var content = (title + ' ' + navbarTags).toUpperCase(); - var content = title + ' ' + navbarTags; + // Check if all words in the filter match the content + var matches = filterWords.every((word) => content.includes(word)); - if (content.toUpperCase().indexOf(filter) > -1) { + if (matches) { card.style.display = ''; groupMatchesFilter = true; } else { diff --git a/src/main/resources/templates/fragments/navbarEntry.html b/src/main/resources/templates/fragments/navbarEntry.html index 321a3b7ccf8..9746d486cfc 100644 --- a/src/main/resources/templates/fragments/navbarEntry.html +++ b/src/main/resources/templates/fragments/navbarEntry.html @@ -2,7 +2,7 @@ th:if="${@endpointConfiguration.isEndpointEnabled(endpoint)}"> + th:data-bs-tags="#{${tagKey}}" th:data-bs-title='#{${titleKey}}'>
From 90d2b4ab154671631ecbf37e112265e60e850166 Mon Sep 17 00:00:00 2001 From: Reece Browne Date: Tue, 14 Jan 2025 02:08:55 +0000 Subject: [PATCH 07/20] Favourites rework --- src/main/resources/static/css/home.css | 142 --------- src/main/resources/static/css/navbar.css | 107 +++++++ .../static/css/theme/componentes.css | 7 + src/main/resources/static/js/favourites.js | 147 +++++---- src/main/resources/static/js/homecard.js | 87 +---- .../templates/convert/img-to-pdf.html | 2 +- .../templates/convert/pdf-to-img.html | 2 +- .../templates/fragments/navElements.html | 238 ++++++++++++++ .../resources/templates/fragments/navbar.html | 245 ++------------- .../templates/fragments/navbarEntry.html | 6 +- .../fragments/navbarEntryCustom.html | 13 +- src/main/resources/templates/home.html | 297 ++++-------------- .../templates/security/auto-redact.html | 2 +- .../resources/templates/security/redact.html | 2 +- 14 files changed, 553 insertions(+), 744 deletions(-) create mode 100644 src/main/resources/templates/fragments/navElements.html diff --git a/src/main/resources/static/css/home.css b/src/main/resources/static/css/home.css index d705b33c7ff..5fc2f301f3e 100644 --- a/src/main/resources/static/css/home.css +++ b/src/main/resources/static/css/home.css @@ -36,106 +36,6 @@ border: 0.1rem solid transparent; } -.features-container { - display: flex; - gap: 30px; - justify-content: center; -} - -.feature-group { - display: flex; - flex-direction: column; - min-width: 10rem; - flex: 1 1 min(12rem, 100%); - box-sizing: border-box; -} - -.feature-rows{ - display: flex; - flex-wrap: wrap; - justify-content: flex-start; - gap: 1rem; - padding: 1rem; - box-sizing: border-box; -} - -.feature-group-header { - display: flex; - justify-content: flex-start; - color: var(--md-sys-color-on-surface); - margin-top: 15px; - user-select: none; - cursor: pointer; - gap: 10px; -} - -.feature-group-container { - padding: 10px; -} - -.feature-group-container.animated-group { - transition: 0.5s all; -} - -.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 { - border: 1px solid var(--md-sys-color-surface-5); - border-radius: 1.75rem; - padding: 1.25rem; - display: flex; - flex-direction: column; - align-items: flex-start; - background: var(--md-sys-color-surface-5); - transition: - transform 0.3s, - border 0.3s; - transform-origin: center center; - outline: 0px solid transparent; -} - -.feature-card a { - text-decoration: none; - color: var(--md-sys-color-on-surface); - display: flex; - flex-direction: column; - width: 100%; - height: 100%; -} - -.feature-card .card-text { - font-size: .875rem; -} - -.feature-card:hover { - cursor: pointer; - transform: scale(1.08); - box-shadow: var(--md-sys-elevation-2); -} - -.card-title.text-primary { - color: #000; -} - -.home-card-icon { - width: 3rem; - height: 3rem; - transform: translateY(-5px); -} - .favorite-icon { display: none; position: absolute; @@ -152,16 +52,6 @@ margin: 0.0rem 0 0 1.25rem; } -.card-title { - margin-bottom: 1rem; - font-size: 1.1rem; -} - -/* Only show the favorite icons when the parent card is being hovered over */ -.feature-card:hover .favorite-icon { - display: block; -} - .favorite-icon img { filter: brightness(0) invert(var(--md-theme-filter-color)); } @@ -199,35 +89,3 @@ -webkit-animation: 2s infinite Pulse steps(20); animation: 2s infinite Pulse steps(20); } - -@keyframes Pulse { - from { - opacity: 0; - } - - 50% { - opacity: 1; - } - - to { - opacity: 0; - } -} - -.update-notice { - animation: scale 1s infinite alternate; -} - -@keyframes scale { - 0% { - transform: scale(0.96); - } - - 100% { - transform: scale(1); - } -} - -.hidden { - visibility: hidden; -} diff --git a/src/main/resources/static/css/navbar.css b/src/main/resources/static/css/navbar.css index 71c573986ef..d6528a7e0fa 100644 --- a/src/main/resources/static/css/navbar.css +++ b/src/main/resources/static/css/navbar.css @@ -374,3 +374,110 @@ span.icon-text::after { #stacked > .navbar-item { margin: 0; } + +.features-container { + display: flex; + gap: 30px; + justify-content: center; +} + +.feature-group { + display: flex; + flex-direction: column; + min-width: 14rem; + max-width: 18rem; + flex: 1 1 min(14rem, 100%); + box-sizing: border-box; + height:auto; +} + +.feature-rows{ + display: flex; + flex-wrap: wrap; + justify-content: flex-start; + gap: 1rem; + padding: 1rem; + box-sizing: border-box; +} + +.feature-group-header { + display: flex; + justify-content: flex-start; + color: var(--md-sys-color-on-surface); + margin-top: 15px; + user-select: none; + cursor: pointer; + gap: 10px; +} + +.feature-group-container { + padding: 10px; +} + +.feature-group-container.animated-group { + transition: 0.5s all; +} + + +.card-title.text-primary { + color: #000; +} + +.home-card-icon { + width: 3rem; + height: 3rem; + transform: translateY(-5px); +} + +.favorite-icon { + display: none; + position: absolute; + top: 10px; + right: 10px; + color: var(--md-sys-color-secondary); +} + +.favorite-icon img { + filter: brightness(0) invert(var(--md-theme-filter-color)); +} + +.favorite-icon:hover .material-symbols-rounded { + transform: scale(1.2); +} + +.favorite-icon .material-symbols-rounded.fill{ + color: #f5c000; +} + + +@keyframes Pulse { + from { + opacity: 0; + } + + 50% { + opacity: 1; + } + + to { + opacity: 0; + } +} + +.update-notice { + animation: scale 1s infinite alternate; +} + +@keyframes scale { + 0% { + transform: scale(0.96); + } + + 100% { + transform: scale(1); + } +} + +.hidden { + visibility: hidden; +} diff --git a/src/main/resources/static/css/theme/componentes.css b/src/main/resources/static/css/theme/componentes.css index 890944c8436..f49ce6489a4 100644 --- a/src/main/resources/static/css/theme/componentes.css +++ b/src/main/resources/static/css/theme/componentes.css @@ -885,6 +885,13 @@ textarea.form-control { border-radius: 3rem; font-variation-settings: var(--md-sys-icon-fill-1); } +.dropdown-item.no-hover:hover, +.dropdown-item.no-hover:focus { + color: var(--md-sys-color-on-surface) !important; + background-color: transparent !important; + border-radius: 3rem !important; + font-variation-settings: initial !important; +} .dropdown-item.active, .dropdown-item:active { diff --git a/src/main/resources/static/js/favourites.js b/src/main/resources/static/js/favourites.js index 16e219f77c8..7500bd6a829 100644 --- a/src/main/resources/static/js/favourites.js +++ b/src/main/resources/static/js/favourites.js @@ -1,73 +1,112 @@ function updateFavoritesDropdown() { - var dropdown = document.querySelector("#favoritesDropdown"); + const favoritesList = JSON.parse(localStorage.getItem('favoritesList')) || []; + + for (var i = 0; i < localStorage.length; i++) { + var key = localStorage.key(i); + var value = localStorage.getItem(key); + + if (value === 'favorite') { + const index = favoritesList.indexOf(key); + if (index === -1) { + favoritesList.push(key); + console.log(`Added to favorites: ${key}`); + } + } + } + + var dropdown = document.querySelector('#favoritesDropdown'); if (!dropdown) { console.error('Dropdown element with ID "favoritesDropdown" not found!'); return; } - dropdown.innerHTML = ""; + dropdown.innerHTML = ''; var hasFavorites = false; var addedFeatures = new Set(); - for (var i = 0; i < localStorage.length; i++) { - var key = localStorage.key(i); - var value = localStorage.getItem(key); + for (var i = 0; i < favoritesList.length; i++) { + var value = favoritesList[i]; + + var navbarEntry = document.querySelector(`a[data-bs-link='${value}']`); + if (navbarEntry) { + var featureName = navbarEntry.textContent.trim(); - if (value === "favorite") { - var navbarEntry = document.querySelector(`a[href='${key}']`); - if (navbarEntry) { - var featureName = navbarEntry.textContent.trim(); - - if (!addedFeatures.has(featureName)) { - var dropdownItem = document.createElement("div"); - dropdownItem.className = "dropdown-item d-flex justify-content-between align-items-center"; - - // Create a wrapper for the original content - var contentWrapper = document.createElement("div"); - contentWrapper.className = "d-flex align-items-center flex-grow-1"; - contentWrapper.style.textDecoration = "none"; - contentWrapper.style.color = "inherit"; - - // Clone the original content - var originalContent = navbarEntry.querySelector('div').cloneNode(true); - contentWrapper.appendChild(originalContent); - - // Create the remove button - var removeButton = document.createElement("button"); - removeButton.className = "btn btn-sm btn-link p-0 ml-2"; - removeButton.innerHTML = 'close'; - removeButton.onclick = function(itemKey, event) { - event.preventDefault(); - event.stopPropagation(); - localStorage.removeItem(itemKey); - updateFavoritesSection(); - updateFavoritesDropdown(); - filterCards(); - }.bind(null, key); - - // Add click event to the content wrapper - contentWrapper.onclick = function(itemHref, event) { - event.preventDefault(); - window.location.href = itemHref; - }.bind(null, navbarEntry.href); - - dropdownItem.appendChild(contentWrapper); - dropdownItem.appendChild(removeButton); - dropdown.appendChild(dropdownItem); - hasFavorites = true; - addedFeatures.add(featureName); - } - } else { - console.warn(`Navbar entry not found for key: ${key}`); + if (!addedFeatures.has(featureName)) { + var dropdownItem = document.createElement('div'); + dropdownItem.className = 'dropdown-item d-flex justify-content-between align-items-center'; + + // Create a wrapper for the original content + var contentWrapper = document.createElement('div'); + contentWrapper.className = 'd-flex align-items-center flex-grow-1'; + contentWrapper.style.textDecoration = 'none'; + contentWrapper.style.color = 'inherit'; + + // Clone the original content + var originalContent = navbarEntry.querySelector('div').cloneNode(true); + contentWrapper.appendChild(originalContent); + + // Create the remove button + var removeButton = document.createElement('button'); + removeButton.className = 'btn btn-sm btn-link p-0 ml-2'; + removeButton.innerHTML = 'close'; + removeButton.onclick = function (itemKey, event) { + event.preventDefault(); + event.stopPropagation(); + addToFavorites(itemKey); + updateFavoritesDropdown(); + }.bind(null, value); + + // Add click event to the content wrapper + contentWrapper.onclick = function (itemHref, event) { + event.preventDefault(); + window.location.href = itemHref; + }.bind(null, navbarEntry.href); + + dropdownItem.appendChild(contentWrapper); + dropdownItem.appendChild(removeButton); + dropdown.appendChild(dropdownItem); + hasFavorites = true; + addedFeatures.add(featureName); } + } else { + console.warn(`Navbar entry not found for : ${value}`); } } if (!hasFavorites) { - var defaultItem = document.createElement("a"); - defaultItem.className = "dropdown-item"; - defaultItem.textContent = noFavourites || "No favorites added"; + var defaultItem = document.createElement('a'); + defaultItem.className = 'dropdown-item'; + defaultItem.textContent = noFavourites || 'No favorites added'; dropdown.appendChild(defaultItem); } } + +function updateFavoriteIcons() { + const favoritesList = JSON.parse(localStorage.getItem('favoritesList')) || []; + document.querySelectorAll('.favorite-icon').forEach((icon) => { + const endpoint = icon.getAttribute('data-endpoint'); + if (favoritesList.includes(endpoint)) { + icon.style.color = 'gold'; + } else { + icon.style.color = ''; + } + }); +} + +function addToFavorites(entryId) { + const favoritesList = JSON.parse(localStorage.getItem('favoritesList')) || []; + const index = favoritesList.indexOf(entryId); + + if (index === -1) { + favoritesList.push(entryId); + console.log(`Added to favorites: ${entryId}`); + } else { + favoritesList.splice(index, 1); + console.log(`Removed from favorites: ${entryId}`); + } + + localStorage.setItem('favoritesList', JSON.stringify(favoritesList)); + updateFavoritesDropdown(); + updateFavoriteIcons(); +} diff --git a/src/main/resources/static/js/homecard.js b/src/main/resources/static/js/homecard.js index 60c1aede30e..593c1a5af47 100644 --- a/src/main/resources/static/js/homecard.js +++ b/src/main/resources/static/js/homecard.js @@ -6,7 +6,6 @@ function filterCards() { var filterWords = filter.split(/[\s,;.\-]+/); let featureGroups = document.querySelectorAll('.feature-group'); - const collapsedGroups = getCollapsedGroups(); for (const featureGroup of featureGroups) { var cards = featureGroup.querySelectorAll('.dropdown-item'); @@ -38,33 +37,16 @@ function filterCards() { featureGroup.style.display = 'none'; } else { featureGroup.style.display = ''; - resetOrTemporarilyExpandGroup(featureGroup, filter, collapsedGroups); + resetOrTemporarilyExpandGroup(featureGroup, filter); } } } -function getCollapsedGroups() { - return localStorage.getItem('collapsedGroups') ? JSON.parse(localStorage.getItem('collapsedGroups')) : []; -} - -function resetOrTemporarilyExpandGroup(featureGroup, filterKeywords = '', collapsedGroups = []) { - const shouldResetCollapse = filterKeywords.trim() === ''; - if (shouldResetCollapse) { - // Resetting the group's expand/collapse to its original state (as in collapsed groups) - const isCollapsed = collapsedGroups.indexOf(featureGroup.id) != -1; - expandCollapseToggle(featureGroup, !isCollapsed); - } else { - // Temporarily expands feature group without affecting the actual/stored collapsed groups - featureGroup.classList.remove('collapsed'); - } -} - function updateFavoritesSection() { const favoritesContainer = document.getElementById('groupFavorites').querySelector('.feature-group-container'); - favoritesContainer.style.maxHeight = 'none'; - favoritesContainer.innerHTML = ''; // Clear the container first + favoritesContainer.innerHTML = ''; const cards = Array.from(document.querySelectorAll('.feature-card:not(.duplicate)')); - const addedCardIds = new Set(); // To keep track of added card IDs + const addedCardIds = new Set(); let favoritesAmount = 0; cards.forEach((card) => { @@ -72,7 +54,7 @@ function updateFavoritesSection() { const duplicate = card.cloneNode(true); duplicate.classList.add('duplicate'); favoritesContainer.appendChild(duplicate); - addedCardIds.add(card.id); // Mark this card as added + addedCardIds.add(card.id); favoritesAmount++; } }); @@ -178,9 +160,9 @@ function initializeCards() { var cardId = card.id; var span = card.querySelector('.favorite-icon span.material-symbols-rounded'); if (localStorage.getItem(cardId) === 'favorite') { - span.classList.remove('no-fill'); - span.classList.add('fill'); - card.classList.add('favorite'); + // span.classList.remove('no-fill'); + // span.classList.add('fill'); + // card.classList.add('favorite'); } }); reorderAllCards(); @@ -215,39 +197,6 @@ function toggleFavoritesOnly() { 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'); - } else if (expand) { - group.classList.remove('collapsed'); - } else { - group.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 = function () { initializeCards(); syncFavorites(); // Ensure everything is in sync on page load @@ -269,32 +218,10 @@ document.addEventListener('DOMContentLoaded', function () { 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.scrollHeight + 'px'; - } header.onclick = () => { expandCollapseToggle(parent); }; }); - const collapsed = localStorage.getItem('collapsedGroups') ? JSON.parse(localStorage.getItem('collapsedGroups')) : []; - const groupsArray = Array.from(document.querySelectorAll('.feature-group')); - - groupsArray.forEach((group) => { - if (collapsed.indexOf(group.id) !== -1) { - expandCollapseToggle(group, false); - } - }); - - // Necessary in order to not fire the transition animation on page load, which looks wrong. - // The timeout isn't doing anything visible to the user, so it's not making the page load look slower. - setTimeout(() => { - groupsArray.forEach((group) => { - const container = group.querySelector('.feature-group-container'); - container.classList.add('animated-group'); - }); - }, 500); - showFavoritesOnly(); }); diff --git a/src/main/resources/templates/convert/img-to-pdf.html b/src/main/resources/templates/convert/img-to-pdf.html index fa0fc74f207..ee63debdba5 100644 --- a/src/main/resources/templates/convert/img-to-pdf.html +++ b/src/main/resources/templates/convert/img-to-pdf.html @@ -14,7 +14,7 @@
- image + picture_as_pdf
diff --git a/src/main/resources/templates/convert/pdf-to-img.html b/src/main/resources/templates/convert/pdf-to-img.html index d83ef4e9915..88747cea9d1 100644 --- a/src/main/resources/templates/convert/pdf-to-img.html +++ b/src/main/resources/templates/convert/pdf-to-img.html @@ -14,7 +14,7 @@ +
\ No newline at end of file diff --git a/src/main/resources/templates/fragments/navbarEntry.html b/src/main/resources/templates/fragments/navbarEntry.html index 9746d486cfc..c4b098fd50b 100644 --- a/src/main/resources/templates/fragments/navbarEntry.html +++ b/src/main/resources/templates/fragments/navbarEntry.html @@ -1,6 +1,6 @@ - @@ -8,5 +8,9 @@
+ + \ No newline at end of file diff --git a/src/main/resources/templates/fragments/navbarEntryCustom.html b/src/main/resources/templates/fragments/navbarEntryCustom.html index c918e32f1b8..a6b024f72a7 100644 --- a/src/main/resources/templates/fragments/navbarEntryCustom.html +++ b/src/main/resources/templates/fragments/navbarEntryCustom.html @@ -1,13 +1,18 @@ - + -
+ th:data-bs-tags="#{${tagKey}}" th:data-bs-title='#{${titleKey}}'> + +
+
+ \ No newline at end of file diff --git a/src/main/resources/templates/home.html b/src/main/resources/templates/home.html index 59049cfbd93..a43893d1c3e 100644 --- a/src/main/resources/templates/home.html +++ b/src/main/resources/templates/home.html @@ -34,6 +34,9 @@

search + + star +
@@ -60,7 +63,6 @@

-->
- - -
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ -
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -386,7 +158,18 @@
- + diff --git a/src/main/resources/templates/security/auto-redact.html b/src/main/resources/templates/security/auto-redact.html index 7a4bcc5dd97..26bfb39eb7e 100644 --- a/src/main/resources/templates/security/auto-redact.html +++ b/src/main/resources/templates/security/auto-redact.html @@ -13,7 +13,7 @@
- ink_eraser + playlist_remove
diff --git a/src/main/resources/templates/security/redact.html b/src/main/resources/templates/security/redact.html index 2088ce4a9bd..efb8ee89015 100644 --- a/src/main/resources/templates/security/redact.html +++ b/src/main/resources/templates/security/redact.html @@ -31,7 +31,7 @@
- ink_eraser + playlist_remove
From 0a7c205beacc10f4f210f799c5071f578a1686b8 Mon Sep 17 00:00:00 2001 From: Reece Browne Date: Tue, 14 Jan 2025 11:36:26 +0000 Subject: [PATCH 08/20] fix fragment --- src/main/resources/templates/fragments/navbar.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/templates/fragments/navbar.html b/src/main/resources/templates/fragments/navbar.html index 014b4c6fc76..3e627baad69 100644 --- a/src/main/resources/templates/fragments/navbar.html +++ b/src/main/resources/templates/fragments/navbar.html @@ -43,7 +43,7 @@ From c40ee9f2406d1dc741d92a0022f322639ca1d201 Mon Sep 17 00:00:00 2001 From: Reece Browne Date: Tue, 14 Jan 2025 12:21:48 +0000 Subject: [PATCH 09/20] Remove unnecessary function --- src/main/resources/static/js/homecard.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/resources/static/js/homecard.js b/src/main/resources/static/js/homecard.js index 593c1a5af47..67fd41d901d 100644 --- a/src/main/resources/static/js/homecard.js +++ b/src/main/resources/static/js/homecard.js @@ -37,7 +37,6 @@ function filterCards() { featureGroup.style.display = 'none'; } else { featureGroup.style.display = ''; - resetOrTemporarilyExpandGroup(featureGroup, filter); } } } From 539c6f049fe617745600b9cbe774093a71f6e8f4 Mon Sep 17 00:00:00 2001 From: Reece Browne Date: Wed, 15 Jan 2025 02:20:10 +0000 Subject: [PATCH 10/20] Restore old homepage as option and add feature row --- src/main/resources/static/css/home-legacy.css | 229 ++++++++ src/main/resources/static/css/navbar.css | 5 +- src/main/resources/static/js/favourites.js | 118 ++-- .../resources/static/js/homecard-legacy.js | 260 +++++++++ src/main/resources/static/js/homecard.js | 155 ++---- .../resources/templates/fragments/card.html | 10 +- .../resources/templates/fragments/common.html | 1 + .../fragments/featureGroupHeaderLegacy.html | 6 + .../templates/fragments/navElements.html | 14 +- .../resources/templates/fragments/navbar.html | 2 + src/main/resources/templates/home-legacy.html | 509 ++++++++++++++++++ src/main/resources/templates/home.html | 31 +- .../resources/templates/misc/auto-rename.html | 7 +- 13 files changed, 1165 insertions(+), 182 deletions(-) create mode 100644 src/main/resources/static/css/home-legacy.css create mode 100644 src/main/resources/static/js/homecard-legacy.js create mode 100644 src/main/resources/templates/fragments/featureGroupHeaderLegacy.html create mode 100644 src/main/resources/templates/home-legacy.html diff --git a/src/main/resources/static/css/home-legacy.css b/src/main/resources/static/css/home-legacy.css new file mode 100644 index 00000000000..b25fafc1791 --- /dev/null +++ b/src/main/resources/static/css/home-legacy.css @@ -0,0 +1,229 @@ +#searchBar { + color: var(--md-sys-color-on-surface); + background-color: var(--md-sys-color-surface-container-low); + width: 100%; + font-size: 16px; + margin-bottom: 2rem; + padding: 0.75rem 3.5rem; + border: 1px solid var(--md-sys-color-outline-variant); + border-radius: 3rem; + 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; + border: 0.1rem solid transparent; + } + + .features-container { + display: flex; + flex-direction: column; + gap: 30px; + } + + .feature-group-legacy { + 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; + overflow: hidden; + margin: -20px; + padding: 20px; + box-sizing:content-box; + } + + .feature-group-container.animated-group { + transition: 0.5s all; + } + + .feature-group-legacy.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 { + border: 1px solid var(--md-sys-color-surface-5); + border-radius: 1.75rem; + padding: 1.25rem; + display: flex; + flex-direction: column; + align-items: flex-start; + background: var(--md-sys-color-surface-5); + transition: + transform 0.3s, + border 0.3s; + transform-origin: center center; + outline: 0px solid transparent; + position:relative; + } + + .feature-card a { + text-decoration: none; + color: var(--md-sys-color-on-surface); + display: flex; + flex-direction: column; + width: 100%; + height: 100%; + } + + .feature-card .card-text { + font-size: .875rem; + } + + .feature-card:hover { + cursor: pointer; + transform: scale(1.08); + box-shadow: var(--md-sys-elevation-2); + } + + .card-title.text-primary { + color: #000; + } + + .home-card-icon { + width: 3rem; + height: 3rem; + transform: translateY(-5px); + } + + .favorite-icon { + display: none !important; + position: absolute; + top: 10px; + right: 10px; + color: var(--md-sys-color-secondary); + } + + #tool-icon { + height: 100%; + } + + #tool-text { + margin: 0.0rem 0 0 1.25rem; + } + + .card-title { + margin-bottom: 1rem; + font-size: 1.1rem; + } + + /* Only show the favorite icons when the parent card is being hovered over */ + .feature-card:hover .favorite-icon { + display: block !important; + } + + .favorite-icon img { + filter: brightness(0) invert(var(--md-theme-filter-color)); + } + + .favorite-icon:hover .material-symbols-rounded { + transform: scale(1.2); + } + + .favorite-icon .material-symbols-rounded.fill{ + color: #f5c000; + } + + .jumbotron { + padding: 3rem 3rem; + /* Reduce vertical padding */ + } + + .lookatme { + opacity: 1; + position: relative; + display: inline-block; + } + + .lookatme::after { + color: #e33100; + text-shadow: 0 0 5px #e33100; + /* in the html, the data-lookatme-text attribute must */ + /* contain the same text as the .lookatme element */ + content: attr(data-lookatme-text); + padding: inherit; + position: absolute; + inset: 0 0 0 0; + z-index: 1; + /* 20 steps / 2 seconds = 10fps */ + -webkit-animation: 2s infinite Pulse steps(20); + animation: 2s infinite Pulse steps(20); + } + + @keyframes Pulse { + from { + opacity: 0; + } + + 50% { + opacity: 1; + } + + to { + opacity: 0; + } + } + + .update-notice { + animation: scale 1s infinite alternate; + } + + @keyframes scale { + 0% { + transform: scale(0.96); + } + + 100% { + transform: scale(1); + } + } + + .hidden { + visibility: hidden; + } diff --git a/src/main/resources/static/css/navbar.css b/src/main/resources/static/css/navbar.css index d6528a7e0fa..c600d54759e 100644 --- a/src/main/resources/static/css/navbar.css +++ b/src/main/resources/static/css/navbar.css @@ -410,13 +410,10 @@ span.icon-text::after { gap: 10px; } -.feature-group-container { +.nav-group-container { padding: 10px; } -.feature-group-container.animated-group { - transition: 0.5s all; -} .card-title.text-primary { diff --git a/src/main/resources/static/js/favourites.js b/src/main/resources/static/js/favourites.js index 7500bd6a829..949a2cf9977 100644 --- a/src/main/resources/static/js/favourites.js +++ b/src/main/resources/static/js/favourites.js @@ -9,6 +9,7 @@ function updateFavoritesDropdown() { const index = favoritesList.indexOf(key); if (index === -1) { favoritesList.push(key); + localStorage.removeItem(key); console.log(`Added to favorites: ${key}`); } } @@ -27,47 +28,48 @@ function updateFavoritesDropdown() { for (var i = 0; i < favoritesList.length; i++) { var value = favoritesList[i]; - - var navbarEntry = document.querySelector(`a[data-bs-link='${value}']`); - if (navbarEntry) { - var featureName = navbarEntry.textContent.trim(); - - if (!addedFeatures.has(featureName)) { - var dropdownItem = document.createElement('div'); - dropdownItem.className = 'dropdown-item d-flex justify-content-between align-items-center'; - - // Create a wrapper for the original content - var contentWrapper = document.createElement('div'); - contentWrapper.className = 'd-flex align-items-center flex-grow-1'; - contentWrapper.style.textDecoration = 'none'; - contentWrapper.style.color = 'inherit'; - - // Clone the original content - var originalContent = navbarEntry.querySelector('div').cloneNode(true); - contentWrapper.appendChild(originalContent); - - // Create the remove button - var removeButton = document.createElement('button'); - removeButton.className = 'btn btn-sm btn-link p-0 ml-2'; - removeButton.innerHTML = 'close'; - removeButton.onclick = function (itemKey, event) { - event.preventDefault(); - event.stopPropagation(); - addToFavorites(itemKey); - updateFavoritesDropdown(); - }.bind(null, value); - - // Add click event to the content wrapper - contentWrapper.onclick = function (itemHref, event) { - event.preventDefault(); - window.location.href = itemHref; - }.bind(null, navbarEntry.href); - - dropdownItem.appendChild(contentWrapper); - dropdownItem.appendChild(removeButton); - dropdown.appendChild(dropdownItem); - hasFavorites = true; - addedFeatures.add(featureName); + if (value) { + var navbarEntry = document.querySelector(`a[data-bs-link='${value}']`); + if (navbarEntry) { + var featureName = navbarEntry.textContent.trim(); + + if (!addedFeatures.has(featureName)) { + var dropdownItem = document.createElement('div'); + dropdownItem.className = 'dropdown-item d-flex justify-content-between align-items-center'; + + // Create a wrapper for the original content + var contentWrapper = document.createElement('div'); + contentWrapper.className = 'd-flex align-items-center flex-grow-1'; + contentWrapper.style.textDecoration = 'none'; + contentWrapper.style.color = 'inherit'; + + // Clone the original content + var originalContent = navbarEntry.querySelector('div').cloneNode(true); + contentWrapper.appendChild(originalContent); + + // Create the remove button + var removeButton = document.createElement('button'); + removeButton.className = 'btn btn-sm btn-link p-0 ml-2'; + removeButton.innerHTML = 'close'; + removeButton.onclick = function (itemKey, event) { + event.preventDefault(); + event.stopPropagation(); + addToFavorites(itemKey); + updateFavoritesDropdown(); + }.bind(null, value); + + // Add click event to the content wrapper + contentWrapper.onclick = function (itemHref, event) { + event.preventDefault(); + window.location.href = itemHref; + }.bind(null, navbarEntry.href); + + dropdownItem.appendChild(contentWrapper); + dropdownItem.appendChild(removeButton); + dropdown.appendChild(dropdownItem); + hasFavorites = true; + addedFeatures.add(featureName); + } } } else { console.warn(`Navbar entry not found for : ${value}`); @@ -95,18 +97,26 @@ function updateFavoriteIcons() { } function addToFavorites(entryId) { - const favoritesList = JSON.parse(localStorage.getItem('favoritesList')) || []; - const index = favoritesList.indexOf(entryId); - - if (index === -1) { - favoritesList.push(entryId); - console.log(`Added to favorites: ${entryId}`); - } else { - favoritesList.splice(index, 1); - console.log(`Removed from favorites: ${entryId}`); - } + if (entryId) { + const favoritesList = JSON.parse(localStorage.getItem('favoritesList')) || []; + const index = favoritesList.indexOf(entryId); - localStorage.setItem('favoritesList', JSON.stringify(favoritesList)); - updateFavoritesDropdown(); - updateFavoriteIcons(); + if (index === -1) { + favoritesList.push(entryId); + console.log(`Added to favorites: ${entryId}`); + } else { + favoritesList.splice(index, 1); + console.log(`Removed from favorites: ${entryId}`); + } + + localStorage.setItem('favoritesList', JSON.stringify(favoritesList)); + updateFavoritesDropdown(); + updateFavoriteIcons(); + const currentPath = window.location.pathname; + if (currentPath.includes('home-legacy')) { + syncFavoritesLegacy(); + } else { + syncFavorites(); + } + } } diff --git a/src/main/resources/static/js/homecard-legacy.js b/src/main/resources/static/js/homecard-legacy.js new file mode 100644 index 00000000000..48d0c526fe5 --- /dev/null +++ b/src/main/resources/static/js/homecard-legacy.js @@ -0,0 +1,260 @@ +function filterCardsLegacy() { + var input = document.getElementById('searchBar'); + var filter = input.value.toUpperCase(); + + let featureGroups = document.querySelectorAll('.feature-group-legacy'); + const collapsedGroups = getCollapsedGroups(); + + for (const featureGroup of featureGroups) { + var cards = featureGroup.querySelectorAll('.feature-card'); + + let groupMatchesFilter = false; + for (var i = 0; i < cards.length; i++) { + var card = cards[i]; + var title = card.querySelector('h5.card-title').innerText; + var text = card.querySelector('p.card-text').innerText; + + // Get the navbar tags associated with the card + var navbarItem = document.querySelector(`a.dropdown-item[href="${card.id}"]`); + var navbarTags = navbarItem ? navbarItem.getAttribute('data-bs-tags') : ''; + + var content = title + ' ' + text + ' ' + navbarTags; + + if (content.toUpperCase().indexOf(filter) > -1) { + card.style.display = ''; + groupMatchesFilter = true; + } else { + card.style.display = 'none'; + } + } + + if (!groupMatchesFilter) { + featureGroup.style.display = 'none'; + } else { + featureGroup.style.display = ''; + resetOrTemporarilyExpandGroup(featureGroup, filter, collapsedGroups); + } + } +} + +function getCollapsedGroups() { + return localStorage.getItem('collapsedGroups') ? JSON.parse(localStorage.getItem('collapsedGroups')) : []; +} + +function resetOrTemporarilyExpandGroup(featureGroup, filterKeywords = '', collapsedGroups = []) { + const shouldResetCollapse = filterKeywords.trim() === ''; + if (shouldResetCollapse) { + // Resetting the group's expand/collapse to its original state (as in collapsed groups) + const isCollapsed = collapsedGroups.indexOf(featureGroup.id) != -1; + expandCollapseToggle(featureGroup, !isCollapsed); + } else { + // Temporarily expands feature group without affecting the actual/stored collapsed groups + featureGroup.classList.remove('collapsed'); + featureGroup.querySelector('.header-expand-button').classList.remove('collapsed'); + } +} + +function updateFavoritesSectionLegacy() { + const favoritesContainer = document.getElementById('groupFavorites').querySelector('.feature-group-container'); + favoritesContainer.innerHTML = ''; + const cards = Array.from(document.querySelectorAll('.feature-card:not(.duplicate)')); + const addedCardIds = new Set(); + let favoritesAmount = 0; + + cards.forEach((card) => { + const favouritesList = JSON.parse(localStorage.getItem('favoritesList') || '[]'); + + if (favouritesList.includes(card.id) && !addedCardIds.has(card.id)) { + const duplicate = card.cloneNode(true); + duplicate.classList.add('duplicate'); + favoritesContainer.appendChild(duplicate); + addedCardIds.add(card.id); + favoritesAmount++; + } + }); + + if (favoritesAmount === 0) { + document.getElementById('groupFavorites').style.display = 'none'; + } else { + document.getElementById('groupFavorites').style.display = 'flex'; + } + reorderCards(favoritesContainer); + favoritesContainer.style.maxHeight = favoritesContainer.scrollHeight + 'px'; +} + +function syncFavoritesLegacy() { + const cards = Array.from(document.querySelectorAll('.feature-card')); + cards.forEach((card) => { + const isFavorite = localStorage.getItem(card.id) === 'favorite'; + const starIcon = card.querySelector('.favorite-icon span.material-symbols-rounded'); + if (starIcon) { + if (isFavorite) { + starIcon.classList.remove('no-fill'); + starIcon.classList.add('fill'); + card.classList.add('favorite'); + } else { + starIcon.classList.remove('fill'); + starIcon.classList.add('no-fill'); + card.classList.remove('favorite'); + } + } + }); + updateFavoritesSectionLegacy(); + updateFavoritesDropdown(); + filterCardsLegacy(); +} + +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'; + if (a.id === 'update-link') { + return -1; + } + if (b.id === 'update-link') { + return 1; + } + + if (aIsFavorite && !bIsFavorite) { + return -1; + } else if (!aIsFavorite && bIsFavorite) { + return 1; + } 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 initializeCardsLegacy() { + reorderAllCards(); + updateFavoritesSectionLegacy(); + updateFavoritesDropdown(); + filterCardsLegacy(); +} + +function showFavoritesOnly() { + const groups = Array.from(document.querySelectorAll('.feature-group-legacy')); + 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-legacy')); + groups.forEach((group) => { + expandCollapseToggle(group, expandAll); + }); +} + +window.onload = function () { + initializeCardsLegacy(); + syncFavoritesLegacy(); // Ensure everything is in sync on page 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-legacy')).forEach((header) => { + const parent = header.parentNode; + const container = header.parentNode.querySelector('.feature-group-container'); + if (parent.id !== 'groupFavorites') { + container.style.maxHeight = container.scrollHeight + 'px'; + } + header.onclick = () => { + expandCollapseToggle(parent); + }; + }); + + const collapsed = localStorage.getItem('collapsedGroups') ? JSON.parse(localStorage.getItem('collapsedGroups')) : []; + const groupsArray = Array.from(document.querySelectorAll('.feature-group-legacy')); + + groupsArray.forEach((group) => { + if (collapsed.indexOf(group.id) !== -1) { + expandCollapseToggle(group, false); + } + }); + + // Necessary in order to not fire the transition animation on page load, which looks wrong. + // The timeout isn't doing anything visible to the user, so it's not making the page load look slower. + setTimeout(() => { + groupsArray.forEach((group) => { + const container = group.querySelector('.feature-group-container'); + container.classList.add('animated-group'); + }); + }, 500); + + showFavoritesOnly(); +}); diff --git a/src/main/resources/static/js/homecard.js b/src/main/resources/static/js/homecard.js index 67fd41d901d..b57b8565f57 100644 --- a/src/main/resources/static/js/homecard.js +++ b/src/main/resources/static/js/homecard.js @@ -42,77 +42,60 @@ function filterCards() { } function updateFavoritesSection() { - const favoritesContainer = document.getElementById('groupFavorites').querySelector('.feature-group-container'); - favoritesContainer.innerHTML = ''; - const cards = Array.from(document.querySelectorAll('.feature-card:not(.duplicate)')); - const addedCardIds = new Set(); - let favoritesAmount = 0; - - cards.forEach((card) => { - if (localStorage.getItem(card.id) === 'favorite' && !addedCardIds.has(card.id)) { - const duplicate = card.cloneNode(true); - duplicate.classList.add('duplicate'); - favoritesContainer.appendChild(duplicate); - addedCardIds.add(card.id); - favoritesAmount++; - } - }); + if (localStorage.getItem('favoritesView') === 'true') { + const favoritesContainer = document.getElementById('groupFavorites').querySelector('.nav-group-container'); + favoritesContainer.innerHTML = ''; + let favoritesAmount = 0; + const favouritesList = JSON.parse(localStorage.getItem('favoritesList') || '[]'); + + favouritesList.forEach((value) => { + var navbarEntry = document.querySelector(`a[data-bs-link='${value}']`); + if (navbarEntry) { + const duplicate = navbarEntry.cloneNode(true); + favoritesContainer.appendChild(duplicate); + } + }); - if (favoritesAmount === 0) { - document.getElementById('groupFavorites').style.display = 'none'; - } else { - document.getElementById('groupFavorites').style.display = 'flex'; + if (favoritesAmount === 0) { + document.getElementById('groupFavorites').style.display = 'none'; + } else { + document.getElementById('groupFavorites').style.display = 'flex'; + } + reorderCards(favoritesContainer); + favoritesContainer.style.maxHeight = favoritesContainer.scrollHeight + 'px'; } - reorderCards(favoritesContainer); - favoritesContainer.style.maxHeight = favoritesContainer.scrollHeight + 'px'; -} -function toggleFavorite(element) { - var span = element.querySelector('span.material-symbols-rounded'); - var card = element.closest('.dropdown-item'); - var cardId = card.id; + function toggleFavorite(element) { + var span = element.querySelector('span.material-symbols-rounded'); + var card = element.closest('.dropdown-item'); + var cardId = card.id; - // Prevent the event from bubbling up to parent elements - event.stopPropagation(); + // Prevent the event from bubbling up to parent elements + event.stopPropagation(); - if (span.classList.contains('no-fill')) { - span.classList.remove('no-fill'); - span.classList.add('fill'); - card.classList.add('favorite'); - localStorage.setItem(cardId, 'favorite'); - } else { - span.classList.remove('fill'); - span.classList.add('no-fill'); - card.classList.remove('favorite'); - localStorage.removeItem(cardId); - } + if (span.classList.contains('no-fill')) { + span.classList.remove('no-fill'); + span.classList.add('fill'); + card.classList.add('favorite'); + localStorage.setItem(cardId, 'favorite'); + } else { + span.classList.remove('fill'); + span.classList.add('no-fill'); + card.classList.remove('favorite'); + localStorage.removeItem(cardId); + } - // Use setTimeout to ensure this runs after the current call stack is clear - setTimeout(() => { - reorderCards(card.parentNode); - updateFavoritesSection(); - updateFavoritesDropdown(); - filterCards(); - }, 0); + // Use setTimeout to ensure this runs after the current call stack is clear + setTimeout(() => { + reorderCards(card.parentNode); + updateFavoritesSection(); + updateFavoritesDropdown(); + filterCards(); + }, 0); + } } function syncFavorites() { - const cards = Array.from(document.querySelectorAll('.dropdown-item')); - cards.forEach((card) => { - const isFavorite = localStorage.getItem(card.id) === 'favorite'; - const starIcon = card.querySelector('.favorite-icon span.material-symbols-rounded'); - if (starIcon) { - if (isFavorite) { - starIcon.classList.remove('no-fill'); - starIcon.classList.add('fill'); - card.classList.add('favorite'); - } else { - starIcon.classList.remove('fill'); - starIcon.classList.add('no-fill'); - card.classList.remove('favorite'); - } - } - }); updateFavoritesSection(); updateFavoritesDropdown(); filterCards(); @@ -146,54 +129,28 @@ function reorderCards(container) { }); } -function reorderAllCards() { - const containers = Array.from(document.querySelectorAll('.feature-group-container')); - containers.forEach(function (container) { - reorderCards(container); - }); -} - function initializeCards() { - var cards = document.querySelectorAll('.dropdown-item'); - cards.forEach(function (card) { - var cardId = card.id; - var span = card.querySelector('.favorite-icon span.material-symbols-rounded'); - if (localStorage.getItem(cardId) === 'favorite') { - // span.classList.remove('no-fill'); - // span.classList.add('fill'); - // card.classList.add('favorite'); - } - }); - 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'; - } - }); +function showFavorites() { + const favoritesGroup = document.querySelector('#groupFavorites'); + if (localStorage.getItem('favoritesView') === 'true') { + favoritesGroup.style.display = 'flex'; } else { - groups.forEach((group) => { - if (group.id !== 'groupFavorites') { - group.style.display = 'flex'; - } - }); + favoritesGroup.style.display = 'none'; } } -function toggleFavoritesOnly() { - if (localStorage.getItem('favoritesOnly') === 'true') { - localStorage.setItem('favoritesOnly', 'false'); +function toggleFavoritesView() { + if (localStorage.getItem('favoritesView') === 'true') { + localStorage.setItem('favoritesView', 'false'); } else { - localStorage.setItem('favoritesOnly', 'true'); + localStorage.setItem('favoritesView', 'true'); } - showFavoritesOnly(); + showFavorites(); } window.onload = function () { @@ -221,6 +178,4 @@ document.addEventListener('DOMContentLoaded', function () { expandCollapseToggle(parent); }; }); - - showFavoritesOnly(); }); diff --git a/src/main/resources/templates/fragments/card.html b/src/main/resources/templates/fragments/card.html index e4f5e9130d1..b2ab67eece7 100644 --- a/src/main/resources/templates/fragments/card.html +++ b/src/main/resources/templates/fragments/card.html @@ -1,5 +1,6 @@ - diff --git a/src/main/resources/templates/fragments/common.html b/src/main/resources/templates/fragments/common.html index a6099f5a121..153e03c8b36 100644 --- a/src/main/resources/templates/fragments/common.html +++ b/src/main/resources/templates/fragments/common.html @@ -52,6 +52,7 @@ + diff --git a/src/main/resources/templates/fragments/featureGroupHeaderLegacy.html b/src/main/resources/templates/fragments/featureGroupHeaderLegacy.html new file mode 100644 index 00000000000..0a8f7e9b12f --- /dev/null +++ b/src/main/resources/templates/fragments/featureGroupHeaderLegacy.html @@ -0,0 +1,6 @@ +
+ + + chevron_right + +
\ No newline at end of file diff --git a/src/main/resources/templates/fragments/navElements.html b/src/main/resources/templates/fragments/navElements.html index 17eb1bbaa26..eabd718be00 100644 --- a/src/main/resources/templates/fragments/navElements.html +++ b/src/main/resources/templates/fragments/navElements.html @@ -1,10 +1,9 @@ -
-
+