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/collapsibles.js | 13 ++ pydis_site/static/js/content/page.js | 13 -- pydis_site/static/js/resources.js | 236 ---------------------------- pydis_site/static/js/resources/resources.js | 236 ++++++++++++++++++++++++++++ 4 files changed, 249 insertions(+), 249 deletions(-) create mode 100644 pydis_site/static/js/collapsibles.js delete mode 100644 pydis_site/static/js/content/page.js delete mode 100644 pydis_site/static/js/resources.js create mode 100644 pydis_site/static/js/resources/resources.js (limited to 'pydis_site/static/js') diff --git a/pydis_site/static/js/collapsibles.js b/pydis_site/static/js/collapsibles.js new file mode 100644 index 00000000..366a033c --- /dev/null +++ b/pydis_site/static/js/collapsibles.js @@ -0,0 +1,13 @@ +document.addEventListener("DOMContentLoaded", () => { + const headers = document.getElementsByClassName("collapsible"); + 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"; + } + }); + } +}); diff --git a/pydis_site/static/js/content/page.js b/pydis_site/static/js/content/page.js deleted file mode 100644 index 366a033c..00000000 --- a/pydis_site/static/js/content/page.js +++ /dev/null @@ -1,13 +0,0 @@ -document.addEventListener("DOMContentLoaded", () => { - const headers = document.getElementsByClassName("collapsible"); - 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"; - } - }); - } -}); diff --git a/pydis_site/static/js/resources.js b/pydis_site/static/js/resources.js deleted file mode 100644 index 718e1e88..00000000 --- a/pydis_site/static/js/resources.js +++ /dev/null @@ -1,236 +0,0 @@ -"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); - } - }); -}); 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