@@ -20,7 +20,7 @@ import { alpha } from '@mui/material/styles';
* @prop {number} [totalCount] - Total items in unfiltered dataset. When unfiltered count <= pageSize, pagination header is removed entirely. When filtering, the header height is preserved to prevent layout shift.
* @prop {number} [totalCount] - Total items in unfiltered dataset. When unfiltered count <= pageSize, pagination header is removed entirely. When filtering, the header height is preserved to prevent layout shift.
* @prop {number} [unfilteredCount] - Alias for totalCount.
* @prop {number} [unfilteredCount] - Alias for totalCount.
* @prop {(page: number) => void} [onPageChange] - Called on page change.
* @prop {(page: number) => void} [onPageChange] - Called on page change.
* @prop {boolean} [virtualScrollbar=false] - Enables smooth continuous virtual scrolling with a scrollbar synchronized with th e pagination controls.
* @prop {boolean} [virtualScrollbar=false] - Shows a synchronized scrollbar alongsid e pagination controls.
* @prop {boolean} [debug=false] - Dashed outline for layout debugging.
* @prop {boolean} [debug=false] - Dashed outline for layout debugging.
* @prop {object} [sx] - Extra styles.
* @prop {object} [sx] - Extra styles.
*/
*/
@@ -71,8 +71,8 @@ class FillSpace extends Component {
viewportHeight : null ,
viewportHeight : null ,
pageSize : Math . min ( maxSize , minSize ) ,
pageSize : Math . min ( maxSize , minSize ) ,
currentPage : 1 ,
currentPage : 1 ,
scrollTop : 0 ,
isDragging : false ,
isDragging : false ,
dragThumbTop : 0 ,
} ;
} ;
this . ref = null ;
this . ref = null ;
this . trackRef = null ;
this . trackRef = null ;
@@ -81,10 +81,8 @@ class FillSpace extends Component {
this . wheelAcc = 0 ;
this . wheelAcc = 0 ;
this . observer = null ;
this . observer = null ;
this . raf = null ;
this . raf = null ;
this . scrollRaf = null ;
this . pendingScrollTop = 0 ;
this . dragStartY = 0 ;
this . dragStartY = 0 ;
this . dragStartScroll Top = 0 ;
this . dragStartThumb Top = 0 ;
const explicitCount = props . unfilteredCount ? ? props . totalCount ;
const explicitCount = props . unfilteredCount ? ? props . totalCount ;
this . maxSeenCount =
this . maxSeenCount =
explicitCount != null
explicitCount != null
@@ -151,30 +149,6 @@ class FillSpace extends Component {
} ) ;
} ) ;
}
}
}
}
if (
this . props . virtualScrollbar &&
( prevProps . items !== this . props . items ||
prevProps . totalCount !== this . props . totalCount ||
prevProps . unfilteredCount !== this . props . unfilteredCount )
) {
const explicitTotal = this . props . unfilteredCount ? ? this . props . totalCount ;
const total =
explicitTotal != null
? Number ( explicitTotal )
: Array . isArray ( this . props . items )
? this . props . items . length
: 0 ;
const maxScrollTop = Math . max (
0 ,
total * this . state . unitHeight - ( this . state . viewportHeight || 0 ) ,
) ;
if ( this . state . scrollTop > maxScrollTop ) {
if ( this . ref && this . ref . scrollTop !== maxScrollTop ) {
this . ref . scrollTop = maxScrollTop ;
}
this . setState ( { scrollTop : maxScrollTop } ) ;
}
}
}
}
componentWillUnmount ( ) {
componentWillUnmount ( ) {
@@ -189,10 +163,6 @@ class FillSpace extends Component {
cancelAnimationFrame ( this . raf ) ;
cancelAnimationFrame ( this . raf ) ;
this . raf = null ;
this . raf = null ;
}
}
if ( this . scrollRaf ) {
cancelAnimationFrame ( this . scrollRaf ) ;
this . scrollRaf = null ;
}
if ( this . observer ) {
if ( this . observer ) {
this . observer . disconnect ( ) ;
this . observer . disconnect ( ) ;
this . observer = null ;
this . observer = null ;
@@ -202,9 +172,20 @@ class FillSpace extends Component {
_onThumbMouseDown = ( e ) => {
_onThumbMouseDown = ( e ) => {
e . preventDefault ( ) ;
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
e . stopPropagation ( ) ;
const { totalCount , unfilteredCount , items } = this . props ;
const { viewportHeight , pageSize , currentPage } = this . state ;
const explicitTotal = unfilteredCount ? ? totalCount ;
const total = explicitTotal != null ? Number ( explicitTotal ) : ( items ? . length || 0 ) ;
const pageCount = Math . max ( 1 , Math . ceil ( total / Math . max ( 1 , pageSize ) ) ) ;
const racewayBottom = Math . max ( 0 , Number ( this . props . racewayMarginBottom ? ? RACEWAY _BOTTOM _MARGIN ) || 0 ) ;
const trackHeight = Math . max ( 0 , ( viewportHeight || 0 ) - racewayBottom ) ;
const thumbHeight = Math . max ( MIN _THUMB _HEIGHT , Math . min ( trackHeight , total > 0 ? ( pageSize / total ) * trackHeight : trackHeight ) ) ;
const maxThumbTravel = Math . max ( 0 , trackHeight - thumbHeight ) ;
const currentThumbTop = pageCount > 1 && maxThumbTravel > 0 ? ( ( currentPage - 1 ) / ( pageCount - 1 ) ) * maxThumbTravel : 0 ;
this . dragStartY = e . clientY ;
this . dragStartY = e . clientY ;
this . dragStartScroll Top = this . state . scroll Top;
this . dragStartThumb Top = currentThumb Top;
this . setState ( { isDragging : true } ) ;
this . setState ( { isDragging : true , dragThumbTop : currentThumbTop } ) ;
document . body . style . userSelect = 'none' ;
document . body . style . userSelect = 'none' ;
document . body . style . cursor = 'grabbing' ;
document . body . style . cursor = 'grabbing' ;
window . addEventListener ( 'mousemove' , this . _onThumbDragMove ) ;
window . addEventListener ( 'mousemove' , this . _onThumbDragMove ) ;
@@ -213,26 +194,27 @@ class FillSpace extends Component {
_onThumbDragMove = ( e ) => {
_onThumbDragMove = ( e ) => {
if ( ! this . state . isDragging ) return ;
if ( ! this . state . isDragging ) return ;
const deltaY = e . clientY - this . dragStartY ;
const { totalCount , unfilteredCount , items } = this . props ;
const { viewportHeight , unitHeight } = this . state ;
const { viewportHeight , pageSize } = this . state ;
const { items , totalCount , unfilteredCount } = this . props ;
const explicitTotal = unfilteredCount ? ? totalCount ;
const explicitTotal = unfilteredCount ? ? totalCount ;
const total = explicitTotal != null ? Number ( explicitTotal ) : ( items ? . length || 0 ) ;
const total = explicitTotal != null ? Number ( explicitTotal ) : ( items ? . length || 0 ) ;
const totalHeight = total * unitHeight ;
const pageCount = Math . max ( 1 , Math . ceil ( total / Math . max ( 1 , pageSize ) ) ) ;
const maxScrollTop = Math . max ( 0 , totalHeight - ( viewportHeigh t || 0 ) ) ;
if ( pageCoun t <= 1 ) return ;
if ( maxScrollTop <= 0 ) return ;
const racewayBottom = Math . max ( 0 , Number ( this . props . racewayMarginBottom ? ? RACEWAY _BOTTOM _MARGIN ) || 0 ) ;
const racewayBottom = Math . max ( 0 , Number ( this . props . racewayMarginBottom ? ? RACEWAY _BOTTOM _MARGIN ) || 0 ) ;
const trackHeight = Math . max ( 0 , ( viewportHeight || 0 ) - racewayBottom ) ;
const trackHeight = Math . max ( 0 , ( viewportHeight || 0 ) - racewayBottom ) ;
if ( trackHeight <= 0 ) return ;
const thumbHeight = Math . max ( MIN _THUMB _HEIGHT , Math . min ( trackHeight , total > 0 ? ( pageSize / total ) * trackHeight : trackHeight ) ) ;
const thumbHeight = Math . max ( MIN _THUMB _HEIGHT , Math . min ( trackHeight , ( viewport Height / totalHeight ) * track Height ) ) ;
const maxThumbTravel = Math . max ( 0 , track Height - thumb Height ) ;
const maxThumbTravel = trackHeight - thumbHeight ;
if ( maxThumbTravel <= 0 ) return ;
if ( maxThumbTravel <= 0 ) return ;
const scrollDelta = ( deltaY / maxThumbTravel ) * maxScrollTop ;
const nextScrollTop = Math . max ( 0 , Math . min ( maxScrollTop , this . dragStartScrollTop + scrollDelta ) ) ;
const deltaY = e . clientY - this . dragStartY ;
if ( this . ref ) {
const nextThumbTop = Math . max ( 0 , Math . min ( maxThumbTravel , this . dragStartThumbTop + deltaY ) ) ;
this . ref . scrollTop = nextScrollTop ;
const targetPage = Math . min ( pageCount , Math . max ( 1 , Math . round ( 1 + ( nextThumbTop / maxThumbTravel ) * ( pageCount - 1 ) ) ) ) ;
this . setState ( { dragThumbTop : nextThumbTop } ) ;
if ( targetPage !== this . state . currentPage ) {
this . _goToPage ( targetPage ) ;
}
}
this . setState ( { scrollTop : nextScrollTop } ) ;
} ;
} ;
_onThumbDragEnd = ( ) => {
_onThumbDragEnd = ( ) => {
@@ -248,23 +230,33 @@ class FillSpace extends Component {
if ( ! this . trackRef ) return ;
if ( ! this . trackRef ) return ;
const trackRect = this . trackRef . getBoundingClientRect ( ) ;
const trackRect = this . trackRef . getBoundingClientRect ( ) ;
const clickY = e . clientY - trackRect . top ;
const clickY = e . clientY - trackRect . top ;
const { viewportHeight , unitHeight } = this . state ;
const { viewportHeight , pageSize } = this . state ;
const { items , totalCount , unfilteredCount } = this . props ;
const { items , totalCount , unfilteredCount } = this . props ;
const explicitTotal = unfilteredCount ? ? totalCount ;
const explicitTotal = unfilteredCount ? ? totalCount ;
const total = explicitTotal != null ? Number ( explicitTotal ) : ( items ? . length || 0 ) ;
const total = explicitTotal != null ? Number ( explicitTotal ) : ( items ? . length || 0 ) ;
const totalHeight = total * unitHeight ;
const pageCount = Math . max ( 1 , Math . ceil ( total / Math . max ( 1 , pageSize ) ) ) ;
const maxScrollTop = Math . max ( 0 , totalHeight - ( viewportHeigh t || 0 ) ) ;
if ( pageCoun t <= 1 ) return ;
if ( maxScrollTop <= 0 ) return ;
const t rackHeight = trackRect . height ;
const racewayBottom = Math . max ( 0 , Number ( this . props . racewayMarginBottom ? ? RACEWAY _BOTTOM _MARGIN ) || 0 ) ;
const trackHeight = Math . max ( 0 , ( viewportHeight || 0 ) - racewayBottom ) ;
if ( trackHeight <= 0 ) return ;
if ( trackHeight <= 0 ) return ;
const thumbHeight = Math . max ( MIN _THUMB _HEIGHT , Math . min ( trackHeight , ( viewportHeight / total Height) * trackHeight ) ) ;
const thumbHeight = Math . max ( MIN _THUMB _HEIGHT , Math . min ( trackHeight , total > 0 ? ( pageSize / total ) * track Height : trackHeight ) ) ;
const maxThumbTravel = trackHeight - thumbHeight ;
const maxThumbTravel = Math . max ( 0 , trackHeight - thumbHeight ) ;
const targetThumbTop = Math . max ( 0 , Math . min ( maxThumbTravel , clickY - thumbHeight / 2 ) ) ;
const targetThumbTop = Math . max ( 0 , Math . min ( maxThumbTravel , clickY - thumbHeight / 2 ) ) ;
const targetScrollTop = maxThumbTravel > 0 ? ( targetThumbTop / maxThumbTravel ) * maxScrollTop : 0 ;
const targetPage = maxThumbTravel > 0 ? Math . min ( pageCount , Math . max ( 1 , Math . round ( 1 + ( targetThumbTop / maxThumbTravel ) * ( pageCount - 1 ) ) ) ) : 1 ;
if ( this. ref ) {
this . _goToPage ( targetPage ) ;
this . ref . scrollTop = targetScrollTop ;
} ;
scrollToTop = ( ) => {
this . _goToPage ( 1 ) ;
} ;
scrollToIndex = ( index ) => {
const { pageSize } = this . state ;
if ( pageSize > 0 ) {
const targetPage = Math . floor ( index / pageSize ) + 1 ;
this . _goToPage ( targetPage ) ;
}
}
this . setState ( { scrollTop : targetScrollTop } ) ;
} ;
} ;
_onViewportChange = ( ) => {
_onViewportChange = ( ) => {
@@ -310,21 +302,6 @@ class FillSpace extends Component {
this . _viewportMeasure ( ) ;
this . _viewportMeasure ( ) ;
} ;
} ;
scrollToTop = ( ) => {
if ( this . ref ) {
this . ref . scrollTop = 0 ;
}
this . setState ( { scrollTop : 0 , currentPage : 1 } ) ;
} ;
scrollToIndex = ( index ) => {
if ( this . ref && this . state . unitHeight > 0 ) {
const top = Math . max ( 0 , index * this . state . unitHeight ) ;
this . ref . scrollTop = top ;
this . setState ( { scrollTop : top } ) ;
}
} ;
/** Only measures the height of the `renderItem` probe. Nothing else. */
/** Only measures the height of the `renderItem` probe. Nothing else. */
_probeMeasure ( ) {
_probeMeasure ( ) {
const firstRow = this . ref ? . querySelector ( '[data-fill-row]' ) ;
const firstRow = this . ref ? . querySelector ( '[data-fill-row]' ) ;
@@ -377,36 +354,6 @@ class FillSpace extends Component {
}
}
}
}
_handleScroll = ( event ) => {
const st = event . currentTarget . scrollTop ;
this . pendingScrollTop = st ;
if ( this . scrollRaf ) return ;
this . scrollRaf = requestAnimationFrame ( ( ) => {
this . scrollRaf = null ;
const nextSt = this . pendingScrollTop ;
if ( nextSt != null && nextSt !== this . state . scrollTop ) {
const { unitHeight , pageSize } = this . state ;
const currentFirst = unitHeight > 0 ? Math . floor ( nextSt / unitHeight ) : 0 ;
const newPage = pageSize > 0 ? Math . floor ( currentFirst / pageSize ) + 1 : 1 ;
const updates = { scrollTop : nextSt } ;
if ( newPage !== this . state . currentPage ) {
updates . currentPage = newPage ;
const { items , onPageChange , totalCount , unfilteredCount } = this . props ;
const explicitTotal = unfilteredCount ? ? totalCount ;
const total =
explicitTotal != null
? Number ( explicitTotal )
: Array . isArray ( items )
? items . length
: 0 ;
const pageCount = Math . max ( 1 , Math . ceil ( total / Math . max ( 1 , pageSize ) ) ) ;
onPageChange ? . ( newPage , { pageCount , pageSize , total } ) ;
}
this . setState ( updates ) ;
}
} ) ;
} ;
_handleWheel = ( event ) => {
_handleWheel = ( event ) => {
const { items , totalCount , unfilteredCount } = this . props ;
const { items , totalCount , unfilteredCount } = this . props ;
if ( ! Array . isArray ( items ) ) return ;
if ( ! Array . isArray ( items ) ) return ;
@@ -432,7 +379,7 @@ class FillSpace extends Component {
} ;
} ;
_goToPage = ( page ) => {
_goToPage = ( page ) => {
const { items , onPageChange , totalCount , unfilteredCount , virtualScrollbar } = this . props ;
const { items , onPageChange , totalCount , unfilteredCount } = this . props ;
const explicitTotal = unfilteredCount ? ? totalCount ;
const explicitTotal = unfilteredCount ? ? totalCount ;
const total =
const total =
explicitTotal != null
explicitTotal != null
@@ -442,16 +389,6 @@ class FillSpace extends Component {
: 0 ;
: 0 ;
const pageCount = Math . max ( 1 , Math . ceil ( total / Math . max ( 1 , this . state . pageSize ) ) ) ;
const pageCount = Math . max ( 1 , Math . ceil ( total / Math . max ( 1 , this . state . pageSize ) ) ) ;
const next = Math . min ( Math . max ( 1 , Number ( page ) || 1 ) , pageCount ) ;
const next = Math . min ( Math . max ( 1 , Number ( page ) || 1 ) , pageCount ) ;
if ( virtualScrollbar ) {
const targetScrollTop = ( next - 1 ) * this . state . pageSize * this . state . unitHeight ;
if ( this . ref ) {
this . ref . scrollTop = targetScrollTop ;
}
if ( next === this . state . currentPage && targetScrollTop === this . state . scrollTop ) return ;
this . setState ( { currentPage : next , scrollTop : targetScrollTop } ) ;
onPageChange ? . ( next , { pageCount , pageSize : this . state . pageSize , total } ) ;
return ;
}
if ( next === this . state . currentPage ) return ;
if ( next === this . state . currentPage ) return ;
this . setState ( { currentPage : next } ) ;
this . setState ( { currentPage : next } ) ;
onPageChange ? . ( next , { pageCount , pageSize : this . state . pageSize , total } ) ;
onPageChange ? . ( next , { pageCount , pageSize : this . state . pageSize , total } ) ;
@@ -487,7 +424,7 @@ class FillSpace extends Component {
// FillSpace owns current page, wheel-pagination and the pagination header.
// FillSpace owns current page, wheel-pagination and the pagination header.
// Caller feeds `items` plus one row element via `renderItem` (component
// Caller feeds `items` plus one row element via `renderItem` (component
// type or callback). `items` may be undefined while data is loading.
// type or callback). `items` may be undefined while data is loading.
const { pageSize , currentPage , viewportHeight , unitHeight , scrollTop } = this . state ;
const { pageSize , currentPage , viewportHeight } = this . state ;
const list = Array . isArray ( items ) ? items : [ ] ;
const list = Array . isArray ( items ) ? items : [ ] ;
const explicitTotal = unfilteredCount ? ? totalCount ;
const explicitTotal = unfilteredCount ? ? totalCount ;
const total = explicitTotal != null ? Number ( explicitTotal ) : list . length ;
const total = explicitTotal != null ? Number ( explicitTotal ) : list . length ;
@@ -495,47 +432,14 @@ class FillSpace extends Component {
const needsPaginationContainer = fullTotal > pageSize ;
const needsPaginationContainer = fullTotal > pageSize ;
const showPaginationControls = total > pageSize ;
const showPaginationControls = total > pageSize ;
const pageCount = Math . max ( 1 , Math . ceil ( total / Math . max ( 1 , pageSize ) ) ) ;
const pageCount = Math . max ( 1 , Math . ceil ( total / Math . max ( 1 , pageSize ) ) ) ;
const safePage = Math . min ( Math . max ( 1 , currentPage ) , pageCount ) ;
const currentFirstIndex =
const offset = ( safePage - 1 ) * pageSize ;
virtualScrollbar && unitHeigh t > 0
const pageItems = list . slice ( offset , offse t + pageSize ) ;
? Math . min ( Math . max ( 0 , total - 1 ) , Math . floor ( scrollTop / unitHeight ) )
: ( Math . min ( Math . max ( 1 , currentPage ) , pageCount ) - 1 ) * pageSize ;
const safePage =
virtualScrollbar && pageSize > 0
? Math . min ( pageCount , Math . floor ( currentFirstIndex / pageSize ) + 1 )
: Math . min ( Math . max ( 1 , currentPage ) , pageCount ) ;
const countStart = total === 0 ? 0 : currentFirstIndex + 1 ;
const countEnd = Math . min ( total , currentFirstIndex + pageSize ) ;
let renderedItems ;
let itemOffset ;
let paddingTop = 0 ;
let paddingBottom = 0 ;
let totalContentHeight ;
if ( virtualScrollbar && unitHeight > 0 ) {
const overscan = 3 ;
const rawStartIndex = Math . floor ( scrollTop / unitHeight ) ;
const startIndex = Math . max ( 0 , rawStartIndex - overscan ) ;
const visibleCount = Math . ceil ( ( viewportHeight || 0 ) / unitHeight ) ;
const endIndex = Math . min ( total , rawStartIndex + visibleCount + overscan ) ;
renderedItems = list . slice ( startIndex , endIndex ) ;
itemOffset = startIndex ;
paddingTop = startIndex * unitHeight ;
paddingBottom = Math . max ( 0 , ( total - endIndex ) * unitHeight ) ;
totalContentHeight = total * unitHeight ;
} else {
const offset = ( safePage - 1 ) * pageSize ;
renderedItems = list . slice ( offset , offset + pageSize ) ;
itemOffset = offset ;
}
const canScroll =
const canScroll =
virtualScrollbar &&
virtualScrollbar &&
viewportHeight > 0 &&
total > pageSize &&
uni tHeight > 0 &&
( viewpor tHeight || 0 ) > 0 ;
total * unitHeight > viewportHeight ;
const racewayBottom = Math . max ( 0 , Number ( racewayMarginBottom ? ? RACEWAY _BOTTOM _MARGIN ) || 0 ) ;
const racewayBottom = Math . max ( 0 , Number ( racewayMarginBottom ? ? RACEWAY _BOTTOM _MARGIN ) || 0 ) ;
const racewayRightOffset = racewayRight != null ? racewayRight : - ( RACEWAY _WIDTH - 8 ) ;
const racewayRightOffset = racewayRight != null ? racewayRight : - ( RACEWAY _WIDTH - 8 ) ;
@@ -545,11 +449,11 @@ class FillSpace extends Component {
if ( canScroll ) {
if ( canScroll ) {
const trackHeight = Math . max ( 0 , ( viewportHeight || 0 ) - racewayBottom ) ;
const trackHeight = Math . max ( 0 , ( viewportHeight || 0 ) - racewayBottom ) ;
const fullHeight = total * unitHeight ;
thumbHeight = Math . max ( MIN _THUMB _HEIGHT , Math . min ( trackHeight , total > 0 ? ( pageSize / total ) * trackHeight : trackHeight ) ) ;
thumbHeight = Math . max ( MIN _THUMB _HEIGHT , Math . min ( trackHeight , ( viewport Height / full Height) * trackHeight ) ) ;
const maxThumbTravel = Math . max ( 0 , track Height - thumb Height) ;
const maxScrollTop = Math . max ( 0 , fullHeight - viewportHeight ) ;
thumbTop = this . state . isDragging
const maxThumbTravel = trackHeight - thumbHeight ;
? this . state . dragThumbTop
thumbTop = maxScrollTop > 0 ? ( scrollTop / maxScrollTop ) * maxThumbTravel : 0 ;
: ( pageCount > 1 && maxThumbTravel > 0 ? ( ( safePage - 1 ) / ( pageCount - 1 ) ) * maxThumbTravel : 0 ) ;
}
}
// Hidden data-free probe: measures `renderItem` itself (no data).
// Hidden data-free probe: measures `renderItem` itself (no data).
@@ -583,7 +487,8 @@ class FillSpace extends Component {
noWrap
noWrap
sx = { { flexShrink : 1 , minWidth : 0 , overflow : 'hidden' , textOverflow : 'ellipsis' } }
sx = { { flexShrink : 1 , minWidth : 0 , overflow : 'hidden' , textOverflow : 'ellipsis' } }
>
>
{ total === 0 ? '0' : ` ${ countStart } – ${ countEnd } ` } von { total }
{ total === 0 ? '0' : ` ${ offset + 1 } – ${ Math . min ( offset + pageSize , total ) } ` } von { ' ' }
{ total }
< / Typography >
< / Typography >
< Pagination
< Pagination
count = { pageCount }
count = { pageCount }
@@ -611,88 +516,69 @@ class FillSpace extends Component {
) : null }
) : null }
{ header }
{ header }
< Box
< Box
ref = { ( el ) => {
this . ref = el ;
} }
onWheel = { ( e ) => {
this . _handleWheel ( e ) ;
onWheel ? . ( e ) ;
} }
{ ...rest }
{ ...rest }
sx = { [
sx = { [
{
{
position : 'relative' ,
position : 'relative' ,
height : viewportHeight ? ? 'auto' ,
height : viewportHeight ? ? 'auto' ,
overflow : 'hidden' ,
width : '100%' ,
width : '100%' ,
// Keep wheel ticks from scrolling the page behind the list.
// (No preventDefault needed — React wheel is passive.)
overscrollBehavior : 'contain' ,
overscrollBehavior : 'contain' ,
} ,
} ,
sx ,
debug
debug
? ( theme ) => ( {
? ( theme ) => ( {
outline : ` 2px dashed ${ theme . palette . primary . main } ` ,
outline : ` 2px dashed ${ theme . palette . primary . main } ` ,
} )
} )
: undefined ,
: undefined ,
sx ,
] }
] }
>
>
< Box
< Box
ref = { ( el ) => {
this . ref = el ;
} }
onScroll = { virtualScrollbar ? this . _handleScroll : undefined }
onWheel = { ( e ) => {
if ( ! virtualScrollbar ) {
this . _handleWheel ( e ) ;
}
onWheel ? . ( e ) ;
} }
sx = { {
sx = { {
position : 'relative' ,
height : '100%' ,
width : '100%' ,
width : '100%' ,
overflowY : virtual Scrollbar ? 'auto' : 'hidden' ,
pr : can Scroll ? ` ${ Math . max ( 0 , RACEWAY _WIDTH + racewayRightOffset + 8 ) } px ` : 0 ,
overflowX : 'hidden ',
boxSizing : 'border-box ',
scrollbarWidth : 'none' ,
'&::-webkit-scrollbar' : { display : 'none' } ,
} }
} }
>
>
< List ref = { listRef } dense disablePadding sx = { { width : '100%' } } >
{ pageItems . map ( ( item , i ) => (
< Box key = { item ? . id ? ? offset + i } data - fill - row sx = { { width : '100%' } } >
{ renderRowElement ( renderItem , item , offset + i ) }
< / Box >
) ) }
< / List >
< / Box >
{ probe != null ? (
< Box
< Box
ref = { ( el ) => {
this . probeRef = el ;
} }
aria - hidden
sx = { {
sx = { {
position : 'absolute' ,
left : 0 ,
top : 0 ,
width : '100%' ,
width : '100%' ,
pr : canScroll ? ` ${ Math . max ( 0 , RACEWAY _WIDTH + racewayRightOffset + 8 ) } px ` : 0 ,
visibility : 'hidden' ,
boxSizing : 'border-box ',
pointerEvents : 'none ',
... ( virtualScrollbar && unitHeight > 0
? {
paddingTop : ` ${ paddingTop } px ` ,
paddingBottom : ` ${ paddingBottom } px ` ,
minHeight : totalContentHeight ,
}
: { } ) ,
} }
} }
>
>
< List ref = { listRef } dense disablePadding sx = { { width : '100%' } } >
< List dense disablePadding sx = { { width : '100%' } } >
{ renderedItems . map ( ( item , i ) => (
< Box data - fill - row sx = { { width : '100%' } } >
< Box key = { item ? . id ? ? itemOffset + i } data - fill - row sx = { { width : '100%' } } >
{ probe }
{ renderRowElement ( renderItem , item , itemOffset + i ) }
< / Box >
< / Box >
) ) }
< / List >
< / List >
< / Box >
< / Box >
{ probe != null ? (
) : null }
< Box
ref = { ( el ) => {
this . probeRef = el ;
} }
aria - hidden
sx = { {
position : 'absolute' ,
left : 0 ,
top : 0 ,
width : '100%' ,
visibility : 'hidden' ,
pointerEvents : 'none' ,
} }
>
< List dense disablePadding sx = { { width : '100%' } } >
< Box data - fill - row sx = { { width : '100%' } } >
{ probe }
< / Box >
< / List >
< / Box >
) : null }
< / Box >
{ canScroll ? (
{ canScroll ? (
< Box
< Box