diff --git a/Libraries/Components/TextInput/TextInput.js b/Libraries/Components/TextInput/TextInput.js index 96f64d82e9f..cb0d18d54ff 100644 --- a/Libraries/Components/TextInput/TextInput.js +++ b/Libraries/Components/TextInput/TextInput.js @@ -632,6 +632,21 @@ export type Props = $ReadOnly<{| */ onKeyPress?: ?(e: KeyPressEvent) => mixed, + /** + * DANGER: this API is not stable and will change in the future. + * + * Callback will be called on the main thread and may result in dropped frames. + * + * Callback that is called when a key is pressed. + * This will be called with `{ nativeEvent: { key: keyValue } }` + * where `keyValue` is `'Enter'` or `'Backspace'` for respective keys and + * the typed-in character otherwise including `' '` for space. + * Fires before `onChange` callbacks. + * + * Only available in Fabric on iOS. + */ + unstable_onKeyPressSync?: ?(e: KeyPressEvent) => mixed, + /** * Called when a touch is engaged. */ @@ -1184,6 +1199,7 @@ function InternalTextInput(props: Props): React.Node { focusable={focusable} mostRecentEventCount={mostRecentEventCount} onBlur={_onBlur} + onKeyPressSync={props.unstable_onKeyPressSync} onChange={_onChange} onContentSizeChange={props.onContentSizeChange} onFocus={_onFocus} diff --git a/Libraries/Text/TextInput/RCTBaseTextInputViewManager.m b/Libraries/Text/TextInput/RCTBaseTextInputViewManager.m index 77d70d14c76..1deb6f6605e 100644 --- a/Libraries/Text/TextInput/RCTBaseTextInputViewManager.m +++ b/Libraries/Text/TextInput/RCTBaseTextInputViewManager.m @@ -61,6 +61,7 @@ RCT_EXPORT_VIEW_PROPERTY(textContentType, NSString) RCT_EXPORT_VIEW_PROPERTY(passwordRules, NSString) RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onKeyPressSync, RCTDirectEventBlock) RCT_EXPORT_VIEW_PROPERTY(onSelectionChange, RCTDirectEventBlock) RCT_EXPORT_VIEW_PROPERTY(onTextInput, RCTDirectEventBlock) RCT_EXPORT_VIEW_PROPERTY(onScroll, RCTDirectEventBlock) diff --git a/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm b/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm index 7123913d9ed..95b0ccae315 100644 --- a/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm @@ -323,16 +323,23 @@ using namespace facebook::react; - (NSString *)textInputShouldChangeText:(NSString *)text inRange:(NSRange)range { + auto const &props = *std::static_pointer_cast(_props); + if (!_backedTextInputView.textWasPasted) { if (_eventEmitter) { KeyPressMetrics keyPressMetrics; keyPressMetrics.text = RCTStringFromNSString(text); keyPressMetrics.eventCount = _mostRecentEventCount; - std::static_pointer_cast(_eventEmitter)->onKeyPress(keyPressMetrics); + + auto const &textInputEventEmitter = *std::static_pointer_cast(_eventEmitter); + if (props.onKeyPressSync) { + textInputEventEmitter.onKeyPressSync(keyPressMetrics); + } else { + textInputEventEmitter.onKeyPress(keyPressMetrics); + } } } - auto const &props = *std::static_pointer_cast(_props); if (props.maxLength) { NSInteger allowedLength = props.maxLength - _backedTextInputView.attributedText.string.length + range.length; diff --git a/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputEventEmitter.cpp b/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputEventEmitter.cpp index 13efb53cb6c..92172e2db1a 100644 --- a/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputEventEmitter.cpp +++ b/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputEventEmitter.cpp @@ -105,6 +105,16 @@ void TextInputEventEmitter::onKeyPress( EventPriority::AsynchronousBatched); } +void TextInputEventEmitter::onKeyPressSync( + KeyPressMetrics const &keyPressMetrics) const { + dispatchEvent( + "keyPressSync", + [keyPressMetrics](jsi::Runtime &runtime) { + return keyPressMetricsPayload(runtime, keyPressMetrics); + }, + EventPriority::SynchronousBatched); +} + void TextInputEventEmitter::onScroll( TextInputMetrics const &textInputMetrics) const { dispatchTextInputEvent("scroll", textInputMetrics); diff --git a/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputEventEmitter.h b/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputEventEmitter.h index 10510ae5c57..e241a2d0d1c 100644 --- a/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputEventEmitter.h +++ b/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputEventEmitter.h @@ -45,6 +45,7 @@ class TextInputEventEmitter : public ViewEventEmitter { void onEndEditing(TextInputMetrics const &textInputMetrics) const; void onSubmitEditing(TextInputMetrics const &textInputMetrics) const; void onKeyPress(KeyPressMetrics const &textInputMetrics) const; + void onKeyPressSync(KeyPressMetrics const &textInputMetrics) const; void onScroll(TextInputMetrics const &textInputMetrics) const; private: diff --git a/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputProps.cpp b/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputProps.cpp index 9e13cb97bb0..c51fde989ae 100644 --- a/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputProps.cpp +++ b/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputProps.cpp @@ -93,6 +93,12 @@ TextInputProps::TextInputProps( rawProps, "inputAccessoryViewID", sourceProps.inputAccessoryViewID, + {})), + onKeyPressSync(convertRawProp( + context, + rawProps, + "onKeyPressSync", + sourceProps.onKeyPressSync, {})){}; TextAttributes TextInputProps::getEffectiveTextAttributes( diff --git a/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputProps.h b/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputProps.h index 9fb44a4b2c1..c78f8b55c02 100644 --- a/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputProps.h +++ b/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputProps.h @@ -62,6 +62,8 @@ class TextInputProps final : public ViewProps, public BaseTextProps { std::string const inputAccessoryViewID{}; + bool onKeyPressSync{false}; + /* * Accessors */