diff --git a/Libraries/Components/Touchable/TouchableBounce.js b/Libraries/Components/Touchable/TouchableBounce.js index 76ec665d351..df0ef8e690d 100644 --- a/Libraries/Components/Touchable/TouchableBounce.js +++ b/Libraries/Components/Touchable/TouchableBounce.js @@ -10,7 +10,9 @@ 'use strict'; -import Pressability from '../../Pressability/Pressability.js'; +import Pressability, { + type PressabilityConfig, +} from '../../Pressability/Pressability.js'; import {PressabilityDebugView} from '../../Pressability/PressabilityDebug.js'; import type {ViewStyleProp} from '../../StyleSheet/StyleSheet.js'; import TVTouchable from './TVTouchable.js'; @@ -39,21 +41,20 @@ class TouchableBounce extends React.Component { _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, + pressability: new Pressability(this._createPressabilityConfig()), + scale: new Animated.Value(1), + }; + + _createPressabilityConfig(): PressabilityConfig { + return { + cancelable: !this.props.rejectResponderTermination, + disabled: this.props.disabled, + hitSlop: this.props.hitSlop, + delayLongPress: this.props.delayLongPress, + delayPressIn: this.props.delayPressIn, + delayPressOut: this.props.delayPressOut, + pressRectOffset: this.props.pressRetentionOffset, + android_disableSound: this.props.touchSoundDisabled, onBlur: event => { if (Platform.isTV) { this._bounceTo(1, 0.4, 0); @@ -115,12 +116,8 @@ class TouchableBounce extends React.Component { this.props.onPressOut(event); } }, - onResponderTerminationRequest: () => - !this.props.rejectResponderTermination, - onStartShouldSetResponder: () => !this.props.disabled, - }), - scale: new Animated.Value(1), - }; + }; + } _bounceTo( toValue: number, @@ -201,6 +198,10 @@ class TouchableBounce extends React.Component { } } + componentDidUpdate(prevProps: Props, prevState: State) { + this.state.pressability.configure(this._createPressabilityConfig()); + } + componentWillUnmount(): void { if (Platform.isTV) { if (this._tvTouchable != null) { diff --git a/Libraries/Components/Touchable/TouchableHighlight.js b/Libraries/Components/Touchable/TouchableHighlight.js index 1a2948cd177..4dbafc67b5f 100644 --- a/Libraries/Components/Touchable/TouchableHighlight.js +++ b/Libraries/Components/Touchable/TouchableHighlight.js @@ -10,7 +10,9 @@ 'use strict'; -import Pressability from '../../Pressability/Pressability.js'; +import Pressability, { + type PressabilityConfig, +} from '../../Pressability/Pressability.js'; import {PressabilityDebugView} from '../../Pressability/PressabilityDebug.js'; import StyleSheet, {type ViewStyleProp} from '../../StyleSheet/StyleSheet.js'; import type {ColorValue} from '../../StyleSheet/StyleSheetTypes.js'; @@ -159,21 +161,21 @@ class TouchableHighlight extends React.Component { _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, + pressability: new Pressability(this._createPressabilityConfig()), + extraStyles: + this.props.testOnly_pressed === true ? this._createExtraStyles() : null, + }; + + _createPressabilityConfig(): PressabilityConfig { + return { + cancelable: !this.props.rejectResponderTermination, + disabled: this.props.disabled, + hitSlop: this.props.hitSlop, + delayLongPress: this.props.delayLongPress, + delayPressIn: this.props.delayPressIn, + delayPressOut: this.props.delayPressOut, + pressRectOffset: this.props.pressRetentionOffset, + android_disableSound: this.props.touchSoundDisabled, onBlur: event => { if (Platform.isTV) { this._hideUnderlay(); @@ -227,13 +229,8 @@ class TouchableHighlight extends React.Component { this.props.onPressOut(event); } }, - onResponderTerminationRequest: () => - !this.props.rejectResponderTermination, - onStartShouldSetResponder: () => !this.props.disabled, - }), - extraStyles: - this.props.testOnly_pressed === true ? this._createExtraStyles() : null, - }; + }; + } _createExtraStyles(): ExtraStyles { return { @@ -363,6 +360,10 @@ class TouchableHighlight extends React.Component { } } + componentDidUpdate(prevProps: Props, prevState: State) { + this.state.pressability.configure(this._createPressabilityConfig()); + } + componentWillUnmount(): void { this._isMounted = false; if (this._hideTimeout != null) { diff --git a/Libraries/Components/Touchable/TouchableNativeFeedback.js b/Libraries/Components/Touchable/TouchableNativeFeedback.js index 52a3a553b0f..b6c87d09861 100644 --- a/Libraries/Components/Touchable/TouchableNativeFeedback.js +++ b/Libraries/Components/Touchable/TouchableNativeFeedback.js @@ -10,7 +10,9 @@ 'use strict'; -import Pressability from '../../Pressability/Pressability.js'; +import Pressability, { + type PressabilityConfig, +} from '../../Pressability/Pressability.js'; import {PressabilityDebugView} from '../../Pressability/PressabilityDebug.js'; import TVTouchable from './TVTouchable.js'; import typeof TouchableWithoutFeedback from './TouchableWithoutFeedback.js'; @@ -144,31 +146,21 @@ class TouchableNativeFeedback extends React.Component { _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, - onLongPress: event => { - if (this.props.onLongPress != null) { - this.props.onLongPress(event); - } - }, - onPress: event => { - if (this.props.onPress != null) { - this.props.onPress(event); - } - }, + pressability: new Pressability(this._createPressabilityConfig()), + }; + + _createPressabilityConfig(): PressabilityConfig { + return { + cancelable: !this.props.rejectResponderTermination, + disabled: this.props.disabled, + hitSlop: this.props.hitSlop, + delayLongPress: this.props.delayLongPress, + delayPressIn: this.props.delayPressIn, + delayPressOut: this.props.delayPressOut, + pressRectOffset: this.props.pressRetentionOffset, + android_disableSound: this.props.touchSoundDisabled, + onLongPress: this.props.onLongPress, + onPress: this.props.onPress, onPressIn: event => { if (Platform.OS === 'android') { this._dispatchPressedStateChange(true); @@ -191,11 +183,8 @@ class TouchableNativeFeedback extends React.Component { this.props.onPressOut(event); } }, - onResponderTerminationRequest: () => - !this.props.rejectResponderTermination, - onStartShouldSetResponder: () => !this.props.disabled, - }), - }; + }; + } _dispatchPressedStateChange(pressed: boolean): void { if (Platform.OS === 'android') { @@ -312,6 +301,10 @@ class TouchableNativeFeedback extends React.Component { } } + componentDidUpdate(prevProps: Props, prevState: State) { + this.state.pressability.configure(this._createPressabilityConfig()); + } + componentWillUnmount(): void { if (Platform.isTV) { if (this._tvTouchable != null) { diff --git a/Libraries/Components/Touchable/TouchableOpacity.js b/Libraries/Components/Touchable/TouchableOpacity.js index dd70883264f..d74c771297d 100644 --- a/Libraries/Components/Touchable/TouchableOpacity.js +++ b/Libraries/Components/Touchable/TouchableOpacity.js @@ -10,7 +10,9 @@ 'use strict'; -import Pressability from '../../Pressability/Pressability.js'; +import Pressability, { + type PressabilityConfig, +} from '../../Pressability/Pressability.js'; import {PressabilityDebugView} from '../../Pressability/PressabilityDebug.js'; import TVTouchable from './TVTouchable.js'; import typeof TouchableWithoutFeedback from './TouchableWithoutFeedback.js'; @@ -134,20 +136,18 @@ class TouchableOpacity extends React.Component { state: State = { anim: new Animated.Value(this._getChildStyleOpacityWithDefault()), - 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, + pressability: new Pressability(this._createPressabilityConfig()), + }; + + _createPressabilityConfig(): PressabilityConfig { + return { + cancelable: !this.props.rejectResponderTermination, + disabled: this.props.disabled, + hitSlop: this.props.hitSlop, + delayLongPress: this.props.delayLongPress, + delayPressIn: this.props.delayPressIn, + delayPressOut: this.props.delayPressOut, + pressRectOffset: this.props.pressRetentionOffset, onBlur: event => { if (Platform.isTV) { this._opacityInactive(250); @@ -164,16 +164,8 @@ class TouchableOpacity extends React.Component { 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); - } - }, + onLongPress: this.props.onLongPress, + onPress: this.props.onPress, onPressIn: event => { this._opacityActive( event.dispatchConfig.registrationName === 'onResponderGrant' @@ -190,11 +182,8 @@ class TouchableOpacity extends React.Component { this.props.onPressOut(event); } }, - onResponderTerminationRequest: () => - !this.props.rejectResponderTermination, - onStartShouldSetResponder: () => !this.props.disabled, - }), - }; + }; + } /** * Animate the touchable to a new opacity. @@ -292,6 +281,7 @@ class TouchableOpacity extends React.Component { } componentDidUpdate(prevProps: Props, prevState: State) { + this.state.pressability.configure(this._createPressabilityConfig()); if (this.props.disabled !== prevProps.disabled) { this._opacityInactive(250); } diff --git a/Libraries/Components/Touchable/TouchableWithoutFeedback.js b/Libraries/Components/Touchable/TouchableWithoutFeedback.js index 6684d6cd293..37789146d8e 100755 --- a/Libraries/Components/Touchable/TouchableWithoutFeedback.js +++ b/Libraries/Components/Touchable/TouchableWithoutFeedback.js @@ -10,7 +10,9 @@ 'use strict'; -import Pressability from '../../Pressability/Pressability.js'; +import Pressability, { + type PressabilityConfig, +} from '../../Pressability/Pressability.js'; import {PressabilityDebugView} from '../../Pressability/PressabilityDebug.js'; import TVTouchable from './TVTouchable.js'; import type { @@ -95,55 +97,7 @@ class TouchableWithoutFeedback extends React.Component { _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, - }), + pressability: new Pressability(createPressabilityConfig(this.props)), }; render(): React.Node { @@ -203,6 +157,10 @@ class TouchableWithoutFeedback extends React.Component { } } + componentDidUpdate(): void { + this.state.pressability.configure(createPressabilityConfig(this.props)); + } + componentWillUnmount(): void { if (Platform.isTV) { if (this._tvTouchable != null) { @@ -213,4 +171,23 @@ class TouchableWithoutFeedback extends React.Component { } } +function createPressabilityConfig(props: Props): PressabilityConfig { + return { + cancelable: !props.rejectResponderTermination, + disabled: props.disabled, + hitSlop: props.hitSlop, + delayLongPress: props.delayLongPress, + delayPressIn: props.delayPressIn, + delayPressOut: props.delayPressOut, + pressRectOffset: props.pressRetentionOffset, + android_disableSound: props.touchSoundDisabled, + onBlur: props.onBlur, + onFocus: props.onFocus, + onLongPress: props.onLongPress, + onPress: props.onPress, + onPressIn: props.onPressIn, + onPressOut: props.onPressOut, + }; +} + module.exports = TouchableWithoutFeedback; diff --git a/Libraries/Pressability/Pressability.js b/Libraries/Pressability/Pressability.js index 5ff89d9f406..fc1e00ad5d4 100644 --- a/Libraries/Pressability/Pressability.js +++ b/Libraries/Pressability/Pressability.js @@ -27,107 +27,124 @@ import * as React from 'react'; export type PressabilityConfig = $ReadOnly<{| /** - * Returns the amount to extend the `VisualRect` by to create `HitRect`. + * Whether a press gesture can be interrupted by a parent gesture such as a + * scroll event. Defaults to true. */ - getHitSlop?: ?() => ?EdgeInsetsProp, + cancelable?: ?boolean, /** - * Returns the duration to wait after hover in before activation. + * Whether to disable initialization of the press gesture. */ - getHoverInDelayMS?: ?() => ?number, + disabled?: ?boolean, /** - * Returns the duration to wait after hover out before deactivation. + * Amount to extend the `VisualRect` by to create `HitRect`. */ - getHoverOutDelayMS?: ?() => ?number, + hitSlop?: ?EdgeInsetsProp, /** - * Returns the duration (in addition to the value from `getPressDelayMS`) - * after which a press gesture becomes a long press gesture. + * Amount to extend the `HitRect` by to create `PressRect`. */ - getLongPressDelayMS?: ?() => ?number, + pressRectOffset?: ?EdgeInsetsProp, /** - * Returns the duration to wait after press down before activation. - */ - getPressDelayMS?: ?() => ?number, - - /** - * Returns the duration to wait after letting up before deactivation. - */ - getPressOutDelayMS?: ?() => ?number, - - /** - * Returns the amount to extend the `HitRect` by to create `PressRect`. - */ - getPressRectOffset?: ?() => ?EdgeInsetsProp, - - /** - * Returns true to disable playing system sound on touch (Android Only) + * Whether to disable the systemm sound when `onPress` fires on Android. **/ - getTouchSoundDisabled?: ?() => ?boolean, + android_disableSound?: ?boolean, + + /** + * Duration to wait after hover in before calling `onHoverIn`. + */ + delayHoverIn?: ?number, + + /** + * Duration to wait after hover out before calling `onHoverOut`. + */ + delayHoverOut?: ?number, + + /** + * Duration (in addition to `delayPressIn`) after which a press gesture is + * considered a long press gesture. Defaults to 500 (milliseconds). + */ + delayLongPress?: ?number, + + /** + * Duration to wait after press down before calling `onPressIn`. + */ + delayPressIn?: ?number, + + /** + * Duration to wait after letting up before calling `onPressOut`. + */ + delayPressOut?: ?number, /** * Called after the element loses focus. */ - onBlur?: ?(event: BlurEvent) => void, + onBlur?: ?(event: BlurEvent) => mixed, /** * Called after the element is focused. */ - onFocus?: ?(event: FocusEvent) => void, + onFocus?: ?(event: FocusEvent) => mixed, /** * Called when the hover is activated to provide visual feedback. */ - onHoverIn?: ?(event: MouseEvent) => void, + onHoverIn?: ?(event: MouseEvent) => mixed, /** * Called when the hover is deactivated to undo visual feedback. */ - onHoverOut?: ?(event: MouseEvent) => void, + onHoverOut?: ?(event: MouseEvent) => mixed, /** * Called when a long press gesture has been triggered. */ - onLongPress?: ?(event: PressEvent) => void, + onLongPress?: ?(event: PressEvent) => mixed, + + /** + * Called when a press gestute has been triggered. + */ + onPress?: ?(event: PressEvent) => mixed, + + /** + * Called when the press is activated to provide visual feedback. + */ + onPressIn?: ?(event: PressEvent) => mixed, + + /** + * Called when the press location moves. (This should rarely be used.) + */ + onPressMove?: ?(event: PressEvent) => mixed, + + /** + * Called when the press is deactivated to undo visual feedback. + */ + onPressOut?: ?(event: PressEvent) => mixed, /** * Returns whether a long press gesture should cancel the press gesture. * Defaults to true. */ - onLongPressShouldCancelPress?: ?() => boolean, - - /** - * Called when a press gestute has been triggered. - */ - onPress?: ?(event: PressEvent) => void, - - /** - * Called when the press is activated to provide visual feedback. - */ - onPressIn?: ?(event: PressEvent) => void, - - /** - * Called when the press location moves. (This should rarely be used.) - */ - onPressMove?: ?(event: PressEvent) => void, - - /** - * Called when the press is deactivated to undo visual feedback. - */ - onPressOut?: ?(event: PressEvent) => void, + onLongPressShouldCancelPress_DEPRECATED?: ?() => boolean, /** + * If `cancelable` is set, this will be ignored. + * * Returns whether to yield to a lock termination request (e.g. if a native * scroll gesture attempts to steal the responder lock). */ - onResponderTerminationRequest?: ?() => boolean, + onResponderTerminationRequest_DEPRECATED?: ?() => boolean, /** + * If `disabled` is set, this will be ignored. + * * Returns whether to start a press gesture. + * + * @deprecated */ - onStartShouldSetResponder?: ?() => boolean, + onStartShouldSetResponder_DEPRECATED?: ?() => boolean, |}>; type EventHandlers = $ReadOnly<{| @@ -315,7 +332,7 @@ const DEFAULT_PRESS_RECT_OFFSETS = { * * ┌────────────────────────┐ * │ ┌──────────────────┐ │ - Presses start anywhere within `HitRect`, which - * │ │ ┌────────────┐ │ │ is expanded via the prop `getHitSlop`. + * │ │ ┌────────────┐ │ │ is expanded via the prop `hitSlop`. * │ │ │ VisualRect │ │ │ * │ │ └────────────┘ │ │ - When pressed down for sufficient amount of time * │ │ HitRect │ │ before letting up, `VisualRect` activates for @@ -323,7 +340,7 @@ const DEFAULT_PRESS_RECT_OFFSETS = { * │ PressRect o │ * └────────────────────│───┘ * Out Region └────── `PressRect`, which is expanded via the prop - * `getPressRectOffset`, allows presses to move + * `pressRectOffset`, allows presses to move * beyond `HitRect` while maintaining activation * and being eligible for a "press". * @@ -379,6 +396,10 @@ export default class Pressability { _touchState: TouchState = 'NOT_RESPONDER'; constructor(config: PressabilityConfig) { + this.configure(config); + } + + configure(config: PressabilityConfig): void { this._config = config; } @@ -420,11 +441,15 @@ export default class Pressability { }; const responderEventHandlers = { - onStartShouldSetResponder: () => { - const {onStartShouldSetResponder} = this._config; - return onStartShouldSetResponder == null - ? true - : onStartShouldSetResponder(); + onStartShouldSetResponder: (): boolean => { + const {disabled} = this._config; + if (disabled == null) { + const {onStartShouldSetResponder_DEPRECATED} = this._config; + return onStartShouldSetResponder_DEPRECATED == null + ? true + : onStartShouldSetResponder_DEPRECATED(); + } + return !disabled; }, onResponderGrant: (event: PressEvent): void => { @@ -436,29 +461,28 @@ export default class Pressability { this._touchState = 'NOT_RESPONDER'; this._receiveSignal('RESPONDER_GRANT', event); - const {getLongPressDelayMS, getPressDelayMS} = this._config; - - const pressDelay = getDelayMS( - getPressDelayMS, + const delayPressIn = normalizeDelay( + this._config.delayPressIn, 0, DEFAULT_PRESS_DELAY_MS, ); - if (pressDelay > 0) { + + if (delayPressIn > 0) { this._pressDelayTimeout = setTimeout(() => { this._receiveSignal('DELAY', event); - }, pressDelay); + }, delayPressIn); } else { this._receiveSignal('DELAY', event); } - const longPressDelay = getDelayMS( - getLongPressDelayMS, + const delayLongPress = normalizeDelay( + this._config.delayLongPress, 10, DEFAULT_LONG_PRESS_DELAY_MS, ); this._longPressDelayTimeout = setTimeout(() => { this._handleLongPress(event); - }, longPressDelay + pressDelay); + }, delayLongPress + delayPressIn); }, onResponderMove: (event: PressEvent): void => { @@ -504,13 +528,17 @@ export default class Pressability { }, onResponderTerminationRequest: (): boolean => { - const {onResponderTerminationRequest} = this._config; - return onResponderTerminationRequest == null - ? true - : onResponderTerminationRequest(); + const {cancelable} = this._config; + if (cancelable == null) { + const {onResponderTerminationRequest_DEPRECATED} = this._config; + return onResponderTerminationRequest_DEPRECATED == null + ? true + : onResponderTerminationRequest_DEPRECATED(); + } + return cancelable; }, - onClick: (event: PressEvent) => { + onClick: (event: PressEvent): void => { const {onPress} = this._config; if (onPress != null) { onPress(event); @@ -526,13 +554,15 @@ export default class Pressability { if (isHoverEnabled()) { this._isHovered = true; this._cancelHoverOutDelayTimeout(); - const {onHoverIn, getHoverInDelayMS} = this._config; + const {onHoverIn} = this._config; if (onHoverIn != null) { - const delay = getDelayMS(getHoverInDelayMS); - if (delay > 0) { + const delayHoverIn = normalizeDelay( + this._config.delayHoverIn, + ); + if (delayHoverIn > 0) { this._hoverInDelayTimeout = setTimeout(() => { onHoverIn(event); - }, delay); + }, delayHoverIn); } else { onHoverIn(event); } @@ -544,13 +574,15 @@ export default class Pressability { if (this._isHovered) { this._isHovered = false; this._cancelHoverInDelayTimeout(); - const {onHoverOut, getHoverOutDelayMS} = this._config; + const {onHoverOut} = this._config; if (onHoverOut != null) { - const delay = getDelayMS(getHoverOutDelayMS); - if (delay > 0) { + const delayHoverOut = normalizeDelay( + this._config.delayHoverOut, + ); + if (delayHoverOut > 0) { this._hoverInDelayTimeout = setTimeout(() => { onHoverOut(event); - }, delay); + }, delayHoverOut); } else { onHoverOut(event); } @@ -633,7 +665,7 @@ export default class Pressability { } if (isPressInSignal(prevState) && signal === 'RESPONDER_RELEASE') { - const {onLongPress, onPress, getTouchSoundDisabled} = this._config; + const {onLongPress, onPress, android_disableSound} = this._config; if (onPress != null) { const isPressCanceledByLongPress = onLongPress != null && @@ -645,10 +677,7 @@ export default class Pressability { this._activate(event); this._deactivate(event); } - const isTouchSoundDisabled = - (getTouchSoundDisabled == null ? null : getTouchSoundDisabled()) ?? - false; - if (Platform.OS === 'android' && !isTouchSoundDisabled) { + if (Platform.OS === 'android' && android_disableSound !== true) { SoundManager.playTouchSound(); } onPress(event); @@ -672,13 +701,13 @@ export default class Pressability { } _deactivate(event: PressEvent): void { - const {onPressOut, getPressOutDelayMS} = this._config; + const {onPressOut} = this._config; if (onPressOut != null) { - const delay = getDelayMS(getPressOutDelayMS); - if (delay > 0) { + const delayPressOut = normalizeDelay(this._config.delayPressOut); + if (delayPressOut > 0) { this._pressOutDelayTimeout = setTimeout(() => { onPressOut(event); - }, delay); + }, delayPressOut); } else { onPressOut(event); } @@ -718,14 +747,13 @@ export default class Pressability { top: number, |}>, ): boolean { - const {getHitSlop, getPressRectOffset} = this._config; + const {hitSlop, pressRectOffset} = this._config; let regionBottom = responderRegion.bottom; let regionLeft = responderRegion.left; let regionRight = responderRegion.right; let regionTop = responderRegion.top; - const hitSlop = getHitSlop == null ? null : getHitSlop(); if (hitSlop != null) { if (hitSlop.bottom != null) { regionBottom += hitSlop.bottom; @@ -741,23 +769,11 @@ export default class Pressability { } } - const rectOffset = getPressRectOffset == null ? null : getPressRectOffset(); regionBottom += - rectOffset == null || rectOffset.bottom == null - ? DEFAULT_PRESS_RECT_OFFSETS.bottom - : rectOffset.bottom; - regionLeft -= - rectOffset == null || rectOffset.left == null - ? DEFAULT_PRESS_RECT_OFFSETS.left - : rectOffset.left; - regionRight += - rectOffset == null || rectOffset.right == null - ? DEFAULT_PRESS_RECT_OFFSETS.right - : rectOffset.right; - regionTop -= - rectOffset == null || rectOffset.top == null - ? DEFAULT_PRESS_RECT_OFFSETS.top - : rectOffset.top; + pressRectOffset?.bottom ?? DEFAULT_PRESS_RECT_OFFSETS.bottom; + regionLeft -= pressRectOffset?.left ?? DEFAULT_PRESS_RECT_OFFSETS.left; + regionRight += pressRectOffset?.right ?? DEFAULT_PRESS_RECT_OFFSETS.right; + regionTop -= pressRectOffset?.top ?? DEFAULT_PRESS_RECT_OFFSETS.top; return ( touch.pageX > regionLeft && @@ -778,8 +794,8 @@ export default class Pressability { _shouldLongPressCancelPress(): boolean { return ( - this._config.onLongPressShouldCancelPress == null || - this._config.onLongPressShouldCancelPress() + this._config.onLongPressShouldCancelPress_DEPRECATED == null || + this._config.onLongPressShouldCancelPress_DEPRECATED() ); } @@ -819,9 +835,9 @@ export default class Pressability { } } -const getDelayMS = (getDelay: ?() => ?number, min = 0, fallback = 0) => { - return Math.max(min, (getDelay == null ? null : getDelay()) ?? fallback); -}; +function normalizeDelay(delay: ?number, min = 0, fallback = 0): number { + return Math.max(min, delay ?? fallback); +} const getTouchFromPressEvent = (event: PressEvent) => { const {changedTouches, touches} = event.nativeEvent; diff --git a/Libraries/Pressability/__tests__/Pressability-test.js b/Libraries/Pressability/__tests__/Pressability-test.js index 5516fa747b9..62c6eaab431 100644 --- a/Libraries/Pressability/__tests__/Pressability-test.js +++ b/Libraries/Pressability/__tests__/Pressability-test.js @@ -31,24 +31,26 @@ function getMock, TReturn>( const createMockPressability = overrides => { const config = { - getHitSlop: jest.fn(), - getHoverInDelayMS: jest.fn(() => null), - getHoverOutDelayMS: jest.fn(() => null), - getLongPressDelayMS: jest.fn(() => null), - getPressDelayMS: jest.fn(() => null), - getPressOutDelayMS: jest.fn(() => null), - getPressRectOffset: jest.fn(), + cancelable: null, + disabled: null, + hitSlop: null, + pressRectOffset: null, + delayHoverIn: null, + delayHoverOut: null, + delayLongPress: null, + delayPressIn: null, + delayPressOut: null, onBlur: jest.fn(), onFocus: jest.fn(), onHoverIn: jest.fn(), onHoverOut: jest.fn(), onLongPress: jest.fn(), - onLongPressShouldCancelPress: jest.fn(), onPress: jest.fn(), onPressIn: jest.fn(), onPressOut: jest.fn(), - onResponderTerminationRequest: jest.fn(() => true), - onStartShouldSetResponder: jest.fn(() => true), + onLongPressShouldCancelPress_DEPRECATED: jest.fn(), + onResponderTerminationRequest_DEPRECATED: jest.fn(() => true), + onStartShouldSetResponder_DEPRECATED: jest.fn(() => true), ...overrides, }; const touchable = new Pressability(config); @@ -282,7 +284,7 @@ describe('Pressability', () => { it('is called after no delay by default', () => { const {config, handlers} = createMockPressability({ - getHoverInDelayMS: () => null, + delayHoverIn: null, }); invariant( typeof handlers.onMouseEnter === 'function', @@ -293,9 +295,9 @@ describe('Pressability', () => { expect(config.onHoverIn).toBeCalled(); }); - it('falls back to no delay if `getHoverInDelayMS` is omitted', () => { + it('falls back to no delay if `delayHoverIn` is omitted', () => { const {config, handlers} = createMockPressability({ - getHoverInDelayMS: null, + delayHoverIn: null, }); invariant( typeof handlers.onMouseEnter === 'function', @@ -308,7 +310,7 @@ describe('Pressability', () => { it('is called after a configured delay', () => { const {config, handlers} = createMockPressability({ - getHoverInDelayMS: () => 500, + delayHoverIn: 500, }); invariant( typeof handlers.onMouseEnter === 'function', @@ -324,7 +326,7 @@ describe('Pressability', () => { it('is called synchronously if delay is 0ms', () => { const {config, handlers} = createMockPressability({ - getHoverInDelayMS: () => 0, + delayHoverIn: 0, }); invariant( typeof handlers.onMouseEnter === 'function', @@ -354,7 +356,7 @@ describe('Pressability', () => { it('is called if pressed for 500ms after the press delay', () => { const {config, handlers} = createMockPressability({ - getPressDelayMS: () => 100, + delayPressIn: 100, }); handlers.onStartShouldSetResponder(); @@ -383,7 +385,7 @@ describe('Pressability', () => { it('falls back to a minimum of 10ms before calling `onLongPress`', () => { const {config, handlers} = createMockPressability({ - getLongPressDelayMS: () => 0, + delayLongPress: 0, }); handlers.onStartShouldSetResponder(); @@ -432,7 +434,7 @@ describe('Pressability', () => { it('is called after no delay by default', () => { const {config, handlers} = createMockPressability({ - getPressDelayMS: () => null, + delayPressIn: null, }); handlers.onStartShouldSetResponder(); @@ -442,9 +444,9 @@ describe('Pressability', () => { expect(config.onPressIn).toBeCalled(); }); - it('falls back to no delay if `getPressDelayMS` is omitted', () => { + it('falls back to no delay if `delayPressIn` is omitted', () => { const {config, handlers} = createMockPressability({ - getPressDelayMS: null, + delayPressIn: null, }); handlers.onStartShouldSetResponder(); @@ -456,7 +458,7 @@ describe('Pressability', () => { it('is called after a configured delay', () => { const {config, handlers} = createMockPressability({ - getPressDelayMS: () => 500, + delayPressIn: 500, }); handlers.onStartShouldSetResponder(); @@ -471,7 +473,7 @@ describe('Pressability', () => { it('is called synchronously if delay is 0ms', () => { const {config, handlers} = createMockPressability({ - getPressDelayMS: () => 0, + delayPressIn: 0, }); handlers.onStartShouldSetResponder(); @@ -489,7 +491,7 @@ describe('Pressability', () => { it('`onPress*` are called when no delay', () => { mockUIManagerMeasure(); const {config, handlers} = createMockPressability({ - getHitSlop: () => mockSlop, + hitSlop: mockSlop, }); handlers.onStartShouldSetResponder(); @@ -521,8 +523,8 @@ describe('Pressability', () => { it('`onPress*` are called after a delay', () => { mockUIManagerMeasure(); const {config, handlers} = createMockPressability({ - getHitSlop: () => mockSlop, - getPressDelayMS: () => 500, + hitSlop: mockSlop, + delayPressIn: 500, }); handlers.onStartShouldSetResponder(); @@ -584,7 +586,7 @@ describe('Pressability', () => { it('`onPress*` are not called after a delay', () => { mockUIManagerMeasure(); const {config, handlers} = createMockPressability({ - getPressDelayMS: () => 500, + delayPressIn: 500, }); handlers.onStartShouldSetResponder(); @@ -611,7 +613,7 @@ describe('Pressability', () => { it('`onPress*` are called when press is released before measure completes', () => { mockUIManagerMeasure({delay: 1000}); const {config, handlers} = createMockPressability({ - getPressDelayMS: () => 500, + delayPressIn: 500, }); handlers.onStartShouldSetResponder(); @@ -643,7 +645,7 @@ describe('Pressability', () => { describe('onStartShouldSetResponder', () => { it('if omitted the responder is set by default', () => { const {handlers} = createMockPressability({ - onStartShouldSetResponder: null, + onStartShouldSetResponder_DEPRECATED: null, }); expect(handlers.onStartShouldSetResponder()).toBe(true); @@ -651,11 +653,14 @@ describe('Pressability', () => { it('if supplied it is called', () => { const {config, handlers} = createMockPressability(); + const onStartShouldSetResponder_DEPRECATED = nullthrows( + config.onStartShouldSetResponder_DEPRECATED, + ); - nullthrows(config.onStartShouldSetResponder).mockReturnValue(false); + onStartShouldSetResponder_DEPRECATED.mockReturnValue(false); expect(handlers.onStartShouldSetResponder()).toBe(false); - nullthrows(config.onStartShouldSetResponder).mockReturnValue(true); + onStartShouldSetResponder_DEPRECATED.mockReturnValue(true); expect(handlers.onStartShouldSetResponder()).toBe(true); }); });