Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Touchable] Add custom delay props to Touchable components #1255

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions Examples/UIExplorer/TouchableExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ exports.examples = [
render: function(): ReactElement {
return <TouchableFeedbackEvents />;
},
}, {
title: 'Touchable delay for events',
description: '<Touchable*> components also accept delayPressIn, ' +
'delayPressOut, and delayLongPress as props. These props impact the ' +
'timing of feedback events.',
render: function(): ReactElement {
return <TouchableDelayEvents />;
},
}];

var TextOnPressBox = React.createClass({
Expand Down Expand Up @@ -148,6 +156,44 @@ var TouchableFeedbackEvents = React.createClass({
},
});

var TouchableDelayEvents = React.createClass({
getInitialState: function() {
return {
eventLog: [],
};
},
render: function() {
return (
<View>
<View style={[styles.row, {justifyContent: 'center'}]}>
<TouchableOpacity
style={styles.wrapper}
onPress={() => this._appendEvent('press')}
delayPressIn={400}
onPressIn={() => this._appendEvent('pressIn - 400ms delay')}
delayPressOut={1000}
onPressOut={() => this._appendEvent('pressOut - 1000ms delay')}
delayLongPress={800}
onLongPress={() => this._appendEvent('longPress - 800ms delay')}>
<Text style={styles.button}>
Press Me
</Text>
</TouchableOpacity>
</View>
<View style={styles.eventLogBox}>
{this.state.eventLog.map((e, ii) => <Text key={ii}>{e}</Text>)}
</View>
</View>
);
},
_appendEvent: function(eventName) {
var limit = 6;
var eventLog = this.state.eventLog.slice(0, limit - 1);
eventLog.unshift(eventName);
this.setState({eventLog});
},
});

var heartImage = {uri: 'https://pbs.twimg.com/media/BlXBfT3CQAA6cVZ.png:small'};

var styles = StyleSheet.create({
Expand Down
18 changes: 17 additions & 1 deletion Libraries/Components/Touchable/TouchableHighlight.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ var View = require('View');

var cloneWithProps = require('cloneWithProps');
var ensureComponentIsNative = require('ensureComponentIsNative');
var ensurePositiveDelayProps = require('ensurePositiveDelayProps');
var keyOf = require('keyOf');
var merge = require('merge');
var onlyChild = require('onlyChild');
Expand Down Expand Up @@ -111,6 +112,7 @@ var TouchableHighlight = React.createClass({
},

componentDidMount: function() {
ensurePositiveDelayProps(this.props);
ensureComponentIsNative(this.refs[CHILD_REF]);
},

Expand All @@ -119,6 +121,7 @@ var TouchableHighlight = React.createClass({
},

componentWillReceiveProps: function(nextProps) {
ensurePositiveDelayProps(nextProps);
if (nextProps.activeOpacity !== this.props.activeOpacity ||
nextProps.underlayColor !== this.props.underlayColor ||
nextProps.style !== this.props.style) {
Expand Down Expand Up @@ -152,7 +155,8 @@ var TouchableHighlight = React.createClass({
touchableHandlePress: function() {
this.clearTimeout(this._hideTimeout);
this._showUnderlay();
this._hideTimeout = this.setTimeout(this._hideUnderlay, 100);
this._hideTimeout = this.setTimeout(this._hideUnderlay,
this.props.delayPressOut || 100);
this.props.onPress && this.props.onPress();
},

Expand All @@ -164,6 +168,18 @@ var TouchableHighlight = React.createClass({
return PRESS_RECT_OFFSET; // Always make sure to predeclare a constant!
},

touchableGetHighlightDelayMS: function() {
return this.props.delayPressIn;
},

touchableGetLongPressDelayMS: function() {
return this.props.delayLongPress;
},

touchableGetPressOutDelayMS: function() {
return this.props.delayPressOut;
},

_showUnderlay: function() {
this.refs[UNDERLAY_REF].setNativeProps(this.state.activeUnderlayProps);
this.refs[CHILD_REF].setNativeProps(this.state.activeProps);
Expand Down
49 changes: 41 additions & 8 deletions Libraries/Components/Touchable/TouchableOpacity.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@
var NativeMethodsMixin = require('NativeMethodsMixin');
var POPAnimationMixin = require('POPAnimationMixin');
var React = require('React');
var TimerMixin = require('react-timer-mixin');
var Touchable = require('Touchable');
var TouchableWithoutFeedback = require('TouchableWithoutFeedback');

var cloneWithProps = require('cloneWithProps');
var ensureComponentIsNative = require('ensureComponentIsNative');
var ensurePositiveDelayProps = require('ensurePositiveDelayProps');
var flattenStyle = require('flattenStyle');
var keyOf = require('keyOf');
var onlyChild = require('onlyChild');
Expand Down Expand Up @@ -50,7 +52,7 @@ var onlyChild = require('onlyChild');
*/

var TouchableOpacity = React.createClass({
mixins: [Touchable.Mixin, NativeMethodsMixin, POPAnimationMixin],
mixins: [TimerMixin, Touchable.Mixin, NativeMethodsMixin, POPAnimationMixin],

propTypes: {
...TouchableWithoutFeedback.propTypes,
Expand All @@ -72,13 +74,18 @@ var TouchableOpacity = React.createClass({
},

componentDidMount: function() {
ensurePositiveDelayProps(this.props);
ensureComponentIsNative(this.refs[CHILD_REF]);
},

componentDidUpdate: function() {
ensureComponentIsNative(this.refs[CHILD_REF]);
},

componentWillReceiveProps: function(nextProps) {
ensurePositiveDelayProps(nextProps);
},

setOpacityTo: function(value) {
if (POPAnimationMixin) {
// Reset with animation if POP is available
Expand All @@ -102,20 +109,24 @@ var TouchableOpacity = React.createClass({
* defined on your component.
*/
touchableHandleActivePressIn: function() {
this.refs[CHILD_REF].setNativeProps({
opacity: this.props.activeOpacity
});
this.clearTimeout(this._hideTimeout);
this._hideTimeout = null;
this._opacityActive();
this.props.onPressIn && this.props.onPressIn();
},

touchableHandleActivePressOut: function() {
var child = onlyChild(this.props.children);
var childStyle = flattenStyle(child.props.style) || {};
this.setOpacityTo(childStyle.opacity === undefined ? 1 : childStyle.opacity);
if (!this._hideTimeout) {
this._opacityInactive();
}
this.props.onPressOut && this.props.onPressOut();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

onPressOut should be called after delayPressOut too, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh actually, Touchable.js will handle that delay, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, delayPressOut is now handled in Touchable, per your recommendations.

},

touchableHandlePress: function() {
this.clearTimeout(this._hideTimeout);
this._opacityActive();
this._hideTimeout = this.setTimeout(this._opacityInactive,
this.props.delayPressOut || 100);
this.props.onPress && this.props.onPress();
},

Expand All @@ -128,7 +139,29 @@ var TouchableOpacity = React.createClass({
},

touchableGetHighlightDelayMS: function() {
return 0;
return this.props.delayPressIn || 0;
},

touchableGetLongPressDelayMS: function() {
return this.props.delayLongPress === 0 ? 0 :
this.props.delayLongPress || 500;
},

touchableGetPressOutDelayMS: function() {
return this.props.delayPressOut;
},

_opacityActive: function() {
this.setOpacityTo(this.props.activeOpacity);
},

_opacityInactive: function() {
this.clearTimeout(this._hideTimeout);
this._hideTimeout = null;
var child = onlyChild(this.props.children);
var childStyle = flattenStyle(child.props.style) || {};
this.setOpacityTo(childStyle.opacity === undefined ? 1 :
childStyle.opacity);
},

render: function() {
Expand Down
35 changes: 33 additions & 2 deletions Libraries/Components/Touchable/TouchableWithoutFeedback.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
'use strict';

var React = require('React');
var TimerMixin = require('react-timer-mixin');
var Touchable = require('Touchable');
var ensurePositiveDelayProps = require('ensurePositiveDelayProps');
var onlyChild = require('onlyChild');

/**
Expand All @@ -31,7 +33,7 @@ type Event = Object;
* one of the primary reason a "web" app doesn't feel "native".
*/
var TouchableWithoutFeedback = React.createClass({
mixins: [Touchable.Mixin],
mixins: [TimerMixin, Touchable.Mixin],

propTypes: {
/**
Expand All @@ -42,12 +44,32 @@ var TouchableWithoutFeedback = React.createClass({
onPressIn: React.PropTypes.func,
onPressOut: React.PropTypes.func,
onLongPress: React.PropTypes.func,
/**
* Delay in ms, from the start of the touch, before onPressIn is called.
*/
delayPressIn: React.PropTypes.number,
/**
* Delay in ms, from the release of the touch, before onPressOut is called.
*/
delayPressOut: React.PropTypes.number,
/**
* Delay in ms, from onPressIn, before onLongPress is called.
*/
delayLongPress: React.PropTypes.number,
},

getInitialState: function() {
return this.touchableGetInitialState();
},

componentDidMount: function() {
ensurePositiveDelayProps(this.props);
},

componentWillReceiveProps: function(nextProps: Object) {
ensurePositiveDelayProps(nextProps);
},

/**
* `Touchable.Mixin` self callbacks. The mixin will invoke these if they are
* defined on your component.
Expand All @@ -73,7 +95,16 @@ var TouchableWithoutFeedback = React.createClass({
},

touchableGetHighlightDelayMS: function(): number {
return 0;
return this.props.delayPressIn || 0;
},

touchableGetLongPressDelayMS: function(): number {
return this.props.delayLongPress === 0 ? 0 :
this.props.delayLongPress || 500;
},

touchableGetPressOutDelayMS: function(): ?number {
return this.props.delayPressOut;
},

render: function(): ReactElement {
Expand Down
24 changes: 24 additions & 0 deletions Libraries/Components/Touchable/ensurePositiveDelayProps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ensurePositiveDelayProps
* @flow
*/
'use strict';

var invariant = require('invariant');

var ensurePositiveDelayProps = function(props: any) {
invariant(
!(props.delayPressIn < 0 || props.delayPressOut < 0 ||
props.delayLongPress < 0),
'Touchable components cannot have negative delay properties'
);
};

module.exports = ensurePositiveDelayProps;
Loading