[feat](trx-frontend-http): paginate bookmarks tab

Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
2026-03-12 23:57:02 +01:00
parent 963d527dfe
commit f8fd4572c7
3 changed files with 87 additions and 1 deletions
@@ -393,6 +393,14 @@
<tbody id="bm-tbody"></tbody> <tbody id="bm-tbody"></tbody>
</table> </table>
<div id="bm-empty" class="bm-empty" style="display:none;">No bookmarks yet. Click <strong>+ Add Bookmark</strong> to save a frequency.</div> <div id="bm-empty" class="bm-empty" style="display:none;">No bookmarks yet. Click <strong>+ Add Bookmark</strong> to save a frequency.</div>
<div id="bm-paginator" class="bm-paginator" style="display:none;">
<div id="bm-page-summary" class="bm-page-summary">Showing 0-0 of 0</div>
<div class="bm-page-controls">
<button id="bm-page-prev" type="button">Previous</button>
<span id="bm-page-indicator" class="bm-page-indicator">Page 1 of 1</span>
<button id="bm-page-next" type="button">Next</button>
</div>
</div>
</div> </div>
</div> </div>
<div id="tab-decoders" class="tab-panel" style="display:none;"> <div id="tab-decoders" class="tab-panel" style="display:none;">
@@ -1,7 +1,10 @@
// --- Bookmarks Tab --- // --- Bookmarks Tab ---
var bmList = []; var bmList = [];
let bmFilteredList = [];
let bmEditId = null; let bmEditId = null;
let bmCurrentPage = 1;
const BM_PAGE_SIZE = 25;
function bmFmtFreq(hz) { function bmFmtFreq(hz) {
if (!Number.isFinite(hz) || hz <= 0) return "--"; if (!Number.isFinite(hz) || hz <= 0) return "--";
@@ -66,6 +69,8 @@ function bmApplyFilters() {
(bm.comment || "").toLowerCase().includes(text) (bm.comment || "").toLowerCase().includes(text)
) )
: filtered; : filtered;
bmFilteredList = filtered;
bmCurrentPage = 1;
bmRender(filtered); bmRender(filtered);
} }
@@ -106,18 +111,30 @@ async function bmRefreshCategoryFilter(keepValue) {
function bmRender(list) { function bmRender(list) {
const tbody = document.getElementById("bm-tbody"); const tbody = document.getElementById("bm-tbody");
const emptyEl = document.getElementById("bm-empty"); const emptyEl = document.getElementById("bm-empty");
const paginatorEl = document.getElementById("bm-paginator");
const pageSummaryEl = document.getElementById("bm-page-summary");
const pageIndicatorEl = document.getElementById("bm-page-indicator");
const prevBtn = document.getElementById("bm-page-prev");
const nextBtn = document.getElementById("bm-page-next");
if (!tbody) return; if (!tbody) return;
tbody.innerHTML = ""; tbody.innerHTML = "";
if (list.length === 0) { if (list.length === 0) {
if (emptyEl) emptyEl.style.display = ""; if (emptyEl) emptyEl.style.display = "";
if (paginatorEl) paginatorEl.style.display = "none";
return; return;
} }
if (emptyEl) emptyEl.style.display = "none"; if (emptyEl) emptyEl.style.display = "none";
const canControl = bmCanControl(); const canControl = bmCanControl();
const totalPages = Math.max(1, Math.ceil(list.length / BM_PAGE_SIZE));
const page = Math.min(Math.max(bmCurrentPage, 1), totalPages);
bmCurrentPage = page;
const startIndex = (page - 1) * BM_PAGE_SIZE;
const endIndex = Math.min(startIndex + BM_PAGE_SIZE, list.length);
const pageItems = list.slice(startIndex, endIndex);
list.forEach((bm) => { pageItems.forEach((bm) => {
const tr = document.createElement("tr"); const tr = document.createElement("tr");
tr.dataset.bmId = bm.id; tr.dataset.bmId = bm.id;
const bwCell = bm.bandwidth_hz ? bmFmtFreq(bm.bandwidth_hz) : "--"; const bwCell = bm.bandwidth_hz ? bmFmtFreq(bm.bandwidth_hz) : "--";
@@ -143,6 +160,20 @@ function bmRender(list) {
`</td>`; `</td>`;
tbody.appendChild(tr); tbody.appendChild(tr);
}); });
if (paginatorEl) paginatorEl.style.display = totalPages > 1 ? "flex" : "";
if (pageSummaryEl) pageSummaryEl.textContent = `Showing ${startIndex + 1}-${endIndex} of ${list.length}`;
if (pageIndicatorEl) pageIndicatorEl.textContent = `Page ${page} of ${totalPages}`;
if (prevBtn) prevBtn.disabled = page <= 1;
if (nextBtn) nextBtn.disabled = page >= totalPages;
}
function bmChangePage(delta) {
const totalPages = Math.max(1, Math.ceil(bmFilteredList.length / BM_PAGE_SIZE));
const nextPage = Math.min(Math.max(bmCurrentPage + delta, 1), totalPages);
if (nextPage === bmCurrentPage) return;
bmCurrentPage = nextPage;
bmRender(bmFilteredList);
} }
// Read decoder checkboxes and return an array of selected decoder names. // Read decoder checkboxes and return an array of selected decoder names.
@@ -370,6 +401,14 @@ async function bmApply(bm) {
bmApplyFilters(); bmApplyFilters();
}); });
document.getElementById("bm-page-prev").addEventListener("click", () => {
bmChangePage(-1);
});
document.getElementById("bm-page-next").addEventListener("click", () => {
bmChangePage(1);
});
// Form submit // Form submit
document.getElementById("bm-form").addEventListener("submit", bmSave); document.getElementById("bm-form").addEventListener("submit", bmSave);
@@ -3135,6 +3135,30 @@ button:focus-visible, input:focus-visible, select:focus-visible {
color: var(--text-muted); color: var(--text-muted);
font-size: 0.9rem; font-size: 0.9rem;
} }
.bm-paginator {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.8rem;
padding: 0.9rem 0.2rem 0;
}
.bm-page-summary,
.bm-page-indicator {
color: var(--text-muted);
font-size: 0.88rem;
}
.bm-page-controls {
display: flex;
align-items: center;
gap: 0.55rem;
}
.bm-page-controls button {
min-width: 6.4rem;
}
.bm-page-controls button:disabled {
opacity: 0.45;
cursor: default;
}
/* ── Donald style ─────────────────────────────────────────────────────── */ /* ── Donald style ─────────────────────────────────────────────────────── */
[data-style="golden-rain"] { [data-style="golden-rain"] {
@@ -3574,6 +3598,21 @@ button:focus-visible, input:focus-visible, select:focus-visible {
.sch-label { .sch-label {
min-width: 100%; min-width: 100%;
} }
.bm-paginator {
flex-direction: column;
align-items: stretch;
}
.bm-page-controls {
justify-content: space-between;
}
.bm-page-controls button {
flex: 1 1 0;
min-width: 0;
}
.bm-page-indicator,
.bm-page-summary {
text-align: center;
}
.bgd-add-row, .bgd-add-row,
.bgd-status-row { .bgd-status-row {
flex-direction: column; flex-direction: column;