From 8e36cc02931d121832ed89efb02092125a728315 Mon Sep 17 00:00:00 2001 From: James Maxell Eroy Date: Thu, 28 Sep 2023 10:00:38 -0700 Subject: [PATCH] Add `onPress` prop (#39701) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/39701 TextInput already uses Pressability, but doesn't expose the onPress prop. Link:https://github.com/facebook/react-native/blob/main/packages/react-native/Libraries/Components/TextInput/TextInput.js#L1381-L1414. Currently TextInput only exposes the onPressIn() and onPressOut() props from Pressability. While onPressOut() can serve the same purpose as onPress() in most cases, it doesn't fare well with PanResponder...say a swipe gesture implemented using PanResponder. When the pointer/cursor exits the hit test bounds of TextInput, onPressOut() will be triggered even though the desired behavior could be that we only want to invoke the event handler when the user lifts their finger from the screen (while still in the hit test bounds of the TextInput). Example of TextInput in a PanResponder: https://snack.expo.dev/jambalaya/panresponder Changelog: [General][Added] Add onPress prop to TextInput Reviewed By: NickGerleman Differential Revision: D49653011 fbshipit-source-id: 28477416c6c0f17a0737986cab49e51a55094ba7 --- .../Components/TextInput/TextInput.d.ts | 5 +++ .../Components/TextInput/TextInput.flow.js | 5 +++ .../Components/TextInput/TextInput.js | 37 +++++++++++++------ 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/packages/react-native/Libraries/Components/TextInput/TextInput.d.ts b/packages/react-native/Libraries/Components/TextInput/TextInput.d.ts index 2c0c099a894fca..fa196a19491df0 100644 --- a/packages/react-native/Libraries/Components/TextInput/TextInput.d.ts +++ b/packages/react-native/Libraries/Components/TextInput/TextInput.d.ts @@ -738,6 +738,11 @@ export interface TextInputProps | ((e: NativeSyntheticEvent) => void) | undefined; + /** + * Called when a single tap gesture is detected. + */ + onPress?: ((e: NativeSyntheticEvent) => void) | undefined; + /** * Callback that is called when a touch is engaged. */ diff --git a/packages/react-native/Libraries/Components/TextInput/TextInput.flow.js b/packages/react-native/Libraries/Components/TextInput/TextInput.flow.js index 9adbfe9f6f190b..39ba481b7e4751 100644 --- a/packages/react-native/Libraries/Components/TextInput/TextInput.flow.js +++ b/packages/react-native/Libraries/Components/TextInput/TextInput.flow.js @@ -766,6 +766,11 @@ export type Props = $ReadOnly<{| */ unstable_onKeyPressSync?: ?(e: KeyPressEvent) => mixed, + /** + * Called when a single tap gesture is detected. + */ + onPress?: ?(event: PressEvent) => mixed, + /** * Called when a touch is engaged. */ diff --git a/packages/react-native/Libraries/Components/TextInput/TextInput.js b/packages/react-native/Libraries/Components/TextInput/TextInput.js index 481938f08434a5..2dddd7ae6bff38 100644 --- a/packages/react-native/Libraries/Components/TextInput/TextInput.js +++ b/packages/react-native/Libraries/Components/TextInput/TextInput.js @@ -808,6 +808,11 @@ export type Props = $ReadOnly<{| */ unstable_onKeyPressSync?: ?(e: KeyPressEvent) => mixed, + /** + * Called when a single tap gesture is detected. + */ + onPress?: ?(event: PressEvent) => mixed, + /** * Called when a touch is engaged. */ @@ -1378,27 +1383,37 @@ function InternalTextInput(props: Props): React.Node { const accessible = props.accessible !== false; const focusable = props.focusable !== false; + const { + editable, + hitSlop, + onPress, + onPressIn, + onPressOut, + rejectResponderTermination, + } = props; + const config = React.useMemo( () => ({ - hitSlop: props.hitSlop, + hitSlop, onPress: (event: PressEvent) => { - if (props.editable !== false) { + onPress?.(event); + if (editable !== false) { if (inputRef.current != null) { inputRef.current.focus(); } } }, - onPressIn: props.onPressIn, - onPressOut: props.onPressOut, - cancelable: - Platform.OS === 'ios' ? !props.rejectResponderTermination : null, + onPressIn: onPressIn, + onPressOut: onPressOut, + cancelable: Platform.OS === 'ios' ? !rejectResponderTermination : null, }), [ - props.editable, - props.hitSlop, - props.onPressIn, - props.onPressOut, - props.rejectResponderTermination, + editable, + hitSlop, + onPress, + onPressIn, + onPressOut, + rejectResponderTermination, ], );