From 148af6abaa86d4e660e52210d31118b8ce56218d Mon Sep 17 00:00:00 2001 From: db-2001 Date: Tue, 6 Feb 2024 22:23:19 -0500 Subject: [PATCH] Filter functionality Removed textbox in top bar and added filter icon that opens up a box where you can search or select a connector/status and combination to filter by. Appearance and behavior needs to be refined. --- Website/index.html | 36 ++++++- Website/interaction.js | 173 +++++++++++++++++++++++++++----- Website/media/filter-funnel.svg | 12 +++ Website/styles/base.css | 158 ++++++++++++++++++++++++++--- 4 files changed, 337 insertions(+), 42 deletions(-) create mode 100644 Website/media/filter-funnel.svg diff --git a/Website/index.html b/Website/index.html index ab12385..5f8165e 100644 --- a/Website/index.html +++ b/Website/index.html @@ -9,17 +9,44 @@ + website image is Blahaj Tranga - - - + filterFunnel settingscog + + + + Filter by: + × + + + + + + + Clear Filter + + +
@@ -54,8 +81,7 @@ - + Settings diff --git a/Website/interaction.js b/Website/interaction.js index 87bc1fc..f6d7732 100644 --- a/Website/interaction.js +++ b/Website/interaction.js @@ -5,10 +5,18 @@ let notificationConnectorTypes = []; let libraryConnectorTypes = []; let selectedManga; let selectedJob; +let searchMatch; + +let connectorMatch = []; +let connectorNameMatch; +let statusMatch = []; +let statusNameMatch = []; const searchBox = document.querySelector("#searchbox"); const settingsPopup = document.querySelector("#settingsPopup"); +const filterBox = document.querySelector("#filterBox"); const settingsCog = document.querySelector("#settingscog"); +const filterFunnel = document.querySelector("#filterFunnel"); const tasksContent = document.querySelector("content"); const createMonitorTaskButton = document.querySelector("#createMonitoJobButton"); const createDownloadChapterTaskButton = document.querySelector("#createDownloadChapterJobButton"); @@ -75,13 +83,40 @@ function Setup(){ GetAvailableControllers().then((json) => { //console.log(json); newMangaConnector.replaceChildren(); - json.forEach(connector => { + connectorFilterBox = document.querySelector("#connectorFilterBox"); + json.forEach(connector => { + //Add the connector to the New Manga dropdown var option = document.createElement('option'); option.value = connector; option.innerText = connector; newMangaConnector.appendChild(option); + + //Add the connector to the filter box + connectorFilter = document.createElement('connector-name'); + connectorFilter.innerText = connector; + connectorFilter.className = "pill"; + connectorFilter.style.backgroundColor = stringToColour(connector); + + connectorFilter.addEventListener("click", (event) => { + ToggleFilterConnector(connector, event); + }); + connectorFilterBox.appendChild(connectorFilter); }); }); + + //Add the publication status options to the filter bar + publicationStatusOptions = ["Ongoing", "Completed", "On Hiatus", "Cancelled", "Upcoming", "Status Unavailable"]; + statusFilterBox = document.querySelector("#statusFilterBox"); + publicationStatusOptions.forEach(publicationStatus => { + var releaseStatus = document.createElement('status-filter'); + releaseStatus.innerText = publicationStatus; + releaseStatus.setAttribute("release-status", publicationStatus); + releaseStatus.addEventListener("click", (event) => { + ToggleFilterStatus(publicationStatus, event); + }); + + statusFilterBox.appendChild(releaseStatus); + }); ResetContent(); UpdateJobs(); @@ -102,9 +137,48 @@ function Setup(){ UpdateJobs(); }, 1000); }); + //Clear the previous values if any exist. + searchBox.value = ""; + connectorMatch.length = 0; + statusMatch.length = 0; } Setup(); +function ToggleFilterConnector(connector) { + //console.log("Initial Array:"); + //console.log(connectorMatch); + if (connectorMatch.includes(connector)) { + idx = connectorMatch.indexOf(connector); + connectorMatch.splice(idx, 1); + } else { + connectorMatch.push(connector); + } + //console.log("Final Array"); + //console.log(connectorMatch); + FilterResults(); +} + +function ToggleFilterStatus(status) { + //console.log("Initial Array:"); + //console.log(statusMatch); + if (statusMatch.includes(status)) { + idx = statusMatch.indexOf(status); + statusMatch.splice(idx, 1); + } else { + statusMatch.push(status); + } + //console.log("Final Array"); + //console.log(statusMatch); + FilterResults(); +} + +function ClearFilter() { + searchBox.value = ""; + statusMatch.length = 0; + connectorMatch.length = 0; + FilterResults(); +} + function updateCSS(){ if (document.getElementById("mangaHoverCheckbox").checked == true){ ChangeStyleSheet('hover') @@ -300,25 +374,72 @@ function HidePublicationPopup(){ searchBox.addEventListener("keyup", () => FilterResults()); //Filter shown jobs function FilterResults(){ - if(searchBox.value.length > 0){ - tasksContent.childNodes.forEach(publication => { - publication.childNodes.forEach(item => { - if(item.nodeName.toLowerCase() == "publication-information"){ - item.childNodes.forEach(information => { - if(information.nodeName.toLowerCase() == "publication-name"){ - if(!information.textContent.toLowerCase().includes(searchBox.value.toLowerCase())){ - publication.style.display = "none"; - }else{ - publication.style.display = "initial"; - } - } - }); - } - }); - }); - }else{ - tasksContent.childNodes.forEach(publication => publication.style.display = "initial"); + //For each publication + tasksContent.childNodes.forEach(publication => { + //If the search box isn't empty check that the title contains the searchbox content. If it does then + //'searchMatch' is true and the manga is shown. If the search box is empty, then consider this field + //to be true anyways. + if (searchBox.value.length > 0) { + publication.childNodes.forEach(item => { + if (item.nodeName.toLowerCase() == "publication-information"){ + item.childNodes.forEach(information => { + if (information.nodeName.toLowerCase() == "publication-name") { + if (information.textContent.toLowerCase().includes(searchBox.value.toLowerCase())){ + searchMatch = 1; + } else { + searchMatch = 0; + } + } + }); + } + }); + } else { + searchMatch = 1; } + + //If the array connectorMatch isn't empty then check that the connector matches one of the ones + //in the array + if (connectorMatch.length > 0) { + publication.childNodes.forEach(item => { + if (item.nodeName.toLowerCase() == "publication-information"){ + item.childNodes.forEach(information => { + if (information.nodeName.toLowerCase() == "connector-name") { + if (connectorMatch.includes(information.textContent)){ + connectorNameMatch = 1; + } else { + connectorNameMatch = 0; + } + } + }); + } + }); + } else { + connectorNameMatch = 1; + } + + //If the array statusMatch isn't empty then check that the status matches one of the ones + //in the array + if (statusMatch.length > 0) { + publication.childNodes.forEach(item => { + if (item.nodeName.toLowerCase() == "publication-status"){ + if (statusMatch.includes(item.getAttribute('release-status'))) { + statusNameMatch = 1; + } else { + statusNameMatch = 0; + } + } + }); + } else { + statusNameMatch = 1; + } + + //If all of the filtering conditions are met then show the manga, otherwise hide it. + if (searchMatch && connectorNameMatch && statusNameMatch) { + publication.style.display = 'initial'; + } else { + publication.style.display = 'none'; + } + }); } settingsCog.addEventListener("click", () => { @@ -326,6 +447,10 @@ settingsCog.addEventListener("click", () => { settingsPopup.style.display = "flex"; }); +filterFunnel.addEventListener("click", () => { + filterBox.classList.toggle("animate"); +}); + settingKomgaUrl.addEventListener("keypress", (event) => { { if(event.key === "Enter") UpdateSettings(); } }); settingKomgaUser.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); settingKomgaPass.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); @@ -430,7 +555,7 @@ function ClearKomga(){ settingKomgaUrl.value = ""; settingKomgaUser.value = ""; settingKomgaPass.value = ""; - settingKomgaConfigured.setAttribute("configuration", "Connector Not Configured"); + settingKomgaConfigured.setAttribute("configuration", "Not Configured"); ResetKomga(); } @@ -438,27 +563,27 @@ function ClearKavita(){ settingKavitaUrl.value = ""; settingKavitaUser.value = ""; settingKavitaPass.value = ""; - settingKavitaConfigured.setAttribute("configuration", "Connector Not Configured"); + settingKavitaConfigured.setAttribute("configuration", "Not Configured"); ResetKavita(); } function ClearGotify(){ settingGotifyUrl.value = ""; settingGotifyAppToken.value = "" - settingGotifyConfigured.setAttribute("configuration", "Connector Not Configured"); + settingGotifyConfigured.setAttribute("configuration", "Not Configured"); ResetGotify(); } function ClearLunasea(){ settingLunaseaWebhook.value = ""; - settingLunaseaConfigured.setAttribute("configuration", "Connector Not Configured"); + settingLunaseaConfigured.setAttribute("configuration", "Not Configured"); ResetLunaSea(); } function ClearNtfy(){ settingNtfyEndpoint.value = ""; settingNtfyAuth.value = ""; - settingNtfyConfigured.setAttribute("configuration", "Connector Not Configured"); + settingNtfyConfigured.setAttribute("configuration", "Not Configured"); ResetNtfy(); } diff --git a/Website/media/filter-funnel.svg b/Website/media/filter-funnel.svg new file mode 100644 index 0000000..5092c6d --- /dev/null +++ b/Website/media/filter-funnel.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/Website/styles/base.css b/Website/styles/base.css index 3aa4c77..8c75ef3 100644 --- a/Website/styles/base.css +++ b/Website/styles/base.css @@ -69,7 +69,6 @@ titlebox span{ titlebox img { height: 100%; - margin-right: 10px; cursor: grab; } @@ -77,22 +76,156 @@ spacer{ flex-grow: 1; } -searchdiv{ +filter-box { + display: none; + align-self: center; + flex-direction: column; + position: relative; + + margin: 10px; + background-color: var(--second-background-color); + border-style: solid; + border-color: var(--primary-color); + border-width: 2px; + border-radius: 15px; + min-width: 300px; + width: 50%; + overflow: hidden; + max-height: 50%; + height: 400px; +} + +filter-box.animate { + display: flex; +} + +filter-box border-bar popup-title{ + font-size: 12pt; +} + +filter-box border-bar popup-close { + height: 20px; + width: 20px; + font-size: 12pt; + -webkit-user-select: none; /* Safari */ + -ms-user-select: none; /* IE 10 and IE 11 */ + user-select: none; /* Standard syntax */ +} + +border-bar-button.clearFilter{ + font-weight: bold; + margin: 0px 10px 10px 10px; + border-color: lightgray; + color: gray; + align-content: center; + justify-content: center; +} + +border-bar-button.clearFilter:hover { + background-color: red; + border-color: var(--second-background-color); + color: var(--second-background-color); +} + +status-filter { display: block; - margin: 0 10px 0 0; + margin: 10px; + + /*Text Properties*/ + font-size:10pt; + font-weight:bold; + color:white; + text-align: center; + + /*Size*/ + padding: 3px 8px; + border-radius: 6px; + border: 0px; + background-color: inherit; + + cursor: pointer; + -webkit-user-select: none; /* Safari */ + -ms-user-select: none; /* IE 10 and IE 11 */ + user-select: none; /* Standard syntax */ +} + +status-filter[release-status="Ongoing"]{ + background-color: limegreen; +} + +status-filter[release-status="Completed"]{ + background-color: blueviolet; +} + +status-filter[release-status="On Hiatus"]{ + background-color: darkorange; +} + +status-filter[release-status="Cancelled"]{ + background-color: firebrick; +} + +status-filter[release-status="Upcoming"]{ + background-color: aqua; +} + +status-filter[release-status="Status Unavailable"]{ + background-color: gray; +} + + +searchdiv{ + display: flex; + width: 100%; } #searchbox { - padding: 3px 10px; - border: 0; - border-radius: 4px; - font-size: 14pt; - width: 250px; + display: flex; + padding: 3px 5px; + margin: 5px; + border-style: solid; + border-width: 2px; + border-radius: 10px; + font-size: 12pt; + outline: none; + border-color: lightgray; + flex-grow: 1; + flex-shrink: 1; +} + +#searchbox:focus { + border-color: var(--secondary-color); +} + +.pill { + flex-grow: 0; + height: 14pt; + font-size: 12pt; + border-radius: 9pt; + background-color: var(--primary-color); + padding: 2pt 17px; + color: black; +} + +#connectorFilterBox .pill { + margin: 10px; + cursor: pointer; + -webkit-user-select: none; /* Safari */ + -ms-user-select: none; /* IE 10 and IE 11 */ + user-select: none; /* Standard syntax */ } #settingscog { cursor: pointer; margin: 0px 30px; + margin-left: 15px; + height: 50%; + filter: invert(100%) sepia(0%) saturate(7465%) hue-rotate(115deg) brightness(116%) contrast(101%); +} + +#filterFunnel { + cursor: pointer; + margin: 0px 15px; height: 50%; filter: invert(100%) sepia(0%) saturate(7465%) hue-rotate(115deg) brightness(116%) contrast(101%); } @@ -179,6 +312,7 @@ border-bar { margin:0; align-items: center; position: relative; + width: 100%; } popup-title { @@ -198,7 +332,7 @@ popup-close { display: flex; cursor: pointer; margin-left: auto; - margin-right: 5; + margin-right: 15px; height: 32px; width: 32px; border-radius: 16px; @@ -268,7 +402,7 @@ popup popup-window { overflow: hidden; } -popup popup-window popup-content{ +popup-content{ display: flex; flex-direction: column; align-items: left; @@ -288,7 +422,7 @@ popup-content > .popup-section { border-top-style: solid; border-top-width: 1px; border-top-color: lightgray; - width: 100%; + width: calc(100%-10px); padding: 10px; } @@ -377,8 +511,6 @@ popup-content > .popup-section { background-color: gray; } - - .section-item > input { margin: 2px; padding: 2px;