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

{{ 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>&nbsp;${result.meta.provider}</span><br><i class="bi bi-hdd-stack"></i><span>&nbsp;${result.meta.system}</span><br><i class="bi bi-calendar"></i><span>&nbsp; ${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 }}