Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Provide filtering capabilities for scalers #1400

Merged
merged 8 commits into from
Jun 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions assets/sass/style.sass
Original file line number Diff line number Diff line change
Expand Up @@ -367,3 +367,31 @@ hr
@media screen and (min-width: 769px)
.md-height-desktop
height: 9rem

.filter-icon
display: none

.filter-options
display: inline-block

.filter-pane
padding-left: 0
padding-top: 20px

@media screen and (max-width: 769px)
.filter-icon
padding: 0.5rem
cursor: pointer
border: 1px solid #DBDBDB
font-size: 18px
border-radius: 0.4rem
display: inline-block
&:hover
border: 1px solid #000000

.filter-options
display: none

.filter-pane
padding-left: 20px
padding-top: 10px
4 changes: 2 additions & 2 deletions config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ notAlternative = true

# parameters for lunr search
[params.lunr]
vars = ["title", "maintainer", "description", "availability"]
params = ["availability", "maintainer"]
vars = ["title", "maintainer", "description", "availability", "category", "type"]
params = ["availability", "maintainer", "category", "type"]

# "Highlighted Samples" section on the main page
[[params.samples]]
Expand Down
4 changes: 2 additions & 2 deletions layouts/_default/list.lunr.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{{- $index := slice -}}
{{- range $scalers := where site.RegularPages ".CurrentSection.Title" "Scalers" -}}
{{- $index = $index | append (dict "title" $scalers.Title "version" (index (first 3 (split (delimit (split $scalers.RelPermalink "/") "," "") ",")) 2) "href" $scalers.RelPermalink "availability" $scalers.Params.availability "description" ($scalers.Description | markdownify) "maintainer" $scalers.Params.maintainer ) -}}
{{- $index = $index | append (dict "title" $scalers.Title "version" (index (first 3 (split (delimit (split $scalers.RelPermalink "/") "," "") ",")) 2) "href" $scalers.RelPermalink "availability" $scalers.Params.availability "description" ($scalers.Description | markdownify) "maintainer" $scalers.Params.maintainer "category" $scalers.Params.category "type" "built-in" ) -}}
{{- end -}}

{{- $index | jsonify -}}
{{- $index | jsonify -}}
234 changes: 92 additions & 142 deletions layouts/partials/javascript.html
Original file line number Diff line number Diff line change
Expand Up @@ -50,36 +50,7 @@
});
</script>

{{/* Scalers filter */}}
<script type="text/javascript">
const builtInScalers = document.querySelectorAll("#built-in-scalers");
const externalScalers = document.getElementById("external-scalers");
const btnFocusOnMount = document.getElementById("btn-focus-on-mount");
const builtInSearchResultCount = document.querySelector(".results");
const externalSearchResultCount = document.querySelector(".external-results");
let btns = document.getElementsByClassName("filterBtn");

for (let i = 0; i < btns.length; i++) {
btns[i].addEventListener("click", function () {
let filterValue = btns[i].value.toLowerCase();
if (filterValue === "built-in-scalers") {
builtInScalers.forEach((node) => (node.style.display = "flex"));
externalScalers.style.display = "none";
builtInSearchResultCount.style.display = "block";
externalSearchResultCount.style.display = "none";
} else if (filterValue === "external-scalers") {
// remove focus when changed
btnFocusOnMount.classList.remove("is-focused");
builtInScalers.forEach((node) => (node.style.display = "none"));
externalScalers.style.display = "inline";
builtInSearchResultCount.style.display = "none";
externalSearchResultCount.style.display = "block";
}
});
}
</script>

{{/* In-built Scalers Search */}}
{{/* Scaler Search and filter */}}
<script src="https://unpkg.com/lunr/lunr.js"></script>
<script>
window.addEventListener(
Expand All @@ -94,144 +65,115 @@
const form = document.getElementById("search");
const input = document.getElementById("search-input");
const target = document.querySelector(".is-search-result");
const externalTarget = document.querySelector(
".is-external-search-result"
);
const filterIcon = document.querySelector(".filter-icon");
const filterOptions = document.querySelector(".filter-options");
const searchResultCount = document.querySelector(".results");
const externalSearchResultCount =
document.querySelector(".external-results");
const template = document.getElementById("is-search-template");
const isExternalScalers = document.getElementById("external-scalers");
const isBuiltInScalers = document.getElementById("built-in-scalers");
const externalBanner = document.getElementById("external-banner");
const groups = document.getElementsByClassName(
"artifacthub-widget-group"
);
const interval = 500;
let query = input.value.trim();
let parse = {};
let searchQueue = [];
let checkboxes = document.querySelectorAll(
'input[name="resource_filter"]'
);

// fetch all scalers on inital load
if (!query) {
initSearchIndex();
externalScalersIndex();
}

// logic for input search
input.addEventListener(
"input",
function (event) {
event.preventDefault();
clearTimeout(debounceTimer);
const keywords = input.value.trim();

// disable all buttons during search by default
const filterBtns = document.querySelectorAll(".filterBtn");
filterBtns.forEach((btn) => (btn.disabled = true));

if (!keywords) {
// enable all buttons when input search is empty
const filterBtns = document.querySelectorAll(".filterBtn");
filterBtns.forEach((btn) => (btn.disabled = false));
}

query = keywords;
if (isExternalScalers.style.display === "inline") {
// hide banner during scaler search
externalBanner.style.display = "none";
if (query === "") {
externalBanner.style.display = "block";
}
let externalSearchUrl = new URL(
"https://artifacthub.io/api/v1/packages/search"
);
externalSearchUrl.search = new URLSearchParams({
kind: "8",
sort: "relevance",
ts_query_web: query !== "" ? query : "",
});
debounceTimer = setTimeout(() => {
groups[0].dataset.url = externalSearchUrl;
externalScalersIndex();
}, interval);

// clear out all the scaler item card when external scalers are being searched
while (externalTarget.firstChild.nextSibling) {
externalTarget.removeChild(
externalTarget.firstChild.nextSibling.nextSibling
.nextElementSibling
);
}
return;
}

debounceTimer = setTimeout(initSearchIndex, interval);

// clear out all the scaler item card when in-built scalers are being searched
// clear out all the scaler item card during search
while (target.firstChild.nextSibling) {
target.removeChild(template.nextSibling.nextElementSibling);
}
},
false
);

async function externalScalersIndex() {
const externalScalerUrl = groups[0].dataset.url;
const results = await fetch(externalScalerUrl)
.then((response) => response.json())
.then((data) => {
return data.packages;
})
.catch((err) => console.error("error:", err));

if ("content" in template) {
// show result count
const title = document.createElement("h3");
title.id = "external-search-results";
title.className = "subtitle is-size-3 external-search-results";
// logic for category filter
checkboxes.forEach((checkbox) => {
checkbox.addEventListener("change", function (event) {
if (checkbox.checked) {
const inputValue = event.target.value.split(" ");
// for single queries with spacing, use the first word
const inputQuery = inputValue[0];
searchQueue.push(inputQuery);
query = searchQueue.join(" ");
initSearchIndex();
}

if (!checkbox.checked){
const inputValue = event.target.value.split(" ");
// for single queries with spacing, use the first word
const inputQuery = inputValue[0];
searchQueue = searchQueue.filter(
(word) => word != inputQuery
);
query = searchQueue.join(" ");
initSearchIndex();
}

if (results.length == 0)
title.textContent = `No results found for "${query}"`;
else if (results.length == 1)
title.textContent = `Found one result for "${query}"`;
else if (results.length > 1 && query === "")
title.textContent = `${results.length} scalers available`;
else
title.textContent = `Found ${results.length} results for "${query}"`;
externalSearchResultCount.replaceChildren(title);
// clear out all the scaler item card during search
while (target.firstChild.nextSibling) {
target.removeChild(template.nextSibling.nextElementSibling);
}
});
});

// show the matched result
results.forEach(function (result) {
const element = template.content.cloneNode(true);
element.querySelector(".scaler-title").textContent =
result.display_name;
element
.querySelector(".scaler-title")
.setAttribute("href", "https://artifacthub.io/packages/keda-scaler/" + result.repository.name + "/" + result.normalized_name);
result.description &&
(element.querySelector(".description").textContent =
result.description);
result.repository.organization_name &&
(element.querySelector(".maintainer").textContent =
result.repository.organization_name);
result.repository.user_alias &&
(element.querySelector(".maintainer").textContent =
result.repository.user_alias);
result.version &&
(element.querySelector(
".availability"
).textContent = `v${result.version}`);
externalTarget.appendChild(element);
}, this);
// show filter options on mobile
filterIcon.addEventListener("click", function () {
if (filterOptions.style.display === "") {
filterOptions.style.display = "none";
}

if (filterOptions.style.display === "none") {
filterOptions.style.display = "flex";
}else {
filterOptions.style.display = "none";
}
}
});

async function initSearchIndex() {
const scalers = await fetch("/index.json", { method: "GET" })
const builtInScalers = await fetch("/index.json", { method: "GET" })
.then((response) => response.json())
.then((data) => {
return data;
})
.catch((err) => console.error("error:", err));

const externalScalerUrl = "https://artifacthub.io/api/v1/packages/search?offset=0&limit=20&facets=false&kind=8&deprecated=false&sort=relevance";
const externalScalersData = await fetch(externalScalerUrl)
.then((response) => response.json())
.then((data) => {
return data.packages;
})
.catch((err) => console.error("error:", err));

const externalScalers = externalScalersData.map(scaler => ({
type: 'external',
availability: `v${scaler.version}+`,
title: scaler.display_name,
maintainer: scaler.repository.organization_name ?? scaler.repository.user_alias,
href: "https://artifacthub.io/packages/keda-scaler/" + scaler.repository.name + "/" + scaler.normalized_name,
version: currentVersion,
description: scaler.description,
category: null,
}));

const scalers = [...builtInScalers, ...externalScalers];

index = lunr(function () {
const documents = scalers;

Expand All @@ -254,6 +196,12 @@
this.field("availability", {
boost: 5,
});
this.field("category", {
boost: 10,
});
this.field("type", {
boost: 20,
});

documents.forEach(function (doc) {
if (doc.version === currentVersion) {
Expand All @@ -264,6 +212,8 @@
maintainer: doc.maintainer,
description: doc.description,
availability: doc.availability,
category: doc.category,
type: doc.type,
};
}
}, this);
Expand Down Expand Up @@ -308,19 +258,19 @@
results.forEach(function (result) {
const doc = parse[result.ref];
const element = template.content.cloneNode(true);
element.querySelector(".scaler-title").textContent = doc.title;
element
.querySelector(".scaler-title")
.setAttribute("href", doc.href);
doc.description &&
(element.querySelector(".description").textContent =
doc.description);
doc.maintainer &&
(element.querySelector(".maintainer").textContent =
doc.maintainer);
doc.availability &&
(element.querySelector(".availability").textContent =
doc.availability);
element.querySelector(".scaler-title").textContent = doc.title;
element
.querySelector(".scaler-title")
.setAttribute("href", doc.href);
doc.description &&
(element.querySelector(".description").textContent =
doc.description);
doc.maintainer &&
(element.querySelector(".maintainer").textContent =
doc.maintainer);
doc.availability &&
(element.querySelector(".availability").textContent =
doc.availability);
target.appendChild(element);
}, this);
}
Expand Down
Loading