1
1
// TODO: support commented props
2
2
import React , { Component } from 'react' ;
3
- import PropTypes from 'prop-types' ;
4
3
import _ from 'lodash' ;
5
4
import Reanimated , { Easing } from 'react-native-reanimated' ;
6
5
import { State } from 'react-native-gesture-handler' ;
7
6
import { timing , fract , between } from 'react-native-redash' ;
8
7
import { Constants } from '../../helpers' ;
9
- import { asBaseComponent } from '../../commons' ;
8
+ import { asBaseComponent } from '../../commons/new ' ;
10
9
import TabBarContext from './TabBarContext' ;
11
10
import TabBar from './TabBar' ;
12
- import TabBarItem from './TabBarItem' ;
11
+ import TabBarItem , { TabBarItemProps } from './TabBarItem' ;
13
12
import TabPage from './TabPage' ;
14
13
import PageCarousel from './PageCarousel' ;
15
14
@@ -36,54 +35,80 @@ const {
36
35
multiply
37
36
} = Reanimated ;
38
37
38
+ interface TabControllerProps {
39
+ /**
40
+ * The list of tab bar items
41
+ */
42
+ items : TabBarItemProps [ ] ;
43
+ /**
44
+ * Initial selected index
45
+ */
46
+ selectedIndex : number ;
47
+ /**
48
+ * callback for when index has change (will not be called on ignored items)
49
+ */
50
+ onChangeIndex : ( index : number ) => void ;
51
+ /**
52
+ * When using TabController.PageCarousel this should be turned on
53
+ */
54
+ asCarousel ?: boolean ;
55
+ /**
56
+ * Pass for custom carousel page width
57
+ */
58
+ carouselPageWidth ?: number ;
59
+ }
60
+
61
+ interface StateProps {
62
+ selectedIndex : number ;
63
+ asCarousel ?: boolean ;
64
+ pageWidth : number ;
65
+ // items
66
+ items : TabControllerProps [ 'items' ] ;
67
+ itemStates : any [ ] ; // TODO: typescript?
68
+ ignoredItems : any [ ] ; // TODO: typescript?
69
+ // animated values
70
+ targetPage : any ; // TODO: typescript?
71
+ currentPage : any ; // TODO: typescript?
72
+ carouselOffset : any ; // TODO: typescript?
73
+ containerWidth : any ; // TODO: typescript?
74
+ // callbacks
75
+ registerTabItems : ( tabItemsCount : number , ignoredItems : StateProps [ 'ignoredItems' ] ) => void ;
76
+ onChangeIndex : ( index : number ) => void ;
77
+ }
78
+
39
79
/**
40
80
* @description : A performant solution for a tab controller with lazy load mechanism
41
81
* @example : https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/TabControllerScreen/index.js
42
82
* @notes : This component is based on react-native-gesture-handler
43
83
* @important : On Android, if using react-native-navigation, make sure to wrap your screen with gestureHandlerRootHOC
44
84
* @importantLink : https://kmagiera.github.io/react-native-gesture-handler/docs/getting-started.html#with-wix-react-native-navigation-https-githubcom-wix-react-native-navigation
45
85
*/
46
- class TabController extends Component {
86
+ class TabController extends Component < TabControllerProps , StateProps > {
47
87
static displayName = 'TabController' ;
48
88
static contextType = TabBarContext ;
49
89
50
- static propTypes = {
51
- /**
52
- * The list of tab bar items
53
- */
54
- items : PropTypes . arrayOf ( PropTypes . shape ( TabBarItem . propTypes ) ) ,
55
- /**
56
- * Initial selected index
57
- */
58
- selectedIndex : PropTypes . number ,
59
- /**
60
- * callback for when index has change (will not be called on ignored items)
61
- */
62
- onChangeIndex : PropTypes . func ,
63
- /**
64
- * When using TabController.PageCarousel this should be turned on
65
- */
66
- asCarousel : PropTypes . bool ,
67
- /**
68
- * Pass for custom carousel page width
69
- */
70
- carouselPageWidth : PropTypes . number
71
- } ;
90
+ static TabBar : typeof TabBar ;
91
+ static TabBarItem : typeof TabBarItem ;
92
+ static TabPage : typeof TabPage ;
93
+ static PageCarousel : typeof PageCarousel ;
72
94
73
95
static defaultProps = {
74
96
selectedIndex : 0 ,
75
97
activeOpacity : 0.2
76
98
} ;
77
99
78
- constructor ( props ) {
100
+ constructor ( props : TabControllerProps ) {
79
101
super ( props ) ;
80
102
81
- let itemStates = [ ] ;
82
- let ignoredItems = [ ] ;
103
+ let itemStates : any [ ] = [ ] ; // TODO: typescript?
104
+ let ignoredItems : any [ ] = [ ] ; // TODO: typescript?
83
105
if ( props . items ) {
84
- const itemsCount = _ . chain ( props . items ) . filter ( item => ! item . ignore ) . size ( ) . value ( ) ;
106
+ const itemsCount = _ . chain ( props . items )
107
+ . filter ( item => ! item . ignore )
108
+ . size ( )
109
+ . value ( ) ;
85
110
itemStates = _ . times ( itemsCount , ( ) => new Value ( State . UNDETERMINED ) ) ;
86
- ignoredItems = _ . filter ( props . items , item => item . ignore ) ;
111
+ ignoredItems = _ . filter < TabBarItemProps [ ] > ( props . items , ( item : TabBarItemProps ) => item . ignore ) ;
87
112
}
88
113
89
114
this . state = {
@@ -105,7 +130,7 @@ class TabController extends Component {
105
130
} ;
106
131
}
107
132
108
- static getDerivedStateFromProps ( nextProps , prevState ) {
133
+ static getDerivedStateFromProps ( nextProps : TabControllerProps , prevState : StateProps ) {
109
134
if ( ! _ . isUndefined ( nextProps . carouselPageWidth ) && nextProps . carouselPageWidth !== prevState . pageWidth ) {
110
135
return {
111
136
pageWidth : nextProps . carouselPageWidth
@@ -114,7 +139,7 @@ class TabController extends Component {
114
139
return null ;
115
140
}
116
141
117
- componentDidUpdate ( prevProps , prevState ) {
142
+ componentDidUpdate ( _prevProps : TabControllerProps , prevState : StateProps ) {
118
143
if ( prevState . pageWidth !== this . state . pageWidth ) {
119
144
this . state . containerWidth . setValue ( this . state . pageWidth ) ;
120
145
}
@@ -124,12 +149,12 @@ class TabController extends Component {
124
149
return this . props . carouselPageWidth || Constants . screenWidth ;
125
150
}
126
151
127
- registerTabItems = ( tabItemsCount , ignoredItems ) => {
152
+ registerTabItems = ( tabItemsCount : number , ignoredItems : StateProps [ 'ignoredItems' ] ) => {
128
153
const itemStates = _ . times ( tabItemsCount , ( ) => new Value ( State . UNDETERMINED ) ) ;
129
154
this . setState ( { itemStates, ignoredItems} ) ;
130
155
} ;
131
156
132
- onPageChange = ( [ index ] ) => {
157
+ onPageChange = ( [ index ] : readonly number [ ] ) => {
133
158
_ . invoke ( this . props , 'onChangeIndex' , index ) ;
134
159
} ;
135
160
@@ -147,40 +172,47 @@ class TabController extends Component {
147
172
..._ . map ( itemStates , ( state , index ) => {
148
173
const ignoredItem = _ . includes ( ignoredItems , index ) ;
149
174
return [
150
- onChange ( state ,
175
+ onChange (
176
+ state ,
177
+ // @ts -ignore TODO: typescript?
151
178
cond ( and ( eq ( state , State . END ) , ! ignoredItem ) , [
152
179
set ( fromPage , toPage ) ,
153
180
set ( toPage , index ) ,
154
181
set ( targetPage , index )
155
- ] ) )
182
+ ] )
183
+ )
156
184
] ;
157
185
} ) ,
158
186
159
187
// Animate currentPage to its target
160
188
cond ( neq ( currentPage , toPage ) , [
161
189
set ( isAnimating , 1 ) ,
162
- set ( currentPage ,
163
- timing ( { clock, from : fromPage , to : toPage , duration : 280 , easing : Easing . bezier ( 0.34 , 1.3 , 0.64 , 1 ) } ) )
190
+ set (
191
+ currentPage ,
192
+ timing ( { clock, from : fromPage , to : toPage , duration : 280 , easing : Easing . bezier ( 0.34 , 1.3 , 0.64 , 1 ) } )
193
+ )
164
194
] ) ,
165
195
// Set isAnimating flag off
166
196
cond ( and ( eq ( isAnimating , 1 ) , not ( clockRunning ( clock ) ) ) , set ( isAnimating , 0 ) ) ,
167
197
168
198
/* Page change by Carousel scroll */
169
199
onChange ( carouselOffset , [
170
200
set ( isScrolling , lessThan ( round ( abs ( diff ( carouselOffset ) ) ) , round ( containerWidth ) ) ) ,
171
- cond ( and ( not ( isAnimating ) ) , [
172
- set ( currentPage ,
201
+ cond ( not ( isAnimating ) , [
202
+ set (
203
+ currentPage ,
173
204
interpolate ( round ( carouselOffset ) , {
174
- inputRange : itemStates . map ( ( v , i ) => round ( multiply ( i , containerWidth ) ) ) ,
175
- outputRange : itemStates . map ( ( v , i ) => i )
176
- } ) ) ,
205
+ inputRange : itemStates . map ( ( _v , i ) => round ( multiply ( i , containerWidth ) ) ) ,
206
+ outputRange : itemStates . map ( ( _v , i ) => i )
207
+ } )
208
+ ) ,
177
209
set ( toPage , currentPage )
178
210
] )
179
211
] ) ,
180
212
// Update/Sync target page when scrolling is done
181
213
cond ( and ( eq ( isScrolling , 1 ) , eq ( floor ( abs ( diff ( carouselOffset ) ) ) , 0 ) ) , [
182
214
set ( isScrolling , 0 ) ,
183
- cond ( not ( between ( fract ( currentPage ) , 0.1 , 0.9 , 1 ) ) , set ( targetPage , round ( currentPage ) ) )
215
+ cond ( not ( between ( fract ( currentPage ) , 0.1 , 0.9 , true ) ) , set ( targetPage , round ( currentPage ) ) )
184
216
] ) ,
185
217
186
218
/* Invoke index change */
@@ -204,4 +236,13 @@ TabController.TabBar = TabBar;
204
236
TabController . TabBarItem = TabBarItem ;
205
237
TabController . TabPage = TabPage ;
206
238
TabController . PageCarousel = PageCarousel ;
207
- export default asBaseComponent ( TabController ) ;
239
+ export default asBaseComponent <
240
+ TabControllerProps ,
241
+ {
242
+ TabBar : typeof TabBar ;
243
+ TabBarItem : typeof TabBarItem ;
244
+ TabPage : typeof TabPage ;
245
+ PageCarousel : typeof PageCarousel ;
246
+ }
247
+ //@ts -ignore typescript - will be fixed when moved to functional component
248
+ > ( TabController ) ;
0 commit comments