History: pagination survives search/sort, pager pinned to page bottom #140

Merged
fen merged 1 commits from fix-133-paging into dev 2026-09-10 04:14:20 +00:00
2 changed files with 28 additions and 5 deletions
+2 -1
View File
@@ -202,7 +202,8 @@ body {
.footnote { display: flex; gap: 20px; padding: 10px 18px; font-size: 20.7px; color: var(--muted-fg); border-top: 1px solid var(--border); flex-wrap: wrap; } .footnote { display: flex; gap: 20px; padding: 10px 18px; font-size: 20.7px; color: var(--muted-fg); border-top: 1px solid var(--border); flex-wrap: wrap; }
/* history */ /* history */
.page { max-width: 1200px; margin: 0 auto; padding: 20px; display: flex; flex-direction: column; gap: 16px; } .page { max-width: 1200px; margin: 0 auto; padding: 20px 20px 28px; display: flex; flex-direction: column; gap: 16px; min-height: calc(100vh - 76px); }
.pager { position: sticky; bottom: 0; margin-top: auto; z-index: 5; }
.head-row { display: flex; align-items: baseline; gap: 14px; } .head-row { display: flex; align-items: baseline; gap: 14px; }
.head-row h1 { font-size: 34.5px; font-weight: 600; } .head-row h1 { font-size: 34.5px; font-weight: 600; }
.search { .search {
+26 -4
View File
@@ -90,10 +90,31 @@ const PaletteTable = (() => {
if (opts.hasPager && pager && showing) { if (opts.hasPager && pager && showing) {
const pages = Math.max(1, Math.ceil(state.total / opts.perPage)); const pages = Math.max(1, Math.ceil(state.total / opts.perPage));
if (filtered || state.sortKey) { if (filtered || state.sortKey) {
showing.textContent = state.sortKey // Pagination operates over the FILTERED set (#133): never hide the bar.
? 'Sorted by ' + state.sortKey + ' (' + (state.sortDir === 1 ? 'ascending' : 'descending') + ') · ' + items.length.toLocaleString() + ' of ' + state.total.toLocaleString() const filtPages = Math.max(1, Math.ceil(items.length / opts.perPage));
: 'Showing ' + items.length.toLocaleString() + ' matches for "' + state.filter + '"'; if (state.page > filtPages) state.page = filtPages;
pager.innerHTML = ''; const fOff = (state.page - 1) * opts.perPage;
const view = items.slice(fOff, fOff + opts.perPage);
rows.innerHTML = view.length ? view.map(opts.rowHtml).join('') : '';
if (!view.length) {
empty.style.display = 'block';
empty.textContent = filtered ? opts.emptyFiltered : opts.emptyAll;
}
showing.textContent = `Showing ${view.length === 0 ? 0 : fOff+1}${fOff+view.length} of ${items.length.toLocaleString()} ${filtered ? 'matches' : 'sorted'} · page ${state.page} of ${filtPages}`;
const btns = [];
const add = (label, target, o={}) => btns.push(`<button ${o.on?'class="on"':''} ${o.dis?'disabled':''} data-p="${target}">${label}</button>`);
add('', state.page-1, {dis: state.page===1});
const win = new Set([1, 2, state.page-1, state.page, state.page+1, filtPages]);
let last = 0;
for (let i = 1; i <= filtPages; i++) {
if (win.has(i)) {
if (last && i - last > 1) btns.push('<span class="dim">…</span>');
add(String(i), i, {on: i===state.page});
last = i;
}
}
add('', state.page+1, {dis: state.page===filtPages});
pager.innerHTML = btns.join('');
} else { } else {
showing.textContent = state.total === 0 ? 'Nothing here yet' : showing.textContent = state.total === 0 ? 'Nothing here yet' :
`Showing ${off+1}${Math.min(off+opts.perPage, state.total)} of ${state.total.toLocaleString()} · page ${state.page} of ${pages}`; `Showing ${off+1}${Math.min(off+opts.perPage, state.total)} of ${state.total.toLocaleString()} · page ${state.page} of ${pages}`;
@@ -126,6 +147,7 @@ const PaletteTable = (() => {
if (!th) return; if (!th) return;
const k = th.dataset.sort; const k = th.dataset.sort;
if (state.sortKey === k) { state.sortDir = -state.sortDir; } else { state.sortKey = k; state.sortDir = 1; } if (state.sortKey === k) { state.sortDir = -state.sortDir; } else { state.sortKey = k; state.sortDir = 1; }
state.page = 1;
renderSortIndicators(); renderSortIndicators();
load(); load();
}); });