From 83cec0f75ac8e79b5f69c5d1a3b883af6ceb1b9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Bachmann?= Date: Sat, 17 Jan 2026 20:47:39 +0000 Subject: [PATCH] 0.5.10 --- .../bahmcloud_store/panel/panel.js | 103 +++++++++++++++--- 1 file changed, 87 insertions(+), 16 deletions(-) diff --git a/custom_components/bahmcloud_store/panel/panel.js b/custom_components/bahmcloud_store/panel/panel.js index 5533828..092ef3f 100644 --- a/custom_components/bahmcloud_store/panel/panel.js +++ b/custom_components/bahmcloud_store/panel/panel.js @@ -15,7 +15,8 @@ class BahmcloudStorePanel extends HTMLElement { this._search = ""; this._category = "all"; - this._provider = "all"; // all|github|gitea|gitlab|other|custom + this._filter = "all"; // all|installed|not_installed|updates|custom + this._sort = "az"; // az|updates_first|installed_first this._detailRepoId = null; this._detailRepo = null; @@ -562,6 +563,25 @@ class BahmcloudStorePanel extends HTMLElement { const root = this.shadowRoot; if (!root) return; + // Preserve focus & cursor position for inputs that trigger a re-render (e.g. search). + // Without this, mobile browsers may drop focus after each keystroke. + const active = root.activeElement; + const restore = { + id: active && active.id ? String(active.id) : null, + start: null, + end: null, + }; + try { + if (active && (active.tagName === "INPUT" || active.tagName === "TEXTAREA")) { + if (typeof active.selectionStart === "number" && typeof active.selectionEnd === "number") { + restore.start = active.selectionStart; + restore.end = active.selectionEnd; + } + } + } catch (_) { + // ignore + } + const subtitle = root.getElementById("subtitle"); if (subtitle) subtitle.textContent = this._view === "detail" ? "Details" : this._view[0].toUpperCase() + this._view.slice(1); @@ -604,6 +624,21 @@ class BahmcloudStorePanel extends HTMLElement { if (this._view === "detail") { this._wireDetail(); // now always wires buttons } + + // Restore focus and cursor for the search field after re-render. + if (restore.id && this._view === "store") { + const el = root.getElementById(restore.id); + if (el && (el.tagName === "INPUT" || el.tagName === "TEXTAREA")) { + try { + el.focus({ preventScroll: true }); + if (restore.start !== null && restore.end !== null && typeof el.setSelectionRange === "function") { + el.setSelectionRange(restore.start, restore.end); + } + } catch (_) { + // ignore + } + } + } } _safeText(v) { @@ -643,16 +678,40 @@ class BahmcloudStorePanel extends HTMLElement { const cat = this._safeText(r?.category) || ""; if (this._category !== "all" && this._category !== cat) return false; - const prov = this._safeText(r?.provider) || "other"; - if (this._provider !== "all") { - if (this._provider === "custom" && r?.source !== "custom") return false; - if (this._provider !== "custom" && prov !== this._provider) return false; - } + const latest = this._safeText(r?.latest_version); + const installed = this._asBoolStrict(r?.installed); + const installedVersion = this._safeText(r?.installed_version); + const updateAvailable = installed && !!latest && (!installedVersion || latest !== installedVersion); + + if (this._filter === "installed" && !installed) return false; + if (this._filter === "not_installed" && installed) return false; + if (this._filter === "updates" && !updateAvailable) return false; + if (this._filter === "custom" && r?.source !== "custom") return false; + return true; }) .sort((a, b) => { const an = (this._safeText(a?.name) || "").toLowerCase(); const bn = (this._safeText(b?.name) || "").toLowerCase(); + + const alatest = this._safeText(a?.latest_version); + const ainstalled = this._asBoolStrict(a?.installed); + const ainstalledVersion = this._safeText(a?.installed_version); + const aupdate = ainstalled && !!alatest && (!ainstalledVersion || alatest !== ainstalledVersion); + + const blatest = this._safeText(b?.latest_version); + const binstalled = this._asBoolStrict(b?.installed); + const binstalledVersion = this._safeText(b?.installed_version); + const bupdate = binstalled && !!blatest && (!binstalledVersion || blatest !== binstalledVersion); + + if (this._sort === "updates_first") { + if (aupdate !== bupdate) return aupdate ? -1 : 1; + return an.localeCompare(bn); + } + if (this._sort === "installed_first") { + if (ainstalled !== binstalled) return ainstalled ? -1 : 1; + return an.localeCompare(bn); + } return an.localeCompare(bn); }); @@ -660,8 +719,6 @@ class BahmcloudStorePanel extends HTMLElement { new Set(repos.map((r) => this._safeText(r?.category)).filter((c) => !!c)) ).sort(); - const providers = ["github", "gitlab", "gitea", "other"]; - const cards = filtered .map((r) => { const id = this._safeId(r?.id); @@ -705,10 +762,17 @@ class BahmcloudStorePanel extends HTMLElement { ${categories.map((c) => ``).join("")} - + + + + + + + @@ -725,7 +789,8 @@ class BahmcloudStorePanel extends HTMLElement { const q = root.getElementById("q"); const cat = root.getElementById("cat"); - const prov = root.getElementById("prov"); + const filter = root.getElementById("filter"); + const sort = root.getElementById("sort"); if (q) { q.addEventListener("input", (e) => { @@ -739,9 +804,15 @@ class BahmcloudStorePanel extends HTMLElement { this._update(); }); } - if (prov) { - prov.addEventListener("change", (e) => { - this._provider = e?.target?.value || "all"; + if (filter) { + filter.addEventListener("change", (e) => { + this._filter = e?.target?.value || "all"; + this._update(); + }); + } + if (sort) { + sort.addEventListener("change", (e) => { + this._sort = e?.target?.value || "az"; this._update(); }); }