11import { ReadTheme , withTheme } from "@draftbit/theme" ;
2- import React from "react" ;
2+ import React , { forwardRef , useImperativeHandle } from "react" ;
33import { View , StyleProp , ViewStyle } from "react-native" ;
44import SwiperComponent from "react-native-web-swiper" ;
55
6+ export interface SwiperRef {
7+ swipeTo : ( index : number ) => void ;
8+ swipeNext : ( ) => void ;
9+ swipePrev : ( ) => void ;
10+ }
11+
612export interface SwiperProps < T > {
713 onSwipe ?: ( index : number ) => void ;
814 onSwipedNext ?: ( index : number ) => void ;
@@ -29,123 +35,140 @@ export interface SwiperProps<T> {
2935 theme : ReadTheme ;
3036}
3137
32- const Swiper = ( {
33- theme ,
34- vertical = false ,
35- loop = false ,
36- timeout = 0 ,
37- from = 0 ,
38- prevTitle = "" ,
39- nextTitle = "" ,
40- prevTitleColor ,
41- nextTitleColor ,
42- dotsTouchable = true ,
43- dotColor = theme . colors . foreground . brand ,
44- dotActiveColor = theme . colors . branding . primary ,
45- data ,
46- keyExtractor ,
47- renderItem ,
48- children : childrenProp ,
49- onIndexChanged : onIndexChangedProp ,
50- onSwipe ,
51- onSwipedNext ,
52- onSwipedPrevious ,
53- minDistanceForAction ,
54- minDistanceToCapture ,
55- style ,
56- } : SwiperProps < any > ) => {
57- const [ currentIndex , setCurrentIndex ] = React . useState ( 0 ) ;
58- const numberOfItems = data ?. length ?? React . Children . count ( childrenProp ) ;
59- const swiperRef = React . useRef < any > ( null ) ;
60-
61- const onIndexChanged = ( index : number ) => {
62- const previous = currentIndex ;
63- const current = index ;
38+ const Swiper = forwardRef < SwiperRef , SwiperProps < any > > (
39+ (
40+ {
41+ theme ,
42+ vertical = false ,
43+ loop = false ,
44+ timeout = 0 ,
45+ from = 0 ,
46+ prevTitle = "" ,
47+ nextTitle = "" ,
48+ prevTitleColor ,
49+ nextTitleColor ,
50+ dotsTouchable = true ,
51+ dotColor = theme ?. colors . foreground . brand ,
52+ dotActiveColor = theme ?. colors . branding . primary ,
53+ data ,
54+ keyExtractor ,
55+ renderItem ,
56+ children : childrenProp ,
57+ onIndexChanged : onIndexChangedProp ,
58+ onSwipe ,
59+ onSwipedNext ,
60+ onSwipedPrevious ,
61+ minDistanceForAction ,
62+ minDistanceToCapture ,
63+ style ,
64+ } : SwiperProps < any > ,
65+ ref
66+ ) => {
67+ const [ currentIndex , setCurrentIndex ] = React . useState ( 0 ) ;
68+ const numberOfItems = data ?. length ?? React . Children . count ( childrenProp ) ;
69+ const swiperRef = React . useRef < any > ( null ) ;
6470
65- onIndexChangedProp ?.( index ) ;
66- setCurrentIndex ( index ) ;
71+ const onIndexChanged = ( index : number ) => {
72+ const previous = currentIndex ;
73+ const current = index ;
6774
68- if ( previous === numberOfItems - 1 && current === 0 ) {
69- //Last -> first swipe
70- onSwipedNext ?.( previous ) ;
71- } else if ( previous === 0 && current === numberOfItems - 1 ) {
72- //First -> last swipe
73- onSwipedPrevious ?.( previous ) ;
74- } else if ( current > previous ) {
75- onSwipedNext ?.( previous ) ;
76- } else if ( current < previous ) {
77- onSwipedPrevious ?.( previous ) ;
78- }
79- onSwipe ?.( previous ) ;
80- } ;
75+ onIndexChangedProp ?.( index ) ;
76+ setCurrentIndex ( index ) ;
8177
82- const children : React . ReactNode = React . useMemo (
83- ( ) =>
84- Array . isArray ( data ) && renderItem
85- ? data . map ( ( item , index ) => {
86- const component = renderItem ( { item, index } ) ;
78+ if ( previous === numberOfItems - 1 && current === 0 ) {
79+ //Last -> first swipe
80+ onSwipedNext ?.( previous ) ;
81+ } else if ( previous === 0 && current === numberOfItems - 1 ) {
82+ //First -> last swipe
83+ onSwipedPrevious ?.( previous ) ;
84+ } else if ( current > previous ) {
85+ onSwipedNext ?.( previous ) ;
86+ } else if ( current < previous ) {
87+ onSwipedPrevious ?.( previous ) ;
88+ }
89+ onSwipe ?.( previous ) ;
90+ } ;
8791
88- if ( ! component ) {
89- return null ;
90- }
92+ const children : React . ReactNode = React . useMemo (
93+ ( ) =>
94+ Array . isArray ( data ) && renderItem
95+ ? data . map ( ( item , index ) => {
96+ const component = renderItem ( { item, index } ) ;
9197
92- const key = keyExtractor ? keyExtractor ( item , index ) : index ;
93- return React . cloneElement ( component , {
94- key,
95- } ) ;
96- } )
97- : childrenProp ,
98- [ childrenProp , data , renderItem , keyExtractor ]
99- ) ;
98+ if ( ! component ) {
99+ return null ;
100+ }
100101
101- /*
102- react-native-web-swiper assigns it's 'children' attribute as follows: `children = (() => React.Children.toArray(this.props.children))();`
103- This is probelematic when state is involved due to anoynmous function effectivley creating new components everytime, losing any state
104-
105- This is a monkey patch that updates the 'children' attribute to just use the children from the props
106- Can be removed when/if https://github.com/reactrondev/react-native-web-swiper/pull/102 is merged
107- */
108- React . useEffect ( ( ) => {
109- const childrenArray = React . Children . toArray (
110- swiperRef . current ?. props ?. children
102+ const key = keyExtractor ? keyExtractor ( item , index ) : index ;
103+ return React . cloneElement ( component , {
104+ key,
105+ } ) ;
106+ } )
107+ : childrenProp ,
108+ [ childrenProp , data , renderItem , keyExtractor ]
111109 ) ;
112- if ( swiperRef . current ) {
113- swiperRef . current . children = childrenArray ;
114- swiperRef . current . count = childrenArray . length ;
115- swiperRef . current . forceUpdate ( ) ;
116- }
117- } , [ children ] ) ;
118110
119- return (
120- < View style = { style } >
121- { /* @ts -ignore */ }
122- < SwiperComponent
123- ref = { swiperRef }
124- from = { from }
125- loop = { loop }
126- timeout = { timeout }
127- vertical = { vertical }
128- onIndexChanged = { onIndexChanged }
129- minDistanceForAction = { minDistanceForAction }
130- minDistanceToCapture = { minDistanceToCapture }
131- controlsProps = { {
132- prevTitle,
133- nextTitle,
134- prevTitleStyle : { color : prevTitleColor } ,
135- nextTitleStyle : { color : nextTitleColor } ,
136- dotsTouchable,
137- ...( dotColor
138- ? { dotProps : { badgeStyle : { backgroundColor : dotColor } } }
139- : { } ) ,
140- ...( dotActiveColor
141- ? { dotActiveStyle : { backgroundColor : dotActiveColor } }
142- : { } ) ,
143- } }
144- >
145- { children }
146- </ SwiperComponent >
147- </ View >
148- ) ;
149- } ;
111+ /*
112+ react-native-web-swiper assigns it's 'children' attribute as follows: `children = (() => React.Children.toArray(this.props.children))();`
113+ This is probelematic when state is involved due to anoynmous function effectivley creating new components everytime, losing any state
114+
115+ This is a monkey patch that updates the 'children' attribute to just use the children from the props
116+ Can be removed when/if https://github.com/reactrondev/react-native-web-swiper/pull/102 is merged
117+ */
118+ React . useEffect ( ( ) => {
119+ const childrenArray = React . Children . toArray (
120+ swiperRef . current ?. props ?. children
121+ ) ;
122+ if ( swiperRef . current ) {
123+ swiperRef . current . children = childrenArray ;
124+ swiperRef . current . count = childrenArray . length ;
125+ swiperRef . current . forceUpdate ( ) ;
126+ }
127+ } , [ children ] ) ;
128+
129+ useImperativeHandle ( ref , ( ) => ( {
130+ swipeTo : ( index : number ) => {
131+ swiperRef . current ?. goTo ( index ) ;
132+ } ,
133+ swipeNext : ( ) => {
134+ swiperRef . current ?. goToNext ( ) ;
135+ } ,
136+ swipePrev : ( ) => {
137+ swiperRef . current ?. goToPrev ( ) ;
138+ } ,
139+ } ) ) ;
140+
141+ return (
142+ < View style = { style } >
143+ { /* @ts -ignore */ }
144+ < SwiperComponent
145+ ref = { swiperRef }
146+ from = { from }
147+ loop = { loop }
148+ timeout = { timeout }
149+ vertical = { vertical }
150+ onIndexChanged = { onIndexChanged }
151+ minDistanceForAction = { minDistanceForAction }
152+ minDistanceToCapture = { minDistanceToCapture }
153+ controlsProps = { {
154+ prevTitle,
155+ nextTitle,
156+ prevTitleStyle : { color : prevTitleColor } ,
157+ nextTitleStyle : { color : nextTitleColor } ,
158+ dotsTouchable,
159+ ...( dotColor
160+ ? { dotProps : { badgeStyle : { backgroundColor : dotColor } } }
161+ : { } ) ,
162+ ...( dotActiveColor
163+ ? { dotActiveStyle : { backgroundColor : dotActiveColor } }
164+ : { } ) ,
165+ } }
166+ >
167+ { children }
168+ </ SwiperComponent >
169+ </ View >
170+ ) ;
171+ }
172+ ) ;
150173
151174export default withTheme ( Swiper ) ;
0 commit comments