Files
react-native/Libraries/Components/Touchable/TouchableWithoutFeedback.js
Eli White c24c8a039c Core files should depend on internals directly
Summary:
By depending on react-native, these files can't be flow strict until index.js is flow strict. By depending on the internals directly they can be flow strict as soon as their dependents are flow strict.

Changelog:
[Internal] Refactoring some core file imports to depend on internals directly

Reviewed By: zackargyle

Differential Revision: D18828324

fbshipit-source-id: 2a347c4e234a64edbb3e6f0ef6387ef1ce78badc
2019-12-05 13:33:58 -08:00

217 lines
6.1 KiB
JavaScript
Executable File

/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
'use strict';
import Pressability from '../../Pressability/Pressability.js';
import {PressabilityDebugView} from '../../Pressability/PressabilityDebug.js';
import TVTouchable from './TVTouchable.js';
import type {
AccessibilityActionEvent,
AccessibilityActionInfo,
AccessibilityRole,
AccessibilityState,
AccessibilityValue,
} from '../../Components/View/ViewAccessibility';
import type {EdgeInsetsProp} from '../../StyleSheet/EdgeInsetsPropType';
import type {
BlurEvent,
FocusEvent,
LayoutEvent,
PressEvent,
} from '../../Types/CoreEventTypes';
import Platform from '../../Utilities/Platform';
import View from '../../Components/View/View';
import * as React from 'react';
type Props = $ReadOnly<{|
accessibilityActions?: ?$ReadOnlyArray<AccessibilityActionInfo>,
accessibilityElementsHidden?: ?boolean,
accessibilityHint?: ?Stringish,
accessibilityIgnoresInvertColors?: ?boolean,
accessibilityLabel?: ?Stringish,
accessibilityLiveRegion?: ?('none' | 'polite' | 'assertive'),
accessibilityRole?: ?AccessibilityRole,
accessibilityState?: ?AccessibilityState,
accessibilityValue?: ?AccessibilityValue,
accessibilityViewIsModal?: ?boolean,
accessible?: ?boolean,
children?: ?React.Node,
delayLongPress?: ?number,
delayPressIn?: ?number,
delayPressOut?: ?number,
disabled?: ?boolean,
focusable?: ?boolean,
hitSlop?: ?EdgeInsetsProp,
importantForAccessibility?: ?('auto' | 'yes' | 'no' | 'no-hide-descendants'),
nativeID?: ?string,
onAccessibilityAction?: ?(event: AccessibilityActionEvent) => mixed,
onBlur?: ?(event: BlurEvent) => mixed,
onFocus?: ?(event: FocusEvent) => mixed,
onLayout?: ?(event: LayoutEvent) => mixed,
onLongPress?: ?(event: PressEvent) => mixed,
onPress?: ?(event: PressEvent) => mixed,
onPressIn?: ?(event: PressEvent) => mixed,
onPressOut?: ?(event: PressEvent) => mixed,
pressRetentionOffset?: ?EdgeInsetsProp,
rejectResponderTermination?: ?boolean,
testID?: ?string,
touchSoundDisabled?: ?boolean,
|}>;
type State = $ReadOnly<{|
pressability: Pressability,
|}>;
const PASSTHROUGH_PROPS = [
'accessibilityActions',
'accessibilityElementsHidden',
'accessibilityHint',
'accessibilityIgnoresInvertColors',
'accessibilityLabel',
'accessibilityLiveRegion',
'accessibilityRole',
'accessibilityState',
'accessibilityValue',
'accessibilityViewIsModal',
'hitSlop',
'importantForAccessibility',
'nativeID',
'onAccessibilityAction',
'onBlur',
'onFocus',
'onLayout',
'testID',
];
class TouchableWithoutFeedback extends React.Component<Props, State> {
_tvTouchable: ?TVTouchable;
state: State = {
pressability: new Pressability({
getHitSlop: () => this.props.hitSlop,
getLongPressDelayMS: () => {
if (this.props.delayLongPress != null) {
const maybeNumber = this.props.delayLongPress;
if (typeof maybeNumber === 'number') {
return maybeNumber;
}
}
return 500;
},
getPressDelayMS: () => this.props.delayPressIn,
getPressOutDelayMS: () => this.props.delayPressOut,
getPressRectOffset: () => this.props.pressRetentionOffset,
getTouchSoundDisabled: () => this.props.touchSoundDisabled,
onBlur: event => {
if (this.props.onBlur != null) {
this.props.onBlur(event);
}
},
onFocus: event => {
if (this.props.onFocus != null) {
this.props.onFocus(event);
}
},
onLongPress: event => {
if (this.props.onLongPress != null) {
this.props.onLongPress(event);
}
},
onPress: event => {
if (this.props.onPress != null) {
this.props.onPress(event);
}
},
onPressIn: event => {
if (this.props.onPressIn != null) {
this.props.onPressIn(event);
}
},
onPressOut: event => {
if (this.props.onPressOut != null) {
this.props.onPressOut(event);
}
},
onResponderTerminationRequest: () =>
!this.props.rejectResponderTermination,
onStartShouldSetResponder: () => !this.props.disabled,
}),
};
render(): React.Node {
const element = React.Children.only(this.props.children);
const children = [element.props.children];
if (__DEV__) {
if (element.type === View) {
children.push(
<PressabilityDebugView color="red" hitSlop={this.props.hitSlop} />,
);
}
}
// BACKWARD-COMPATIBILITY: Focus and blur events were never supported before
// adopting `Pressability`, so preserve that behavior.
const {
onBlur,
onFocus,
...eventHandlersWithoutBlurAndFocus
} = this.state.pressability.getEventHandlers();
const elementProps: {[string]: mixed, ...} = {
...eventHandlersWithoutBlurAndFocus,
accessible: this.props.accessible !== false,
focusable:
this.props.focusable !== false && this.props.onPress !== undefined,
};
for (const prop of PASSTHROUGH_PROPS) {
if (this.props[prop] !== undefined) {
elementProps[prop] = this.props[prop];
}
}
return React.cloneElement(element, elementProps, ...children);
}
componentDidMount(): void {
if (Platform.isTV) {
this._tvTouchable = new TVTouchable(this, {
getDisabled: () => this.props.disabled === true,
onBlur: event => {
if (this.props.onBlur != null) {
this.props.onBlur(event);
}
},
onFocus: event => {
if (this.props.onFocus != null) {
this.props.onFocus(event);
}
},
onPress: event => {
if (this.props.onPress != null) {
this.props.onPress(event);
}
},
});
}
}
componentWillUnmount(): void {
if (Platform.isTV) {
if (this._tvTouchable != null) {
this._tvTouchable.destroy();
}
}
this.state.pressability.reset();
}
}
module.exports = TouchableWithoutFeedback;