@@ -5,13 +5,25 @@ import * as React from "react";
55type UseLoadMoreSentinelOptions = {
66 enabled : boolean ;
77 rootMargin ?: string ;
8+ rootRef ?: React . RefObject < HTMLElement | null > ;
89 targetRef : React . RefObject < Element | null > ;
910 onLoadMore : ( ) => void | Promise < void > ;
1011} ;
1112
13+ function rootMarginToPixels ( rootMargin : string ) : number {
14+ const values = rootMargin . trim ( ) . split ( / \s + / ) . filter ( Boolean ) ;
15+ const value = values . length >= 3 ? values [ 2 ] : values [ 0 ] ?? "" ;
16+ const match = value . match ( / ^ ( - ? \d + (?: \. \d + ) ? ) p x $ / ) ;
17+ if ( ! match ) {
18+ return 120 ;
19+ }
20+ return Number ( match [ 1 ] ) || 0 ;
21+ }
22+
1223export function useLoadMoreSentinel ( {
1324 enabled,
1425 rootMargin = "120px" ,
26+ rootRef,
1527 targetRef,
1628 onLoadMore,
1729} : UseLoadMoreSentinelOptions ) {
@@ -31,7 +43,33 @@ export function useLoadMoreSentinel({
3143 return ;
3244 }
3345
34- const root = target . parentElement ?. closest < HTMLElement > ( "[data-sidebar-scroll-root='true']" ) ?? null ;
46+ const root = rootRef ?. current ?? target . parentElement ?. closest < HTMLElement > ( "[data-sidebar-scroll-root='true']" ) ?? null ;
47+ const marginPx = rootMarginToPixels ( rootMargin ) ;
48+ let animationFrame : number | null = null ;
49+ let disposed = false ;
50+
51+ const isNearLoadMorePoint = ( ) => {
52+ if ( root ) {
53+ return root . scrollHeight - root . scrollTop - root . clientHeight <= marginPx ;
54+ }
55+ return target . getBoundingClientRect ( ) . top <= window . innerHeight + marginPx ;
56+ } ;
57+
58+ const check = ( ) => {
59+ animationFrame = null ;
60+ if ( disposed || ! isNearLoadMorePoint ( ) ) {
61+ return ;
62+ }
63+ void onLoadMoreRef . current ( ) ;
64+ } ;
65+
66+ const scheduleCheck = ( ) => {
67+ if ( animationFrame !== null ) {
68+ return ;
69+ }
70+ animationFrame = window . requestAnimationFrame ( check ) ;
71+ } ;
72+
3573 const observer = new IntersectionObserver (
3674 ( entries ) => {
3775 const entry = entries [ 0 ] ;
@@ -43,6 +81,27 @@ export function useLoadMoreSentinel({
4381 ) ;
4482
4583 observer . observe ( target ) ;
46- return ( ) => observer . disconnect ( ) ;
47- } , [ enabled , rootMargin , targetRef ] ) ;
84+ const scrollTarget : HTMLElement | Window = root ?? window ;
85+ scrollTarget . addEventListener ( "scroll" , scheduleCheck , { passive : true } ) ;
86+ window . addEventListener ( "resize" , scheduleCheck ) ;
87+
88+ const resizeObserver = typeof ResizeObserver === "undefined" ? null : new ResizeObserver ( scheduleCheck ) ;
89+ resizeObserver ?. observe ( target ) ;
90+ if ( root ) {
91+ resizeObserver ?. observe ( root ) ;
92+ }
93+
94+ scheduleCheck ( ) ;
95+
96+ return ( ) => {
97+ disposed = true ;
98+ observer . disconnect ( ) ;
99+ scrollTarget . removeEventListener ( "scroll" , scheduleCheck ) ;
100+ window . removeEventListener ( "resize" , scheduleCheck ) ;
101+ resizeObserver ?. disconnect ( ) ;
102+ if ( animationFrame !== null ) {
103+ window . cancelAnimationFrame ( animationFrame ) ;
104+ }
105+ } ;
106+ } , [ enabled , rootMargin , rootRef , targetRef ] ) ;
48107}
0 commit comments