From 16383cb9584c8a39b05bda96048f7cf41d33ebdb Mon Sep 17 00:00:00 2001
From: Julia Kent <46687291+jukent@users.noreply.github.com>
Date: Thu, 13 Jun 2024 12:27:13 -0600
Subject: [PATCH] dropdown gallery changes (#444)
* dropdown gallery changes
* fix indent
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* dark theme changes
* dark theme cards
* them to theme
* rm accidental link color chagne
---------
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
portal/_extensions/gallery_generator.py | 98 +++++-----
portal/_static/custom.css | 66 +++++++
portal/_static/custom.js | 233 ++++++++----------------
portal/conf.py | 4 +-
4 files changed, 195 insertions(+), 206 deletions(-)
diff --git a/portal/_extensions/gallery_generator.py b/portal/_extensions/gallery_generator.py
index 45905a94..922f2365 100644
--- a/portal/_extensions/gallery_generator.py
+++ b/portal/_extensions/gallery_generator.py
@@ -1,5 +1,6 @@
import itertools
import pathlib
+import re
from truncatehtml import truncate
@@ -9,13 +10,22 @@ def _generate_sorted_tag_keys(all_items):
return sorted(key_set)
+def _title_case_preserve(s):
+ return re.sub(r'\b(\w)', lambda m: m.group(1).upper(), s)
+
+
+def _make_class(s):
+ return re.sub(r'^\d+', '', s.replace(' ', '-').lower())
+
+
def _generate_tag_set(all_items, tag_key=None):
tag_set = set()
for item in all_items:
for k, e in item['tags'].items():
+ tags = [_title_case_preserve(t) for t in e]
if tag_key and k != tag_key:
continue
- for t in e:
+ for t in tags:
tag_set.add(t)
return tag_set
@@ -26,20 +36,18 @@ def _generate_tag_menu(all_items, tag_key):
tag_list = sorted(tag_set)
options = ''.join(
- f'
'
+ f''
for tag in tag_list
)
return f"""
-
-
-
-
-
+ :::{{dropdown}} {tag_key}
+
+ :::
"""
@@ -71,10 +79,9 @@ def build_from_items(items, filename, title='Gallery', subtitle=None, subtext=No
tag_list = sorted((itertools.chain(*item['tags'].values())))
tag_list_f = [tag.replace(' ', '-') for tag in tag_list]
- tags = [f'{tag}' for tag in tag_list_f]
+ tags = [f'{_title_case_preserve(tag)}' for tag in tag_list_f]
tags = '\n'.join(tags)
-
- # tag_class_str = ' '.join(tag_list_f)
+ tag_classes = ' '.join(tag_list_f)
author_strs = set()
affiliation_strs = set()
@@ -108,51 +115,52 @@ def build_from_items(items, filename, title='Gallery', subtitle=None, subtext=No
if ellipsis_str in short_description:
modal_str = f"""
-
-

-
{item["title"]}
- {authors_str}
-
- {affiliations_str}
-
{item['description']}
-
{tags}
-
Visit Website
-
+
+

+
{item["title"]}
+ {authors_str}
+
+ {affiliations_str}
+
{item['description']}
+
{tags}
+
Visit Website
+
"""
modal_str = '\n'.join([m.lstrip() for m in modal_str.split('\n')])
else:
modal_str = ''
- new_card = f"""\
- :::{{grid-item-card}}
- :shadow: md
- :class-footer: card-footer
-
-

-
-
{item["title"]}
-
{authors_str}
{affiliations_str}
-
{short_description}
-
-
- {modal_str}
+ new_card = f"""
+ :::{{grid-item-card}}
+ :shadow: md
+ :class-footer: card-footer
+ :class-card: tagged-card {tag_classes}
- +++
+
+

+
+
{item["title"]}
+
{authors_str}
{affiliations_str}
+
{short_description}
+
+
+ {modal_str}
- {tags}
+ +++
- :::
+ {tags}
- """
+ :::
- grid_body.append('\n'.join([m.lstrip() for m in new_card.split('\n')]))
+ """
- grid_body = '\n'.join(grid_body)
+ grid_body.append('\n'.join([m.lstrip() for m in new_card.split('\n')]))
stitle = f'#### {subtitle}' if subtitle else ''
stext = subtext if subtext else ''
+ grid_body = '\n'.join(grid_body)
grid = f"""\
{title}
{'=' * len(title)}
@@ -163,12 +171,12 @@ def build_from_items(items, filename, title='Gallery', subtitle=None, subtext=No
{menu_html}
::::{{grid}} 1
- :gutter: 4
+ :gutter: 0
{grid_body}
-
+
"""
grid = '\n'.join([m.lstrip() for m in grid.split('\n')])
diff --git a/portal/_static/custom.css b/portal/_static/custom.css
index 6a1edd29..bb94f5ab 100644
--- a/portal/_static/custom.css
+++ b/portal/_static/custom.css
@@ -144,3 +144,69 @@ div.horizontalgap {
height: 1px;
width: 0px;
}
+
+.dropdown ul {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+.dropdown-item {
+ display: block;
+}
+
+.dropdown-item input[type="checkbox"] {
+ margin-right: 0.5em;
+}
+
+details.sd-dropdown {
+ box-shadow: none !important;
+}
+
+details.sd-dropdown summary.sd-card-header + div.sd-summary-content {
+ background-color: white !important;
+ border: 0.2rem solid var(--pst-sd-dropdown-color) !important;
+ border-radius: calc(.25rem - 1px);
+}
+
+.sd-summary-content.sd-card-body.docutils {
+ position: absolute;
+ z-index: 100;
+}
+
+details.sd-dropdown:not([open])>.sd-card-header {
+ background-color: white !important;
+ border: 2px solid #1a648f !important;
+ color: #1a648f;
+ border-radius: .5rem;
+}
+
+details.sd-dropdown[open]>.sd-card-header {
+ background-color: #1a648f !important;
+ color: white;
+ border-radius: .5rem;
+}
+
+p {
+ color: black;
+}
+
+.sd-col.sd-d-flex-row.docutils.has-visible-card {
+ margin-bottom: 1rem;
+}
+
+html[data-theme="dark"] h1.display-1 {
+ color: white;
+}
+
+html[data-them="dark"] h4.display-4.p-0 {
+ color: black !important;
+}
+
+html[data-theme="dark"] .sd-card-body.docutils {
+ background-color: white;
+}
+
+html[data-theme="dark"] .container p {
+ color: black !important;
+}
diff --git a/portal/_static/custom.js b/portal/_static/custom.js
index f0f3d574..139a1454 100644
--- a/portal/_static/custom.js
+++ b/portal/_static/custom.js
@@ -1,184 +1,99 @@
-var buttons = document.querySelectorAll('.modal-btn')
-var backdrop = document.querySelector('.modal-backdrop')
-var modals = document.querySelectorAll('.modal')
-
-function openModal(i) {
- backdrop.style.display = 'block'
- modals[i].style.display = 'block'
-}
-
-function closeModal(i) {
- backdrop.style.display = 'none'
- modals[i].style.display = 'none'
-}
-
-for (i = 0; i < buttons.length; i++) {
- buttons[i].addEventListener(
- 'click',
- (function (j) {
- return function () {
- openModal(j)
- }
- })(i)
- )
- backdrop.addEventListener(
- 'click',
- (function (j) {
- return function () {
- closeModal(j)
- }
- })(i)
- )
-}
-
-
-function change() {
- var affiliationCbs = document.querySelectorAll(".affiliation input[type='checkbox']");
- var domainsCbs = document.querySelectorAll(".domains input[type='checkbox']");
- var formatsCbs = document.querySelectorAll(".formats input[type='checkbox']");
- var packagesCbs = document.querySelectorAll(".packages input[type='checkbox']");
-
- var filters = {
- affiliation: getClassOfCheckedCheckboxes(affiliationCbs),
- domains: getClassOfCheckedCheckboxes(domainsCbs),
- formats: getClassOfCheckedCheckboxes(formatsCbs),
- packages: getClassOfCheckedCheckboxes(packagesCbs)
- };
-
- filterResults(filters);
-}
-
function getClassOfCheckedCheckboxes(checkboxes) {
- var classes = [];
-
- if (checkboxes && checkboxes.length > 0) {
- for (var i = 0; i < checkboxes.length; i++) {
- var cb = checkboxes[i];
-
+ var tags = [];
+ checkboxes.forEach(function (cb) {
if (cb.checked) {
- classes.push(cb.getAttribute("rel"));
+ tags.push(cb.getAttribute("rel"));
}
- }
+ });
+ return tags;
}
- return classes;
-}
-
-function filterResults(filters) {
- var rElems = document.querySelectorAll(".tagged-card");
- var hiddenElems = [];
-
- if (!rElems || rElems.length <= 0) {
- return;
+ function change() {
+ console.log("Change event fired.");
+ var affiliationsCbs = document.querySelectorAll(".affiliation input[type='checkbox']");
+ var domainsCbs = document.querySelectorAll(".domains input[type='checkbox']");
+ var formatsCbs = document.querySelectorAll(".formats input[type='checkbox']");
+ var packagesCbs = document.querySelectorAll(".packages input[type='checkbox']");
+
+ var affiliatioinTags = getClassOfCheckedCheckboxes(affiliationsCbs);
+ var domainTags = getClassOfCheckedCheckboxes(domainsCbs);
+ var formatTags = getClassOfCheckedCheckboxes(formatsCbs);
+ var packageTags = getClassOfCheckedCheckboxes(packagesCbs);
+
+ var filters = {
+ affiliations: affiliatioinTags,
+ domains: domainTags,
+ formats: formatTags,
+ packages: packageTags
+ };
+
+ filterResults(filters);
}
- for (var i = 0; i < rElems.length; i++) {
- var el = rElems[i];
-
- if (filters.affiliation.length > 0) {
- var isHidden = true;
-
- for (var j = 0; j < filters.affiliation.length; j++) {
- var filter = filters.affiliation[j];
-
- if (el.classList.contains(filter)) {
- isHidden = false;
- break;
- }
- }
-
- if (isHidden) {
- hiddenElems.push(el);
- }
- }
-
- if (filters.domains.length > 0) {
- var isHidden = true;
+ function filterResults(filters) {
+ console.log("Filtering results...");
+ var rElems = document.querySelectorAll(".tagged-card");
- for (var j = 0; j < filters.domains.length; j++) {
- var filter = filters.domains[j];
+ rElems.forEach(function (el) {
+ var isVisible = true; // Assume visible by default
- if (el.classList.contains(filter)) {
- isHidden = false;
- break;
- }
- }
-
- if (isHidden) {
- hiddenElems.push(el);
- }
- }
-
- if (filters.formats.length > 0) {
- var isHidden = true;
-
- for (var j = 0; j < filters.formats.length; j++) {
- var filter = filters.formats[j];
-
- if (el.classList.contains(filter)) {
- isHidden = false;
- break;
- }
- }
-
- if (isHidden) {
- hiddenElems.push(el);
- }
- }
+ // Check if the element has any domain or package filter
+ if (filters.affiliations.length > 0 || filters.domains.length > 0 || filters.formats.length > 0 || filters.packages.length > 0) {
+ var hasMatchingAffiliation = filters.affiliations.length === 0 || filters.affiliations.some(affiliation => el.classList.contains(affiliation));
+ var hasMatchingDomain = filters.domains.length === 0 || filters.domains.some(domain => el.classList.contains(domain));
+ var hasMatchingFormat = filters.formats.length === 0 || filters.formats.some(format => el.classList.contains(format));
+ var hasMatchingPackage = filters.packages.length === 0 || filters.packages.some(package => el.classList.contains(package));
- if (filters.packages.length > 0) {
- var isHidden = true;
-
- for (var j = 0; j < filters.packages.length; j++) {
- var filter = filters.packages[j];
-
- if (el.classList.contains(filter)) {
- isHidden = false;
- break;
- }
+ // The element should be visible if it matches any filter within each category
+ isVisible = hasMatchingAffiliation && hasMatchingDomain && hasMatchingFormat && hasMatchingPackage;
}
- if (isHidden) {
- hiddenElems.push(el);
+ // Toggle visibility based on the result
+ if (isVisible) {
+ el.classList.remove("d-none");
+ el.classList.add("d-flex");
+ } else {
+ el.classList.remove("d-flex");
+ el.classList.add("d-none");
}
- }
- }
+ });
- for (var i = 0; i < rElems.length; i++) {
- rElems[i].classList.replace("d-none", "d-flex");
+ // Update the margins after filtering
+ updateMargins();
}
- if (hiddenElems.length <= 0) {
- return;
- }
+ var checkboxes = document.querySelectorAll('input[type="checkbox"]');
+ checkboxes.forEach(function (checkbox) {
+ checkbox.addEventListener("change", change);
+ });
- for (var i = 0; i < hiddenElems.length; i++) {
- hiddenElems[i].classList.replace("d-flex", "d-none");
- }
-}
+ function updateMargins() {
+ const columns = document.querySelectorAll('.sd-col.sd-d-flex-row.docutils');
+ columns.forEach(column => {
+ // Check if this column has any visible cards
+ const hasVisibleCard = Array.from(column.children).some(child => !child.classList.contains('d-none'));
-function clearCbs() {
- var affiliationCbs = document.querySelectorAll(".affiliation input[type='checkbox']");
- var domainsCbs = document.querySelectorAll(".domains input[type='checkbox']");
- var formatsCbs = document.querySelectorAll(".formats input[type='checkbox']");
- var packagesCbs = document.querySelectorAll(".packages input[type='checkbox']");
-
- for (var i = 0; i < affiliationCbs.length; i++) {
- affiliationCbs[i].checked=false;
+ // Toggle a class based on whether there are visible cards
+ if (hasVisibleCard) {
+ column.classList.add('has-visible-card');
+ } else {
+ column.classList.remove('has-visible-card');
+ }
+ });
}
- for (var i = 0; i < domainsCbs.length; i++) {
- domainsCbs[i].checked=false;
- }
+ function clearCbs() {
+ // Select all checkbox inputs and uncheck them
+ var checkboxes = document.querySelectorAll('input[type="checkbox"]');
+ checkboxes.forEach(function(checkbox) {
+ checkbox.checked = false;
+ });
- for (var i = 0; i < formatsCbs.length; i++) {
- formatsCbs[i].checked=false;
+ change();
}
- for (var i = 0; i < packagesCbs.length; i++) {
- packagesCbs[i].checked=false;
- }
+ // Initial call to set up correct margins when the page loads
+ document.addEventListener('DOMContentLoaded', updateMargins);
- change();
-}
+ console.log("Script loaded.");
diff --git a/portal/conf.py b/portal/conf.py
index ce57d8e0..79cbb61e 100644
--- a/portal/conf.py
+++ b/portal/conf.py
@@ -34,9 +34,9 @@
extensions = [
'resource_gallery_generator',
'myst_nb',
+ 'sphinx_design',
'ablog',
'sphinx.ext.intersphinx',
- 'sphinx_design',
]
# Define what extensions will parse which kind of source file
@@ -77,7 +77,7 @@
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_css_files = ['custom.css']
-# html_js_files = ['custom.js']
+html_js_files = ['custom.js']
# Disable Sidebars on special pages
html_sidebars = {