1
1
import _ from 'lodash' ;
2
- import PropTypes from 'prop-types' ;
3
- import React , { useCallback } from 'react' ;
4
- import { Animated , ScrollView , FlatList } from 'react-native' ;
2
+ import React , { Component , useCallback } from 'react' ;
3
+ import {
4
+ Animated ,
5
+ ScrollView ,
6
+ FlatList ,
7
+ FlatListProps ,
8
+ ImageSourcePropType ,
9
+ NativeSyntheticEvent ,
10
+ NativeScrollEvent ,
11
+ LayoutChangeEvent
12
+ } from 'react-native' ;
5
13
import { Constants } from '../../helpers' ;
6
14
import { Colors } from '../../style' ;
7
- import { BaseComponent , forwardRef } from '../../commons' ;
15
+ import { asBaseComponent , forwardRef , ForwardRefInjectedProps } from '../../commons/new ' ;
8
16
import View from '../view' ;
9
17
import Image from '../image' ;
10
18
11
- const GRADIENT_WIDTH = 76 ;
12
- const defaultImage = ( ) => require ( './assets/gradientOverlay.png' ) ;
13
-
14
- /**
15
- * @description : Scrollable container with animated gradient overlay for horizontal scroll
16
- * @extends : ScrollView / FlatList
17
- */
18
-
19
- class ScrollBar extends BaseComponent {
20
- static displayName = 'ScrollBar' ;
21
19
22
- static propTypes = {
23
- ...ScrollView . propTypes ,
24
- ...FlatList . propTypes ,
25
- /**
20
+ export interface ScrollBarProps extends FlatListProps < any > {
21
+ /**
26
22
* Whether to use a FlatList. NOTE: you must pass 'data' and 'renderItem' props as well
27
23
*/
28
- useList : PropTypes . bool ,
24
+ useList ?: boolean ,
29
25
/**
30
26
* The element to use as a container, instead of a View
31
27
*/
32
- containerView : PropTypes . oneOfType ( [ PropTypes . element , PropTypes . elementType ] ) ,
28
+ containerView ?: JSX . Element ,
33
29
/**
34
30
* The props to pass the container
35
31
*/
36
- containerProps : PropTypes . object ,
32
+ containerProps ?: object ,
37
33
/**
38
34
* The component's height
39
35
*/
40
- height : PropTypes . number ,
36
+ height ?: number ,
41
37
/**
42
38
* The gradient's height, defaults to the component's height
43
39
*/
44
- gradientHeight : PropTypes . number ,
40
+ gradientHeight ?: number ,
45
41
/**
46
42
* The gradient's width
47
43
*/
48
- gradientWidth : PropTypes . number ,
44
+ gradientWidth ?: number ,
49
45
/**
50
46
* The gradient's margins for the edge
51
47
*/
52
- gradientMargins : PropTypes . number ,
48
+ gradientMargins ?: number ,
53
49
/**
54
50
* The gradient's tint color
55
51
*/
56
- gradientColor : PropTypes . string ,
52
+ gradientColor ?: string ,
57
53
/**
58
54
* The gradient's image, instead of the default image.
59
55
* NOTE: pass an image for the right-hand side and it will be flipped to match the left-hand side
60
56
*/
61
- gradientImage : PropTypes . oneOfType ( [ PropTypes . string , PropTypes . number ] ) ,
57
+ gradientImage ?: ImageSourcePropType ,
62
58
/**
63
59
* The index to currently focus on
64
60
*/
65
- focusIndex : PropTypes . number
66
- } ;
61
+ focusIndex ?: number
62
+ } ;
63
+ type Props = ScrollBarProps & ForwardRefInjectedProps ;
64
+
65
+ export type State = {
66
+ gradientOpacity : Animated . Value ,
67
+ gradientOpacityLeft : Animated . Value
68
+ } ;
69
+
70
+
71
+ const GRADIENT_WIDTH = 76 ;
72
+ const defaultImage = ( ) => require ( './assets/gradientOverlay.png' ) ;
73
+
74
+ /**
75
+ * @description : Scrollable container with animated gradient overlay for horizontal scroll
76
+ * @extends : ScrollView / FlatList
77
+ */
78
+
79
+ class ScrollBar extends Component < Props , State > {
80
+ static displayName = 'ScrollBar' ;
67
81
68
82
static defaultProps = {
69
83
gradientWidth : GRADIENT_WIDTH ,
@@ -72,27 +86,31 @@ class ScrollBar extends BaseComponent {
72
86
focusIndex : 0
73
87
} ;
74
88
75
- constructor ( props ) {
89
+ static Item : typeof Item ;
90
+
91
+ constructor ( props : Props ) {
76
92
super ( props ) ;
77
93
78
94
this . state = {
79
95
gradientOpacity : new Animated . Value ( 0 ) ,
80
96
gradientOpacityLeft : new Animated . Value ( 0 )
81
97
} ;
82
-
83
- this . scrollContentWidth = undefined ;
84
- this . itemsLayouts = { } ;
85
- this . contentOffset = 0 ;
86
98
}
87
99
88
- componentDidUpdate ( prevProps , prevState ) {
100
+ private scrollbar : any = undefined ;
101
+ private itemsLayouts : any = { } ;
102
+ private contentOffset : number = 0 ;
103
+ private scrollContentWidth : number = 0 ;
104
+ private containerWidth : number = 0 ;
105
+
106
+ componentDidUpdate ( prevProps : Props ) {
89
107
const { focusIndex} = this . props ;
90
- if ( focusIndex && prevProps . focusIndex !== focusIndex ) {
108
+ if ( prevProps . focusIndex !== focusIndex ) {
91
109
this . focusIndex ( focusIndex ) ;
92
110
}
93
111
}
94
112
95
- focusIndex = index => {
113
+ focusIndex = ( index : number = 0 ) => {
96
114
const focusedItemLayout = this . itemsLayouts [ index ] ;
97
115
if ( focusedItemLayout ) {
98
116
const { x, width} = focusedItemLayout ;
@@ -105,7 +123,7 @@ class ScrollBar extends BaseComponent {
105
123
}
106
124
} ;
107
125
108
- animateGradientOpacity = ( offsetX , contentWidth , containerWidth ) => {
126
+ animateGradientOpacity = ( offsetX : number , contentWidth : number , containerWidth : number ) => {
109
127
const overflow = contentWidth - containerWidth ;
110
128
const newValue = offsetX > 0 && offsetX >= overflow - 1 ? 0 : 1 ;
111
129
const newValueLeft = offsetX > 0 ? 1 : 0 ;
@@ -124,7 +142,7 @@ class ScrollBar extends BaseComponent {
124
142
] ) . start ( ) ;
125
143
} ;
126
144
127
- onScroll = event => {
145
+ onScroll = ( event : NativeSyntheticEvent < NativeScrollEvent > ) => {
128
146
const { layoutMeasurement, contentOffset, contentSize} = event . nativeEvent ;
129
147
this . contentOffset = contentOffset . x ;
130
148
const offsetX = contentOffset . x ;
@@ -136,7 +154,7 @@ class ScrollBar extends BaseComponent {
136
154
_ . invoke ( this . props , 'onScroll' , event ) ;
137
155
} ;
138
156
139
- onContentSizeChange = ( contentWidth , contentHeight ) => {
157
+ onContentSizeChange = ( contentWidth : number , contentHeight : number ) => {
140
158
if ( this . scrollContentWidth !== contentWidth ) {
141
159
this . scrollContentWidth = contentWidth ;
142
160
@@ -149,34 +167,37 @@ class ScrollBar extends BaseComponent {
149
167
}
150
168
} ;
151
169
152
- onLayout = ( { nativeEvent } ) => {
153
- this . containerWidth = nativeEvent . layout . width ;
170
+ onLayout = ( event : LayoutChangeEvent ) => {
171
+ this . containerWidth = event . nativeEvent . layout . width ;
154
172
155
173
// 1 - for race condition, in case onContentSizeChange() is called before
156
174
// 0 - for containerWidth change, when onContentSizeChange() is called first
157
175
this . setState ( { gradientOpacity : new Animated . Value ( this . scrollContentWidth > this . containerWidth ? 1 : 0 ) } ) ;
158
176
} ;
159
177
160
- onItemLayout = ( { layout, index} ) => {
178
+ onItemLayout = ( { layout, index} : any ) => {
161
179
this . itemsLayouts [ index ] = layout ;
162
- if ( _ . keys ( this . itemsLayouts ) . length === this . props . children . length ) {
163
- this . focusIndex ( this . props . focusIndex ) ;
180
+
181
+ const { children, focusIndex} = this . props ;
182
+ if ( children && _ . keys ( this . itemsLayouts ) . length === _ . keys ( children ) . length ) {
183
+ this . focusIndex ( focusIndex ) ;
164
184
}
165
185
} ;
166
186
167
187
renderScrollable ( ) {
168
188
const { useList, forwardedRef, children} = this . props ;
169
- const Component = useList ? FlatList : ScrollView ;
189
+ const Component : any = useList ? FlatList : ScrollView ;
170
190
171
191
return (
172
192
< Component
173
193
scrollEventThrottle = { 100 }
174
- { ...this . getThemeProps ( ) }
175
- ref = { r => {
194
+ { ...this . props }
195
+ ref = { ( r : any ) => {
176
196
this . scrollbar = r ;
177
197
if ( _ . isFunction ( forwardedRef ) ) {
178
198
forwardedRef ( r ) ;
179
199
} else if ( _ . isObject ( forwardedRef ) ) {
200
+ //@ts -ignore
180
201
forwardedRef . current = r ;
181
202
}
182
203
} }
@@ -197,9 +218,9 @@ class ScrollBar extends BaseComponent {
197
218
) ;
198
219
}
199
220
200
- renderGradient ( left ) {
221
+ renderGradient ( left : boolean ) {
201
222
const { gradientOpacity, gradientOpacityLeft} = this . state ;
202
- const { gradientWidth, gradientHeight, gradientMargins, height, gradientColor, gradientImage} = this . getThemeProps ( ) ;
223
+ const { gradientWidth, gradientHeight, gradientMargins, height, gradientColor, gradientImage} = this . props ;
203
224
const imageTransform = Constants . isRTL ? ( left ? undefined : [ { scaleX : - 1 } ] ) : left ? [ { scaleX : - 1 } ] : undefined ;
204
225
const heightToUse = gradientHeight || height || '100%' ;
205
226
return (
@@ -229,23 +250,23 @@ class ScrollBar extends BaseComponent {
229
250
}
230
251
231
252
render ( ) {
232
- const { containerView, containerProps} = this . getThemeProps ( ) ;
233
- const Container = containerView || View ;
253
+ const { containerView, containerProps} = this . props ;
254
+ const Container : any = containerView || View ;
234
255
235
256
return (
236
257
< Container row { ...containerProps } onLayout = { this . onLayout } >
237
258
{ this . renderScrollable ( ) }
238
- { this . renderGradient ( ) }
259
+ { this . renderGradient ( false ) }
239
260
{ this . renderGradient ( true ) }
240
261
</ Container >
241
262
) ;
242
263
}
243
264
}
244
265
245
- const Item = ( { children, index, onLayout} ) => {
266
+ const Item = ( { children, index, onLayout} : any ) => {
246
267
const onItemLayout = useCallback ( ( { nativeEvent : { layout} } ) => {
247
268
onLayout ( { layout, index} ) ;
248
- } ) ;
269
+ } , [ children ] ) ;
249
270
250
271
return (
251
272
< View flexG onLayout = { onItemLayout } >
@@ -256,4 +277,5 @@ const Item = ({children, index, onLayout}) => {
256
277
257
278
Item . displayName = 'IGNORE' ;
258
279
ScrollBar . Item = Item ;
259
- export default forwardRef ( ScrollBar ) ;
280
+ export default asBaseComponent < ScrollBarProps , typeof ScrollBar > ( forwardRef ( ScrollBar ) ) ;
281
+
0 commit comments