From dda3e355ac31dc4b9629f2e9e63474bbba69d740 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sun, 30 Jan 2022 14:29:48 +0100 Subject: Refactor: collapsibles as a stand-alone component --- pydis_site/static/js/resources/resources.js | 236 ++++++++++++++++++++++++++++ 1 file changed, 236 insertions(+) create mode 100644 pydis_site/static/js/resources/resources.js (limited to 'pydis_site/static/js/resources/resources.js') diff --git a/pydis_site/static/js/resources/resources.js b/pydis_site/static/js/resources/resources.js new file mode 100644 index 00000000..718e1e88 --- /dev/null +++ b/pydis_site/static/js/resources/resources.js @@ -0,0 +1,236 @@ +"use strict"; + +// Filters that are currently selected +var activeFilters = { + topics: [], + type: [], + "payment-tiers": [], + difficulty: [] +}; + +function addFilter(filterName, filterItem) { + // Push the filter into the stack + var filterIndex = activeFilters[filterName].indexOf(filterItem); + if (filterIndex === -1) { + activeFilters[filterName].push(filterItem); + } + updateUI(); + + // Show a corresponding filter box tag + $(`.filter-box-tag[data-filter-name=${filterName}][data-filter-item=${filterItem}]`).show(); + + // Make corresponding resource tags active + $(`.resource-tag[data-filter-name=${filterName}][data-filter-item=${filterItem}]`).addClass("active"); + + // Hide the "No filters selected" tag. + $(".no-tags-selected.tag").hide(); +} + +function removeFilter(filterName, filterItem) { + // Remove the filter from the stack + var filterIndex = activeFilters[filterName].indexOf(filterItem); + if (filterIndex !== -1) { + activeFilters[filterName].splice(filterIndex, 1); + } + updateUI(); + + // Hide the corresponding filter box tag + $(`.filter-box-tag[data-filter-name=${filterName}][data-filter-item=${filterItem}]`).hide(); + + // Make corresponding resource tags inactive + $(`.resource-tag[data-filter-name=${filterName}][data-filter-item=${filterItem}]`).removeClass("active"); + + // Show "No filters selected" tag, if there are no filters active + if (noFilters()) { + $(".no-tags-selected.tag").show(); + } +} + +/* Check if there are no filters */ +function noFilters() { + return ( + activeFilters.topics.length === 0 && + activeFilters.type.length === 0 && + activeFilters["payment-tiers"].length === 0 && + activeFilters.difficulty.length === 0 + ); +} + +/* Get the params out of the URL and use them. This is run when the page loads. */ +function deserializeURLParams() { + let searchParams = new window.URLSearchParams(window.location.search); + + // Work through the parameters and add them to the filter object + $.each(Object.keys(activeFilters), function(_, filterType) { + let paramFilterContent = searchParams.get(filterType); + + if (paramFilterContent !== null) { + // We use split here because we always want an array, not a string. + let paramFilterArray = paramFilterContent.split(","); + activeFilters[filterType] = paramFilterArray; + + // Update the corresponding filter UI, so it reflects the internal state. + $(paramFilterArray).each(function(_, filter) { + let checkbox = $(`.filter-checkbox[data-filter-name='${filterType}'][data-filter-item='${filter}']`); + let filterTag = $(`.filter-box-tag[data-filter-name='${filterType}'][data-filter-item='${filter}']`); + let resourceTags = $(`.resource-tag[data-filter-name='${filterType}'][data-filter-item='${filter}']`); + checkbox.prop("checked", true); + filterTag.show(); + resourceTags.addClass("active"); + }); + } + }); +} + +/* Update the URL with new parameters */ +function updateURL() { + // If there's nothing in the filters, we don't want anything in the URL. + if (noFilters()) { + window.history.replaceState(null, document.title, './'); + return; + } + + // Iterate through and get rid of empty ones + let searchParams = new URLSearchParams(activeFilters); + $.each(activeFilters, function(filterType, filters) { + if (filters.length === 0) { + searchParams.delete(filterType); + } + }); + + // Now update the URL + window.history.replaceState(null, document.title, `?${searchParams.toString()}`); +} + +/* Update the resources to match 'active_filters' */ +function updateUI() { + let resources = $('.resource-box'); + let filterTags = $('.filter-box-tag'); + + // Update the URL to match the new filters. + updateURL(); + + // If there's nothing in the filters, show everything and return. + if (noFilters()) { + resources.show(); + filterTags.hide(); + return; + } + + // Otherwise, hide everything and then filter the resources to decide what to show. + let hasMatches = false; + resources.hide(); + resources.filter(function() { + let validation = { + topics: false, + type: false, + 'payment-tiers': false, + difficulty: false + }; + let resourceBox = $(this); + + // Validate the filters + $.each(activeFilters, function(filterType, filters) { + // If the filter list is empty, this passes validation. + if (filters.length === 0) { + validation[filterType] = true; + return; + } + + // Otherwise, we need to check if one of the classes exist. + $.each(filters, function(index, filter) { + if (resourceBox.hasClass(`${filterType}-${filter}`)) { + validation[filterType] = true; + } + }); + }); + + // If validation passes, show the resource. + if (Object.values(validation).every(Boolean)) { + hasMatches = true; + return true; + } else { + return false; + } + }).show(); + + // If there are no matches, show the no matches message + if (!hasMatches) { + $(".no-resources-found").show(); + } else { + $(".no-resources-found").hide(); + } +} + +// Executed when the page has finished loading. +document.addEventListener("DOMContentLoaded", function () { + /* Check if the user has navigated to one of the old resource pages, + like pydis.com/resources/communities. In this case, we'll rewrite + the URL before we do anything else. */ + let resourceTypeInput = $("#resource-type-input").val(); + if (resourceTypeInput !== "None") { + window.history.replaceState(null, document.title, `../?type=${resourceTypeInput}`); + } + + // Update the filters on page load to reflect URL parameters. + $('.filter-box-tag').hide(); + deserializeURLParams(); + updateUI(); + + // If you collapse or uncollapse a filter group, swap the icon. + $('button.collapsible').click(function() { + let icon = $(this).find(".card-header-icon i"); + + if ($(icon).hasClass("fa-window-minimize")) { + $(icon).removeClass(["far", "fa-window-minimize"]); + $(icon).addClass(["fas", "fa-angle-down"]); + } else { + $(icon).removeClass(["fas", "fa-angle-down"]); + $(icon).addClass(["far", "fa-window-minimize"]); + } + }); + + // If you click on the div surrounding the filter checkbox, it clicks the corresponding checkbox. + $('.filter-panel').click(function() { + let checkbox = $(this).find(".filter-checkbox"); + checkbox.prop("checked", !checkbox.prop("checked")); + checkbox.change(); + }); + + // If you click on one of the tags in the filter box, it unchecks the corresponding checkbox. + $('.filter-box-tag').click(function() { + let filterItem = this.dataset.filterItem; + let filterName = this.dataset.filterName; + let checkbox = $(`.filter-checkbox[data-filter-name='${filterName}'][data-filter-item='${filterItem}']`); + + removeFilter(filterName, filterItem); + checkbox.prop("checked", false); + }); + + // If you click on one of the tags in the resource cards, it clicks the corresponding checkbox. + $('.resource-tag').click(function() { + let filterItem = this.dataset.filterItem; + let filterName = this.dataset.filterName; + let checkbox = $(`.filter-checkbox[data-filter-name='${filterName}'][data-filter-item='${filterItem}']`); + + if (!$(this).hasClass("active")) { + addFilter(filterName, filterItem); + checkbox.prop("checked", true); + } else { + removeFilter(filterName, filterItem); + checkbox.prop("checked", false); + } + }); + + // When checkboxes are toggled, trigger a filter update. + $('.filter-checkbox').change(function () { + let filterItem = this.dataset.filterItem; + let filterName = this.dataset.filterName; + + if (this.checked) { + addFilter(filterName, filterItem); + } else { + removeFilter(filterName, filterItem); + } + }); +}); -- cgit v1.2.3 From ca45357255f5957cf779c109fc24e83de904a195 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sun, 30 Jan 2022 20:25:34 +0100 Subject: Collapse category headers on load for mobile only. --- pydis_site/static/css/resources/resources.css | 8 ++++++++ pydis_site/static/js/resources/resources.js | 13 ++++++++++++- pydis_site/templates/resources/resources.html | 2 +- 3 files changed, 21 insertions(+), 2 deletions(-) (limited to 'pydis_site/static/js/resources/resources.js') diff --git a/pydis_site/static/css/resources/resources.css b/pydis_site/static/css/resources/resources.css index 59f8b78e..b24fcb5b 100644 --- a/pydis_site/static/css/resources/resources.css +++ b/pydis_site/static/css/resources/resources.css @@ -198,4 +198,12 @@ span.resource-tag.active.has-background-info-light { padding-top: 4px; padding-bottom: 4px; } +} + +/* Disable transitions */ +.no-transition { + -webkit-transition: none !important; + -moz-transition: none !important; + -o-transition: none !important; + transition: none !important; } \ No newline at end of file diff --git a/pydis_site/static/js/resources/resources.js b/pydis_site/static/js/resources/resources.js index 718e1e88..44d4db5c 100644 --- a/pydis_site/static/js/resources/resources.js +++ b/pydis_site/static/js/resources/resources.js @@ -178,7 +178,7 @@ document.addEventListener("DOMContentLoaded", function () { updateUI(); // If you collapse or uncollapse a filter group, swap the icon. - $('button.collapsible').click(function() { + $('button.collapsible').on("click", function() { let icon = $(this).find(".card-header-icon i"); if ($(icon).hasClass("fa-window-minimize")) { @@ -190,6 +190,17 @@ document.addEventListener("DOMContentLoaded", function () { } }); + // If this is a mobile device, collapse the categories to win back some screen real estate. + if (screen.width < 480) { + let categoryHeaders = $(".filter-category-header .collapsible-content"); + let icons = $('.filter-category-header button .card-header-icon i'); + categoryHeaders.addClass("no-transition"); + categoryHeaders.css("max-height", 0); + icons.removeClass(["far", "fa-window-minimize"]); + icons.addClass(["fas", "fa-angle-down"]); + categoryHeaders.removeClass("no-transition"); + } + // If you click on the div surrounding the filter checkbox, it clicks the corresponding checkbox. $('.filter-panel').click(function() { let checkbox = $(this).find(".filter-checkbox"); diff --git a/pydis_site/templates/resources/resources.html b/pydis_site/templates/resources/resources.html index 80577c2b..a7b25f5c 100644 --- a/pydis_site/templates/resources/resources.html +++ b/pydis_site/templates/resources/resources.html @@ -104,7 +104,7 @@ {% if filter_data.hidden %}
{% else %} -
+
{% endif %}
{% for filter_item in filter_data.filters %} -- cgit v1.2.3 From 91935d88476bade9701d354e82cafba912a33f69 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Mon, 31 Jan 2022 18:02:59 +0100 Subject: Get rid of invalid filters in the URL. --- pydis_site/apps/resources/views/resources.py | 11 +++++++++++ pydis_site/static/js/resources/resources.js | 24 +++++++++++++++++------- pydis_site/templates/resources/resources.html | 5 +++++ 3 files changed, 33 insertions(+), 7 deletions(-) (limited to 'pydis_site/static/js/resources/resources.js') diff --git a/pydis_site/apps/resources/views/resources.py b/pydis_site/apps/resources/views/resources.py index a5c2cf7c..a38c3b59 100644 --- a/pydis_site/apps/resources/views/resources.py +++ b/pydis_site/apps/resources/views/resources.py @@ -1,3 +1,4 @@ +import json import typing as t from pathlib import Path @@ -8,6 +9,7 @@ from django.shortcuts import render from django.views import View from pydis_site import settings +from pydis_site.apps.resources.templatetags.as_css_class import as_css_class RESOURCES_PATH = Path(settings.BASE_DIR, "pydis_site", "apps", "resources", "resources") @@ -83,6 +85,14 @@ class ResourceView(View): self.filters["Topics"]["filters"].remove("Other") self.filters["Topics"]["filters"].append("Other") + # A complete list of valid filter names + self.valid_filters = { + "topics": [as_css_class(topic) for topic in self.filters["Topics"]["filters"]], + "payment_tiers": [as_css_class(tier) for tier in self.filters["Payment tiers"]["filters"]], + "type": [as_css_class(type_) for type_ in self.filters["Type"]["filters"]], + "difficulty": [as_css_class(tier) for tier in self.filters["Difficulty"]["filters"]], + } + def get(self, request: WSGIRequest, resource_type: t.Optional[str] = None) -> HttpResponse: """List out all the resources, and any filtering options from the URL.""" # Add type filtering if the request is made to somewhere like /resources/video. @@ -101,6 +111,7 @@ class ResourceView(View): context={ "resources": self.resources, "filters": self.filters, + "valid_filters": json.dumps(self.valid_filters), "resource_type": resource_type, } ) diff --git a/pydis_site/static/js/resources/resources.js b/pydis_site/static/js/resources/resources.js index 44d4db5c..ad26afd4 100644 --- a/pydis_site/static/js/resources/resources.js +++ b/pydis_site/static/js/resources/resources.js @@ -67,17 +67,27 @@ function deserializeURLParams() { if (paramFilterContent !== null) { // We use split here because we always want an array, not a string. let paramFilterArray = paramFilterContent.split(","); - activeFilters[filterType] = paramFilterArray; // Update the corresponding filter UI, so it reflects the internal state. $(paramFilterArray).each(function(_, filter) { - let checkbox = $(`.filter-checkbox[data-filter-name='${filterType}'][data-filter-item='${filter}']`); - let filterTag = $(`.filter-box-tag[data-filter-name='${filterType}'][data-filter-item='${filter}']`); - let resourceTags = $(`.resource-tag[data-filter-name='${filterType}'][data-filter-item='${filter}']`); - checkbox.prop("checked", true); - filterTag.show(); - resourceTags.addClass("active"); + // Make sure the filter is valid before we do anything. + if (String(filter) === "rickroll" && filterType === "type") { + window.location.href = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"; + } else if (String(filter) === "sneakers" && filterType === "topics") { + window.location.href = "https://www.youtube.com/watch?v=NNZscmNE9QI"; + } else if (validFilters[filterType].includes(String(filter))) { + let checkbox = $(`.filter-checkbox[data-filter-name='${filterType}'][data-filter-item='${filter}']`); + let filterTag = $(`.filter-box-tag[data-filter-name='${filterType}'][data-filter-item='${filter}']`); + let resourceTags = $(`.resource-tag[data-filter-name='${filterType}'][data-filter-item='${filter}']`); + checkbox.prop("checked", true); + filterTag.show(); + resourceTags.addClass("active"); + activeFilters[filterType].push(filter); + } }); + + // Ditch all the params from the URL, and recalculate the URL params + updateURL(); } }); } diff --git a/pydis_site/templates/resources/resources.html b/pydis_site/templates/resources/resources.html index 13bba1f2..a37bf80a 100644 --- a/pydis_site/templates/resources/resources.html +++ b/pydis_site/templates/resources/resources.html @@ -6,6 +6,11 @@ {% block title %}Resources{% endblock %} {% block head %} + {# Inject a JSON object of all valid filter types from the view #} + + -- cgit v1.2.3 From 1e62517558b7aebfac3043b3cc799f075de5f0b5 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Mon, 31 Jan 2022 18:07:34 +0100 Subject: Hide no filters selected in all cases. --- pydis_site/static/js/resources/resources.js | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'pydis_site/static/js/resources/resources.js') diff --git a/pydis_site/static/js/resources/resources.js b/pydis_site/static/js/resources/resources.js index ad26afd4..d24fbfca 100644 --- a/pydis_site/static/js/resources/resources.js +++ b/pydis_site/static/js/resources/resources.js @@ -69,6 +69,7 @@ function deserializeURLParams() { let paramFilterArray = paramFilterContent.split(","); // Update the corresponding filter UI, so it reflects the internal state. + let filterAdded = false; $(paramFilterArray).each(function(_, filter) { // Make sure the filter is valid before we do anything. if (String(filter) === "rickroll" && filterType === "type") { @@ -83,11 +84,17 @@ function deserializeURLParams() { filterTag.show(); resourceTags.addClass("active"); activeFilters[filterType].push(filter); + filterAdded = true; } }); // Ditch all the params from the URL, and recalculate the URL params updateURL(); + + // If we've added a filter, hide the no filters tag. + if (filterAdded) { + $(".no-tags-selected.tag").hide(); + } } }); } -- cgit v1.2.3 From ec6490ff796669ca6ba12b42c5876072ed0f0f88 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Mon, 31 Jan 2022 19:43:58 +0100 Subject: Improve performance of addFilter() up to 300% --- pydis_site/static/js/resources/resources.js | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'pydis_site/static/js/resources/resources.js') diff --git a/pydis_site/static/js/resources/resources.js b/pydis_site/static/js/resources/resources.js index d24fbfca..d2244a9d 100644 --- a/pydis_site/static/js/resources/resources.js +++ b/pydis_site/static/js/resources/resources.js @@ -147,6 +147,7 @@ function updateUI() { let resourceBox = $(this); // Validate the filters + $.each(activeFilters, function(filterType, filters) { // If the filter list is empty, this passes validation. if (filters.length === 0) { @@ -171,6 +172,7 @@ function updateUI() { } }).show(); + // If there are no matches, show the no matches message if (!hasMatches) { $(".no-resources-found").show(); @@ -219,14 +221,18 @@ document.addEventListener("DOMContentLoaded", function () { } // If you click on the div surrounding the filter checkbox, it clicks the corresponding checkbox. - $('.filter-panel').click(function() { - let checkbox = $(this).find(".filter-checkbox"); - checkbox.prop("checked", !checkbox.prop("checked")); - checkbox.change(); + $('.filter-panel').on("click",function(event) { + let hitsCheckbox = Boolean(String(event.target)); + + if (!hitsCheckbox) { + let checkbox = $(this).find(".filter-checkbox"); + checkbox.prop("checked", !checkbox.prop("checked")); + checkbox.trigger("change"); + } }); // If you click on one of the tags in the filter box, it unchecks the corresponding checkbox. - $('.filter-box-tag').click(function() { + $('.filter-box-tag').on("click", function() { let filterItem = this.dataset.filterItem; let filterName = this.dataset.filterName; let checkbox = $(`.filter-checkbox[data-filter-name='${filterName}'][data-filter-item='${filterItem}']`); @@ -236,7 +242,7 @@ document.addEventListener("DOMContentLoaded", function () { }); // If you click on one of the tags in the resource cards, it clicks the corresponding checkbox. - $('.resource-tag').click(function() { + $('.resource-tag').on("click", function() { let filterItem = this.dataset.filterItem; let filterName = this.dataset.filterName; let checkbox = $(`.filter-checkbox[data-filter-name='${filterName}'][data-filter-item='${filterItem}']`); @@ -251,13 +257,13 @@ document.addEventListener("DOMContentLoaded", function () { }); // When checkboxes are toggled, trigger a filter update. - $('.filter-checkbox').change(function () { + $('.filter-checkbox').on("change", function (event) { let filterItem = this.dataset.filterItem; let filterName = this.dataset.filterName; - if (this.checked) { + if (this.checked && !activeFilters[filterName].includes(filterItem)) { addFilter(filterName, filterItem); - } else { + } else if (!this.checked && activeFilters[filterName].includes(filterItem)) { removeFilter(filterName, filterItem); } }); -- cgit v1.2.3 From f8b2d6cbef76ad1644100ae02133e5d324ec2aad Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Mon, 31 Jan 2022 19:54:44 +0100 Subject: Fix category header double click bug on mobile. --- pydis_site/static/js/resources/resources.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'pydis_site/static/js/resources/resources.js') diff --git a/pydis_site/static/js/resources/resources.js b/pydis_site/static/js/resources/resources.js index d2244a9d..0e951c38 100644 --- a/pydis_site/static/js/resources/resources.js +++ b/pydis_site/static/js/resources/resources.js @@ -214,7 +214,7 @@ document.addEventListener("DOMContentLoaded", function () { let categoryHeaders = $(".filter-category-header .collapsible-content"); let icons = $('.filter-category-header button .card-header-icon i'); categoryHeaders.addClass("no-transition"); - categoryHeaders.css("max-height", 0); + categoryHeaders.css("max-height", ""); icons.removeClass(["far", "fa-window-minimize"]); icons.addClass(["fas", "fa-angle-down"]); categoryHeaders.removeClass("no-transition"); -- cgit v1.2.3 From 24edd0ada26af34e946965d5692b6a35b7622ef9 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Mon, 31 Jan 2022 22:16:20 +0100 Subject: Add a button for removing all active filters. --- pydis_site/static/css/resources/resources.css | 21 ++++- pydis_site/static/js/resources/resources.js | 67 +++++++++------ pydis_site/templates/resources/resources.html | 118 ++++++++++++++------------ 3 files changed, 123 insertions(+), 83 deletions(-) (limited to 'pydis_site/static/js/resources/resources.js') diff --git a/pydis_site/static/css/resources/resources.css b/pydis_site/static/css/resources/resources.css index 16226936..2fb3a9b5 100644 --- a/pydis_site/static/css/resources/resources.css +++ b/pydis_site/static/css/resources/resources.css @@ -97,12 +97,29 @@ i.is-primary { color: #7289da; } -/* A little space around the filter card, please! */ +/* A little space above the filter card, please! */ .filter-tags { padding-bottom: .5em; - padding-right: .5em; } +/* Style the close all filters button */ +.close-filters-button { + margin-left: auto; + display:none; +} +.close-filters-button a { + height: fit-content; + width: fit-content; + margin-right: 6px; +} +.close-filters-button a i { + color: #939bb3; +} +.close-filters-button a i:hover { + filter: brightness(115%); +} + + /* Set default display to inline-flex, for centering. */ span.filter-box-tag { display: none; diff --git a/pydis_site/static/js/resources/resources.js b/pydis_site/static/js/resources/resources.js index 0e951c38..8627a359 100644 --- a/pydis_site/static/js/resources/resources.js +++ b/pydis_site/static/js/resources/resources.js @@ -8,42 +8,33 @@ var activeFilters = { difficulty: [] }; +/* Add a filter, and update the UI */ function addFilter(filterName, filterItem) { - // Push the filter into the stack var filterIndex = activeFilters[filterName].indexOf(filterItem); if (filterIndex === -1) { activeFilters[filterName].push(filterItem); } updateUI(); +} - // Show a corresponding filter box tag - $(`.filter-box-tag[data-filter-name=${filterName}][data-filter-item=${filterItem}]`).show(); - - // Make corresponding resource tags active - $(`.resource-tag[data-filter-name=${filterName}][data-filter-item=${filterItem}]`).addClass("active"); - - // Hide the "No filters selected" tag. - $(".no-tags-selected.tag").hide(); +/* Remove all filters, and update the UI */ +function removeAllFilters() { + activeFilters = { + topics: [], + type: [], + "payment-tiers": [], + difficulty: [] + }; + updateUI(); } +/* Remove a filter, and update the UI */ function removeFilter(filterName, filterItem) { - // Remove the filter from the stack var filterIndex = activeFilters[filterName].indexOf(filterItem); if (filterIndex !== -1) { activeFilters[filterName].splice(filterIndex, 1); } updateUI(); - - // Hide the corresponding filter box tag - $(`.filter-box-tag[data-filter-name=${filterName}][data-filter-item=${filterItem}]`).hide(); - - // Make corresponding resource tags inactive - $(`.resource-tag[data-filter-name=${filterName}][data-filter-item=${filterItem}]`).removeClass("active"); - - // Show "No filters selected" tag, if there are no filters active - if (noFilters()) { - $(".no-tags-selected.tag").show(); - } } /* Check if there are no filters */ @@ -76,7 +67,7 @@ function deserializeURLParams() { window.location.href = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"; } else if (String(filter) === "sneakers" && filterType === "topics") { window.location.href = "https://www.youtube.com/watch?v=NNZscmNE9QI"; - } else if (validFilters[filterType].includes(String(filter))) { + } else if (validFilters.hasOwnProperty(filterType) && validFilters[filterType].includes(String(filter))) { let checkbox = $(`.filter-checkbox[data-filter-name='${filterType}'][data-filter-item='${filter}']`); let filterTag = $(`.filter-box-tag[data-filter-name='${filterType}'][data-filter-item='${filter}']`); let resourceTags = $(`.resource-tag[data-filter-name='${filterType}'][data-filter-item='${filter}']`); @@ -91,9 +82,10 @@ function deserializeURLParams() { // Ditch all the params from the URL, and recalculate the URL params updateURL(); - // If we've added a filter, hide the no filters tag. + // If we've added a filter, hide stuff if (filterAdded) { $(".no-tags-selected.tag").hide(); + $(".close-filters-button").show(); } } }); @@ -123,15 +115,36 @@ function updateURL() { function updateUI() { let resources = $('.resource-box'); let filterTags = $('.filter-box-tag'); + let noTagsSelected = $(".no-tags-selected.tag"); + let closeFiltersButton = $(".close-filters-button"); // Update the URL to match the new filters. updateURL(); - // If there's nothing in the filters, show everything and return. + // If there's nothing in the filters, we can return early. if (noFilters()) { resources.show(); filterTags.hide(); + noTagsSelected.show(); + closeFiltersButton.hide(); + $(`.filter-checkbox:checked`).prop("checked", false) return; + } else { + $.each(activeFilters, function(filterType, filters) { + $.each(filters, function(index, filter) { + // Show a corresponding filter box tag + $(`.filter-box-tag[data-filter-name=${filterType}][data-filter-item=${filter}]`).show(); + + // Make corresponding resource tags active + $(`.resource-tag[data-filter-name=${filterType}][data-filter-item=${filter}]`).addClass("active"); + + // Hide the "No filters selected" tag. + noTagsSelected.hide(); + + // Show the close filters button + closeFiltersButton.show(); + }); + }); } // Otherwise, hide everything and then filter the resources to decide what to show. @@ -147,7 +160,6 @@ function updateUI() { let resourceBox = $(this); // Validate the filters - $.each(activeFilters, function(filterType, filters) { // If the filter list is empty, this passes validation. if (filters.length === 0) { @@ -256,6 +268,11 @@ document.addEventListener("DOMContentLoaded", function () { } }); + // When you click the little gray x, remove all filters. + $(".close-filters-button").on("click", function() { + removeAllFilters(); + }); + // When checkboxes are toggled, trigger a filter update. $('.filter-checkbox').on("change", function (event) { let filterItem = this.dataset.filterItem; diff --git a/pydis_site/templates/resources/resources.html b/pydis_site/templates/resources/resources.html index 4b7e9040..cebaace2 100644 --- a/pydis_site/templates/resources/resources.html +++ b/pydis_site/templates/resources/resources.html @@ -31,64 +31,70 @@ {# Filter box tags #}
-
- {# A little x in the top right, visible only when filters are active, which removes all filters. #} - - - {# A filter tag for when there are no filters active #} - - - No filters selected - +
+
+ {# A filter tag for when there are no filters active #} + + + No filters selected + - {% for filter_name, filter_data in filters.items %} - {% for filter_item in filter_data.filters %} - {% if filter_name == "Difficulty" %} - - - {{ filter_item|title }} - - - {% endif %} - {% if filter_name == "Type" %} - - - {{ filter_item|title }} - - - {% endif %} - {% if filter_name == "Payment tiers" %} - - - {{ filter_item|title }} - - - {% endif %} - {% if filter_name == "Topics" %} - - - {{ filter_item|title }} - - - {% endif %} + {% for filter_name, filter_data in filters.items %} + {% for filter_item in filter_data.filters %} + {% if filter_name == "Difficulty" %} + + + {{ filter_item|title }} + + + {% endif %} + {% if filter_name == "Type" %} + + + {{ filter_item|title }} + + + {% endif %} + {% if filter_name == "Payment tiers" %} + + + {{ filter_item|title }} + + + {% endif %} + {% if filter_name == "Topics" %} + + + {{ filter_item|title }} + + + {% endif %} + {% endfor %} {% endfor %} - {% endfor %} +
+
+ {# A little x in the top right, visible only when filters are active, which removes all filters. #} + + + + +
-- cgit v1.2.3 From 4dfde7fbf09a38134e997abe5b2b7ebe08fc0538 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Tue, 1 Feb 2022 01:18:57 +0100 Subject: Make resource tags correctly deactivate. --- pydis_site/static/js/resources/resources.js | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'pydis_site/static/js/resources/resources.js') diff --git a/pydis_site/static/js/resources/resources.js b/pydis_site/static/js/resources/resources.js index 8627a359..bffb5e91 100644 --- a/pydis_site/static/js/resources/resources.js +++ b/pydis_site/static/js/resources/resources.js @@ -115,6 +115,7 @@ function updateURL() { function updateUI() { let resources = $('.resource-box'); let filterTags = $('.filter-box-tag'); + let resourceTags = $('.resource-tag'); let noTagsSelected = $(".no-tags-selected.tag"); let closeFiltersButton = $(".close-filters-button"); @@ -127,9 +128,17 @@ function updateUI() { filterTags.hide(); noTagsSelected.show(); closeFiltersButton.hide(); + resourceTags.removeClass("active"); $(`.filter-checkbox:checked`).prop("checked", false) return; } else { + // Hide everything + $('.filter-box-tag').hide(); + $('.resource-tag').removeClass("active"); + noTagsSelected.show(); + closeFiltersButton.hide(); + + // Now conditionally show the stuff we want $.each(activeFilters, function(filterType, filters) { $.each(filters, function(index, filter) { // Show a corresponding filter box tag -- cgit v1.2.3 From d31340f8abeabc5df1c778d7d8eae4d0806fad0e Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Tue, 1 Feb 2022 02:13:34 +0100 Subject: Fix bug where transition wouldn't work on first collapse. --- pydis_site/static/css/collapsibles.css | 6 +++++- pydis_site/static/js/collapsibles.js | 6 +----- pydis_site/static/js/resources/resources.js | 7 ++++--- pydis_site/templates/resources/resources.html | 4 ++-- 4 files changed, 12 insertions(+), 11 deletions(-) (limited to 'pydis_site/static/js/resources/resources.js') diff --git a/pydis_site/static/css/collapsibles.css b/pydis_site/static/css/collapsibles.css index 8fdd1f12..d35e77ea 100644 --- a/pydis_site/static/css/collapsibles.css +++ b/pydis_site/static/css/collapsibles.css @@ -6,7 +6,11 @@ } .collapsible-content { + max-height: 40em; + transition: max-height 0.3s ease-out; +} + +.collapsible-content.collapsed { overflow: hidden; max-height: 0; - transition: max-height 0.2s ease-out; } diff --git a/pydis_site/static/js/collapsibles.js b/pydis_site/static/js/collapsibles.js index 366a033c..d12d9f86 100644 --- a/pydis_site/static/js/collapsibles.js +++ b/pydis_site/static/js/collapsibles.js @@ -3,11 +3,7 @@ document.addEventListener("DOMContentLoaded", () => { for (const header of headers) { header.addEventListener("click", () => { var content = header.nextElementSibling; - if (content.style.maxHeight){ - content.style.maxHeight = null; - } else { - content.style.maxHeight = content.scrollHeight + "px"; - } + content.classList.toggle('collapsed'); }); } }); diff --git a/pydis_site/static/js/resources/resources.js b/pydis_site/static/js/resources/resources.js index bffb5e91..00bc6ad8 100644 --- a/pydis_site/static/js/resources/resources.js +++ b/pydis_site/static/js/resources/resources.js @@ -234,11 +234,12 @@ document.addEventListener("DOMContentLoaded", function () { if (screen.width < 480) { let categoryHeaders = $(".filter-category-header .collapsible-content"); let icons = $('.filter-category-header button .card-header-icon i'); - categoryHeaders.addClass("no-transition"); - categoryHeaders.css("max-height", ""); + categoryHeaders.addClass("no-transition collapsed"); icons.removeClass(["far", "fa-window-minimize"]); icons.addClass(["fas", "fa-angle-down"]); - categoryHeaders.removeClass("no-transition"); + + // Wait 10ms before removing this class, or else the transition will animate due to a race condition. + setTimeout(() => { categoryHeaders.removeClass("no-transition"); }, 10); } // If you click on the div surrounding the filter checkbox, it clicks the corresponding checkbox. diff --git a/pydis_site/templates/resources/resources.html b/pydis_site/templates/resources/resources.html index cebaace2..c221c8a3 100644 --- a/pydis_site/templates/resources/resources.html +++ b/pydis_site/templates/resources/resources.html @@ -116,9 +116,9 @@ {# Checkboxes #} {% if filter_data.hidden %} -
+