import * as React from 'react' ; import classnames from 'classnames' ; import { IVirtualListProps } from '../VirtualList' ; import * as styles from '../VirtualList.scss' ; export interface IVirtualListHelperCalculations { containerSizePx : number ; itemSizePx : number ; itemsCount : number ; overscanItemsAheadInt : number ; overscanItemsBehindInt : number ; overscanItemsCount : number ; overscanItemsCountInt : number ; overscanSizePx : number ; scrollTop : number ; scrollIndex : number ; viewportEndIndex : number ; viewportEndIndexInt : number ; viewportItemsCount : number ; viewportSizePx : number ; viewportStartIndex : number ; viewportStartIndexInt : number ; virtualEndIndexInt : number ; virtualItemsCount : number ; virtualStartIndexInt : number ; } export class VirtualListHelper { public calculateItems ( itemsCount , itemHeight , wrapperSizePx , scrollTop , overscan , disableVirtualization = false ) { const measurements = { containerSizePx : itemsCount * itemHeight , itemsCount : itemsCount , itemSizePx : itemHeight , overscanItemsBehindInt : NaN , overscanItemsAheadInt : NaN , overscanItemsCount : NaN , overscanItemsCountInt : NaN , overscanSizePx : NaN , scrollTop : scrollTop , scrollIndex : scrollTop / itemHeight , viewportEndIndex : NaN , viewportEndIndexInt : NaN , viewportItemsCount : wrapperSizePx / itemHeight , viewportSizePx : wrapperSizePx , viewportStartIndex : NaN , viewportStartIndexInt : NaN , virtualEndIndexInt : NaN , virtualItemsCount : NaN , virtualStartIndexInt : NaN , } ; measurements . viewportStartIndex = measurements . scrollTop / measurements . itemSizePx ; measurements . viewportStartIndexInt = Math . max ( Math . floor ( measurements . viewportStartIndex ) , 0 ) ; measurements . viewportEndIndex = measurements . viewportStartIndex + ( measurements . viewportSizePx / measurements . itemSizePx ) ; measurements . viewportEndIndexInt = Math . min ( Math . ceil ( measurements . viewportEndIndex ) , measurements . itemsCount ) ; if ( disableVirtualization ) { measurements . overscanSizePx = 0 ; measurements . overscanItemsCount = 0 ; measurements . overscanItemsCountInt = 0 ; measurements . overscanItemsAheadInt = 0 ; measurements . overscanItemsBehindInt = 0 ; measurements . virtualStartIndexInt = 0 ; measurements . virtualEndIndexInt = measurements . itemsCount ; measurements . virtualItemsCount = measurements . itemsCount ; } else { const autoOverscan = measurements . itemSizePx * 2 > 500 ? measurements . itemSizePx * 2 : 500 ; measurements . overscanSizePx = overscan === 'auto' ? autoOverscan : Number ( overscan ) ; measurements . overscanItemsCount = measurements . overscanSizePx / measurements . itemSizePx ; measurements . overscanItemsCountInt = Math . ceil ( measurements . overscanItemsCount ) ; measurements . overscanItemsAheadInt = Math . min ( Math . floor ( measurements . viewportStartIndex ) , measurements . overscanItemsCountInt ) ; measurements . overscanItemsBehindInt = Math . min ( measurements . itemsCount - measurements . viewportEndIndexInt , measurements . overscanItemsCountInt ) ; measurements . virtualStartIndexInt = Math . max ( measurements . viewportStartIndexInt - measurements . overscanItemsAheadInt , 0 ) ; measurements . virtualEndIndexInt = Math . min ( measurements . viewportEndIndexInt + measurements . overscanItemsBehindInt , measurements . itemsCount ) ; measurements . virtualItemsCount = measurements . virtualEndIndexInt - measurements . virtualStartIndexInt ; } return measurements ; } public generateAndGetRenderedItems ( data , itemRenderer , calculations , itemRendererExtraData , striped ) { const renderedListItems = [ ] ; const customRendererStyles = { transform : `translateY(${ calculations . virtualStartIndexInt * calculations . itemSizePx }px)` , } ; let renderedItem ; let className ; if ( data ) { for ( let index = calculations . virtualStartIndexInt ; index < calculations . virtualEndIndexInt ; index ++ ) { renderedItem = itemRenderer ( data [ index ] , index , customRendererStyles , itemRendererExtraData ) as React . ReactElement < any > ; className = ! striped ? '' : ( index + 1 ) % 2 === 0 ? styles . VirtualList_Item__StripedEven : styles . VirtualList_Item__StripedOdd ; renderedListItems . push ( React . cloneElement ( renderedItem , { className : classnames ( renderedItem . props . className || '' , className ) , style : { ... ( renderedItem . props . style || { } ) , ... customRendererStyles } , } ) ) ; } } return renderedListItems ; } }