aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pydis_site/static/css/resources/resources.css91
-rw-r--r--pydis_site/static/js/resources.js123
-rw-r--r--pydis_site/templates/resources/resource_box.html37
-rw-r--r--pydis_site/templates/resources/resources.html123
4 files changed, 300 insertions, 74 deletions
diff --git a/pydis_site/static/css/resources/resources.css b/pydis_site/static/css/resources/resources.css
index f70cbd64..c6347eab 100644
--- a/pydis_site/static/css/resources/resources.css
+++ b/pydis_site/static/css/resources/resources.css
@@ -25,3 +25,94 @@
i.is-primary {
color: #7289da;
}
+
+/* A little space around the filter card, please! */
+.filter-tags {
+ padding-bottom: .5em;
+ padding-right: .5em;
+}
+
+/* Set default display to inline-flex, for centering. */
+span.filter-box-tag {
+ display: inline-flex;
+ align-items: center;
+ cursor: pointer;
+ user-select: none;
+}
+
+/* Make resource tags clickable */
+.resource-tag {
+ cursor: pointer;
+ user-select: none;
+}
+
+/* Move the x down 1 pixel to align center */
+button.delete {
+ margin-top: 1px;
+}
+
+/* Colors for delete button x's */
+button.delete.is-primary::before,
+button.delete.is-primary::after {
+ background-color: #2a45a2;
+}
+button.delete.is-success::before,
+button.delete.is-success::after {
+ background-color: #2c9659;
+}
+button.delete.is-danger::before,
+button.delete.is-danger::after {
+ background-color: #c32841;
+}
+button.delete.is-info::before,
+button.delete.is-info::after {
+ background-color: #237fbd;
+}
+
+/* Give outlines to active tags */
+span.filter-box-tag,
+span.resource-tag.active {
+ outline-width: 1px;
+ outline-style: solid;
+}
+
+/* Make filter tags sparkle when selected! */
+@keyframes glow_success {
+ from { box-shadow: 0 0 2px 2px #aef4af; }
+ 33% { box-shadow: 0 0 2px 2px #87af7a; }
+ 66% { box-shadow: 0 0 2px 2px #9ceaac; }
+ to { box-shadow: 0 0 2px 2px #7cbf64; }
+}
+
+@keyframes glow_primary {
+ from { box-shadow: 0 0 2px 2px #aeb8f3; }
+ 33% { box-shadow: 0 0 2px 2px #909ed9; }
+ 66% { box-shadow: 0 0 2px 2px #6d7ed4; }
+ to { box-shadow: 0 0 2px 2px #6383b3; }
+}
+
+@keyframes glow_danger {
+ from { box-shadow: 0 0 2px 2px #c9495f; }
+ 33% { box-shadow: 0 0 2px 2px #92486f; }
+ 66% { box-shadow: 0 0 2px 2px #d455ba; }
+ to { box-shadow: 0 0 2px 2px #ff8192; }
+}
+@keyframes glow_info {
+ from { box-shadow: 0 0 2px 2px #4592c9; }
+ 33% { box-shadow: 0 0 2px 2px #6196bb; }
+ 66% { box-shadow: 0 0 2px 2px #5adade; }
+ to { box-shadow: 0 0 2px 2px #6bcfdc; }
+}
+
+span.resource-tag.active.is-primary {
+ animation: glow_primary 4s infinite alternate;
+}
+span.resource-tag.active.has-background-danger-light {
+ animation: glow_danger 4s infinite alternate;
+}
+span.resource-tag.active.has-background-success-light {
+ animation: glow_success 4s infinite alternate;
+}
+span.resource-tag.active.has-background-info-light {
+ animation: glow_info 4s infinite alternate;
+}
diff --git a/pydis_site/static/js/resources.js b/pydis_site/static/js/resources.js
index 836ef4ec..c9ce408b 100644
--- a/pydis_site/static/js/resources.js
+++ b/pydis_site/static/js/resources.js
@@ -8,6 +8,37 @@ var activeFilters = {
complexity: []
};
+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();
+ $(".filter-tags").css("padding-bottom", "0.5em");
+
+ // Make corresponding resource tags active
+ $(`.resource-tag[data-filter-name=${filterName}][data-filter-item=${filterItem}]`).addClass("active");
+}
+
+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");
+}
+
/* Check if there are no filters */
function noFilters() {
return (
@@ -18,26 +49,6 @@ function noFilters() {
);
}
-/* 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()}`);
-}
-
/* 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);
@@ -51,18 +62,43 @@ function deserializeURLParams() {
let paramFilterArray = paramFilterContent.split(",");
activeFilters[filterType] = paramFilterArray;
- // Check corresponding checkboxes, so the UI reflects the internal state.
+ // 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 update() {
+function updateUI() {
let resources = $('.resource-box');
+ let filterTags = $('.filter-box-tag');
// Update the URL to match the new filters.
updateURL();
@@ -70,6 +106,8 @@ function update() {
// If there's nothing in the filters, show everything and return.
if (noFilters()) {
resources.show();
+ filterTags.hide();
+ $(".filter-tags").css("padding-bottom", "0");
return;
}
@@ -112,8 +150,9 @@ function update() {
// Executed when the page has finished loading.
document.addEventListener("DOMContentLoaded", function () {
// Update the filters on page load to reflect URL parameters.
+ $('.filter-box-tag').hide();
deserializeURLParams();
- update();
+ updateUI();
// If you collapse or uncollapse a filter group, swap the icon.
$('button.collapsible').click(function() {
@@ -128,29 +167,47 @@ document.addEventListener("DOMContentLoaded", function () {
}
});
- // If you click on the div surrounding the filter checkbox, it clicks the checkbox.
+ // 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;
- var filterIndex = activeFilters[filterName].indexOf(filterItem);
if (this.checked) {
- if (filterIndex === -1) {
- activeFilters[filterName].push(filterItem);
- }
- update();
+ addFilter(filterName, filterItem);
} else {
- if (filterIndex !== -1) {
- activeFilters[filterName].splice(filterIndex, 1);
- }
- update();
+ removeFilter(filterName, filterItem);
}
});
});
diff --git a/pydis_site/templates/resources/resource_box.html b/pydis_site/templates/resources/resource_box.html
index 09256751..476a4841 100644
--- a/pydis_site/templates/resources/resource_box.html
+++ b/pydis_site/templates/resources/resource_box.html
@@ -1,4 +1,5 @@
{% load as_icon %}
+{% load as_css_class %}
{% load get_category_icon %}
<div class="box resource-box {{ resource.css_classes }}">
@@ -34,16 +35,44 @@
{# Tags #}
<div class="is-flex ml-auto is-flex-wrap-wrap is-justify-content-end">
{% for tag in resource.tags.topics %}
- <span class="tag is-primary is-light ml-2 mt-2"><i class="{{ tag|title|get_category_icon }} mr-1"></i>{{ tag|title }}</span>
+ <span
+ class="tag resource-tag is-primary is-light ml-2 mt-2"
+ data-filter-name="topics"
+ data-filter-item="{{ tag|as_css_class }}"
+ >
+ <i class="{{ tag|title|get_category_icon }} mr-1"></i>
+ {{ tag|title }}
+ </span>
{% endfor %}
{% for tag in resource.tags.type %}
- <span class="tag has-background-success-light has-text-success-dark ml-2 mt-2"><i class="{{ tag|title|get_category_icon }} mr-1"></i>{{ tag|title }}</span>
+ <span
+ class="tag resource-tag has-background-success-light has-text-success-dark ml-2 mt-2"
+ data-filter-name="type"
+ data-filter-item="{{ tag|as_css_class }}"
+ >
+ <i class="{{ tag|title|get_category_icon }} mr-1"></i>
+ {{ tag|title }}
+ </span>
{% endfor %}
{% for tag in resource.tags.payment_tiers %}
- <span class="tag has-background-danger-light has-text-danger-dark ml-2 mt-2"><i class="{{ tag|title|get_category_icon }} mr-1"></i>{{ tag|title }}</span>
+ <span
+ class="tag resource-tag has-background-danger-light has-text-danger-dark ml-2 mt-2"
+ data-filter-name="payment-tiers"
+ data-filter-item="{{ tag|as_css_class }}"
+ >
+ <i class="{{ tag|title|get_category_icon }} mr-1"></i>
+ {{ tag|title }}
+ </span>
{% endfor %}
{% for tag in resource.tags.complexity %}
- <span class="tag has-background-info-light has-text-info-dark ml-2 mt-2"><i class="{{ tag|title|get_category_icon }} mr-1"></i>{{ tag|title }}</span>
+ <span
+ class="tag resource-tag has-background-info-light has-text-info-dark ml-2 mt-2"
+ data-filter-name="complexity"
+ data-filter-item="{{ tag|as_css_class }}"
+ >
+ <i class="{{ tag|title|get_category_icon }} mr-1"></i>
+ {{ tag|title }}
+ </span>
{% endfor %}
</div>
</div>
diff --git a/pydis_site/templates/resources/resources.html b/pydis_site/templates/resources/resources.html
index 134c826b..87c28153 100644
--- a/pydis_site/templates/resources/resources.html
+++ b/pydis_site/templates/resources/resources.html
@@ -1,74 +1,123 @@
{% extends 'base/base.html' %}
{% load as_icon %}
{% load as_css_class %}
+{% load get_category_icon %}
{% load static %}
{% block title %}Resources{% endblock %}
{% block head %}
- <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
- <script src="{% static "js/resources.js" %}"></script>
- <script src="{% static "js/content/page.js" %}"></script>
<link rel="stylesheet" href="{% static "css/resources/resources.css" %}">
<link rel="stylesheet" href="{% static "css/resources/resources_list.css" %}">
<link rel="stylesheet" href="{% static "css/content/page.css" %}">
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
+ <script defer src="{% static "js/resources.js" %}"></script>
+ <script defer src="{% static "js/content/page.js" %}"></script>
{% endblock %}
{% block content %}
{% include "base/navbar.html" %}
{% if resources|length > 0 %}
<section class="section">
- {# Headline #}
- <div class="content">
- <h1 class="resource-title has-text-centered">Resources</h1>
- <hr/>
- </div>
-
<div class="columns is-centered">
{# Filtering toolbox #}
<div class="column is-one-third">
<div class="content is-justify-content-center">
<nav id="resource-filtering-panel" class="panel is-primary">
- <p class="panel-heading has-text-centered" id="filter-panel-header">Filters</p>
+ <p class="panel-heading has-text-centered" id="filter-panel-header">Filter Resources</p>
+
+ {# Filter box tags #}
+ <div class="card filter-tags">
+ <div class="is-flex ml-auto is-flex-wrap-wrap">
+ {% for filter_name, filter_data in filters.items %}
+ {% for filter_item in filter_data.filters %}
+ {% if filter_name == "Complexity" %}
+ <span
+ class="filter-box-tag tag has-background-info-light has-text-info-dark ml-2 mt-2"
+ data-filter-name="{{ filter_name|as_css_class }}"
+ data-filter-item="{{ filter_item|as_css_class }}"
+ >
+ <i class="{{ filter_item|title|get_category_icon }} mr-1"></i>
+ {{ filter_item|title }}
+ <button class="delete is-small is-info has-background-info-light"></button>
+ </span>
+ {% endif %}
+ {% if filter_name == "Type" %}
+ <span
+ class="filter-box-tag tag has-background-success-light has-text-success-dark ml-2 mt-2"
+ data-filter-name="{{ filter_name|as_css_class }}"
+ data-filter-item="{{ filter_item|as_css_class }}"
+ >
+ <i class="{{ filter_item|title|get_category_icon }} mr-1"></i>
+ {{ filter_item|title }}
+ <button class="delete is-small is-success has-background-success-light"></button>
+ </span>
+ {% endif %}
+ {% if filter_name == "Payment tiers" %}
+ <span
+ class="filter-box-tag tag has-background-danger-light has-text-danger-dark ml-2 mt-2"
+ data-filter-name="{{ filter_name|as_css_class }}"
+ data-filter-item="{{ filter_item|as_css_class }}"
+ >
+ <i class="{{ filter_item|title|get_category_icon }} mr-1"></i>
+ {{ filter_item|title }}
+ <button class="delete is-small is-danger has-background-danger-light"></button>
+ </span>
+ {% endif %}
+ {% if filter_name == "Topics" %}
+ <span
+ class="filter-box-tag tag is-primary is-light ml-2 mt-2"
+ data-filter-name="{{ filter_name|as_css_class }}"
+ data-filter-item="{{ filter_item|as_css_class }}"
+ >
+ <i class="{{ filter_item|title|get_category_icon }} mr-1"></i>
+ {{ filter_item|title }}
+ <button class="delete is-small is-primary has-background-primary-light"></button>
+ </span>
+ {% endif %}
+ {% endfor %}
+ {% endfor %}
+ </div>
+ </div>
{# Filter checkboxes #}
{% for filter_name, filter_data in filters.items %}
- <div class="card">
- <button type="button" class="card-header collapsible">
+ <div class="card">
+ <button type="button" class="card-header collapsible">
<span class="card-header-title subtitle is-6 my-2 ml-2">
<i class="{{ filter_data.icon }} is-primary" aria-hidden="true"></i>&nbsp&nbsp{{ filter_name }}
</span>
- <span class="card-header-icon">
+ <span class="card-header-icon">
{% if not filter_data.hidden %}
<i class="far fa-window-minimize is-6 title" aria-hidden="true"></i>
{% else %}
<i class="fas fa-angle-down is-6 title" aria-hidden="true"></i>
{% endif %}
</span>
- </button>
+ </button>
- {# Checkboxes #}
- {% if filter_data.hidden %}
- <div class="collapsible-content">
- {% else %}
- <div class="collapsible-content" style="max-height: 480px;">
- {% endif %}
- <div class="card-content">
- {% for filter_item in filter_data.filters %}
- <a class="panel-block filter-panel">
- <label class="checkbox">
- <input
- class="filter-checkbox"
- type="checkbox"
- data-filter-name="{{ filter_name|as_css_class }}"
- data-filter-item="{{ filter_item|as_css_class }}"
- >
- {{ filter_item }}
- </label>
- </a>
- {% endfor %}
- </div>
- </div>
- </div>
+ {# Checkboxes #}
+ {% if filter_data.hidden %}
+ <div class="collapsible-content">
+ {% else %}
+ <div class="collapsible-content" style="max-height: 480px;">
+ {% endif %}
+ <div class="card-content">
+ {% for filter_item in filter_data.filters %}
+ <a class="panel-block filter-panel">
+ <label class="checkbox">
+ <input
+ class="filter-checkbox"
+ type="checkbox"
+ data-filter-name="{{ filter_name|as_css_class }}"
+ data-filter-item="{{ filter_item|as_css_class }}"
+ >
+ {{ filter_item }}
+ </label>
+ </a>
+ {% endfor %}
+ </div>
+ </div>
+ </div>
{% endfor %}
</nav>
</div>