react-native/Libraries/Components/Touchable/TouchableWithoutFeedback.js

261 lines
8.3 KiB
JavaScript
Raw Normal View History

2015-01-30 01:10:49 +00:00
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
2015-01-30 01:10:49 +00:00
*
* @format
* @flow
2015-01-30 01:10:49 +00:00
*/
2015-01-30 01:10:49 +00:00
'use strict';
const EdgeInsetsPropType = require('EdgeInsetsPropType');
const React = require('React');
const PropTypes = require('prop-types');
const TimerMixin = require('react-timer-mixin');
const Touchable = require('Touchable');
const createReactClass = require('create-react-class');
const ensurePositiveDelayProps = require('ensurePositiveDelayProps');
const warning = require('fbjs/lib/warning');
2015-01-30 01:10:49 +00:00
const {
AccessibilityComponentTypes,
AccessibilityTraits,
} = require('ViewAccessibility');
import type {PressEvent} from 'CoreEventTypes';
import type {EdgeInsetsProp} from 'EdgeInsetsPropType';
import type {
AccessibilityComponentType,
AccessibilityTraits as AccessibilityTraitsFlow,
} from 'ViewAccessibility';
const PRESS_RETENTION_OFFSET = {top: 20, left: 20, right: 20, bottom: 30};
2015-01-30 01:10:49 +00:00
export type Props = $ReadOnly<{|
accessible?: ?boolean,
accessibilityComponentType?: ?AccessibilityComponentType,
accessibilityLabel?:
| null
| React$PropType$Primitive<any>
| string
| Array<any>
| any,
accessibilityTraits?: ?AccessibilityTraitsFlow,
children?: ?React.Node,
delayLongPress?: ?number,
delayPressIn?: ?number,
delayPressOut?: ?number,
disabled?: ?boolean,
hitSlop?: ?EdgeInsetsProp,
nativeID?: ?string,
onLayout?: ?Function,
onLongPress?: ?Function,
onPress?: ?Function,
onPressIn?: ?Function,
onPressOut?: ?Function,
pressRetentionOffset?: ?EdgeInsetsProp,
rejectResponderTermination?: ?boolean,
testID?: ?string,
|}>;
2015-01-30 01:10:49 +00:00
/**
* Do not use unless you have a very good reason. All elements that
* respond to press should have a visual feedback when touched.
*
* TouchableWithoutFeedback supports only one child.
* If you wish to have several child components, wrap them in a View.
2015-01-30 01:10:49 +00:00
*/
const TouchableWithoutFeedback = ((createReactClass({
displayName: 'TouchableWithoutFeedback',
mixins: [TimerMixin, Touchable.Mixin],
2015-01-30 01:10:49 +00:00
propTypes: {
accessible: PropTypes.bool,
accessibilityLabel: PropTypes.node,
accessibilityComponentType: PropTypes.oneOf(AccessibilityComponentTypes),
accessibilityTraits: PropTypes.oneOfType([
PropTypes.oneOf(AccessibilityTraits),
PropTypes.arrayOf(PropTypes.oneOf(AccessibilityTraits)),
]),
Trigger onFocus/onBlur instead of onPressIn/onPressOut (eventually, but for now just deprecate) (#18470) Summary: Currently on iOS and Android focus/blur events trigger onPressIn/onPressOut. Based on discussions with people are several companies who use react-native we're proposing instead triggering new events onFocus/onBlur. Initial discussion on Slack with some from the core team on Slack seemed positive. Couple reasons: * The current API behavior overloads onPressIn/onPressOut. That means on platforms like react-native-web, if focus/blur support was added (as we're hoping for), even though onPressIn/onPressOut would be useful as the name describes, you wouldn't be able to distinguish between it and browser element focus/blur events. * The names aren't as self-documenting/intuitive as onFocus/onBlur, especially for react-dom users. There aren't any current tests around this, but I intend to add them if we solidify the API. There's also an option question on the transition--do we deprecate the existing API with a warning? This PR just deprecates them, though it will on any TV platform when something becomes focused regardless of whether they use the API or not. This isn't ideal. It's not clear if there are alternatives or if just right away breaking the API for TV users is the correct solution, if we can get consensus between the few parties who are using it. *** I'm interested to hear counter points or prior discussions. Cc/ matthargett dlowder-salesforce rozele Closes https://github.com/facebook/react-native/pull/18470 Differential Revision: D8368109 Pulled By: hramos fbshipit-source-id: 22587b82e091645e748b6c2d721fdff06d54837f
2018-06-11 22:11:01 +00:00
/**
* When `accessible` is true (which is the default) this may be called when
* the OS-specific concept of "focus" occurs. Some platforms may not have
* the concept of focus.
*/
onFocus: PropTypes.func,
/**
* When `accessible` is true (which is the default) this may be called when
* the OS-specific concept of "blur" occurs, meaning the element lost focus.
* Some platforms may not have the concept of blur.
*/
onBlur: PropTypes.func,
/**
* If true, disable all interactions for this component.
*/
disabled: PropTypes.bool,
/**
* Called when the touch is released, but not if cancelled (e.g. by a scroll
* that steals the responder lock).
*/
onPress: PropTypes.func,
/**
* Called as soon as the touchable element is pressed and invoked even before onPress.
* This can be useful when making network requests.
*/
onPressIn: PropTypes.func,
/**
* Called as soon as the touch is released even before onPress.
*/
onPressOut: PropTypes.func,
/**
* Invoked on mount and layout changes with
*
* `{nativeEvent: {layout: {x, y, width, height}}}`
*/
onLayout: PropTypes.func,
onLongPress: PropTypes.func,
nativeID: PropTypes.string,
testID: PropTypes.string,
/**
* Delay in ms, from the start of the touch, before onPressIn is called.
*/
delayPressIn: PropTypes.number,
/**
* Delay in ms, from the release of the touch, before onPressOut is called.
*/
delayPressOut: PropTypes.number,
/**
* Delay in ms, from onPressIn, before onLongPress is called.
*/
delayLongPress: PropTypes.number,
/**
* When the scroll view is disabled, this defines how far your touch may
* move off of the button, before deactivating the button. Once deactivated,
* try moving it back and you'll see that the button is once again
* reactivated! Move it back and forth several times while the scroll view
* is disabled. Ensure you pass in a constant to reduce memory allocations.
*/
pressRetentionOffset: EdgeInsetsPropType,
/**
* This defines how far your touch can start away from the button. This is
* added to `pressRetentionOffset` when moving off of the button.
* ** NOTE **
* The touch area never extends past the parent view bounds and the Z-index
* of sibling views always takes precedence if a touch hits two overlapping
* views.
*/
hitSlop: EdgeInsetsPropType,
},
2015-01-30 01:10:49 +00:00
getInitialState: function() {
return this.touchableGetInitialState();
},
componentDidMount: function() {
ensurePositiveDelayProps(this.props);
},
UNSAFE_componentWillReceiveProps: function(nextProps: Object) {
ensurePositiveDelayProps(nextProps);
},
2015-01-30 01:10:49 +00:00
/**
* `Touchable.Mixin` self callbacks. The mixin will invoke these if they are
* defined on your component.
*/
touchableHandlePress: function(e: PressEvent) {
2015-01-30 01:10:49 +00:00
this.props.onPress && this.props.onPress(e);
},
touchableHandleActivePressIn: function(e: PressEvent) {
this.props.onPressIn && this.props.onPressIn(e);
2015-01-30 01:10:49 +00:00
},
touchableHandleActivePressOut: function(e: PressEvent) {
this.props.onPressOut && this.props.onPressOut(e);
2015-01-30 01:10:49 +00:00
},
touchableHandleLongPress: function(e: PressEvent) {
this.props.onLongPress && this.props.onLongPress(e);
2015-01-30 01:10:49 +00:00
},
touchableGetPressRectOffset: function(): typeof PRESS_RETENTION_OFFSET {
// $FlowFixMe Invalid prop usage
return this.props.pressRetentionOffset || PRESS_RETENTION_OFFSET;
2015-01-30 01:10:49 +00:00
},
touchableGetHitSlop: function(): ?Object {
return this.props.hitSlop;
},
touchableGetHighlightDelayMS: function(): number {
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 || 0;
2015-01-30 01:10:49 +00:00
},
render: function(): React.Element<any> {
// Note(avik): remove dynamic typecast once Flow has been upgraded
// $FlowFixMe(>=0.41.0)
const child = React.Children.only(this.props.children);
let children = child.props.children;
warning(
!child.type || child.type.displayName !== 'Text',
'TouchableWithoutFeedback does not work well with Text children. Wrap children in a View instead. See ' +
((child._owner && child._owner.getName && child._owner.getName()) ||
'<unknown>'),
);
if (
Touchable.TOUCH_TARGET_DEBUG &&
child.type &&
child.type.displayName === 'View'
) {
children = React.Children.toArray(children);
children.push(
Touchable.renderDebugView({color: 'red', hitSlop: this.props.hitSlop}),
);
}
const style =
Touchable.TOUCH_TARGET_DEBUG &&
child.type &&
child.type.displayName === 'Text'
? [child.props.style, {color: 'red'}]
: child.props.style;
return (React: any).cloneElement(child, {
accessible: this.props.accessible !== false,
accessibilityLabel: this.props.accessibilityLabel,
accessibilityComponentType: this.props.accessibilityComponentType,
accessibilityTraits: this.props.accessibilityTraits,
nativeID: this.props.nativeID,
2015-01-30 01:10:49 +00:00
testID: this.props.testID,
onLayout: this.props.onLayout,
hitSlop: this.props.hitSlop,
2015-01-30 01:10:49 +00:00
onStartShouldSetResponder: this.touchableHandleStartShouldSetResponder,
onResponderTerminationRequest: this
.touchableHandleResponderTerminationRequest,
2015-01-30 01:10:49 +00:00
onResponderGrant: this.touchableHandleResponderGrant,
onResponderMove: this.touchableHandleResponderMove,
onResponderRelease: this.touchableHandleResponderRelease,
onResponderTerminate: this.touchableHandleResponderTerminate,
style,
children,
2015-01-30 01:10:49 +00:00
});
},
}): any): React.ComponentType<Props>);
2015-01-30 01:10:49 +00:00
module.exports = TouchableWithoutFeedback;