diff --git a/public/components/views/search/search.html b/public/components/views/search/search.html
new file mode 100644
index 00000000..5859489f
--- /dev/null
+++ b/public/components/views/search/search.html
@@ -0,0 +1,23 @@
+
+
+
+
+
[[=z.token('search.recentPackages')]]
+
+
+
[[=z.token('search.packagesCache')]]
+
+
+
diff --git a/public/components/views/search/search.js b/public/components/views/search/search.js
index 14d39b70..b03867e8 100644
--- a/public/components/views/search/search.js
+++ b/public/components/views/search/search.js
@@ -29,171 +29,210 @@ export class SearchView {
this.secureDataSet = secureDataSet;
this.nsn = nsn;
+ this.mount();
this.initialize();
}
+ mount() {
+ const template = document.getElementById("search-view-template");
+ /** @type {HTMLTemplateElement} */
+ const clone = document.importNode(template.content, true);
+
+ const view = document.getElementById("search--view");
+ view.innerHTML = "";
+ view.appendChild(clone);
+ }
+
initialize() {
- this.searchContainer = document.querySelector("#search--view .container");
this.searchForm = document.querySelector("#search--view form");
- const formGroup = this.searchForm.querySelector(".form-group");
- const input = this.searchForm.querySelector("input");
- const lang = currentLang();
+ this.searchForm.addEventListener("submit", (event) => {
+ event.preventDefault();
+ });
+ const input = this.searchForm.querySelector("input");
input.addEventListener("input", debounce(async() => {
- document.querySelector(".result-container")?.remove();
- this.searchForm.querySelector(".hint")?.remove();
+ await this.#handleSearchInput(input.value);
+ }, 500));
- const packageName = input.value;
- if (packageName.length === 0) {
- return;
- }
- else if (packageName.length < kMinPackageNameLength || packageName.length > kMaxPackageNameLength) {
- const hintElement = document.createElement("div");
- hintElement.classList.add("hint");
- hintElement.textContent = window.i18n[lang].search.packageLengthErr;
- this.searchForm.appendChild(hintElement);
+ this.#initializePackages(
+ ".cache-packages",
+ window.scannedPackageCache
+ );
+ this.#initializePackages(
+ ".recent-packages",
+ window.recentPackageCache
+ );
+ }
- return;
- }
+ #initializePackages(
+ selector,
+ specs
+ ) {
+ const packagesElement = document.querySelector(`#search--view .container ${selector}`);
+ if (!packagesElement) {
+ return;
+ }
- const loaderElement = createDOMElement("div", {
- classList: ["spinner-small", "search-spinner"]
- });
- formGroup.appendChild(loaderElement);
+ packagesElement.classList.toggle(
+ "hidden",
+ specs.length === 0
+ );
+ const fragment = document.createDocumentFragment();
+ for (const spec of specs) {
+ fragment.appendChild(this.#cachePackageElement(spec));
+ }
+ packagesElement.appendChild(fragment);
+ }
- const { result, count } = await getJSON(`/search/${encodeURIComponent(packageName)}`);
+ async #handleSearchInput(
+ packageName
+ ) {
+ const lang = currentLang();
+ const formGroup = this.searchForm.querySelector(".form-group");
- this.searchForm.querySelector(".spinner-small").remove();
+ document.querySelector(".result-container")?.remove();
+ this.searchForm.querySelector(".hint")?.remove();
- const divResultContainer = document.createElement("div");
- divResultContainer.classList.add("result-container");
+ if (packageName.length === 0) {
+ return;
+ }
+ else if (
+ packageName.length < kMinPackageNameLength ||
+ packageName.length > kMaxPackageNameLength
+ ) {
+ const hintElement = document.createElement("div");
+ hintElement.classList.add("hint");
+ hintElement.textContent = window.i18n[lang].search.packageLengthErr;
+ this.searchForm.appendChild(hintElement);
- if (count === 0) {
- const divResultElement = document.createElement("div");
- divResultElement.classList.add("result-not-found");
- divResultElement.textContent = window.i18n[lang].search.noPackageFound;
- divResultContainer.appendChild(divResultElement);
- this.searchForm.appendChild(divResultContainer);
+ return;
+ }
- return;
- }
+ const loaderElement = createDOMElement("div", {
+ classList: ["spinner-small", "search-spinner"]
+ });
+ formGroup.appendChild(loaderElement);
- for (const { name, version, description } of result) {
- const divResultElement = document.createElement("div");
- divResultElement.classList.add("result");
- if (packageName === name) {
- divResultElement.classList.add("exact");
- }
+ const { result, count } = await getJSON(`/search/${encodeURIComponent(packageName)}`);
- const pkgElement = document.createElement("div");
- pkgElement.classList.add("package-result");
- const pkgSpanElement = document.createElement("span");
- pkgSpanElement.textContent = name;
- pkgSpanElement.addEventListener("click", async() => {
- const packageVersion = divResultElement.querySelector("select option:checked");
- await this.fetchPackage(name, packageVersion.value);
- }, { once: true });
- pkgElement.appendChild(pkgSpanElement);
- const pkgDescriptionElement = document.createElement("p");
- pkgDescriptionElement.textContent = description;
- pkgDescriptionElement.classList.add("description");
- pkgElement.appendChild(pkgDescriptionElement);
- divResultElement.appendChild(pkgElement);
-
- const selectElement = document.createElement("select");
- const optionElement = document.createElement("option");
- optionElement.value = version;
- optionElement.textContent = version;
- selectElement.appendChild(optionElement);
- selectElement.addEventListener("click", async() => {
- const spinnerOption = "";
- selectElement.insertAdjacentHTML("beforeend", spinnerOption);
-
- function spinnerOptionSpin() {
- const spinnerOptionElement = selectElement.querySelector(".spinner-option");
- spinnerOptionElement.textContent += ".";
- if (spinnerOptionElement.textContent.length > 3) {
- spinnerOptionElement.textContent = ".";
- }
- }
+ this.searchForm.querySelector(".spinner-small").remove();
- const spinIntervalId = setInterval(spinnerOptionSpin, 180);
+ this.#displaySearchResults({ results: result, count, packageName, lang });
+ }
- try {
- const versions = await this.fetchPackageVersions(name);
+ #displaySearchResults({ results, count, packageName, lang }) {
+ const divResultContainer = document.createElement("div");
+ divResultContainer.classList.add("result-container");
- clearInterval(spinIntervalId);
+ if (count === 0) {
+ const divResultElement = document.createElement("div");
+ divResultElement.classList.add("result-not-found");
+ divResultElement.textContent = window.i18n[lang].search.noPackageFound;
+ divResultContainer.appendChild(divResultElement);
+ this.searchForm.appendChild(divResultContainer);
- selectElement.querySelector(".spinner-option").remove();
+ return;
+ }
- for (const pkgVersion of versions) {
- if (pkgVersion === version) {
- continue;
- }
- const optionElement = document.createElement("option");
- optionElement.value = pkgVersion;
- optionElement.textContent = pkgVersion;
- selectElement.appendChild(optionElement);
- }
- }
- catch {
- clearInterval(spinIntervalId);
- selectElement.querySelector(".spinner-option").remove();
- }
- }, { once: true });
- divResultElement.appendChild(selectElement);
- divResultContainer.appendChild(divResultElement);
- }
- this.searchForm.parentNode.insertBefore(divResultContainer, this.searchForm.nextSibling);
- }, 500));
+ for (const { name, version, description } of results) {
+ const divResultElement = this.#createSearchResultElement({
+ name,
+ version,
+ description,
+ packageName
+ });
+ divResultContainer.appendChild(divResultElement);
+ }
- this.searchForm.addEventListener("submit", (event) => {
- event.preventDefault();
- });
+ this.searchForm.parentNode.insertBefore(divResultContainer, this.searchForm.nextSibling);
+ }
- const cachePackagesElement = this.searchContainer.querySelector(".cache-packages");
- if (cachePackagesElement === null) {
- return;
+ #createSearchResultElement({ name, version, description, packageName }) {
+ const divResultElement = document.createElement("div");
+ divResultElement.classList.add("result");
+ if (packageName === name) {
+ divResultElement.classList.add("exact");
}
- if (window.scannedPackageCache.length > 0) {
- cachePackagesElement.classList.remove("hidden");
- const h1Element = document.createElement("h1");
- h1Element.textContent = window.i18n[lang].search.packagesCache;
- cachePackagesElement.appendChild(h1Element);
-
- for (const pkg of window.scannedPackageCache) {
- cachePackagesElement.appendChild(this.#cachePackageElement(pkg));
+
+ const pkgElement = document.createElement("div");
+ pkgElement.classList.add("package-result");
+ const pkgSpanElement = document.createElement("span");
+ pkgSpanElement.textContent = name;
+ pkgSpanElement.addEventListener("click", () => {
+ const packageVersion = divResultElement.querySelector("select option:checked");
+ this.fetchPackage(name, packageVersion.value);
+ }, { once: true });
+ pkgElement.appendChild(pkgSpanElement);
+
+ const pkgDescriptionElement = document.createElement("p");
+ pkgDescriptionElement.textContent = description;
+ pkgDescriptionElement.classList.add("description");
+ pkgElement.appendChild(pkgDescriptionElement);
+ divResultElement.appendChild(pkgElement);
+
+ const selectElement = this.#createVersionSelect(name, version);
+ divResultElement.appendChild(selectElement);
+
+ return divResultElement;
+ }
+
+ #createVersionSelect(name, version) {
+ const selectElement = document.createElement("select");
+ const optionElement = document.createElement("option");
+ optionElement.value = version;
+ optionElement.textContent = version;
+ selectElement.appendChild(optionElement);
+
+ selectElement.addEventListener("click", async() => {
+ const spinnerOption = "";
+ selectElement.insertAdjacentHTML("beforeend", spinnerOption);
+
+ function spinnerOptionSpin() {
+ const spinnerOptionElement = selectElement.querySelector(".spinner-option");
+ spinnerOptionElement.textContent += ".";
+ if (spinnerOptionElement.textContent.length > 3) {
+ spinnerOptionElement.textContent = ".";
+ }
}
- }
- else {
- cachePackagesElement.classList.add("hidden");
- }
- const recentPackagesElement = this.searchContainer.querySelector(".recent-packages");
- if (window.recentPackageCache.length > 0) {
- recentPackagesElement.classList.remove("hidden");
- const h1Element = document.createElement("h1");
- h1Element.textContent = window.i18n[lang].search.recentPackages;
- recentPackagesElement.appendChild(h1Element);
+ const spinIntervalId = setInterval(spinnerOptionSpin, 180);
- for (const pkg of window.recentPackageCache) {
- recentPackagesElement.appendChild(this.#cachePackageElement(pkg));
+ try {
+ const versions = await this.fetchPackageVersions(name);
+
+ clearInterval(spinIntervalId);
+ selectElement.querySelector(".spinner-option").remove();
+
+ for (const pkgVersion of versions) {
+ if (pkgVersion === version) {
+ continue;
+ }
+ const optionElement = document.createElement("option");
+ optionElement.value = pkgVersion;
+ optionElement.textContent = pkgVersion;
+ selectElement.appendChild(optionElement);
+ }
}
- }
- else {
- recentPackagesElement.classList.add("hidden");
- }
+ catch {
+ clearInterval(spinIntervalId);
+ selectElement.querySelector(".spinner-option").remove();
+ }
+ }, { once: true });
+
+ return selectElement;
}
#cachePackageElement(pkg) {
const { name, version, local } = parseNpmSpec(pkg);
const pkgElement = document.createElement("div");
pkgElement.classList.add("package-cache-result");
+
const pkgSpanElement = document.createElement("span");
pkgSpanElement.innerHTML = `${name}@${version}${local ? " local" : ""}`;
pkgSpanElement.addEventListener("click", () => {
window.socket.send(JSON.stringify({ action: "SEARCH", pkg }));
}, { once: true });
+
const removeButton = createDOMElement("button", {
classList: ["remove"],
text: "x"
@@ -202,12 +241,13 @@ export class SearchView {
event.stopPropagation();
window.socket.send(JSON.stringify({ action: "REMOVE", pkg }));
}, { once: true });
+
pkgElement.append(pkgSpanElement, removeButton);
return pkgElement;
}
- async fetchPackage(packageName, version) {
+ fetchPackage(packageName, version) {
const pkg = `${packageName}@${version}`;
window.socket.send(JSON.stringify({ action: "SEARCH", pkg }));
@@ -219,39 +259,9 @@ export class SearchView {
return versions.reverse();
}
- reset() {
- const lang = currentLang();
-
- const searchViewContainer = document.querySelector("#search--view .container");
- searchViewContainer.innerHTML = "";
- const form = document.createElement("form");
- const formGroup = document.createElement("div");
- formGroup.classList.add("form-group");
- const iconSearch = document.createElement("i");
- iconSearch.classList.add("icon-search");
- const input = document.createElement("input");
- input.type = "text";
- input.placeholder = window.i18n[lang].search.registryPlaceholder;
- input.name = "package";
- input.id = "package";
- formGroup.appendChild(iconSearch);
- formGroup.appendChild(input);
- form.appendChild(formGroup);
- searchViewContainer.appendChild(form);
-
- const cachePackagesElement = document.createElement("div");
- cachePackagesElement.classList.add("cache-packages", "hidden");
- searchViewContainer.appendChild(cachePackagesElement);
- const recentPackagesElement = document.createElement("div");
- recentPackagesElement.classList.add("recent-packages", "hidden");
- searchViewContainer.appendChild(recentPackagesElement);
-
- this.initialize();
- }
-
onScan(pkg) {
const searchViewForm = document.querySelector("#search--view form");
- searchViewForm.remove();
+ searchViewForm?.remove();
const containerResult = document.querySelector("#search--view .result-container");
containerResult?.remove();
diff --git a/public/main.js b/public/main.js
index 48537b4d..97fea7f6 100644
--- a/public/main.js
+++ b/public/main.js
@@ -72,7 +72,8 @@ document.addEventListener("DOMContentLoaded", async() => {
nsn, secureDataSet
}
});
- searchview.reset();
+ searchview.mount();
+ searchview.initialize();
const nsnActivePackage = secureDataSet.linker.get(0);
const nsnRootPackage = nsnActivePackage ? `${nsnActivePackage.name}@${nsnActivePackage.version}` : null;
if (data.status === "RELOAD" && nsnRootPackage !== null && nsnRootPackage !== window.activePackage) {
diff --git a/views/index.html b/views/index.html
index 9a24cd0c..6b7b41d8 100644
--- a/views/index.html
+++ b/views/index.html
@@ -89,22 +89,7 @@
-
-
-
-
-
[[=z.token('search.recentPackages')]]
-
-
-
[[=z.token('search.packagesCache')]]
-
-
-
+
[[=z.token('settings.general.title')]]