Skip to content

Commit 55721bc

Browse files
committed
version2 fixed bug & add onpull wrap, onhold wrap features
1 parent b5535b9 commit 55721bc

File tree

4 files changed

+261
-157
lines changed

4 files changed

+261
-157
lines changed

lib/ControlledRefreshableListView.js

Lines changed: 102 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,34 @@
11
var React = require('react-native')
22
var {
33
PropTypes,
4-
StyleSheet,
5-
View,
6-
Platform,
74
} = React
85
var ListView = require('./ListView')
96
var createElementFrom = require('./createElementFrom')
107
var RefreshingIndicator = require('./RefreshingIndicator')
8+
var RefreshingOnPull = require('./RefreshingOnPull')
9+
var RefreshingOnHold = require('./RefreshingOnHold')
1110

1211
const SCROLL_EVENT_THROTTLE = 32
1312
const MIN_PULLDOWN_DISTANCE = 40
14-
const REFRESHING_INDICATOR_HEIGHT = 60
15-
const LISTVIEW_REF = 'listview'
1613

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'
2715

2816
var ControlledRefreshableListView = React.createClass({
2917
propTypes: {
3018
onRefresh: PropTypes.func.isRequired,
3119
isRefreshing: PropTypes.bool.isRequired,
32-
refreshPrompt: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
3320
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]),
3524
minPulldownDistance: PropTypes.number,
3625
ignoreInertialScroll: PropTypes.bool,
3726
scrollEventThrottle: PropTypes.number,
3827
onScroll: PropTypes.func,
28+
renderHeader: PropTypes.func,
29+
renderHeaderWrapper: PropTypes.func,
3930
onResponderGrant: PropTypes.func,
4031
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-
}
5932
},
6033
getInitialState() {
6134
return {
@@ -64,152 +37,137 @@ var ControlledRefreshableListView = React.createClass({
6437
},
6538
componentWillReceiveProps(nextProps) {
6639
if (!this.props.isRefreshing && nextProps.isRefreshing && this.isTouching) {
67-
6840
this.waitingForRelease = true
6941
this.setState({waitingForRelease: true})
7042
}
7143
},
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,
9452
}
9553
},
9654
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
10257
if (this.isTouching || (!this.isTouching && !this.props.ignoreInertialScroll)) {
10358
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();
10863
}
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();
10971
}
11072
}
11173

11274
this.props.onScroll && this.props.onScroll(e)
11375
},
11476
handleResponderGrant() {
115-
this.isTouching = true
116-
if (this.props.onResponderGrant) {
117-
this.props.onResponderGrant.apply(null, arguments)
118-
}
77+
this.isTouching = true;
11978
},
12079
handleResponderRelease() {
121-
this.isTouching = false
122-
if (this.isWaitingForRelease()) {
123-
this.waitingForRelease = false
124-
this.setState({waitingForRelease: false})
125-
}
80+
this.isTouching = false;
12681
if (this.props.onResponderRelease) {
127-
this.props.onResponderRelease.apply(null, arguments)
82+
this.props.onResponderRelease.apply(null, arguments);
12883
}
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();
13494
},
13595
getScrollResponder() {
13696
return this.refs[LISTVIEW_REF].getScrollResponder()
13797
},
13898
setNativeProps(props) {
13999
this.refs[LISTVIEW_REF].setNativeProps(props)
140100
},
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+
},
141150
isWaitingForRelease() {
142151
return this.waitingForRelease || this.props.waitingForRelease
143152
},
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-
},
160153
render() {
161154
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+
/>
178165
)
179166
},
180167
})
181168

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-
212169
ControlledRefreshableListView.DataSource = ListView.DataSource
213170
ControlledRefreshableListView.RefreshingIndicator = RefreshingIndicator
171+
ControlledRefreshableListView.RefreshingOnHold = RefreshingOnHold
214172

215173
module.exports = ControlledRefreshableListView

0 commit comments

Comments
 (0)