diff --git a/assets/css/ads-radio-button.css b/assets/css/ads-radio-button.css new file mode 100644 index 00000000..0f483d14 --- /dev/null +++ b/assets/css/ads-radio-button.css @@ -0,0 +1,61 @@ + +/* inspiration from here: https://stackoverflow.com/questions/4253920/how-do-i-change-the-color-of-radio-buttons */ +.ads-radio-style { + display: inline-block; + position: relative; + padding: 0; + margin: 10px 0 0; + white-space: nowrap; + } + + .ads-radio-style input[type='radio'] { + display: none; + } + + .ads-radio-style label { + color: var(--ads-checkbox-label-color); + font-weight: normal; + } + + .ads-radio-style label:before { + vertical-align: top; + content: " "; + display: inline-block; + position: relative; + top: 5px; + margin: 0 8px 0 0; + width: 16px; + height: 16px; + border-radius: 11px; + border: 1px solid var(--ads-checkbox-border-color); + background-color: var(--ads-checkbox-background-color); + } + + .ads-radio-style input[type=radio]:hover + label:after { + border-radius: 11px; + width: 8px; + height: 8px; + position: absolute; + top: 9px; + left: 4px; + content: " "; + display: block; + background: var(--ads-checkbox-cross-color-hover); /* depends on theme */ + } + + + .ads-radio-style input[type=radio]:checked + label:before { + border-color:var(--ads-checkbox-border-color-checked); + } + + .ads-radio-style input[type=radio]:checked + label:after { + border-radius: 11px; + width: 8px; + height: 8px; + position: absolute; + top: 9px; + left: 4px; + content: " "; + display: block; + background: var(--ads-checkbox-cross-color-checked); + } diff --git a/assets/css/list-pages.css b/assets/css/list-pages.css index 238a3df9..ed834b5f 100644 --- a/assets/css/list-pages.css +++ b/assets/css/list-pages.css @@ -79,21 +79,22 @@ html[theme="dark"] .sorting-criteria.active { border-bottom: 2.2px solid hsla(0,0%,100%,.9); } + + .filter-facet { - --ads-tag-background-color: #fff; - --ads-tag-background-color-hover: #f7f7f7; - --ads-tag-border-color: 1px solid #5d6c7a; - --ads-tag-border-color-hover: 1px solid #11809f; - --ads-tag-color: #5d6c7a; - --ads-tag-color-hover: #11809f; -} -html[theme="dark"] .filter-facet { - --ads-tag-background-color: #1F2023; - --ads-tag-background-color-hover: #1a1a1a; - --ads-tag-border-color: 1px solid #e5eceb; - --ads-tag-border-color-hover: 1px solid #00c1de; - --ads-tag-color: #fff; - --ads-tag-color-hover: #11809f; + --ads-tag-background-color: var(--arm-color-footing); + --ads-tag-background-color-hover: var(--arm-color-footing); + --ads-tag-border-color: var(--arm-dark-grey); + --ads-tag-border-color-hover: var(--arm-dark-grey); + --ads-tag-color: var(--white); + --ads-tag-color-hover: var(--white); +} + +.filter-facet.not-all { + --ads-tag-background-color: var(--arm-black); + --ads-tag-background-color-hover: var(--arm-black); + --ads-tag-border-color: var(--arm-green); + --ads-tag-border-color-hover: var(--arm-green); } diff --git a/build_steps/update_active_categories.py b/build_steps/update_active_categories.py index 54a7d349..6fc457ea 100644 --- a/build_steps/update_active_categories.py +++ b/build_steps/update_active_categories.py @@ -1,33 +1,54 @@ import os, yaml +from pathlib import Path + + +# Get the absolute path to this python file's directory +script_dir = Path(__file__).parent.absolute() + +# Relative path to content from script, then tet absolute path to content by combining, and use Resolve to handle backwards".." +opensource_relative_path = Path('../content/opensource_packages') +opensource_absolute_path = (script_dir / opensource_relative_path).resolve() + +commercial_relative_path = Path('../content/commercial_packages') +commercial_absolute_path = (script_dir / commercial_relative_path).resolve() + +# Same process for the YAML data file: +yaml_relative_path = Path('../data/all_categories.yml') +yaml_absolute_path = (script_dir / yaml_relative_path).resolve() + + + +print('Updating active categories...') # LOAD ALL CATS all_cats = '' -with open('data/all_categories.yml', 'r') as all_cat_file: +with open(yaml_absolute_path, 'r') as all_cat_file: all_cats = yaml.safe_load(all_cat_file) + # CREATE NEW ACTIVE_CATS LIST -content_dir = 'content/sw_database' +content_directories = [opensource_absolute_path, commercial_absolute_path] + active_cats = [] -for content_file in os.listdir(content_dir): - if (content_file != ('_index.md')) and content_file.endswith('.md'): - # OBTAIN CATEGORY FROM FILE - cat = None - with open(os.path.join(content_dir, content_file), 'r') as f: - for line in f: - if 'category:' in line: - print(line.replace('category:','').strip()) - cat = line.replace('category:','').strip() - break - - #content_all = f.read() - #start = content_all.find('---') - #end = content_all.find('---', start + 3) - #content_metadata = content_all[start+3:end].strip() - #cat = yaml.safe_load(content_metadata).get('category') - # ADD TO ACTIVE_CATS LIST IF NOT ALREADY PRESENT - if cat in all_cats: - if cat not in active_cats: - active_cats.append(cat) +for content_directory in content_directories: + for content_file in os.listdir(content_directory): + if (content_file != ('_index.md')) and content_file.endswith('.md'): + # OBTAIN CATEGORY FROM FILE + cat = None + with open(os.path.join(content_directory, content_file), 'r') as f: + for line in f: + if 'category:' in line: + cat = line.replace('category:','').strip() + break + # ADD TO ACTIVE_CATS LIST IF NOT ALREADY PRESENT + if cat in all_cats: + if cat not in active_cats: + active_cats.append(cat) + +# Order list alphabetically +active_cats = sorted(active_cats) +for cat in active_cats: + print(' '+cat) # SAVE ACTIVE_CATS LIST with open('data/active_categories.yml', 'w') as active_cats_file: diff --git a/data/active_categories.yml b/data/active_categories.yml index 28d600ed..2b3837d2 100644 --- a/data/active_categories.yml +++ b/data/active_categories.yml @@ -1,22 +1,22 @@ +- AI/ML - CI/CD +- Compilers/Tools +- Compression +- Containers and Orchestration +- Content mgmt platforms +- Crypto - Data-format +- Databases - Big-data - Databases - noSQL -- Storage -- Containers and Orchestration -- Runtimes -- Networking -- Compilers/Tools -- Monitoring/Observability +- Distros +- E-commerce platforms +- Languages and Frameworks - Messaging/Comms -- Databases - Big-data +- Monitoring/Observability +- Networking +- Operating System +- Runtimes - Service Mesh +- Storage - Video -- E-commerce platforms -- Crypto - Web Server -- Languages and Frameworks -- AI/ML -- Distros -- Compression -- Operating System -- Content mgmt platforms diff --git a/data/categories.yml b/data/categories.yml new file mode 100644 index 00000000..99a9d9af --- /dev/null +++ b/data/categories.yml @@ -0,0 +1,73 @@ +category_list: + - group: OS & Languages + categories: + - name: Operating System + description: Operating systems including linux kernel and tools. + - name: Compilers/Tools + description: Compilers and other build tools that help generate and optimize machine readable binary code from various high-level programming languages. + - name: Runtimes + description: Frameworks for development and execution of high-level application bytecode into machine-specific code at runtime such as Java or .Net. + - name: Languages and Frameworks + description: Programming languages and frameworks for building software applications. + - group: Web + categories: + - name: Web Server + description: Software that processes and delivers HTTP based requests for web content. + - name: E-commerce platforms + description: Platforms for building and managing online stores and transactions. + - name: Content mgmt platforms + description: Software that manages, collects, retrieves and delivers different types of content on the web. + - group: Database + categories: + - name: Database + description: Structured, un-structured or hybrid organized collection of data. + - name: Databases - noSQL + description: Non-relational databases designed for scalability and flexibility in handling unstructured data. + - name: Databases - Big-data + description: Database systems optimized for storing and processing large volumes of data across distributed environments. + - name: Data-format + description: Formats for structuring and encoding data for storage, transmission, or processing. + - name: Compression + description: Algorithms and techniques for reducing the size of data for storage or transmission. + - group: Cloud-native + categories: + - name: Containers and Orchestration + description: Containers package the source code of an application and its dependencies in a single unit. Container Orchestration tools automate the management and deployment of containerized applications. + - name: Messaging/Comms + description: Systems for exchanging messages or facilitating communication between distributed applications. + - name: Monitoring/Observability + description: Tools and practices for tracking and understanding the behavior and performance of software systems. + - name: Service Mesh + description: Service Mesh manages and secures communication between services in distributed computing environments. + - name: DevOps + description: Tools that help automate software development process. + - group: Storage + categories: + - name: Storage + description: Systems for storing and managing data, ranging from traditional disk storage to distributed file systems. + - group: Networking + categories: + - name: Networking + description: Communication of data across different types of network media - both wired and wireless. + - group: Media + categories: + - name: Video + description: Software for encoding, decoding and translating video from one format to another. + - name: Gaming + description: Software for running and streaming video games and other device applications on the cloud. + - group: AI/ML/HPC + categories: + - name: AI/ML + description: Technologies and algorithms for creating systems that can learn from data and make predictions or decisions. + - name: HPC + description: Software to solve advance computational problems using large clusters of systems. + - group: Security + categories: + - name: Crypto + description: Software for cryptographic operations on data including encryption, decryption, authentication etc. + - name: Security applications + description: Practices, tools, and protocols for protecting systems, networks, and data from unauthorized access or attacks. + - group: Miscellaneous + categories: + - name: Miscellaneous + description: Packages that do not fit in any other categories. \ No newline at end of file diff --git a/themes/arm-design-system-hugo-theme/layouts/index.html b/themes/arm-design-system-hugo-theme/layouts/index.html index 660aab38..acc0d6c3 100644 --- a/themes/arm-design-system-hugo-theme/layouts/index.html +++ b/themes/arm-design-system-hugo-theme/layouts/index.html @@ -27,7 +27,17 @@ {{ $recent_packages := $.Site.Data.recently_added_packages }} +{{ $filter_category_dict := .Site.Data.categories }} + +{{/* REMOVE WHEN NOT NEEDED */}} {{ $filter_categories := .Site.Data.active_categories }} +{{ $all := slice "All" }} +{{ $filter_categories := $all | append $filter_categories }} + + + + +{{ $filter_licenses := slice "All" "Open source" "Commercial" }} {{/******************************************************* *******************************************************/}} @@ -54,7 +64,17 @@
- {{ partial "eco-dashboard/filter-box.html" (dict "context" . "categories" $filter_categories)}} +
+ {{/* MULTI CATEGORY STRUCTURE */}} + {{ partial "eco-dashboard/filter-dropdown-multilevel.html" (dict "context" . "title" "Category" "items" $filter_category_dict)}} + + {{ partial "eco-dashboard/filter-box.html" (dict "context" . "title" "License" "items" $filter_licenses)}} + {{/* {{ partial "eco-dashboard/filter-box.html" (dict "context" . "title" "Category" "items" $filter_categories)}} */}} + + {{/* SINGLE CATEGORY STRUCTURE + {{ partial "eco-dashboard/filter-dropdown.html" (dict "context" . "title" "Category" "items" $filter_categories)}} */}} + +
@@ -81,7 +101,10 @@

Most recently added packa
-

XYZ more packages. Explore by:

+

+ {{sub (len $packages_alphabetical_order) (len $recent_packages) }} + more packages. Explore by: +

Filter by Category Search by Name @@ -118,7 +141,7 @@

Most recently added packa console.log('OPEN MODAL, MOBILE'); } else { - var category_element = document.getElementsByClassName('filter-column-div')[0]; + var category_element = document.getElementById('filter-container-div'); // force focus to top of element via smoth scroll (100px to account for sticky global nav) var rect = category_element.getBoundingClientRect(); // position relative to current viewport diff --git a/themes/arm-design-system-hugo-theme/layouts/partials/eco-dashboard/active-filter-and-sort-bar.html b/themes/arm-design-system-hugo-theme/layouts/partials/eco-dashboard/active-filter-and-sort-bar.html index 59c5c207..790bdf30 100644 --- a/themes/arm-design-system-hugo-theme/layouts/partials/eco-dashboard/active-filter-and-sort-bar.html +++ b/themes/arm-design-system-hugo-theme/layouts/partials/eco-dashboard/active-filter-and-sort-bar.html @@ -13,12 +13,26 @@
- Filters: + Active Filters: + + + +
License: All
+
+
+ + +
Category: All
+
+
+ + {{/* Not needed for radio behavior + */}}
diff --git a/themes/arm-design-system-hugo-theme/layouts/partials/eco-dashboard/filter-box.html b/themes/arm-design-system-hugo-theme/layouts/partials/eco-dashboard/filter-box.html index 68b6a2e6..e36f59b9 100644 --- a/themes/arm-design-system-hugo-theme/layouts/partials/eco-dashboard/filter-box.html +++ b/themes/arm-design-system-hugo-theme/layouts/partials/eco-dashboard/filter-box.html @@ -1,16 +1,36 @@ + +{{$group_display_name := .title}} +{{$group_name := .title | urlize}} + - Categories + {{.title}}
- {{- range .categories -}} + {{- range .items -}} {{/* URLIZE with changing / to - of category names */}} {{ $replaced := replace . "/" "__" }} - {{ $urlized := $replaced | urlize }} - + {{ $urlized_name := $replaced | urlize }} + +
+ + +
+ + {{/* CHECKBOXES + {{ . }} + */}} {{end}}
diff --git a/themes/arm-design-system-hugo-theme/layouts/partials/eco-dashboard/filter-dropdown-multilevel.html b/themes/arm-design-system-hugo-theme/layouts/partials/eco-dashboard/filter-dropdown-multilevel.html new file mode 100644 index 00000000..8378845e --- /dev/null +++ b/themes/arm-design-system-hugo-theme/layouts/partials/eco-dashboard/filter-dropdown-multilevel.html @@ -0,0 +1,81 @@ + + +{{$group_display_name := .title}} +{{$group_name := .title | urlize}} + + + + {{.title}} +
+
+ + +

+ Filter by category and view the category description here. +

+
+
+
+ + + + + diff --git a/themes/arm-design-system-hugo-theme/layouts/partials/eco-dashboard/filter-dropdown.html b/themes/arm-design-system-hugo-theme/layouts/partials/eco-dashboard/filter-dropdown.html new file mode 100644 index 00000000..27711103 --- /dev/null +++ b/themes/arm-design-system-hugo-theme/layouts/partials/eco-dashboard/filter-dropdown.html @@ -0,0 +1,68 @@ + + +{{$group_display_name := .title}} +{{$group_name := .title | urlize}} + + + + {{.title}} +
+
+ + +

+ Filter by category and view the category description here. +

+
+
+
+ + + + \ No newline at end of file diff --git a/themes/arm-design-system-hugo-theme/layouts/partials/head/head.html b/themes/arm-design-system-hugo-theme/layouts/partials/head/head.html index 4b439847..66ccf186 100644 --- a/themes/arm-design-system-hugo-theme/layouts/partials/head/head.html +++ b/themes/arm-design-system-hugo-theme/layouts/partials/head/head.html @@ -45,7 +45,7 @@ --> -{{$all_page_css := slice (resources.Get "css/eco-dashboard.css") (resources.Get "css/cookie.css") (resources.Get "css/cross-page.css") (resources.Get "css/mobile.css")}} +{{$all_page_css := slice (resources.Get "css/eco-dashboard.css") (resources.Get "css/cookie.css") (resources.Get "css/cross-page.css") (resources.Get "css/ads-radio-button.css") (resources.Get "css/mobile.css")}} {{ $css_home := $all_page_css | append diff --git a/themes/arm-design-system-hugo-theme/layouts/partials/package-display/row-main.html b/themes/arm-design-system-hugo-theme/layouts/partials/package-display/row-main.html index 2996e537..c240b5b4 100644 --- a/themes/arm-design-system-hugo-theme/layouts/partials/package-display/row-main.html +++ b/themes/arm-design-system-hugo-theme/layouts/partials/package-display/row-main.html @@ -1,6 +1,25 @@ +{{/* Hugo processing to create proper tag + + tag-license-open-source + tag-category-ai-ml + + + + tag-{{$filter_type}}-{{.category}} +*/}} + +{{$license_type := "open-source"}} +{{if .metadata.Params.vendor}} + {{$license_type = "commercial"}} +{{end}} +{{ $search_license_tag := print "tag-license-" $license_type}} +{{ $search_category_tag := print "tag-category-" .category}} + + + diff --git a/themes/arm-design-system-hugo-theme/static/js/eco-dashboard/search.js b/themes/arm-design-system-hugo-theme/static/js/eco-dashboard/search.js index b733df2d..6a4adb15 100644 --- a/themes/arm-design-system-hugo-theme/static/js/eco-dashboard/search.js +++ b/themes/arm-design-system-hugo-theme/static/js/eco-dashboard/search.js @@ -1,18 +1,21 @@ function setToBrowse() { - /* - 1) Hide dashboard information - 2) Show all package table - - */ - console.log('in browse function'); + var main_table_div = document.getElementById('all-packages-div'); + + // quickly return if browse already activated + if (main_table_div.getAttribute('browse')) + { return } + // Hide dashboard information document.getElementById('initial-dashboard-display').classList.add('hidden'); // Show all package table - document.getElementById('all-packages-div').classList.remove('hidden'); + main_table_div.classList.remove('hidden'); // Update shown number of packages updateShownNumber(); + + // Set browse 'True' attribute to + main_table_div.setAttribute('browse',true); } @@ -106,65 +109,66 @@ function filter_card(card) { const active_tags = document.getElementsByClassName('filter-facet'); if (active_tags.length==0) { return false } // Return already if no tags...no filtering neccecary - // create dictionary, grouping together tags by group - // gropued_active_tags = {'group-subjects': [tag1,tag2], 'group-skill-level': [tag3]} - // group_status = {'group-subjects': true, 'group-skill-level': true} - let grouped_active_tags = {}; - let group_status = {}; - for (let tag of active_tags) { - let all_tag_classes = tag.classList; - for (let c of all_tag_classes) { - if (c.includes('group-')) { - let group_name = c; - if (group_name in grouped_active_tags) { - // add tag to existing dict list - grouped_active_tags[group_name].push(tag); - } - else { - // create new dict list (and initalize group_status) - grouped_active_tags[group_name] = [tag]; - group_status[group_name] = true; - } - } + let filter_facet_html = tag.textContent.trim(); + // Category: AI/ML --> tag-category-ai-ml let pass all cards that have 'tag-category-ai-ml' as part of className. + // License: All --> tag-license let pass all cards that have 'tag-license-' as part of className. + let filter_facet_sanitized = filter_facet_html.replace('/','__').toLowerCase() // license: open source category: ai__ml + let filter_facet_organized = filter_facet_sanitized.replace(': ','-'); // license-open source category-ai__ml + //let active_tag_name = 'tag-'+filter_facet_organized.replaceAll(' ','-'); // tag-license-open-source tag-category-ai__ml + + // Very complex regex expression to simply always replace ' ' with dashes, EXCEPT when preceeding or following dashes (such as in a category like 'Databases - big-data' + let active_tag_name = 'tag-' + filter_facet_organized.replace(/(\s+)(?=-)|(?<=-)(\s+)/g, '').replaceAll(/\s/g, '-'); + + if ((active_tag_name.endsWith('-all')) || (card.classList.contains(active_tag_name))) { + // Card is not proven to be hidden; continue through the other filter groups. + continue } - } - for (let group_name in grouped_active_tags) { - // iterate over tags in this group - for (let tag of grouped_active_tags[group_name]){ - // If card's classList contains any tag in this group (OR behavior), then don't hide - let active_tag_name = tag.id.replace('filter-',''); // tag.id = filter-tag-databases strip off 'filter-' - if (card.classList.contains(active_tag_name)) { - // Don't hide, it matches a tag in this category (and we can break as we already know we're set in this group) - group_status[group_name] = false; - break - } + else { + // Card should be hidden, so just return true now. + return true } - } + } + // If Card was never proven to be hidden through all filter groups, then it is shown. Return to_hide = False + return false + +} + + + +function updateFacet(filter_group, item_name) { + const all_path_cards = document.querySelectorAll('.search-div'); - // If there are any trues, that means that this path should be hidden. If all falses, then it should be shown (to_hide = false) - if(!Object.values(group_status).includes(true)){ to_hide = false; } - - /* OLD implementation of ANDS only - for (tag of active_tags) { - let active_tag_name = tag.id.replace('filter-',''); // tag.id = filter-tag-databases strip off 'filter-' - if (card.classList.contains(active_tag_name)) { - // DO NOT hide this card as it matches an active tag! - to_hide = false; - } - else { // If here, this tag isn't in the card's classlist (not a match) - // If the same group is present, OR behavior applies, so don't hide it. - // CARD: tag-ci-cd tag-web tag-linux - // TAG: tag-ci-cd group-subjects - - //Return true, meaning hide it - return true - } + + // Replace the filter group's tag with the currently selected item name + // license + var filter_facet = document.getElementById('filter-facet-display-name-group-'+filter_group.toLowerCase()); + filter_facet.textContent = filter_group+": "+item_name; + + let ads_filter_component = filter_facet.closest('ads-tag'); + // Update tag's class + if ((item_name == "All") || (item_name == "all")) { + console.log(item_name,'in removing') + ads_filter_component.classList.remove('not-all') + } + else { + console.log(item_name,'in adding') + ads_filter_component.classList.add('not-all') } - */ - return to_hide + // Apply search and filters to current parameters + // deal with ads search promise + document.getElementById('search-box').value().then((value) => { + let results_to_hide = applySearchAndFilters(all_path_cards, value); // apply active search & filter terms to the specified divs + hideElements(all_path_cards,results_to_hide); + updateShownNumber(); // Update UI telling how many are displayed + }); } + + + + + function removeFacet(tag) { const all_path_cards = document.querySelectorAll('.search-div'); //////// Remove Facet @@ -257,6 +261,7 @@ function addFacet(element) { }); } + function clearAllFilters() { // call removeFacet on each tag let active_facets = document.querySelectorAll('ads-tag.filter-facet'); @@ -326,7 +331,6 @@ function applySearchAndFilters(all_path_cards, search_string) { - function searchHandler(search_string) { // HANDLE if coming from ads search box (event.value) or URL (string) if (! (typeof search_string === 'string')) { @@ -359,39 +363,55 @@ function filterHandler(element) { Update facets to appear Apply only updated filter to search results (show what isn't that matches & vice versa) */ - const all_path_cards = document.querySelectorAll('.search-div'); - + const all_path_cards = document.querySelectorAll('.search-div'); + + // Set page state to Browse, if not already + setToBrowse(); - // Set page state to Browse, if not already - setToBrowse(); + // get status of checkbox (true for checked, false for unchecked) + element.value().then((value) => { + if (value === true) { + // add 'checked' value to html + addFacet(element,all_path_cards); + } + else { + //????????????????????????????????????????????????????????????????????????? + // This is being called when there is no facet. Strange behaivor with checkbox value being set strangely + // ADS team to fix this problem. + let tag = null; + const tags = element.classList.values(); + for (let t of tags) { + if (t.includes('tag')) { + tag = t; + break; + } + }; + removeFacet(tag); + } + }); +} +function filterHandler_radio(element) { + /* Called from 'input' components themselves, triggered from a user press on radio + Add Facet for correct one + Remove all other facets + */ + const all_path_cards = document.querySelectorAll('div.search-div'); - // get status of checkbox (true for checked, false for unchecked) - element.value().then((value) => { - if (value === true) { - // add 'checked' value to html - addFacet(element,all_path_cards); - } - else { - //????????????????????????????????????????????????????????????????????????? - // This is being called when there is no facet. Strange behaivor with checkbox value being set strangely - // ADS team to fix this problem. - let tag = null; - const tags = element.classList.values(); - for (let t of tags) { - if (t.includes('tag')) { - tag = t; - break; - } - }; - removeFacet(tag); - } - }); + // Set page state to Browse, if not already + setToBrowse(); + + // Update the facet + var item_name = element.getAttribute('data-display-name'); + var group_name = element.getAttribute('data-group-display-name'); + updateFacet(group_name,item_name); + } + document.addEventListener("DOMContentLoaded", function () { // Assign inputChangeHandler to search box