1
1
var React = require ( 'react-native' )
2
2
var {
3
3
PropTypes,
4
- StyleSheet,
5
- View,
6
- Platform,
7
4
} = React
8
5
var ListView = require ( './ListView' )
9
6
var createElementFrom = require ( './createElementFrom' )
10
7
var RefreshingIndicator = require ( './RefreshingIndicator' )
8
+ var RefreshingOnPull = require ( './RefreshingOnPull' )
9
+ var RefreshingOnHold = require ( './RefreshingOnHold' )
11
10
12
11
const SCROLL_EVENT_THROTTLE = 32
13
12
const MIN_PULLDOWN_DISTANCE = 40
14
- const REFRESHING_INDICATOR_HEIGHT = 60
15
- const LISTVIEW_REF = 'listview'
16
13
17
- /*
18
- * state transitions:
19
- * {isRefreshing: false}
20
- * v - show loading spinner
21
- * {isRefreshing: true, waitingForRelease: true}
22
- * v - reset scroll position, offset scroll top
23
- * {isRefreshing: true, waitingForRelease: false}
24
- * v - hide loading spinner
25
- * {isRefreshing: false}
26
- */
14
+ const LISTVIEW_REF = 'listview'
27
15
28
16
var ControlledRefreshableListView = React . createClass ( {
29
17
propTypes : {
30
18
onRefresh : PropTypes . func . isRequired ,
31
19
isRefreshing : PropTypes . bool . isRequired ,
32
- refreshPrompt : PropTypes . oneOfType ( [ PropTypes . string , PropTypes . element ] ) ,
33
20
refreshDescription : PropTypes . oneOfType ( [ PropTypes . string , PropTypes . element ] ) ,
34
- refreshingIndicatorComponent : PropTypes . oneOfType ( [ PropTypes . func , PropTypes . element ] ) ,
21
+ refreshingIndictatorComponent : PropTypes . oneOfType ( [ PropTypes . func , PropTypes . element ] ) ,
22
+ refreshingOnPullComponent : PropTypes . oneOfType ( [ PropTypes . func , PropTypes . element ] ) ,
23
+ refreshingOnHoldComponent : PropTypes . oneOfType ( [ PropTypes . func , PropTypes . element ] ) ,
35
24
minPulldownDistance : PropTypes . number ,
36
25
ignoreInertialScroll : PropTypes . bool ,
37
26
scrollEventThrottle : PropTypes . number ,
38
27
onScroll : PropTypes . func ,
28
+ renderHeader : PropTypes . func ,
29
+ renderHeaderWrapper : PropTypes . func ,
39
30
onResponderGrant : PropTypes . func ,
40
31
onResponderRelease : PropTypes . func ,
41
- renderHeaderWrapper : ( props , propName , componentName ) => {
42
- if ( props [ propName ] ) {
43
- return new Error ( "The 'renderHeaderWrapper' prop is no longer used" ) ;
44
- }
45
- } ,
46
- refreshingIndictatorComponent : ( props , propName , componentName ) => {
47
- if ( props [ propName ] ) {
48
- return new Error ( "The 'refreshingIndictatorComponent' prop has been renamed to 'refreshingIndicatorComponent'" ) ;
49
- }
50
- } ,
51
- } ,
52
- getDefaultProps ( ) {
53
- return {
54
- minPulldownDistance : MIN_PULLDOWN_DISTANCE ,
55
- scrollEventThrottle : SCROLL_EVENT_THROTTLE ,
56
- ignoreInertialScroll : true ,
57
- refreshingIndicatorComponent : RefreshingIndicator ,
58
- }
59
32
} ,
60
33
getInitialState ( ) {
61
34
return {
@@ -64,152 +37,137 @@ var ControlledRefreshableListView = React.createClass({
64
37
} ,
65
38
componentWillReceiveProps ( nextProps ) {
66
39
if ( ! this . props . isRefreshing && nextProps . isRefreshing && this . isTouching ) {
67
-
68
40
this . waitingForRelease = true
69
41
this . setState ( { waitingForRelease : true } )
70
42
}
71
43
} ,
72
- componentWillUpdate ( nextProps , nextState ) {
73
- if ( Platform . OS === 'ios' ) {
74
- if (
75
- this . isReleaseUpdate ( this . props , this . state , nextProps , nextState )
76
- ) {
77
- this . getScrollResponder ( ) . scrollWithoutAnimationTo (
78
- - ( this . lastContentInsetTop + REFRESHING_INDICATOR_HEIGHT ) ,
79
- this . lastContentOffsetX
80
- )
81
- }
82
- }
83
- } ,
84
- componentDidUpdate ( prevProps , prevState ) {
85
- if ( Platform . OS === 'ios' ) {
86
- if (
87
- this . isReleaseUpdate ( prevProps , prevState , this . props , this . state )
88
- ) {
89
- this . getScrollResponder ( ) . scrollWithoutAnimationTo (
90
- - ( this . lastContentInsetTop ) ,
91
- this . lastContentOffsetX
92
- )
93
- }
44
+ getDefaultProps ( ) {
45
+ return {
46
+ minPulldownDistance : MIN_PULLDOWN_DISTANCE ,
47
+ scrollEventThrottle : SCROLL_EVENT_THROTTLE ,
48
+ ignoreInertialScroll : true ,
49
+ refreshingOnPullComponent : RefreshingOnPull ,
50
+ refreshingOnHoldComponent : RefreshingOnHold ,
51
+ refreshingIndictatorComponent : RefreshingIndicator ,
94
52
}
95
53
} ,
96
54
handleScroll ( e ) {
97
- var scrollY = e . nativeEvent . contentInset . top + e . nativeEvent . contentOffset . y
98
- this . lastScrollY = scrollY
99
- this . lastContentInsetTop = e . nativeEvent . contentInset . top
100
- this . lastContentOffsetX = e . nativeEvent . contentOffset . x
101
-
55
+ var scrollY = e . nativeEvent . contentInset . top + e . nativeEvent . contentOffset . y ;
56
+ // slowing show out the icons
102
57
if ( this . isTouching || ( ! this . isTouching && ! this . props . ignoreInertialScroll ) ) {
103
58
if ( scrollY < - this . props . minPulldownDistance ) {
104
- if ( ! this . props . isRefreshing ) {
105
- if ( this . props . onRefresh ) {
106
- this . props . onRefresh ( )
107
- }
59
+ if ( ! this . isWaitingForRelease ( ) ) {
60
+ this . waitingForRelease = true ;
61
+ this . setState ( { waitingForRelease : true } ) ;
62
+ this . props . handleRefreshOnHold ( ) ;
108
63
}
64
+ } else {
65
+ // holding but not exceed mini distance
66
+ if ( this . isWaitingForRelease ( ) ) {
67
+ this . waitingForRelease = false ;
68
+ this . setState ( { waitingForRelease : false } ) ;
69
+ }
70
+ this . props . destroyRefreshOnHold ( ) ;
109
71
}
110
72
}
111
73
112
74
this . props . onScroll && this . props . onScroll ( e )
113
75
} ,
114
76
handleResponderGrant ( ) {
115
- this . isTouching = true
116
- if ( this . props . onResponderGrant ) {
117
- this . props . onResponderGrant . apply ( null , arguments )
118
- }
77
+ this . isTouching = true ;
119
78
} ,
120
79
handleResponderRelease ( ) {
121
- this . isTouching = false
122
- if ( this . isWaitingForRelease ( ) ) {
123
- this . waitingForRelease = false
124
- this . setState ( { waitingForRelease : false } )
125
- }
80
+ this . isTouching = false ;
126
81
if ( this . props . onResponderRelease ) {
127
- this . props . onResponderRelease . apply ( null , arguments )
82
+ this . props . onResponderRelease . apply ( null , arguments ) ;
128
83
}
129
- } ,
130
- getContentContainerStyle ( ) {
131
- if ( ! this . props . isRefreshing || this . isWaitingForRelease ( ) ) return null
132
-
133
- return { marginTop : REFRESHING_INDICATOR_HEIGHT }
84
+ if ( this . isWaitingForRelease ( ) ) {
85
+ this . waitingForRelease = false ;
86
+ this . setState ( { waitingForRelease : false } ) ;
87
+ if ( ! this . props . isRefreshing ) {
88
+ if ( this . props . onRefresh ) {
89
+ this . props . onRefresh ( ) ;
90
+ }
91
+ }
92
+ }
93
+ this . props . destroyRefreshOnHold ( ) ;
134
94
} ,
135
95
getScrollResponder ( ) {
136
96
return this . refs [ LISTVIEW_REF ] . getScrollResponder ( )
137
97
} ,
138
98
setNativeProps ( props ) {
139
99
this . refs [ LISTVIEW_REF ] . setNativeProps ( props )
140
100
} ,
101
+ renderHeader ( ) {
102
+ var description = this . props . refreshDescription
103
+
104
+ var refreshingIndictator ;
105
+ var refreshingOnHold ;
106
+ var refreshingOnPull ;
107
+ if ( this . isTouching && this . isWaitingForRelease ( ) ) {
108
+ refreshingOnPull = null ;
109
+ refreshingOnHold = createElementFrom ( this . props . refreshingOnHoldComponent ) ;
110
+ } else if ( this . isTouching && ! this . isWaitingForRelease ( ) ) {
111
+ refreshingOnHold = null ;
112
+ refreshingOnPull = createElementFrom ( this . props . refreshingOnPullComponent ) ;
113
+ } else {
114
+ if ( this . props . isRefreshing ) {
115
+ refreshingIndictator = createElementFrom ( this . props . refreshingIndictatorComponent , { description} ) ;
116
+ } else {
117
+ refreshingIndictator = null ;
118
+ }
119
+ refreshingOnPull = null ;
120
+ refreshingOnHold = null ;
121
+ }
122
+
123
+ if ( this . props . renderHeaderWrapper ) {
124
+ if ( this . isTouching && this . isWaitingForRelease ( ) ) {
125
+ return this . props . renderHeaderWrapper ( refreshingOnHold )
126
+ } else if ( this . isTouching && ! this . isWaitingForRelease ( ) ) {
127
+ return this . props . renderHeaderWrapper ( refreshingOnPull )
128
+ } else {
129
+ return this . props . renderHeaderWrapper ( refreshingIndictator )
130
+ }
131
+ } else if ( this . props . renderHeader ) {
132
+ console . warn ( 'renderHeader is deprecated. Use renderHeaderWrapper instead.' )
133
+ if ( this . isTouching && this . isWaitingForRelease ( ) ) {
134
+ return this . props . renderHeaderWrapper ( refreshingOnHold )
135
+ } else if ( this . isTouching && ! this . isWaitingForRelease ( ) ) {
136
+ return this . props . renderHeaderWrapper ( refreshingOnPull )
137
+ } else {
138
+ return this . props . renderHeaderWrapper ( refreshingIndictator )
139
+ }
140
+ } else {
141
+ if ( this . isTouching && this . isWaitingForRelease ( ) ) {
142
+ return refreshingOnHold
143
+ } else if ( this . isTouching && ! this . isWaitingForRelease ( ) ) {
144
+ return refreshingOnPull
145
+ } else {
146
+ return refreshingIndictator
147
+ }
148
+ }
149
+ } ,
141
150
isWaitingForRelease ( ) {
142
151
return this . waitingForRelease || this . props . waitingForRelease
143
152
} ,
144
- isReleaseUpdate ( oldProps , oldState , newProps , newState ) {
145
- return (
146
- ( ! oldProps . isRefreshing && newProps . isRefreshing && ! this . waitingForRelease ) ||
147
- ( oldProps . isRefreshing && oldState . waitingForRelease && ! newState . waitingForRelease )
148
- )
149
- } ,
150
- renderRefreshingIndicator ( ) {
151
- var { isRefreshing, refreshDescription, refreshPrompt} = this . props
152
- var refreshingIndicatorProps = {
153
- isRefreshing,
154
- description : refreshDescription ,
155
- prompt : refreshPrompt ,
156
- pulldownDistance : - ( this . lastScrollY || 0 )
157
- }
158
- return createElementFrom ( this . props . refreshingIndicatorComponent , refreshingIndicatorProps )
159
- } ,
160
153
render ( ) {
161
154
return (
162
- < View style = { [ stylesheet . container ] } >
163
- < View style = { [ stylesheet . fillParent ] } >
164
- { this . renderRefreshingIndicator ( ) }
165
- </ View >
166
- < View style = { [ stylesheet . fillParent ] } >
167
- < ListView
168
- { ...this . props }
169
- ref = { LISTVIEW_REF }
170
- contentContainerStyle = { this . getContentContainerStyle ( ) }
171
- onScroll = { this . handleScroll }
172
- scrollEventThrottle = { this . props . scrollEventThrottle }
173
- onResponderGrant = { this . handleResponderGrant }
174
- onResponderRelease = { this . handleResponderRelease }
175
- />
176
- </ View >
177
- </ View >
155
+ < ListView
156
+ { ...this . props }
157
+ ref = { LISTVIEW_REF }
158
+ onScroll = { this . handleScroll }
159
+ renderHeader = { this . renderHeader }
160
+ scrollEventThrottle = { this . props . scrollEventThrottle }
161
+ onStartShouldSetResponder = { true }
162
+ onResponderGrant = { this . handleResponderGrant }
163
+ onResponderRelease = { this . handleResponderRelease }
164
+ />
178
165
)
179
166
} ,
180
167
} )
181
168
182
- var stylesheet = StyleSheet . create ( {
183
- container : {
184
- // flex: 1,
185
- } ,
186
- fillParent : {
187
- backgroundColor : 'transparent' ,
188
- position : 'absolute' ,
189
- top : 0 ,
190
- left : 0 ,
191
- right : 0 ,
192
- bottom : 0 ,
193
- } ,
194
- // offsetParent: {
195
- // position: 'relative',
196
- // },
197
- // positionTopLeft: {
198
- // position: 'absolute',
199
- // top: 0,
200
- // left: 0,
201
- // },
202
- // fill: {
203
- // flex: 1
204
- // },
205
- // center: {
206
- // flex: 1,
207
- // justifyContent: 'space-around',
208
- // alignItems: 'center',
209
- // },
210
- } )
211
-
212
169
ControlledRefreshableListView . DataSource = ListView . DataSource
213
170
ControlledRefreshableListView . RefreshingIndicator = RefreshingIndicator
171
+ ControlledRefreshableListView . RefreshingOnHold = RefreshingOnHold
214
172
215
173
module . exports = ControlledRefreshableListView
0 commit comments