AppManager: Show spinner during search
Search form doesn't throw an error when not on main page
Change-Id: I108c681101c3b03901205695ee3775fb18eaa900
diff --git a/core/installer/welcome/appmanager-tmpl/base.html b/core/installer/welcome/appmanager-tmpl/base.html
index 7944783..2d7b94e 100644
--- a/core/installer/welcome/appmanager-tmpl/base.html
+++ b/core/installer/welcome/appmanager-tmpl/base.html
@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<link rel="stylesheet" href="/stat/pico.2.0.6.min.css">
- <link rel="stylesheet" type="text/css" href="/stat/appmanager.css?v=0.0.14">
+ <link rel="stylesheet" type="text/css" href="/stat/appmanager.css?v=0.0.15">
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
@@ -26,6 +26,6 @@
{{ block "content" . }}{{ end }}
</div>
</main>
- <script src="/stat/app-manager.js?v=0.0.10"></script>
+ <script src="/stat/app-manager.js?v=0.0.11"></script>
</body>
</html>
diff --git a/core/installer/welcome/appmanager-tmpl/index.html b/core/installer/welcome/appmanager-tmpl/index.html
index fcb613f..b3ca2ab 100644
--- a/core/installer/welcome/appmanager-tmpl/index.html
+++ b/core/installer/welcome/appmanager-tmpl/index.html
@@ -1,6 +1,6 @@
{{ define "header" }}
<form id="search-form" class="search-bar" method="GET" action="/{{ .SearchTarget }}">
- <input id="search-input" name="query" type="search" placeholder="Search" value="{{ .SearchValue }}"/>
+ <input id="search-input" class="search-icon" name="query" type="search" placeholder="Search" value="{{ .SearchValue }}"/>
</form>
<input type="hidden" id="page-type" value="{{ .SearchTarget }}" />
{{ end }}
diff --git a/core/installer/welcome/stat/app-manager.js b/core/installer/welcome/stat/app-manager.js
index 55357f3..9cafd83 100644
--- a/core/installer/welcome/stat/app-manager.js
+++ b/core/installer/welcome/stat/app-manager.js
@@ -9,46 +9,64 @@
document.addEventListener("DOMContentLoaded", function () {
let searchRequestCount = 0;
const page = document.documentElement;
- const headerHeight = parseFloat(getComputedStyle(page).getPropertyValue('--pico-header-height').replace("px", ""));
+ const headerHeight = parseFloat(getComputedStyle(page).getPropertyValue("--pico-header-height").replace("px", ""));
const nav = document.getElementById("menu");
const windowHeight = window.innerHeight - headerHeight;
nav.style.setProperty("--max-height", `${windowHeight}px`);
const menu = document.getElementById("menu-nav");
const menuHeight = parseFloat(getComputedStyle(document.getElementById('menu-nav')).height.replace("px", "")) + 15;
menu.style.setProperty("height", `${menuHeight}px`);
- const searchForm = document.getElementById('search-form');
- const searchInput = document.getElementById('search-input');
+ const searchForm = document.getElementById("search-form");
+ const searchInput = document.getElementById("search-input");
+ function startSearchAnimation() {
+ searchInput.classList.remove("search-icon");
+ searchInput.classList.add("search-progress-icon");
+ }
+ function stopSearchAnimation() {
+ searchInput.classList.remove("search-progress-icon");
+ searchInput.classList.add("search-icon");
+ }
function fetchAndUpdateAppList() {
searchRequestCount++;
const currentRequest = searchRequestCount;
const formData = new FormData(searchForm);
- const query = formData.get('query');
- const pageType = document.getElementById('page-type').value;
+ const query = formData.get("query");
+ const pageType = document.getElementById("page-type").value;
const url = `/${pageType}?query=${encodeURIComponent(query)}`;
+ startSearchAnimation();
fetch(url, {
- method: 'GET'
+ method: "GET"
})
.then(response => response.text())
.then(html => {
if (currentRequest !== searchRequestCount) {
return;
}
- const tempDiv = document.createElement('div');
+ const tempDiv = document.createElement("div");
tempDiv.innerHTML = html;
- const newAppListHTML = tempDiv.querySelector('#app-list').innerHTML;
+ const newAppListHTML = tempDiv.querySelector("#app-list").innerHTML;
const appListContainer = document.getElementById("app-list");
appListContainer.innerHTML = newAppListHTML;
})
- .catch(error => console.error('Error fetching app list:', error));
+ .catch(error => {
+ console.error("Error fetching app list:", error);
+ })
+ .finally(() => {
+ stopSearchAnimation();
+ });
}
const delayedFetchAndUpdateAppList = delaySearch(fetchAndUpdateAppList, 300);
- searchForm.addEventListener('submit', (event) => {
- event.preventDefault();
- fetchAndUpdateAppList();
- });
- searchInput.addEventListener('input', () => {
- delayedFetchAndUpdateAppList();
- });
+ if (searchForm) {
+ searchForm.addEventListener("submit", (event) => {
+ event.preventDefault();
+ fetchAndUpdateAppList();
+ });
+ }
+ if (searchInput) {
+ searchInput.addEventListener("input", () => {
+ delayedFetchAndUpdateAppList();
+ });
+ }
});
let prevWindowHeight = window.innerHeight;
diff --git a/core/installer/welcome/stat/appmanager.css b/core/installer/welcome/stat/appmanager.css
index cde49ab..f8ee945 100644
--- a/core/installer/welcome/stat/appmanager.css
+++ b/core/installer/welcome/stat/appmanager.css
@@ -327,3 +327,11 @@
.primary {
color: #7f9f7f;
}
+
+.search-icon {
+ background-image: var(--pico-icon-search) !important;
+}
+
+.search-progress-icon {
+ background-image: var(--pico-icon-loading) !important;
+}