Files
reflector-dashboard/lib/pager.js
T
bjorneandClaude Fable 5.1 25b481e2ae Public web-only release: SvxPortal front-end + auth backend, install templates and INSTALL.md
Stripped from the internal repo: per-box deploy trees, Go services (nodelist/astmeta/proxy/svxdr),
deploy/sync scripts and all secrets. Config templates for nginx, PHP-FPM, Redis and tmpfiles
live in install/ with CHANGEME placeholders.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-26 23:07:05 +02:00

85 lines
4.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// pager.js — shared pagination (dmr-web, svx-web).
// Canonical copy: callmgr2/www-mumien/lib/pager.js; other repos carry a COPY.
//
// pageSizeFor('monitorBody', 32, 160) rows that fit below the element (min 5)
// pagerHtml({page, pages, total, noun, cls}) footer « ‹ 1..n › » as HTML;
// buttons carry class `cls` + data-pg, the page wires the click.
// paginate.slice / totalPages / render(container, total, pageSize, page, onChange)
// the container-driven pager (dmr-web lists), 1-based pages.
(function () {
'use strict';
function pageSizeFor(bodyId, rowPx, reservePx) {
const body = document.getElementById(bodyId);
const top = body ? body.getBoundingClientRect().top : 220;
const avail = window.innerHeight - top - (reservePx == null ? 160 : reservePx);
return Math.max(5, Math.floor(avail / (rowPx || 32)));
}
function pagerHtml(o) {
const page = o.page, pages = o.pages, total = o.total, cls = o.cls || 'pg', noun = o.noun || 'rows';
if (pages <= 1) return '';
const span = 3;
let from = Math.max(1, page - span), to = Math.min(pages, page + span);
while (to - from < 6 && (from > 1 || to < pages)) { if (from > 1) from--; else if (to < pages) to++; }
const btn = (label, target, dis, cur) =>
`<button class="btn btn-sm ${cls} ${cur ? 'btn-primary' : 'btn-outline-secondary'} mx-1"${dis ? ' disabled' : ''}${target != null ? ` data-pg="${target}"` : ''} style="padding:1px 8px;">${label}</button>`;
let h = `<div class="d-flex align-items-center flex-wrap justify-content-center mt-2" style="gap:2px;">`;
h += `<span class="small text-secondary me-2">${total} ${noun} · page ${page}/${pages}</span>`;
h += btn('&laquo;', 1, page === 1) + btn('&lsaquo;', page - 1, page === 1);
for (let p = from; p <= to; p++) h += btn(String(p), p, false, p === page);
h += btn('&rsaquo;', page + 1, page === pages) + btn('&raquo;', pages, page === pages);
return h + `</div>`;
}
const paginate = {
slice(items, pageSize, page) {
if (!Array.isArray(items)) return [];
if (pageSize <= 0) return items;
const start = Math.max(0, (page - 1) * pageSize);
return items.slice(start, start + pageSize);
},
totalPages(totalItems, pageSize) {
if (pageSize <= 0 || totalItems <= 0) return 1;
return Math.max(1, Math.ceil(totalItems / pageSize));
},
render(container, totalItems, pageSize, page, onChange) {
if (!container) return;
const total = this.totalPages(totalItems, pageSize);
if (total <= 1) { container.innerHTML = ''; return; }
const cur = Math.min(Math.max(1, page), total);
const btn = (label, target, disabled, isCurrent) =>
`<button class="btn btn-sm ${isCurrent ? 'btn-primary' : 'btn-outline-secondary'} mx-1"`
+ (disabled ? ' disabled' : '')
+ (target != null ? ` data-pg="${target}"` : '')
+ `>${label}</button>`;
const span = 2;
let from = Math.max(1, cur - span);
let to = Math.min(total, cur + span);
while (to - from < 4 && (from > 1 || to < total)) {
if (from > 1) from--;
else if (to < total) to++;
}
let nums = '';
for (let p = from; p <= to; p++) nums += btn(String(p), p, false, p === cur);
container.innerHTML =
`<div class="d-flex justify-content-center align-items-center mt-2 small">` +
btn('« First', 1, cur === 1, false) +
btn('‹ Prev', cur - 1, cur === 1, false) +
nums +
btn('Next ›', cur + 1, cur === total, false) +
btn('Last »', total, cur === total, false) +
`<span class="text-secondary ms-2">${cur} / ${total} (${totalItems} rows)</span>` +
`</div>`;
container.onclick = (e) => {
const b = e.target.closest('button[data-pg]');
if (!b) return;
const p = parseInt(b.dataset.pg, 10);
if (!isNaN(p) && p >= 1 && p <= total && typeof onChange === 'function') {
onChange(p);
}
};
},
};
window.pageSizeFor = pageSizeFor;
window.pagerHtml = pagerHtml;
window.paginate = paginate;
})();