@fto-consult/expo-ui
Version:
Bibliothèque de composants UI Expo,react-native
230 lines (221 loc) • 10 kB
JavaScript
import React from "$react";
import { prepareItems as customPrepareItems,getBToTopRef } from "./utils";
import {grid,StylePropTypes} from "$theme";
import PropTypes from "prop-types";
import {defaultObj,defaultStr,extendObj,isObj,defaultDecimal,defaultArray,defaultFunc} from "$cutils";
import {isMobileMedia} from "$cplatform/dimensions";
import BackToTop from "$ecomponents/BackToTop";
import {FlatList,StyleSheet,View} from "react-native";
import Label from "$ecomponents/Label";
import { useList,useGetNumColumns } from "./hooks";
const CommonListComponent = React.forwardRef((props,ref)=>{
const context = useList(props);
let {testID,isBigList,defaultItemHeight,itemHeight,onRender,componentProps,columnWrapperStyle,onViewableItemsChanged,withFlatListItem,Component,withBackToTop,backToTopRef:customBackToTopRef,withBackToTopButton,onScroll,onScrollEnd,onMount,onUnmount,renderScrollViewWrapper,prepareItems,getItemKey,getKey,keyExtractor,items,filter,renderItem,numColumns:cNumColumns,containerProps,bindResizeEvents,...rest} = props;
withBackToTopButton = withBackToTop === true || withBackToTopButton == true || isMobileMedia()? true : false;
rest = defaultObj(rest);
containerProps = defaultObj(containerProps);
const {itemWindowWidth,numColumns} = useGetNumColumns(props);
let scrollEndTimeout = React.useRef(null);
const listRef = React.useRef(null);
const hasCustomBackToTop = typeof customBackToTopRef == 'function'? true : false;
const backToTopRef = React.useRef(null);
const isFlatList = Component === FlatList;
defaultStr(props.testID,"RN_CommonListComponent");
extendObj(context,{
getKey : typeof keyExtractor =='function'? keyExtractor : typeof getItemKey =='function'? getItemKey : typeof getKey =='function'? getKey : undefined,
addItemsRefs : function(ref, itemRef){
context.itemsRefs[itemRef.index] = {
ref,
item: itemRef.item,
index: itemRef.index,
}
},
renderItem : function({item,index,section,...rest}){
rest = rest ? rest : {};
if(isObj(item) && item.isSectionListHeader){
rest.isSectionListHeader = true;
}
let ret = renderItem({item,numColumns,index,section,numColumns,itemContainerWidth:itemWindowWidth,itemWindowWidth,...rest,isScrolling:listRef.current?.isScrolling?true:false,items:defaultArray(context.items)});
if(typeof ret =='string' || typeof ret =='number'){
return <Label testID={testID+"_ListItemLabel"} children = {ret}/>
}
return (React.isValidElement(ret)) ? ret : null;
},
/*** @params : {animated?: ?boolean,index: number,viewOffset?: number,viewPosition?: number,} */
scrollToIndex : function(params) {
if (listRef.current && typeof listRef.current.scrollToIndex =='function') {
listRef.current.scrollToIndex(params);
}
},
scrollToTop : function(params){
return context.scrollToIndex({animated:true,...defaultObj(params),index:0});
},
/** params?: ?{animated?: ?boolean} */
scrollToEnd : function(params) {
if (listRef.current && listRef.current.scrollToEnd) {
listRef.current.scrollToEnd(params);
}
},
/*** @params : {animated?: ?boolean,item: ItemT,viewPosition?: number} */
scrollToItem : function(params) {
if (listRef.current) {
listRef.current.scrollToItem(params);
}
},
/*** @params : {animated?: ?boolean, offset: number} */
scrollToOffset : function(params) {
if (listRef.current) {
listRef.current.scrollToOffset(params);
}
},
handleOnScroll : (event)=>{
if(customBackToTopRef === false) {
if(onScroll){
onScroll(event);
}
return;
}
const bToTopRef = getBToTopRef(hasCustomBackToTop ? customBackToTopRef() : backToTopRef);
if (withBackToTopButton && bToTopRef) {
bToTopRef.toggleVisibility(event);
}
if(listRef.current){
listRef.current.isScrolling = true;
context.isScrolling = true;
}
clearTimeout(scrollEndTimeout.current);
scrollEndTimeout.current = setTimeout(()=>{
if(listRef.current){
listRef.current.isScrolling = false;
context.isScrolling = false;
}
clearTimeout(scrollEndTimeout.current);
if(onScrollEnd){
onScrollEnd(event);
}
},1000);
if(onScroll){
onScroll(event);
}
},
onScroll : function(event){
context.handleOnScroll(event);
},
keyExtractor : function(item,index){
if(context.getKey){
return context.getKey(item,index);
}
return React.key(item,index);
},
itemHeight : typeof itemHeight =='number' && itemHeight ? itemHeight : function(section,index){
if(typeof index ==='undefined') return 0;
if(!Array.isArray(context.items)){
context.items = [];
}
if(typeof itemHeight ==='function'){
return itemHeight({section,numColumns,itemContainerWidth:itemWindowWidth,itemWindowWidth,index,context,item:context.items[index],items:context.items});
}
return defaultItemHeight
},
onBackActionPress : !hasCustomBackToTop ? function(){
return context?.scrollToTop()
}: undefined,
})
context.listRef = listRef.current;
React.useOnRender(onRender,Math.max(items.length/10 || 0,500));
React.setRef(ref,context);
React.useEffect(()=>{
React.setRef(ref,context);
if(typeof onMount =='function'){
onMount(context);
}
return ()=>{
React.setRef(ref,null);
if(typeof onUnmount ==='function'){
onUnmount();
}
}
},[]);
const restP = numColumns > 1 && isFlatList ? {
columnWrapperStyle : [styles.columnWrapperStyle,props.columnWrapperStyle]
} : {};
const itemsLength = context.items?.length;
const isNotVirtual = false && isBigList && itemsLength <=10;
return <View testID={testID+"_CommonListContainer"} {...containerProps} style={[styles.container,containerProps.style]}>
{!isNotVirtual ? <Component
onEndReachedThreshold={0}
scrollEventThrottle={16}
{...rest}
{...restP}
testID = {testID}
ref = {listRef}
onScroll={context.onScroll}
data = {context.items}
numColumns={numColumns}
keyExtractor = {context.keyExtractor}
renderItem = {context.renderItem}
itemHeight = {itemHeight === false ? undefined : context.itemHeight}
onViewableItemsChanged={onViewableItemsChanged}
{...defaultObj(componentProps)}
/> : context.items.map((item,index)=>{
const key = context.keyExtractor(item,index);
return <View key={key} testID={`${testID}_RN_RealBigList_${index}_${key}`} style={[styles.realListItem]}>
{context.renderItem({item,index})}
</View>
})}
{!isNotVirtual && !hasCustomBackToTop && customBackToTopRef !== false ? <BackToTop ref={backToTopRef} onPress={context.onBackActionPress} /> : null}
</View>
})
CommonListComponent.propTypes = {
onViewableItemsChanged : PropTypes.func,
...defaultObj(FlatList.propTypes),
defaultItemHeight : PropTypes.number,///la valeur de la hauteur des items par défaut
backToTopRef : PropTypes.oneOfType([
PropTypes.func,
PropTypes.bool,
]),
Component : PropTypes.oneOfType([
PropTypes.element,
PropTypes.node,
PropTypes.elementType
]),
withBackToTopButton : PropTypes.bool,
withBackToTop : PropTypes.bool,
onScroll : PropTypes.func,
onScrollEnd : PropTypes.func,
/**** les props de la scrollView, qui wrapp le composant ScrollView dans le rendu BigList */
contentContainerStyle: StylePropTypes,
prepareItems : PropTypes.oneOfType([
PropTypes.bool,///si la fonction permettant de faire un travail préparaoire des données de la liste sera appelée
PropTypes.func,
]),
items : PropTypes.oneOfType([
PropTypes.array,
PropTypes.object,
]),
responsive : PropTypes.bool,//si le nombre de columns de la big list sera déterminées dynamiquement
//si la liste prendra en compte le redimessionnement de la page, ce qui poussera à mettre à jour le nombre de colonnes lorsque la liste est rédimensionnée
bindResizeEvents : PropTypes.bool,
renderItem : PropTypes.func,
containerProps : PropTypes.object,///les props du container à la big list
filter : PropTypes.func, //la fonction utilisée pour le filtre des éléments à rendre pour la liste
onMount : PropTypes.func,
onUnmount : PropTypes.func,
}
const styles = StyleSheet.create({
container: {
flex: 1,
minHeight : 100,
},
columnWrapperStyle : {
flex : 1,
},
realListItem : {
width : "100%",
paddingVertical : 0,
paddingHorizontal : 0,
minHeight : 40,
},
});
export default CommonListComponent;
CommonListComponent.displayName = "CommonListComponent";