2
2
// TODO: disable scroll when content width is shorter than screen width
3
3
import React , { PureComponent } from 'react' ;
4
4
import { StyleSheet , ScrollView , ViewPropTypes , Platform , Text as RNText } from 'react-native' ;
5
- import Reanimated , { Easing } from 'react-native-reanimated' ;
5
+ import Reanimated from 'react-native-reanimated' ;
6
6
import PropTypes from 'prop-types' ;
7
7
import _ from 'lodash' ;
8
8
@@ -17,22 +17,7 @@ import {LogService} from '../../services';
17
17
18
18
const DEFAULT_HEIGHT = 48 ;
19
19
const INDICATOR_INSET = Spacings . s4 ;
20
- const {
21
- Code,
22
- Clock,
23
- Value,
24
- and,
25
- eq,
26
- neq,
27
- cond,
28
- stopClock,
29
- startClock,
30
- interpolate,
31
- Extrapolate,
32
- timing,
33
- block,
34
- set
35
- } = Reanimated ;
20
+ const { Code, Value, interpolate, block, set} = Reanimated ;
36
21
37
22
/**
38
23
* @description : TabController's TabBar component
@@ -95,7 +80,11 @@ class TabBar extends PureComponent {
95
80
/**
96
81
* The TabBar container width
97
82
*/
98
- containerWidth : PropTypes . number
83
+ containerWidth : PropTypes . number ,
84
+ /**
85
+ * Pass to center selected item
86
+ */
87
+ centerSelected : PropTypes . bool
99
88
} ;
100
89
101
90
static defaultProps = {
@@ -115,6 +104,7 @@ class TabBar extends PureComponent {
115
104
this . tabBarScrollOffset = 0 ;
116
105
117
106
this . _itemsWidths = _ . times ( itemsCount , ( ) => null ) ;
107
+ this . _itemsOffsets = _ . times ( itemsCount , ( ) => null ) ;
118
108
this . _indicatorOffset = new Value ( 0 ) ;
119
109
this . _indicatorWidth = new Value ( 0 ) ;
120
110
@@ -136,7 +126,7 @@ class TabBar extends PureComponent {
136
126
}
137
127
138
128
get children ( ) {
139
- return _ . filter ( this . props . children , child => ! ! child ) ;
129
+ return _ . filter ( this . props . children , ( child ) => ! ! child ) ;
140
130
}
141
131
142
132
get itemsCount ( ) {
@@ -148,6 +138,11 @@ class TabBar extends PureComponent {
148
138
}
149
139
}
150
140
141
+ get centerOffset ( ) {
142
+ const { centerSelected} = this . props ;
143
+ return centerSelected ? Constants . screenWidth / 2 : 0 ;
144
+ }
145
+
151
146
registerTabItems ( ) {
152
147
const { registerTabItems} = this . context ;
153
148
const { items} = this . props ;
@@ -176,41 +171,44 @@ class TabBar extends PureComponent {
176
171
177
172
// TODO: move this logic into a ScrollPresenter or something
178
173
focusSelected = ( [ index ] ) => {
179
- const { itemsOffsets, itemsWidths} = this . state ;
180
- const itemOffset = itemsOffsets [ index ] ;
181
- const itemWidth = itemsWidths [ index ] ;
174
+ const { centerSelected} = this . props ;
175
+ const itemOffset = this . _itemsOffsets [ index ] ;
176
+ const itemWidth = this . _itemsWidths [ index ] ;
177
+
182
178
if ( itemOffset && itemWidth ) {
183
- if ( itemOffset < this . tabBarScrollOffset ) {
179
+ if ( centerSelected ) {
180
+ this . tabBar . current . scrollTo ( { x : itemOffset - this . centerOffset + itemWidth / 2 } ) ;
181
+ } else if ( itemOffset < this . tabBarScrollOffset ) {
184
182
this . tabBar . current . scrollTo ( { x : itemOffset - itemWidth } ) ;
185
183
} else if ( itemOffset + itemWidth > this . tabBarScrollOffset + this . containerWidth ) {
186
184
const offsetChange = Math . max ( 0 , itemOffset - ( this . tabBarScrollOffset + this . containerWidth ) ) ;
187
185
this . tabBar . current . scrollTo ( { x : this . tabBarScrollOffset + offsetChange + itemWidth } ) ;
188
186
}
189
187
}
190
- }
188
+ } ;
191
189
192
- onItemLayout = ( itemWidth , itemIndex ) => {
193
- this . _itemsWidths [ itemIndex ] = itemWidth ;
190
+ onItemLayout = ( { width, x} , itemIndex ) => {
191
+ this . _itemsWidths [ itemIndex ] = width ;
192
+ this . _itemsOffsets [ itemIndex ] = x ;
194
193
if ( ! _ . includes ( this . _itemsWidths , null ) ) {
195
194
const { selectedIndex} = this . context ;
196
- const itemsOffsets = _ . map ( this . _itemsWidths ,
197
- ( w , index ) => INDICATOR_INSET + _ . sum ( _ . take ( this . _itemsWidths , index ) ) ) ;
198
- const itemsWidths = _ . map ( this . _itemsWidths , width => width - INDICATOR_INSET * 2 ) ;
195
+ const itemsOffsets = _ . map ( this . _itemsOffsets , offset => offset + INDICATOR_INSET ) ;
196
+ const itemsWidths = _ . map ( this . _itemsWidths , ( width ) => width - INDICATOR_INSET * 2 ) ;
199
197
200
198
this . setState ( { itemsWidths, itemsOffsets} ) ;
201
199
const selectedItemOffset = itemsOffsets [ selectedIndex ] - INDICATOR_INSET ;
202
-
203
- if ( selectedItemOffset + this . _itemsWidths [ selectedIndex ] > Constants . screenWidth ) {
200
+
201
+ if ( selectedItemOffset + this . _itemsWidths [ selectedIndex ] > Constants . screenWidth ) {
204
202
this . tabBar . current . scrollTo ( { x : selectedItemOffset , animated : true } ) ;
205
203
}
206
204
}
207
205
} ;
208
206
209
207
onScroll = ( { nativeEvent : { contentOffset} } ) => {
210
208
this . tabBarScrollOffset = contentOffset . x ;
211
- }
209
+ } ;
212
210
213
- onContentSizeChange = width => {
211
+ onContentSizeChange = ( width ) => {
214
212
if ( width > this . containerWidth ) {
215
213
this . setState ( { scrollEnabled : true } ) ;
216
214
}
@@ -288,29 +286,19 @@ class TabBar extends PureComponent {
288
286
}
289
287
290
288
renderCodeBlock = ( ) => {
291
- const { carouselOffset , asCarousel , currentPage } = this . context ;
289
+ const { currentPage , targetPage } = this . context ;
292
290
const { itemsWidths, itemsOffsets} = this . state ;
293
291
const nodes = [ ] ;
294
292
295
- if ( asCarousel ) {
296
- nodes . push ( set ( this . _indicatorOffset ,
297
- interpolate ( carouselOffset , {
298
- inputRange : itemsOffsets . map ( ( value , index ) => index * Constants . screenWidth ) ,
299
- outputRange : itemsOffsets ,
300
- extrapolate : Extrapolate . CLAMP
301
- } ) ) ,
302
- set ( this . _indicatorWidth ,
303
- interpolate ( carouselOffset , {
304
- inputRange : itemsWidths . map ( ( value , index ) => index * Constants . screenWidth ) ,
305
- outputRange : itemsWidths ,
306
- extrapolate : Extrapolate . CLAMP
307
- } ) ) ) ;
308
- } else {
309
- nodes . push ( set ( this . _indicatorOffset , runIndicatorTimer ( new Clock ( ) , currentPage , itemsOffsets ) ) ,
310
- set ( this . _indicatorWidth , runIndicatorTimer ( new Clock ( ) , currentPage , itemsWidths ) ) ) ;
311
- }
293
+ nodes . push ( set ( this . _indicatorOffset ,
294
+ interpolate ( currentPage , {
295
+ inputRange : itemsOffsets . map ( ( v , i ) => i ) ,
296
+ outputRange : itemsOffsets
297
+ } ) ) ) ;
298
+ nodes . push ( set ( this . _indicatorWidth ,
299
+ interpolate ( currentPage , { inputRange : itemsWidths . map ( ( v , i ) => i ) , outputRange : itemsWidths } ) ) ) ;
312
300
313
- nodes . push ( Reanimated . onChange ( currentPage , Reanimated . call ( [ currentPage ] , this . focusSelected ) ) ) ;
301
+ nodes . push ( Reanimated . onChange ( targetPage , Reanimated . call ( [ targetPage ] , this . focusSelected ) ) ) ;
314
302
315
303
return block ( nodes ) ;
316
304
} ;
@@ -334,7 +322,9 @@ class TabBar extends PureComponent {
334
322
scrollEventThrottle = { 100 }
335
323
testID = { testID }
336
324
>
337
- < View style = { [ styles . tabBar , height && { height} ] } > { this . renderTabBarItems ( ) } </ View >
325
+ < View style = { [ styles . tabBar , height && { height} , { paddingHorizontal : this . centerOffset } ] } >
326
+ { this . renderTabBarItems ( ) }
327
+ </ View >
338
328
{ this . renderSelectedIndicator ( ) }
339
329
</ ScrollView >
340
330
{ _ . size ( itemsWidths ) > 1 && < Code > { this . renderCodeBlock } </ Code > }
@@ -389,38 +379,4 @@ const styles = StyleSheet.create({
389
379
}
390
380
} ) ;
391
381
392
- function runIndicatorTimer ( clock , currentPage , values ) {
393
- const state = {
394
- finished : new Value ( 0 ) ,
395
- position : new Value ( 0 ) ,
396
- time : new Value ( 0 ) ,
397
- frameTime : new Value ( 0 )
398
- } ;
399
-
400
- const config = {
401
- duration : 200 ,
402
- toValue : new Value ( 100 ) ,
403
- easing : Easing . inOut ( Easing . ease )
404
- } ;
405
-
406
- return block ( [
407
- ..._ . map ( values , ( value , index ) => {
408
- return cond ( and ( eq ( currentPage , index ) , neq ( config . toValue , index ) ) , [
409
- set ( state . finished , 0 ) ,
410
- set ( state . time , 0 ) ,
411
- set ( state . frameTime , 0 ) ,
412
- set ( config . toValue , index ) ,
413
- startClock ( clock )
414
- ] ) ;
415
- } ) ,
416
- timing ( clock , state , config ) ,
417
- cond ( state . finished , stopClock ( clock ) ) ,
418
- interpolate ( state . position , {
419
- inputRange : _ . times ( values . length ) ,
420
- outputRange : values ,
421
- extrapolate : Extrapolate . CLAMP
422
- } )
423
- ] ) ;
424
- }
425
-
426
382
export default asBaseComponent ( forwardRef ( TabBar ) ) ;
0 commit comments