/** * 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 { AccessibilityActionEvent, AccessibilityActionInfo, AccessibilityRole, AccessibilityState, AccessibilityValue, } from '../../Components/View/ViewAccessibility'; import type {EdgeInsetsOrSizeProp} from '../../StyleSheet/EdgeInsetsPropType'; import type { BlurEvent, FocusEvent, LayoutEvent, PressEvent, } from '../../Types/CoreEventTypes'; import View from '../../Components/View/View'; import {PressabilityDebugView} from '../../Pressability/PressabilityDebug'; import usePressability from '../../Pressability/usePressability'; import * as React from 'react'; import {useMemo} from 'react'; type Props = $ReadOnly<{| accessibilityActions?: ?$ReadOnlyArray, accessibilityElementsHidden?: ?boolean, accessibilityHint?: ?Stringish, accessibilityLanguage?: ?Stringish, accessibilityIgnoresInvertColors?: ?boolean, accessibilityLabel?: ?Stringish, accessibilityLiveRegion?: ?('none' | 'polite' | 'assertive'), accessibilityRole?: ?AccessibilityRole, accessibilityState?: ?AccessibilityState, accessibilityValue?: ?AccessibilityValue, 'aria-valuemax'?: AccessibilityValue['max'], 'aria-valuemin'?: AccessibilityValue['min'], 'aria-valuenow'?: AccessibilityValue['now'], 'aria-valuetext'?: AccessibilityValue['text'], accessibilityViewIsModal?: ?boolean, 'aria-modal'?: ?boolean, accessible?: ?boolean, /** * alias for accessibilityState * * see https://reactnative.dev/docs/accessibility#accessibilitystate */ 'aria-busy'?: ?boolean, 'aria-checked'?: ?boolean | 'mixed', 'aria-disabled'?: ?boolean, 'aria-expanded'?: ?boolean, 'aria-selected'?: ?boolean, 'aria-hidden'?: ?boolean, 'aria-live'?: ?('polite' | 'assertive' | 'off'), 'aria-label'?: ?Stringish, children?: ?React.Node, delayLongPress?: ?number, delayPressIn?: ?number, delayPressOut?: ?number, disabled?: ?boolean, focusable?: ?boolean, hitSlop?: ?EdgeInsetsOrSizeProp, id?: string, 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?: ?EdgeInsetsOrSizeProp, rejectResponderTermination?: ?boolean, testID?: ?string, touchSoundDisabled?: ?boolean, |}>; const PASSTHROUGH_PROPS = [ 'accessibilityActions', 'accessibilityElementsHidden', 'accessibilityHint', 'accessibilityLanguage', 'accessibilityIgnoresInvertColors', 'accessibilityLabel', 'accessibilityLiveRegion', 'accessibilityRole', 'accessibilityValue', 'aria-valuemax', 'aria-valuemin', 'aria-valuenow', 'aria-valuetext', 'accessibilityViewIsModal', 'aria-modal', 'hitSlop', 'importantForAccessibility', 'nativeID', 'onAccessibilityAction', 'onBlur', 'onFocus', 'onLayout', 'testID', ]; module.exports = function TouchableWithoutFeedback(props: Props): React.Node { const { disabled, rejectResponderTermination, 'aria-disabled': ariaDisabled, accessibilityState, hitSlop, delayLongPress, delayPressIn, delayPressOut, pressRetentionOffset, touchSoundDisabled, onBlur: _onBlur, onFocus: _onFocus, onLongPress, onPress, onPressIn, onPressOut, } = props; const pressabilityConfig = useMemo( () => ({ cancelable: !rejectResponderTermination, disabled: disabled !== null ? disabled : ariaDisabled ?? accessibilityState?.disabled, hitSlop: hitSlop, delayLongPress: delayLongPress, delayPressIn: delayPressIn, delayPressOut: delayPressOut, minPressDuration: 0, pressRectOffset: pressRetentionOffset, android_disableSound: touchSoundDisabled, onBlur: _onBlur, onFocus: _onFocus, onLongPress: onLongPress, onPress: onPress, onPressIn: onPressIn, onPressOut: onPressOut, }), [ rejectResponderTermination, disabled, ariaDisabled, accessibilityState?.disabled, hitSlop, delayLongPress, delayPressIn, delayPressOut, pressRetentionOffset, touchSoundDisabled, _onBlur, _onFocus, onLongPress, onPress, onPressIn, onPressOut, ], ); const eventHandlers = usePressability(pressabilityConfig); const element = React.Children.only<$FlowFixMe>(props.children); const children: Array = [element.props.children]; const ariaLive = props['aria-live']; if (__DEV__) { if (element.type === View) { children.push( , ); } } let _accessibilityState = { busy: props['aria-busy'] ?? props.accessibilityState?.busy, checked: props['aria-checked'] ?? props.accessibilityState?.checked, disabled: props['aria-disabled'] ?? props.accessibilityState?.disabled, expanded: props['aria-expanded'] ?? props.accessibilityState?.expanded, selected: props['aria-selected'] ?? props.accessibilityState?.selected, }; // BACKWARD-COMPATIBILITY: Focus and blur events were never supported before // adopting `Pressability`, so preserve that behavior. const {onBlur, onFocus, ...eventHandlersWithoutBlurAndFocus} = eventHandlers; const elementProps: {[string]: mixed, ...} = { ...eventHandlersWithoutBlurAndFocus, accessible: props.accessible !== false, accessibilityState: props.disabled != null ? { ..._accessibilityState, disabled: props.disabled, } : _accessibilityState, focusable: props.focusable !== false && props.onPress !== undefined && !props.disabled, accessibilityElementsHidden: props['aria-hidden'] ?? props.accessibilityElementsHidden, importantForAccessibility: props['aria-hidden'] === true ? 'no-hide-descendants' : props.importantForAccessibility, accessibilityLiveRegion: ariaLive === 'off' ? 'none' : ariaLive ?? props.accessibilityLiveRegion, nativeID: props.id ?? props.nativeID, }; for (const prop of PASSTHROUGH_PROPS) { if (props[prop] !== undefined) { elementProps[prop] = props[prop]; } } // $FlowFixMe[incompatible-call] return React.cloneElement(element, elementProps, ...children); };