11import React from 'react' ;
2- import { View , FlatList , StyleSheet } from 'react-native' ;
2+ import { View , FlatList , StyleSheet , useWindowDimensions } from 'react-native' ;
33
4- import { SettingsContext } from '~/contexts/settings' ;
54import { ThemeContext } from '~/contexts/theme' ;
5+ import { SettingsContext } from '~/contexts/settings' ;
66import IconButton from '~/components/button/IconButton' ;
77import size from '~/styles/size' ;
88
9- const CustomFlat = ( { data, renderItem, style = { width : '100%' } , contentContainerStyle = { paddingHorizontal : 20 , columnGap : 10 } } ) => {
9+ const CustomFlat = ( { data, renderItem, style = { width : '100%' } , contentContainerStyle = { paddingHorizontal : 20 , columnGap : 10 } , widthItem = 0 } ) => {
1010 const theme = React . useContext ( ThemeContext )
1111 const settings = React . useContext ( SettingsContext )
1212 const indexScroll = React . useRef ( 0 )
1313 const refScroll = React . useRef ( null )
14+ const [ isScrollHelper , setIsScrollHelper ] = React . useState ( false )
15+ const { width } = useWindowDimensions ( )
16+ const visibleItems = React . useMemo ( ( ) => {
17+ if ( widthItem > 0 ) return width / widthItem
18+ else return 3
19+ } , [ width , widthItem ] )
20+ const [ isLeftVisible , setIsLeftVisible ] = React . useState ( false ) ;
21+ const [ isRightVisible , setIsRightVisible ] = React . useState ( true ) ;
1422
1523 const goRight = ( ) => {
16- if ( indexScroll . current + 3 >= data . length ) indexScroll . current = data . length - 1
17- else indexScroll . current = indexScroll . current + 3
24+ if ( Math . floor ( indexScroll . current ) + Math . floor ( visibleItems ) >= data . length ) indexScroll . current = data . length - 1
25+ else indexScroll . current = Math . floor ( indexScroll . current ) + Math . floor ( visibleItems )
26+ setVisibility ( )
1827 refScroll . current . scrollToIndex ( { index : indexScroll . current , animated : true , viewOffset : 20 } )
1928 }
2029
2130 const goLeft = ( ) => {
22- if ( indexScroll . current < 3 ) indexScroll . current = 0
23- else indexScroll . current = indexScroll . current - 3
31+ if ( indexScroll . current < Math . floor ( visibleItems ) ) indexScroll . current = 0
32+ else indexScroll . current = indexScroll . current - Math . floor ( visibleItems )
33+ setVisibility ( )
2434 refScroll . current . scrollToIndex ( { index : indexScroll . current , animated : true , viewOffset : 20 } )
2535 }
2636
37+ const setVisibility = ( ) => {
38+ if ( indexScroll . current === 0 ) setIsLeftVisible ( false )
39+ else setIsLeftVisible ( true )
40+ if ( indexScroll . current + visibleItems >= data . length ) setIsRightVisible ( false )
41+ else setIsRightVisible ( true )
42+ }
43+
2744 // https://github.com/facebook/react-native/issues/39421
2845 if ( ! data || data . length === 0 ) return null ;
2946
30- // View is necessary to show the scroll helper
3147 return (
32- < View >
48+ < View
49+ onPointerEnter = { ( ) => setIsScrollHelper ( settings . scrollHelper && true ) }
50+ onPointerLeave = { ( ) => setIsScrollHelper ( false ) }
51+ >
52+ {
53+ ( isScrollHelper && isLeftVisible ) ? (
54+ < View style = { [ styles . scrollContainer , { left : 0 } ] } >
55+ < IconButton icon = "angle-left" size = { size . icon . tiny } onPress = { goLeft } color = { theme . primaryText } pressEffect = { false }
56+ styleIcon = { { lineHeight : size . icon . tiny } }
57+ style = { styles . scrollHelper ( theme ) } />
58+ </ View >
59+ ) : null
60+ }
3361 {
34- settings ?. scrollHelper && (
35- < View style = { styles . scrollContainer } >
36- < IconButton icon = "chevron-left" size = { size . icon . tiny } onPress = { goLeft } color = { theme . secondaryText } style = { styles . scrollHelper ( theme ) } />
37- < IconButton icon = "chevron-right" size = { size . icon . tiny } onPress = { goRight } color = { theme . secondaryText } style = { styles . scrollHelper ( theme ) } />
62+ ( isScrollHelper && isRightVisible ) ? (
63+ < View style = { [ styles . scrollContainer , { right : 0 } ] } >
64+ < IconButton icon = "angle-right" size = { size . icon . tiny } onPress = { goRight } color = { theme . primaryText }
65+ pressEffect = { false }
66+ style = { styles . scrollHelper ( theme ) } />
3867 </ View >
39- )
68+ ) : null
4069 }
4170 < FlatList
4271 ref = { refScroll }
@@ -45,6 +74,14 @@ const CustomFlat = ({ data, renderItem, style = { width: '100%' }, contentContai
4574 renderItem = { renderItem }
4675 horizontal = { true }
4776 style = { style }
77+ onScroll = { ( { nativeEvent } ) => {
78+ if ( widthItem > 0 ) {
79+ const index = nativeEvent . contentOffset . x / widthItem
80+ if ( index < 0 ) indexScroll . current = 0
81+ else indexScroll . current = index
82+ setVisibility ( )
83+ }
84+ } }
4885 onScrollToIndexFailed = { ( ) => { } }
4986 contentContainerStyle = { contentContainerStyle }
5087 showsHorizontalScrollIndicator = { false }
@@ -57,19 +94,18 @@ const styles = StyleSheet.create({
5794 scrollContainer : {
5895 position : 'absolute' ,
5996 zIndex : 1 ,
60- flexDirection : 'row' ,
61- right : 10 ,
62- top : - 40 ,
63- columnGap : 1 ,
97+ top : 0 ,
98+ bottom : 0 ,
99+ justifyContent : 'center' ,
64100 } ,
65101 scrollHelper : theme => ( {
66- backgroundColor : theme . secondaryBack ,
67- height : 30 ,
68- width : 30 ,
69- borderTopLeftRadius : 5 ,
70- borderBottomLeftRadius : 5 ,
102+ width : size . icon . tiny + 20 ,
103+ height : size . icon . tiny + 20 ,
71104 justifyContent : 'center' ,
72105 alignItems : 'center' ,
106+ backgroundColor : theme . secondaryBack ,
107+ borderRadius : size . radius . circle ,
108+ marginHorizontal : 10 ,
73109 } ) ,
74110} )
75111
0 commit comments