From 1ceb7cccb62e1ddef47575dd6b00fa82e2240ad5 Mon Sep 17 00:00:00 2001 From: Julia Kent <46687291+jukent@users.noreply.github.com> Date: Wed, 12 Jun 2024 13:52:40 -0600 Subject: [PATCH 1/7] dropdown gallery changes --- portal/_extensions/gallery_generator.py | 99 ++++----- portal/_static/custom.css | 54 +++++ portal/_static/custom.js | 263 ++++++++---------------- portal/conf.py | 4 +- 4 files changed, 197 insertions(+), 223 deletions(-) diff --git a/portal/_extensions/gallery_generator.py b/portal/_extensions/gallery_generator.py index 61a033b9..264127aa 100644 --- a/portal/_extensions/gallery_generator.py +++ b/portal/_extensions/gallery_generator.py @@ -1,6 +1,6 @@ import itertools import pathlib - +import re from truncatehtml import truncate @@ -9,13 +9,20 @@ 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 +33,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 +76,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,50 +112,51 @@ def build_from_items(items, filename, title='Gallery', subtitle=None, subtext=No if ellipsis_str in short_description: modal_str = f""" """ 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 - - {modal_str} + new_card = f""" + :::{{grid-item-card}} + :shadow: md + :class-footer: card-footer + :class-card: tagged-card {tag_classes} - +++ + + {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)} @@ -162,12 +167,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..a35c408f 100644 --- a/portal/_static/custom.css +++ b/portal/_static/custom.css @@ -144,3 +144,57 @@ 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; +} + +main.bd-content #main-content a { + color: #1a648f; +} + +.sd-col.sd-d-flex-row.docutils.has-visible-card { + margin-bottom: 1rem; +} \ No newline at end of file diff --git a/portal/_static/custom.js b/portal/_static/custom.js index f0f3d574..449db0d6 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; - - for (var j = 0; j < filters.domains.length; j++) { - var filter = filters.domains[j]; - - 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); + + function filterResults(filters) { + console.log("Filtering results..."); + var rElems = document.querySelectorAll(".tagged-card"); + + rElems.forEach(function (el) { + var isVisible = true; // Assume visible by default + + // 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)); + + // The element should be visible if it matches any filter within each category + isVisible = hasMatchingAffiliation && hasMatchingDomain && hasMatchingFormat && hasMatchingPackage; } - } - - 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; - } + + // 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"); } - - if (isHidden) { - hiddenElems.push(el); - } - } - } - - for (var i = 0; i < rElems.length; i++) { - rElems[i].classList.replace("d-none", "d-flex"); - } - - if (hiddenElems.length <= 0) { - return; - } - - for (var i = 0; i < hiddenElems.length; i++) { - hiddenElems[i].classList.replace("d-flex", "d-none"); + }); + + // Update the margins after filtering + updateMargins(); } -} - - -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; - } - - for (var i = 0; i < domainsCbs.length; i++) { - domainsCbs[i].checked=false; - } - - for (var i = 0; i < formatsCbs.length; i++) { - formatsCbs[i].checked=false; + + var checkboxes = document.querySelectorAll('input[type="checkbox"]'); + checkboxes.forEach(function (checkbox) { + checkbox.addEventListener("change", change); + }); + + 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')); + + // 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 < packagesCbs.length; i++) { - packagesCbs[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; + }); + + change(); } - - change(); -} + + // Initial call to set up correct margins when the page loads + document.addEventListener('DOMContentLoaded', updateMargins); + + console.log("Script loaded."); \ No newline at end of file diff --git a/portal/conf.py b/portal/conf.py index ca5f8c43..57fc5d29 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 = { From f9be8f726627f4821f0a0fe9771de376d500855c Mon Sep 17 00:00:00 2001 From: Julia Kent <46687291+jukent@users.noreply.github.com> Date: Wed, 12 Jun 2024 13:55:25 -0600 Subject: [PATCH 2/7] fix indent --- portal/_extensions/gallery_generator.py | 37 +++++++++++++------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/portal/_extensions/gallery_generator.py b/portal/_extensions/gallery_generator.py index 264127aa..56583f7d 100644 --- a/portal/_extensions/gallery_generator.py +++ b/portal/_extensions/gallery_generator.py @@ -127,29 +127,30 @@ def build_from_items(items, filename, title='Gallery', subtitle=None, subtext=No 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 - :class-card: tagged-card {tag_classes} - -