diff --git a/packages/react-native/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h b/packages/react-native/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h index 1f1af7ee7f0..d2cec7f1bfb 100644 --- a/packages/react-native/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h +++ b/packages/react-native/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h @@ -30,6 +30,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign) BOOL caretHidden; @property (nonatomic, assign) BOOL enablesReturnKeyAutomatically; @property (nonatomic, assign) UITextFieldViewMode clearButtonMode; +@property (nonatomic, assign) UIDataDetectorTypes dataDetectorTypes; @property (nonatomic, getter=isScrollEnabled) BOOL scrollEnabled; @property (nonatomic, strong, nullable) NSString *inputAccessoryViewID; @property (nonatomic, strong, nullable) NSString *inputAccessoryViewButtonLabel; diff --git a/packages/react-native/Libraries/Text/TextInput/Singleline/RCTUITextField.mm b/packages/react-native/Libraries/Text/TextInput/Singleline/RCTUITextField.mm index 338a1f4fa99..d1bcd88a988 100644 --- a/packages/react-native/Libraries/Text/TextInput/Singleline/RCTUITextField.mm +++ b/packages/react-native/Libraries/Text/TextInput/Singleline/RCTUITextField.mm @@ -19,6 +19,10 @@ NSArray *_initialValueTrailingBarButtonGroups; } +// This should not be needed but internal build were failing without it. +// This variable is unused. +@synthesize dataDetectorTypes; + - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm index faa494a615d..940c4bd608e 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm @@ -194,6 +194,12 @@ static NSSet *returnKeyTypesSet; _backedTextInputView.editable = newTextInputProps.traits.editable; } + if (newTextInputProps.multiline && + newTextInputProps.traits.dataDetectorTypes != oldTextInputProps.traits.dataDetectorTypes) { + _backedTextInputView.dataDetectorTypes = + RCTUITextViewDataDetectorTypesFromStringVector(newTextInputProps.traits.dataDetectorTypes); + } + if (newTextInputProps.traits.enablesReturnKeyAutomatically != oldTextInputProps.traits.enablesReturnKeyAutomatically) { _backedTextInputView.enablesReturnKeyAutomatically = newTextInputProps.traits.enablesReturnKeyAutomatically; diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputUtils.h b/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputUtils.h index 02a1570df34..5f20d8762f4 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputUtils.h +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputUtils.h @@ -41,4 +41,6 @@ UITextInputPasswordRules *RCTUITextInputPasswordRulesFromString(const std::strin UITextSmartInsertDeleteType RCTUITextSmartInsertDeleteTypeFromOptionalBool(std::optional smartInsertDelete); +UIDataDetectorTypes RCTUITextViewDataDetectorTypesFromStringVector(const std::vector &dataDetectorTypes); + NS_ASSUME_NONNULL_END diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputUtils.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputUtils.mm index 6e562ada332..fb7d5ca1c17 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputUtils.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputUtils.mm @@ -269,3 +269,31 @@ UITextSmartInsertDeleteType RCTUITextSmartInsertDeleteTypeFromOptionalBool(std:: ? (*smartInsertDelete ? UITextSmartInsertDeleteTypeYes : UITextSmartInsertDeleteTypeNo) : UITextSmartInsertDeleteTypeDefault; } + +UIDataDetectorTypes RCTUITextViewDataDetectorTypesFromStringVector(const std::vector &dataDetectorTypes) +{ + static dispatch_once_t onceToken; + static NSDictionary *dataDetectorTypesMap = nil; + + dispatch_once(&onceToken, ^{ + dataDetectorTypesMap = @{ + @"link" : @(UIDataDetectorTypeLink), + @"phoneNumber" : @(UIDataDetectorTypePhoneNumber), + @"address" : @(UIDataDetectorTypeAddress), + @"calendarEvent" : @(UIDataDetectorTypeCalendarEvent), + @"trackingNumber" : @(UIDataDetectorTypeShipmentTrackingNumber), + @"flightNumber" : @(UIDataDetectorTypeFlightNumber), + @"lookupSuggestion" : @(UIDataDetectorTypeLookupSuggestion), + @"all" : @(UIDataDetectorTypeAll) + }; + }); + + UIDataDetectorTypes ret = UIDataDetectorTypeNone; + for (const auto &dataType : dataDetectorTypes) { + NSNumber *val = dataDetectorTypesMap[RCTNSStringFromString(dataType)]; + if (val) { + ret |= (UIDataDetectorTypes)val.unsignedIntValue; + } + } + return ret; +} diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/primitives.h b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/primitives.h index a6e6dfd2a49..a5ace05d642 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/primitives.h +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/primitives.h @@ -10,6 +10,7 @@ #include #include #include +#include namespace facebook::react { @@ -192,6 +193,12 @@ class TextInputTraits final { */ bool selectTextOnFocus{false}; + /* + * iOS-only + * Default value: `empty` (`null`). + */ + std::vector dataDetectorTypes{}; + /* * iOS-only (inherently iOS-specific) * Default value: `` (default content type). diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/propsConversions.h b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/propsConversions.h index ccbbe207736..8039fbf56b1 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/propsConversions.h +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/propsConversions.h @@ -136,6 +136,13 @@ static TextInputTraits convertRawProp( sourceTraits.smartInsertDelete, defaultTraits.smartInsertDelete); + traits.dataDetectorTypes = convertRawProp( + context, + rawProps, + "dataDetectorTypes", + sourceTraits.dataDetectorTypes, + defaultTraits.dataDetectorTypes); + return traits; } diff --git a/packages/rn-tester/js/examples/TextInput/TextInputExample.ios.js b/packages/rn-tester/js/examples/TextInput/TextInputExample.ios.js index d94a57d4b1b..fcec23ea083 100644 --- a/packages/rn-tester/js/examples/TextInput/TextInputExample.ios.js +++ b/packages/rn-tester/js/examples/TextInput/TextInputExample.ios.js @@ -633,6 +633,14 @@ const textInputExamples: Array = [ style={styles.multiline} dataDetectorTypes="phoneNumber" /> + ); },