You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
490 lines
14 KiB
490 lines
14 KiB
{{ define "main" }}
|
|
<section id="ecosystem-catalog-listing">
|
|
<div class="container">
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="loading"><img src="/images/icon.svg" alt="AlmaLinux OS icon" height="64">{{ i18n "LOADING" }}</div>
|
|
</div>
|
|
<div class="col-12">
|
|
<a href="#!" class="filter-btn" id="filter-link">
|
|
<i class="bi bi-filter-circle"></i>{{ i18n "FILTER RESULTS" }}
|
|
</a>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div id="filter-container">
|
|
<div id="filter-header">
|
|
{{ i18n "FILTER RESULTS" }}
|
|
<div class="filter-close">
|
|
<a id="filter-close-btn" href="#"><i class="bi bi-x-circle-fill"></i></a>
|
|
</div>
|
|
</div>
|
|
<div id="filter-results"></div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-9">
|
|
<div id="search-container">
|
|
<div class="row">
|
|
<div class="col-12 pr">
|
|
<input type="text" id="search-box" placeholder="Search the catalog..." />
|
|
<button id="search-button">{{ i18n "SEARCH" }}</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="search-results"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<style>
|
|
.filter-btn {
|
|
border: 1px solid #24c2ff;
|
|
display: none;
|
|
text-align:center;
|
|
padding:0.75rem;
|
|
text-decoration: none;
|
|
margin-bottom: 1rem;
|
|
font-weight: bold;
|
|
color: #24c2ff;
|
|
font-size: 1.2rem;
|
|
}
|
|
.filter-header {
|
|
display: none;
|
|
}
|
|
.filter-btn i {
|
|
margin-right: 0.25rem;
|
|
font-size: 1.2rem;
|
|
}
|
|
.filter-btn:hover, .filter-btn:visited, .filter-btn:active {
|
|
text-decoration: none;
|
|
color: #24c2ff;
|
|
}
|
|
#search-box {
|
|
width: 100%;
|
|
border: none;
|
|
border-radius: 1rem;
|
|
height: 3rem;
|
|
margin-bottom:1.5rem;
|
|
border: 2px solid gray;
|
|
padding-left: 1rem;
|
|
}
|
|
#search-button {
|
|
width:25%;
|
|
height: 3rem;
|
|
border: 2px solid gray;
|
|
border-radius: 1rem;
|
|
position:absolute;
|
|
right:0;
|
|
background: #04AA6D;
|
|
color: white;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.pr {
|
|
margin:0 !important;
|
|
padding:0 !important;
|
|
}
|
|
#search-container {
|
|
display: none;
|
|
}
|
|
|
|
@keyframes spin {
|
|
0% {
|
|
transform: rotate(0deg);
|
|
}
|
|
100% {
|
|
transform: rotate(360deg);
|
|
}
|
|
}
|
|
|
|
#ecosystem-catalog-listing {
|
|
padding-top: 1.5rem;
|
|
padding-bottom: 1.5rem;
|
|
background: white;
|
|
color: black;
|
|
}
|
|
|
|
.filter {
|
|
display: block;
|
|
font-size:1rem;
|
|
border-bottom: 1px solid lightgray;
|
|
border-top: 1px solid lightgray;
|
|
padding-top: 0.2rem;
|
|
padding-bottom: 0.4rem;
|
|
summary, summary h2 {
|
|
font-size: 1.5rem;
|
|
line-height: 1.5rem;
|
|
font-weight: bold;
|
|
text-transform: capitalize;
|
|
}
|
|
summary h2 {
|
|
display: inline-block;
|
|
}
|
|
|
|
label {
|
|
display: block;
|
|
font-size:1rem;
|
|
margin-left: 1rem;
|
|
}
|
|
|
|
input {
|
|
font-size: 1rem !important;
|
|
}
|
|
|
|
label:first-of-type {
|
|
margin-top: 0.5rem;
|
|
}
|
|
|
|
}
|
|
.loading {
|
|
margin: 0 auto;
|
|
font-size: 5rem;
|
|
color: black;
|
|
text-align: center;
|
|
|
|
img {
|
|
text-align: center;
|
|
display: block;
|
|
margin: 3rem auto 0 auto;
|
|
animation: spin 2s linear infinite;
|
|
}
|
|
}
|
|
.search-result, .search-result:hover, .search-result:visited {
|
|
text-decoration: none;
|
|
color: black;
|
|
}
|
|
|
|
.search-result p {
|
|
overflow: hidden;
|
|
display: -webkit-box;
|
|
-webkit-line-clamp: 2;
|
|
-webkit-box-orient: vertical;
|
|
text-overflow: ellipsis; /* This adds the ellipsis */
|
|
}
|
|
|
|
.search-result {
|
|
display: block;
|
|
border: 1px solid lightgray;
|
|
padding: 1rem;
|
|
margin-top: 0.5rem;
|
|
border-radius: 1rem;
|
|
}
|
|
|
|
.search-result:first-of-type {
|
|
margin-top: 0;
|
|
}
|
|
|
|
.search-result:hover {
|
|
background: lightgray;
|
|
}
|
|
.result-img {
|
|
max-height: 75px;
|
|
max-width: 100%;
|
|
margin:0 auto;
|
|
}
|
|
.img-con {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
.es-details span {
|
|
font-size: smaller;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
.es-details {
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
#filter-container {
|
|
display: block;
|
|
}
|
|
#filter-header {
|
|
display: none;
|
|
}
|
|
@media (max-width: 991px) {
|
|
.filter summary h2 {
|
|
font-size: 1.1rem;
|
|
}
|
|
}
|
|
@media (max-width: 767px) {
|
|
#filter-container {
|
|
display: none;
|
|
position: fixed;
|
|
overflow-y: auto; /* Enable vertical scrolling for content that overflows */
|
|
bottom: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background-color: white; /* Optional: Set a background color */
|
|
z-index: 1020;
|
|
|
|
border: 2px solid black;
|
|
}
|
|
#filter-results {
|
|
padding-top: 3.2rem;
|
|
position: fixed;
|
|
overflow-y: auto;
|
|
height: calc(100% - 3.2rem);
|
|
overflow-y: auto;
|
|
width: 100%;
|
|
}
|
|
#filter-header {
|
|
position: fixed;
|
|
top:0;
|
|
left:0;
|
|
height:3.2rem;
|
|
width:100%;
|
|
z-index:1021;
|
|
display: block;
|
|
background:black;
|
|
color: white;
|
|
line-height: 3.2rem;
|
|
padding: 0 1rem 0 1rem;
|
|
font-weight: 1000;
|
|
}
|
|
.filter-btn {
|
|
display: block;
|
|
}
|
|
.filter-close {
|
|
font-size:2rem;
|
|
display: inline-block;
|
|
float: right;
|
|
line-height:2.7rem;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<script>
|
|
|
|
document.addEventListener("DOMContentLoaded", async function () {
|
|
const pagefind = await import("/pagefind/pagefind.js");
|
|
await pagefind.init();
|
|
|
|
const filters = await pagefind.filters();
|
|
|
|
|
|
|
|
// This function returns a string suitable for being used as an HTML id
|
|
function sanitizeForId(...parts) {
|
|
const sanitizedParts = parts.map((part) => {
|
|
if (typeof part === "string") {
|
|
return part.replace(/[^a-zA-Z0-9-_]/g, "").toLowerCase(); //Sanitize string and lowercase them
|
|
} else if (typeof part === "number") {
|
|
return part.toString(); // Convert numbers to strings
|
|
} else {
|
|
return ""; // Ignore other types I guess?
|
|
}
|
|
});
|
|
|
|
const joined = sanitizedParts.join("-"); // Join with hyphens
|
|
|
|
// Ensure the ID starts with a letter (important for CSS selectors)
|
|
if (!/^[a-zA-Z]/.test(joined)) {
|
|
return "id-" + joined;
|
|
}
|
|
return joined;
|
|
}
|
|
|
|
// Handles and displays filters
|
|
function createFilterElements(filters, containerId) {
|
|
const filterContainer = document.getElementById(containerId);
|
|
|
|
//Get checkbox status and store for restoration later
|
|
const checkedBoxIds = Array.from(
|
|
filterContainer.querySelectorAll('input[type="checkbox"]:checked')
|
|
).map((checkbox) => checkbox.id);
|
|
|
|
//Get the open / closed state of details elements and store for restoration later
|
|
const detailsElements = filterContainer.querySelectorAll("details");
|
|
const stateMap = {};
|
|
detailsElements.forEach((details) => {
|
|
stateMap[details.id] = details.open;
|
|
});
|
|
|
|
//Blank out the filters
|
|
filterContainer.innerHTML = "";
|
|
|
|
//Rebuild the filters
|
|
for (const filterName in filters) {
|
|
const filterValues = filters[filterName];
|
|
|
|
const filterElement = document.createElement("details"); // Container for each filter
|
|
filterElement.setAttribute("open", ""); //This makes the filters open by default
|
|
filterElement.id = sanitizeForId(filterName);
|
|
filterElement.classList.add("filter");
|
|
|
|
const filterLabel = document.createElement("summary");
|
|
const filterHeading = document.createElement("h2");
|
|
filterHeading.textContent = filterName.replace(/-/g, " ");
|
|
filterLabel.appendChild(filterHeading);
|
|
filterElement.appendChild(filterLabel);
|
|
|
|
for (const filterValue in filterValues) {
|
|
//Axe the empty filters
|
|
if (`${filterValues[filterValue]}` < 1) {
|
|
continue;
|
|
}
|
|
|
|
const checkbox = document.createElement("input");
|
|
checkbox.type = "checkbox";
|
|
checkbox.id = sanitizeForId(filterName, filterValue);
|
|
checkbox.name = filterName;
|
|
checkbox.value = filterValue;
|
|
checkbox.checked = false; // By default uncheck all filters
|
|
|
|
const label = document.createElement("label");
|
|
label.textContent = ` ${filterValue} (${filterValues[filterValue]})`;
|
|
label.prepend(checkbox); // Put checkbox before the label text
|
|
|
|
filterElement.appendChild(label);
|
|
}
|
|
|
|
filterContainer.appendChild(filterElement);
|
|
}
|
|
|
|
//Restore the checked state of filter checkboxes from the previous search
|
|
checkedBoxIds.forEach((id) => {
|
|
const checkbox = document.getElementById(id);
|
|
if (checkbox) {
|
|
checkbox.checked = true;
|
|
}
|
|
});
|
|
|
|
//Restore the open/closed state of filter summaries from the previous search
|
|
detailsElements.forEach((details) => {
|
|
if (details.open !== true) {
|
|
const detailsElement = document.getElementById(details.id);
|
|
if (detailsElement) {
|
|
detailsElement.removeAttribute("open");
|
|
}
|
|
}
|
|
});
|
|
|
|
document
|
|
.querySelectorAll('.filter input[type="checkbox"]')
|
|
.forEach((checkbox) => {
|
|
checkbox.addEventListener("change", searchWithFilters); // Trigger a search on filter change
|
|
});
|
|
}
|
|
|
|
// Search and Display Results Function
|
|
async function searchWithFilters(e = null, query = null) {
|
|
if (query === '' || typeof query === "undefined" || !query) {
|
|
query = null;
|
|
}
|
|
|
|
// Get active filters from the checkboxes
|
|
const activeFilters = {};
|
|
const checkboxes = document.querySelectorAll(
|
|
'.filter input[type="checkbox"]'
|
|
);
|
|
checkboxes.forEach((checkbox) => {
|
|
if (checkbox.checked) {
|
|
activeFilters[checkbox.name] =
|
|
activeFilters[checkbox.name] || [];
|
|
activeFilters[checkbox.name].push(checkbox.value);
|
|
}
|
|
});
|
|
|
|
const results = await pagefind.search(query, { filters: activeFilters });
|
|
|
|
//Map the search results to make them easier to work with
|
|
const pages = await Promise.all(results.results.map((r) => r.data()));
|
|
|
|
// Axe the loading div if it exists
|
|
const loadingDiv = document.querySelector('.loading');
|
|
if (loadingDiv) {
|
|
loadingDiv.remove();
|
|
}
|
|
|
|
//Show the search box
|
|
document.getElementById("search-container").style.display = "block";
|
|
|
|
// Display Search Results in a Div
|
|
const resultsContainer = document.getElementById("search-results"); // Get your results container
|
|
|
|
resultsContainer.innerHTML = ""; // Clear previous results
|
|
if (pages.length > 0) {
|
|
|
|
// Only Update the filters based on the new search if
|
|
// there are results
|
|
const newFilters = await results.filters;
|
|
createFilterElements(newFilters, "filter-results");
|
|
|
|
pages.forEach((result) => {
|
|
const resultElement = document.createElement("a");
|
|
resultElement.classList.add("search-result"); // Add a class for styling
|
|
resultElement.href = result.url;
|
|
resultElement.innerHTML = `<div class="row"><div class="col-md-2 col-12 img-con"><img class="result-img" src="${result.meta.image}"></div><div class="col-md-7 col-12"><h3>${result.meta.title}</h3><p>${result.meta.excerpt}</p></div><div class="col-md-3 col-12 es-details"><i class="bi bi-building"></i><span> ${result.meta.provider}</span><br><i class="bi bi-hdd-stack"></i><span> ${result.meta.system}</span><br><i class="bi bi-calendar"></i><span> ${result.meta.published}</span></div></div>`;
|
|
resultsContainer.appendChild(resultElement);
|
|
});
|
|
} else {
|
|
resultsContainer.innerHTML = "<p>No results found.</p>";
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function initializeSearch() {
|
|
const searchBox = document.getElementById("search-box");
|
|
const searchButton = document.getElementById("search-button");
|
|
|
|
// Event Listener for Enter key press
|
|
searchBox.addEventListener("keyup", function(event) {
|
|
if (event.key === "Enter") {
|
|
searchWithFilters(null, searchBox.value.trim()); // Trim to remove extra spaces
|
|
}
|
|
});
|
|
|
|
// Event Listener for button click
|
|
searchButton.addEventListener("click", function() {
|
|
searchWithFilters(null, searchBox.value.trim());
|
|
});
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get references to the link and the filter container
|
|
|
|
|
|
|
|
|
|
//Do a search as soon as the page loads
|
|
searchWithFilters();
|
|
initializeSearch();
|
|
|
|
//filter results link on mobile
|
|
const filterLink = document.getElementById("filter-link");
|
|
const filterContainer = document.getElementById("filter-container");
|
|
|
|
filterLink.addEventListener("click", function (event) {
|
|
event.preventDefault(); // Prevent default link behavior (page jump)
|
|
// Toggle the display of the filter container
|
|
if (filterContainer.style.display != "block") {
|
|
filterContainer.style.display = "block";
|
|
} else {
|
|
filterContainer.style.display = "none";
|
|
}
|
|
});
|
|
|
|
|
|
// Filter results close button on mobile
|
|
const closeFilterButton = document.getElementById("filter-close-btn");
|
|
|
|
closeFilterButton.addEventListener("click", function() {
|
|
filterContainer.style.display = "none"; // Hide the filter container
|
|
});
|
|
|
|
});
|
|
|
|
</script>
|
|
{{ end }}
|