import * as React from 'react' ; import { FlatList , FlatListProps , LayoutChangeEvent , NativeScrollEvent , NativeSyntheticEvent , } from 'react-native' ; import { Colunm } from './Column' ; interface IFlatListProps < T > extends FlatListProps < T > { data ? : any ; renderItem ? : any ; } export interface IColumnsProps < T > { asyncHeightForItem ? : ( item : T ) => Promise < number > ; numColumns : number ; heightForItem ? : ( item : T ) => number ; data : T [ ] ; renderItem : ( { item , index } : { item : T ; index : number ; } ) => JSX . Element ; keyForItem : ( item : T ) => string ; onEndReached : ( event : NativeSyntheticEvent < NativeScrollEvent > ) => void ; onScroll ? : ( event : NativeSyntheticEvent < NativeScrollEvent > ) => void ; columnsFlatListProps ? : IFlatListProps < T > ; columnFlatListProps ? : IFlatListProps < T > ; } export interface IColumnsHandles { clear : ( ) => void ; addIteming : boolean ; } const Columns = < T extends { offsetTop : number ; itemHeight : number ; onLayout : any ; _keyForItem_ : any ; } > ( { columnsFlatListProps , ... props } , ref ) => { const { numColumns } = props ; const [ addIteming , setAddIteming ] = React . useState ( false ) ; const [ columns , setColumns ] = React . useState < T [ ] [ ] > ( Array ( numColumns ) . fill ( '' ) . map ( ( ) => [ ] ) ) ; const columnsHeight = React . useMemo ( ( ) => Array ( numColumns ) . fill ( 0 ) , [ numColumns ] ) ; const keysList = React . useMemo < string [ ] > ( ( ) => [ ] , [ ] ) ; const heightForItemAddItems = ( data ) => { let tempColumns = Array ( numColumns ) . fill ( [ ] ) . map ( ( ) => [ ] as T [ ] ) ; for ( const item of data ) { item . _keyForItem_ = props . keyForItem ( item ) ; if ( checkIsExist ( item . _keyForItem_ ) ) { continue ; } const addItemValue = addItem ( item ) ; const height = props . heightForItem ! ( item ) ; columnsHeight [ addItemValue . minColumnsIndex ] += height ; tempColumns = addItemValue . tempColumns ; } return setColumns ( tempColumns ) ; } ; const addItems = ( data , isSyncHeightForItem = false ) : any => { if ( data . length === 0 ) { return setAddIteming ( false ) ; } setAddIteming ( true ) ; const item = data . shift ( ) as T ; item . _keyForItem_ = props . keyForItem ( item ) ; if ( checkIsExist ( item . _keyForItem_ ) ) { return addItems ( data ) ; } const { tempColumns } = addItem ( item , addItems . bind ( addItems , data ) , isSyncHeightForItem ) ; setColumns ( tempColumns ) ; } ; const addItem = ( item , cb ? , isSyncHeightForItem = false ) => { const tempColumns = [ ... columns ] ; const minColumnsIndex = [ ... columnsHeight ] . indexOf ( Math . min . apply ( Math , [ ... columnsHeight ] ) ) ; if ( typeof cb === 'function' ) { item . onLayout = async ( e ) => { item . onLayout = null ; let height = 0 ; if ( isSyncHeightForItem ) { height = await props . asyncHeightForItem ! ( item ) ; } else { height = e . nativeEvent . layout . height ; } columnsHeight [ minColumnsIndex ] += height ; cb ( ) ; } ; } ; const currentColumn = tempColumns [ minColumnsIndex ] ; currentColumn . push ( item ) ; return { tempColumns , minColumnsIndex } ; } ; const clear = ( ) => { for ( let index = 0 ; index < columnsHeight . length ; index ++ ) { columnsHeight [ index ] = 0 ; } keysList . splice ( 0 , keysList . length ) ; setColumns ( Array ( numColumns ) . fill ( [ ] ) . map ( ( ) => [ ] ) ) ; } ; const checkIsExist = React . useCallback ( ( key ) => { const check = keysList . indexOf ( key ) !== - 1 ; if ( ! check ) { keysList . push ( key ) ; } return check ; } , [ columns , keysList ] ) ; React . useEffect ( ( ) => { if ( ! props . data . length ) { return ; } if ( typeof props . heightForItem === 'function' ) { if ( props . asyncHeightForItem ) { addItems ( props . data . slice ( ) , true ) ; return ; } heightForItemAddItems ( props . data . slice ( ) ) ; return ; } addItems ( props . data . slice ( ) ) ; } , [ props . data ] ) ; React . useImperativeHandle ( ref , ( ) => ( { clear , addIteming , } ) ) ; return ( < FlatList data = { props . data . length ? columns as any : null } keyExtractor = { ( columnItem ) => `item-${ columnItem . _keyForItem_ }` } onScroll = { ( e ) => { props . onEndReached ( e ) ; props . onScroll && props . onScroll ( e ) ; } } removeClippedSubviews = { true } { ... columnsFlatListProps } numColumns = { props . numColumns } renderItem = { ( { item , index } ) => { return < Colunm columnFlatListProps = { props . columnFlatListProps } key = { `column-${ index }` } listKey = { `column-${ index }` } keyExtractor = { ( columnItem ) => `item-${ columnItem . _keyForItem_ }` } data = { item } renderItem = { props . renderItem } / > ; } } />); } ; export default React . forwardRef ( Columns as React . RefForwardingComponent < IColumnsHandles , IColumnsProps < any > > ) ;