Skip to content

Commit 80a7e3c

Browse files
committed
[Touchable] Add custom delay props and alter longPress implementation
1 parent 62fef10 commit 80a7e3c

File tree

6 files changed

+255
-25
lines changed

6 files changed

+255
-25
lines changed

Examples/UIExplorer/TouchableExample.js

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ var {
2626
View,
2727
} = React;
2828

29-
exports.title = '<Touchable*> and onPress';
29+
exports.title = '<Touchable*>, onPress, and delayPress';
3030
exports.examples = [
3131
{
3232
title: '<TouchableHighlight>',
@@ -75,6 +75,14 @@ exports.examples = [
7575
render: function(): ReactElement {
7676
return <TouchableFeedbackEvents />;
7777
},
78+
}, {
79+
title: 'Touchable delay for events',
80+
description: '<Touchable*> components also accept delayPress, ' +
81+
'delayPressIn, delayPressOut, and delayLongPress as props. These props ' +
82+
'impact the timing of feedback events.',
83+
render: function(): ReactElement {
84+
return <TouchableDelayEvents />;
85+
},
7886
}];
7987

8088
var TextOnPressBox = React.createClass({
@@ -148,6 +156,45 @@ var TouchableFeedbackEvents = React.createClass({
148156
},
149157
});
150158

159+
var TouchableDelayEvents = React.createClass({
160+
getInitialState: function() {
161+
return {
162+
eventLog: [],
163+
};
164+
},
165+
render: function() {
166+
return (
167+
<View>
168+
<View style={[styles.row, {justifyContent: 'center'}]}>
169+
<TouchableOpacity
170+
style={styles.wrapper}
171+
delayPress={200}
172+
onPress={() => this._appendEvent('press - 200ms delay')}
173+
delayPressIn={0}
174+
onPressIn={() => this._appendEvent('pressIn - 0ms delay')}
175+
delayPressOut={1000}
176+
onPressOut={() => this._appendEvent('pressOut - 1000ms delay')}
177+
delayLongPress={800}
178+
onLongPress={() => this._appendEvent('longPress - 800ms delay')}>
179+
<Text style={styles.button}>
180+
Press Me
181+
</Text>
182+
</TouchableOpacity>
183+
</View>
184+
<View style={styles.eventLogBox}>
185+
{this.state.eventLog.map((e, ii) => <Text key={ii}>{e}</Text>)}
186+
</View>
187+
</View>
188+
);
189+
},
190+
_appendEvent: function(eventName) {
191+
var limit = 6;
192+
var eventLog = this.state.eventLog.slice(0, limit - 1);
193+
eventLog.unshift(eventName);
194+
this.setState({eventLog});
195+
},
196+
});
197+
151198
var heartImage = {uri: 'https://pbs.twimg.com/media/BlXBfT3CQAA6cVZ.png:small'};
152199

153200
var styles = StyleSheet.create({

Libraries/Components/Touchable/TouchableHighlight.js

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ var View = require('View');
2323

2424
var cloneWithProps = require('cloneWithProps');
2525
var ensureComponentIsNative = require('ensureComponentIsNative');
26+
var ensurePositiveDelayProps = require('ensurePositiveDelayProps');
2627
var keyOf = require('keyOf');
2728
var merge = require('merge');
2829
var onlyChild = require('onlyChild');
@@ -32,6 +33,8 @@ var DEFAULT_PROPS = {
3233
underlayColor: 'black',
3334
};
3435

36+
var DEFAULT_HIDE_MS = 100;
37+
3538
/**
3639
* A wrapper for making views respond properly to touches.
3740
* On press down, the opacity of the wrapped view is decreased, which allows
@@ -111,10 +114,12 @@ var TouchableHighlight = React.createClass({
111114
},
112115

113116
componentDidMount: function() {
117+
ensurePositiveDelayProps(this.props);
114118
ensureComponentIsNative(this.refs[CHILD_REF]);
115119
},
116120

117121
componentDidUpdate: function() {
122+
ensurePositiveDelayProps(this.props);
118123
ensureComponentIsNative(this.refs[CHILD_REF]);
119124
},
120125

@@ -136,23 +141,45 @@ var TouchableHighlight = React.createClass({
136141
* defined on your component.
137142
*/
138143
touchableHandleActivePressIn: function() {
139-
this.clearTimeout(this._hideTimeout);
140-
this._hideTimeout = null;
141144
this._showUnderlay();
142145
this.props.onPressIn && this.props.onPressIn();
143146
},
144147

145148
touchableHandleActivePressOut: function() {
149+
if (this.props.delayPressOut) {
150+
this._onPressOutTimeout = this.setTimeout(function() {
151+
this._onPressOut();
152+
}, this.props.delayPressOut);
153+
} else {
154+
this._onPressOut();
155+
}
156+
},
157+
158+
_onPressOut: function() {
146159
if (!this._hideTimeout) {
147160
this._hideUnderlay();
148161
}
149162
this.props.onPressOut && this.props.onPressOut();
150163
},
151164

152165
touchableHandlePress: function() {
153-
this.clearTimeout(this._hideTimeout);
166+
if (this.props.delayPress) {
167+
if (!this._onPressTimeout) {
168+
this._onPressTimeout = this.setTimeout(function() {
169+
this.clearTimeout(this._onPressTimeout);
170+
this._onPressTimeout = null;
171+
this._onPress();
172+
}, this.props.delayPress);
173+
}
174+
} else {
175+
this._onPress();
176+
}
177+
},
178+
179+
_onPress: function() {
154180
this._showUnderlay();
155-
this._hideTimeout = this.setTimeout(this._hideUnderlay, 100);
181+
this._hideTimeout = this.setTimeout(this._hideUnderlay,
182+
this.props.delayPressOut || DEFAULT_HIDE_MS);
156183
this.props.onPress && this.props.onPress();
157184
},
158185

@@ -164,6 +191,14 @@ var TouchableHighlight = React.createClass({
164191
return PRESS_RECT_OFFSET; // Always make sure to predeclare a constant!
165192
},
166193

194+
touchableGetHighlightDelayMS: function() {
195+
return this.props.delayPressIn;
196+
},
197+
198+
touchableGetLongPressDelayMS: function() {
199+
return this.props.delayLongPress;
200+
},
201+
167202
_showUnderlay: function() {
168203
this.refs[UNDERLAY_REF].setNativeProps(this.state.activeUnderlayProps);
169204
this.refs[CHILD_REF].setNativeProps(this.state.activeProps);
@@ -183,14 +218,22 @@ var TouchableHighlight = React.createClass({
183218
}
184219
},
185220

221+
_componentHandleResponderGrant: function(e, dispatchID) {
222+
this.clearTimeout(this._onPressOutTimeout);
223+
this._onPressOutTimeout = null;
224+
this.clearTimeout(this._hideTimeout);
225+
this._hideTimeout = null;
226+
this.touchableHandleResponderGrant(e, dispatchID);
227+
},
228+
186229
render: function() {
187230
return (
188231
<View
189232
ref={UNDERLAY_REF}
190233
style={this.state.underlayStyle}
191234
onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder}
192235
onResponderTerminationRequest={this.touchableHandleResponderTerminationRequest}
193-
onResponderGrant={this.touchableHandleResponderGrant}
236+
onResponderGrant={this._componentHandleResponderGrant}
194237
onResponderMove={this.touchableHandleResponderMove}
195238
onResponderRelease={this.touchableHandleResponderRelease}
196239
onResponderTerminate={this.touchableHandleResponderTerminate}>

Libraries/Components/Touchable/TouchableOpacity.js

Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@
1515
var NativeMethodsMixin = require('NativeMethodsMixin');
1616
var POPAnimationMixin = require('POPAnimationMixin');
1717
var React = require('React');
18+
var TimerMixin = require('react-timer-mixin');
1819
var Touchable = require('Touchable');
1920
var TouchableWithoutFeedback = require('TouchableWithoutFeedback');
2021

2122
var cloneWithProps = require('cloneWithProps');
2223
var ensureComponentIsNative = require('ensureComponentIsNative');
24+
var ensurePositiveDelayProps = require('ensurePositiveDelayProps');
2325
var flattenStyle = require('flattenStyle');
2426
var keyOf = require('keyOf');
2527
var onlyChild = require('onlyChild');
@@ -50,7 +52,7 @@ var onlyChild = require('onlyChild');
5052
*/
5153

5254
var TouchableOpacity = React.createClass({
53-
mixins: [Touchable.Mixin, NativeMethodsMixin, POPAnimationMixin],
55+
mixins: [TimerMixin, Touchable.Mixin, NativeMethodsMixin, POPAnimationMixin],
5456

5557
propTypes: {
5658
...TouchableWithoutFeedback.propTypes,
@@ -72,10 +74,12 @@ var TouchableOpacity = React.createClass({
7274
},
7375

7476
componentDidMount: function() {
77+
ensurePositiveDelayProps(this.props);
7578
ensureComponentIsNative(this.refs[CHILD_REF]);
7679
},
7780

7881
componentDidUpdate: function() {
82+
ensurePositiveDelayProps(this.props);
7983
ensureComponentIsNative(this.refs[CHILD_REF]);
8084
},
8185

@@ -102,20 +106,45 @@ var TouchableOpacity = React.createClass({
102106
* defined on your component.
103107
*/
104108
touchableHandleActivePressIn: function() {
105-
this.refs[CHILD_REF].setNativeProps({
106-
opacity: this.props.activeOpacity
107-
});
109+
this._fromPressIn = true;
110+
this.clearTimeout(this._hideTimeout);
111+
this._hideTimeout = null;
112+
this._opacityActive();
108113
this.props.onPressIn && this.props.onPressIn();
109114
},
110115

111116
touchableHandleActivePressOut: function() {
112-
var child = onlyChild(this.props.children);
113-
var childStyle = flattenStyle(child.props.style) || {};
114-
this.setOpacityTo(childStyle.opacity === undefined ? 1 : childStyle.opacity);
115-
this.props.onPressOut && this.props.onPressOut();
117+
if (this.props.delayPressOut) {
118+
this._onPressOutTimeout = this.setTimeout(function() {
119+
this._opacityInactive();
120+
this.props.onPressOut && this.props.onPressOut();
121+
}, this.props.delayPressOut);
122+
} else {
123+
this._opacityInactive();
124+
this.props.onPressOut && this.props.onPressOut();
125+
}
116126
},
117127

118128
touchableHandlePress: function() {
129+
if (this.props.delayPress) {
130+
if (!this._onPressTimeout) {
131+
this._onPressTimeout = this.setTimeout(function() {
132+
this.clearTimeout(this._onPressTimeout);
133+
this._onPressTimeout = null;
134+
this._onPress();
135+
}, this.props.delayPress);
136+
}
137+
} else {
138+
this._onPress();
139+
}
140+
},
141+
142+
_onPress: function() {
143+
if (!this._fromPressIn) {
144+
this._opacityActive();
145+
this._hideTimeout = this.setTimeout(this._opacityInactive,
146+
this.props.delayPressOut || 100);
147+
}
119148
this.props.onPress && this.props.onPress();
120149
},
121150

@@ -128,7 +157,34 @@ var TouchableOpacity = React.createClass({
128157
},
129158

130159
touchableGetHighlightDelayMS: function() {
131-
return 0;
160+
return this.props.delayPressIn || 0;
161+
},
162+
163+
touchableGetLongPressDelayMS: function() {
164+
return this.props.delayLongPress === 0 ? 0 :
165+
this.props.delayLongPress || 500;
166+
},
167+
168+
_opacityActive: function() {
169+
this.setOpacityTo(this.props.activeOpacity);
170+
},
171+
172+
_opacityInactive: function() {
173+
this.clearTimeout(this._hideTimeout);
174+
this._hideTimeout = null;
175+
var child = onlyChild(this.props.children);
176+
var childStyle = flattenStyle(child.props.style) || {};
177+
this.setOpacityTo(childStyle.opacity === undefined ? 1 :
178+
childStyle.opacity);
179+
},
180+
181+
_componentHandleResponderGrant: function(e, dispatchID) {
182+
this._fromPressIn = false;
183+
this.clearTimeout(this._onPressOutTimeout);
184+
this._onPressOutTimeout = null;
185+
this.clearTimeout(this._hideTimeout);
186+
this._hideTimeout = null;
187+
this.touchableHandleResponderGrant(e, dispatchID);
132188
},
133189

134190
render: function() {
@@ -138,7 +194,7 @@ var TouchableOpacity = React.createClass({
138194
testID: this.props.testID,
139195
onStartShouldSetResponder: this.touchableHandleStartShouldSetResponder,
140196
onResponderTerminationRequest: this.touchableHandleResponderTerminationRequest,
141-
onResponderGrant: this.touchableHandleResponderGrant,
197+
onResponderGrant: this._componentHandleResponderGrant,
142198
onResponderMove: this.touchableHandleResponderMove,
143199
onResponderRelease: this.touchableHandleResponderRelease,
144200
onResponderTerminate: this.touchableHandleResponderTerminate,

0 commit comments

Comments
 (0)