@@ -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