amis-rpc-design/node_modules/react-native/Libraries/Components/Touchable/TouchableOpacity.js
2023-10-07 19:42:30 +08:00

326 lines
9.6 KiB
JavaScript

/**
* Copyright (c) Meta Platforms, Inc. and 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
*/
import type {ViewStyleProp} from '../../StyleSheet/StyleSheet';
import typeof TouchableWithoutFeedback from './TouchableWithoutFeedback';
import Animated from '../../Animated/Animated';
import Easing from '../../Animated/Easing';
import Pressability, {
type PressabilityConfig,
} from '../../Pressability/Pressability';
import {PressabilityDebugView} from '../../Pressability/PressabilityDebug';
import flattenStyle from '../../StyleSheet/flattenStyle';
import Platform from '../../Utilities/Platform';
import * as React from 'react';
type TVProps = $ReadOnly<{|
hasTVPreferredFocus?: ?boolean,
nextFocusDown?: ?number,
nextFocusForward?: ?number,
nextFocusLeft?: ?number,
nextFocusRight?: ?number,
nextFocusUp?: ?number,
|}>;
type Props = $ReadOnly<{|
...React.ElementConfig<TouchableWithoutFeedback>,
...TVProps,
activeOpacity?: ?number,
style?: ?ViewStyleProp,
hostRef?: ?React.Ref<typeof Animated.View>,
|}>;
type State = $ReadOnly<{|
anim: Animated.Value,
pressability: Pressability,
|}>;
/**
* A wrapper for making views respond properly to touches.
* On press down, the opacity of the wrapped view is decreased, dimming it.
*
* Opacity is controlled by wrapping the children in an Animated.View, which is
* added to the view hierarchy. Be aware that this can affect layout.
*
* Example:
*
* ```
* renderButton: function() {
* return (
* <TouchableOpacity onPress={this._onPressButton}>
* <Image
* style={styles.button}
* source={require('./myButton.png')}
* />
* </TouchableOpacity>
* );
* },
* ```
* ### Example
*
* ```ReactNativeWebPlayer
* import React, { Component } from 'react'
* import {
* AppRegistry,
* StyleSheet,
* TouchableOpacity,
* Text,
* View,
* } from 'react-native'
*
* class App extends Component {
* state = { count: 0 }
*
* onPress = () => {
* this.setState(state => ({
* count: state.count + 1
* }));
* };
*
* render() {
* return (
* <View style={styles.container}>
* <TouchableOpacity
* style={styles.button}
* onPress={this.onPress}>
* <Text> Touch Here </Text>
* </TouchableOpacity>
* <View style={[styles.countContainer]}>
* <Text style={[styles.countText]}>
* { this.state.count !== 0 ? this.state.count: null}
* </Text>
* </View>
* </View>
* )
* }
* }
*
* const styles = StyleSheet.create({
* container: {
* flex: 1,
* justifyContent: 'center',
* paddingHorizontal: 10
* },
* button: {
* alignItems: 'center',
* backgroundColor: '#DDDDDD',
* padding: 10
* },
* countContainer: {
* alignItems: 'center',
* padding: 10
* },
* countText: {
* color: '#FF00FF'
* }
* })
*
* AppRegistry.registerComponent('App', () => App)
* ```
*
*/
class TouchableOpacity extends React.Component<Props, State> {
state: State = {
anim: new Animated.Value(this._getChildStyleOpacityWithDefault()),
pressability: new Pressability(this._createPressabilityConfig()),
};
_createPressabilityConfig(): PressabilityConfig {
return {
cancelable: !this.props.rejectResponderTermination,
disabled:
this.props.disabled ??
this.props['aria-disabled'] ??
this.props.accessibilityState?.disabled,
hitSlop: this.props.hitSlop,
delayLongPress: this.props.delayLongPress,
delayPressIn: this.props.delayPressIn,
delayPressOut: this.props.delayPressOut,
minPressDuration: 0,
pressRectOffset: this.props.pressRetentionOffset,
onBlur: event => {
if (Platform.isTV) {
this._opacityInactive(250);
}
if (this.props.onBlur != null) {
this.props.onBlur(event);
}
},
onFocus: event => {
if (Platform.isTV) {
this._opacityActive(150);
}
if (this.props.onFocus != null) {
this.props.onFocus(event);
}
},
onLongPress: this.props.onLongPress,
onPress: this.props.onPress,
onPressIn: event => {
this._opacityActive(
event.dispatchConfig.registrationName === 'onResponderGrant'
? 0
: 150,
);
if (this.props.onPressIn != null) {
this.props.onPressIn(event);
}
},
onPressOut: event => {
this._opacityInactive(250);
if (this.props.onPressOut != null) {
this.props.onPressOut(event);
}
},
};
}
/**
* Animate the touchable to a new opacity.
*/
_setOpacityTo(toValue: number, duration: number): void {
Animated.timing(this.state.anim, {
toValue,
duration,
easing: Easing.inOut(Easing.quad),
useNativeDriver: true,
}).start();
}
_opacityActive(duration: number): void {
this._setOpacityTo(this.props.activeOpacity ?? 0.2, duration);
}
_opacityInactive(duration: number): void {
this._setOpacityTo(this._getChildStyleOpacityWithDefault(), duration);
}
_getChildStyleOpacityWithDefault(): number {
// $FlowFixMe[underconstrained-implicit-instantiation]
const opacity = flattenStyle(this.props.style)?.opacity;
return typeof opacity === 'number' ? opacity : 1;
}
render(): React.Node {
// BACKWARD-COMPATIBILITY: Focus and blur events were never supported before
// adopting `Pressability`, so preserve that behavior.
const {onBlur, onFocus, ...eventHandlersWithoutBlurAndFocus} =
this.state.pressability.getEventHandlers();
let _accessibilityState = {
busy: this.props['aria-busy'] ?? this.props.accessibilityState?.busy,
checked:
this.props['aria-checked'] ?? this.props.accessibilityState?.checked,
disabled:
this.props['aria-disabled'] ?? this.props.accessibilityState?.disabled,
expanded:
this.props['aria-expanded'] ?? this.props.accessibilityState?.expanded,
selected:
this.props['aria-selected'] ?? this.props.accessibilityState?.selected,
};
_accessibilityState =
this.props.disabled != null
? {
..._accessibilityState,
disabled: this.props.disabled,
}
: _accessibilityState;
const accessibilityValue = {
max: this.props['aria-valuemax'] ?? this.props.accessibilityValue?.max,
min: this.props['aria-valuemin'] ?? this.props.accessibilityValue?.min,
now: this.props['aria-valuenow'] ?? this.props.accessibilityValue?.now,
text: this.props['aria-valuetext'] ?? this.props.accessibilityValue?.text,
};
const accessibilityLiveRegion =
this.props['aria-live'] === 'off'
? 'none'
: this.props['aria-live'] ?? this.props.accessibilityLiveRegion;
const accessibilityLabel =
this.props['aria-label'] ?? this.props.accessibilityLabel;
return (
<Animated.View
accessible={this.props.accessible !== false}
accessibilityLabel={accessibilityLabel}
accessibilityHint={this.props.accessibilityHint}
accessibilityLanguage={this.props.accessibilityLanguage}
accessibilityRole={this.props.accessibilityRole}
accessibilityState={_accessibilityState}
accessibilityActions={this.props.accessibilityActions}
onAccessibilityAction={this.props.onAccessibilityAction}
accessibilityValue={accessibilityValue}
importantForAccessibility={
this.props['aria-hidden'] === true
? 'no-hide-descendants'
: this.props.importantForAccessibility
}
accessibilityViewIsModal={
this.props['aria-modal'] ?? this.props.accessibilityViewIsModal
}
accessibilityLiveRegion={accessibilityLiveRegion}
accessibilityElementsHidden={
this.props['aria-hidden'] ?? this.props.accessibilityElementsHidden
}
style={[this.props.style, {opacity: this.state.anim}]}
nativeID={this.props.nativeID}
testID={this.props.testID}
onLayout={this.props.onLayout}
nextFocusDown={this.props.nextFocusDown}
nextFocusForward={this.props.nextFocusForward}
nextFocusLeft={this.props.nextFocusLeft}
nextFocusRight={this.props.nextFocusRight}
nextFocusUp={this.props.nextFocusUp}
hasTVPreferredFocus={this.props.hasTVPreferredFocus}
hitSlop={this.props.hitSlop}
focusable={
this.props.focusable !== false && this.props.onPress !== undefined
}
ref={this.props.hostRef}
{...eventHandlersWithoutBlurAndFocus}>
{this.props.children}
{__DEV__ ? (
<PressabilityDebugView color="cyan" hitSlop={this.props.hitSlop} />
) : null}
</Animated.View>
);
}
componentDidUpdate(prevProps: Props, prevState: State) {
this.state.pressability.configure(this._createPressabilityConfig());
if (
this.props.disabled !== prevProps.disabled ||
// $FlowFixMe[underconstrained-implicit-instantiation]
flattenStyle(prevProps.style)?.opacity !==
// $FlowFixMe[underconstrained-implicit-instantiation]
flattenStyle(this.props.style)?.opacity
) {
this._opacityInactive(250);
}
}
componentWillUnmount(): void {
this.state.pressability.reset();
}
}
const Touchable = (React.forwardRef((props, ref) => (
<TouchableOpacity {...props} hostRef={ref} />
)): React.AbstractComponent<Props, React.ElementRef<typeof Animated.View>>);
Touchable.displayName = 'TouchableOpacity';
module.exports = Touchable;