Skip to content

Commit d7a04d3

Browse files
Update papers.html
1 parent 38b1403 commit d7a04d3

File tree

1 file changed

+54
-6
lines changed

1 file changed

+54
-6
lines changed

papers.html

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ <h2 style="font-size: 1.8em; margin-bottom: 10px;">📚 Search Publications</h2>
5050
<div id="cardsGrid" class="cards" style="display:none;" aria-live="polite"></div>
5151
<p id="emptyState" class="empty" style="display:none;">No papers match your search.</p>
5252

53+
<!-- Sentinel for progressive loading -->
54+
<div id="loadMoreTrigger" style="height: 1px;"></div>
55+
5356
<!-- Hidden Data Table -->
5457
<table id="papers-table" class="display stripe hover" style="width:100%; display:none;">
5558
<thead>
@@ -317,32 +320,48 @@ <h2 style="font-size: 1.8em; margin-bottom: 10px;">📚 Search Publications</h2>
317320
const gridEl = document.getElementById('cardsGrid');
318321
const emptyEl = document.getElementById('emptyState');
319322
const sortEl = document.getElementById('sortSelect');
323+
const loadMoreTrigger = document.getElementById('loadMoreTrigger');
320324

321325
let datatable;
326+
const PAGE_CHUNK = 80; // how many cards to render at a time
327+
let visibleLimit = PAGE_CHUNK;
328+
let loadMoreObserver = null;
322329

323330
function updateVisibleCount(){
324331
if (!datatable || !countEl) return;
325-
countEl.textContent = datatable.rows({ filter:'applied' }).count() + ' papers';
332+
const total = datatable.rows({ filter:'applied' }).count();
333+
const shown = Math.min(visibleLimit, total);
334+
countEl.textContent = `Showing ${shown} of ${total} papers`;
326335
}
327336

328337
function renderCards(){
329338
if (!datatable) return;
339+
330340
const rows = datatable.rows({ filter:'applied' }).data().toArray();
341+
const total = rows.length;
342+
331343
gridEl.innerHTML = '';
332344

333345
if (!rows.length){
334346
gridEl.style.display='none';
335347
emptyEl.style.display='';
348+
if (loadMoreTrigger) loadMoreTrigger.style.display = 'none';
336349
updateVisibleCount();
337350
return;
338351
}
352+
339353
emptyEl.style.display='none';
340354
gridEl.style.display='grid';
341355

356+
// Ensure visibleLimit never exceeds available rows
357+
visibleLimit = Math.min(visibleLimit, total);
358+
342359
const now = Math.floor(Date.now()/1000);
343360
const seven = 7*24*3600;
344361

345-
rows.forEach(r => {
362+
const slice = rows.slice(0, visibleLimit);
363+
364+
slice.forEach(r => {
346365
const titleHTML = r[0];
347366
const shortAuth = r[1] || '';
348367
const year = +r[2] || '';
@@ -380,6 +399,11 @@ <h3 class="title">${titleHTML}</h3>
380399
gridEl.appendChild(card);
381400
});
382401

402+
// Show/hide the sentinel depending on whether there's more to load
403+
if (loadMoreTrigger){
404+
loadMoreTrigger.style.display = (visibleLimit < total) ? 'block' : 'none';
405+
}
406+
383407
updateVisibleCount();
384408
}
385409

@@ -475,7 +499,9 @@ <h3 class="title">${titleHTML}</h3>
475499
responsive: { details: false },
476500
autoWidth: false,
477501
paging: false,
478-
searching: true,
502+
searching: true, // keep filtering enabled
503+
info: false, // hide "Showing 1–10 of N"
504+
dom: 't', // only table body; removes built-in search bar / controls
479505

480506
// Base sort; will be overridden by our custom sort logic in initComplete
481507
order: [[2,'desc'], [3,'desc']],
@@ -498,6 +524,22 @@ <h3 class="title">${titleHTML}</h3>
498524

499525
datatable.on('draw', renderCards);
500526

527+
// Infinite-scroll style loading
528+
if ('IntersectionObserver' in window && loadMoreTrigger){
529+
loadMoreObserver = new IntersectionObserver((entries) => {
530+
entries.forEach(entry => {
531+
if (!entry.isIntersecting) return;
532+
const total = datatable.rows({ filter:'applied' }).count();
533+
if (visibleLimit < total) {
534+
visibleLimit += PAGE_CHUNK;
535+
renderCards();
536+
}
537+
});
538+
}, { rootMargin: '200px 0px' });
539+
540+
loadMoreObserver.observe(loadMoreTrigger);
541+
}
542+
501543
const debounce = (fn, ms=120) => {
502544
let t;
503545
return (...a) => {
@@ -507,6 +549,7 @@ <h3 class="title">${titleHTML}</h3>
507549
};
508550
const apply = debounce(() => {
509551
const q = inputEl ? (inputEl.value || '') : '';
552+
visibleLimit = PAGE_CHUNK; // reset visible cards when query changes
510553
datatable.search(q).draw(false);
511554
setHash(q);
512555
}, 120);
@@ -516,6 +559,7 @@ <h3 class="title">${titleHTML}</h3>
516559

517560
resetEl.addEventListener('click', () => {
518561
inputEl.value = '';
562+
visibleLimit = PAGE_CHUNK;
519563
datatable.search('').draw(false);
520564
setHash('');
521565
inputEl.focus();
@@ -524,6 +568,7 @@ <h3 class="title">${titleHTML}</h3>
524568
window.addEventListener('hashchange', () => {
525569
const q = readHashQuery();
526570
inputEl.value = q;
571+
visibleLimit = PAGE_CHUNK;
527572
datatable.search(q).draw(false);
528573
});
529574

@@ -533,6 +578,7 @@ <h3 class="title">${titleHTML}</h3>
533578
inputEl.focus();
534579
} else if (e.key === 'Escape') {
535580
inputEl.value='';
581+
visibleLimit = PAGE_CHUNK;
536582
datatable.search('').draw();
537583
setHash('');
538584
}
@@ -542,7 +588,7 @@ <h3 class="title">${titleHTML}</h3>
542588
const applySort = (mode) => {
543589
switch (mode) {
544590
case 'year_desc':
545-
// Year ↓ (newest first) – publication year
591+
// Year ↓ (newest first)
546592
datatable.order([[2, 'desc'], [3, 'desc']]).draw(false);
547593
break;
548594
case 'year_asc':
@@ -565,11 +611,13 @@ <h3 class="title">${titleHTML}</h3>
565611
}
566612
};
567613

568-
// Start with "Newest & NEW first" as the default sort
569614
sortEl.value = 'default';
570615
applySort('default');
571616

572-
sortEl.addEventListener('change', () => applySort(sortEl.value));
617+
sortEl.addEventListener('change', () => {
618+
visibleLimit = PAGE_CHUNK;
619+
applySort(sortEl.value);
620+
});
573621
}
574622

575623
setTimeout(() => datatable.columns.adjust().draw(false), 60);

0 commit comments

Comments
 (0)