diff --git a/packages/react-native/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js b/packages/react-native/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js index de487e09a704eb..40738366b5d2c2 100644 --- a/packages/react-native/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js +++ b/packages/react-native/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js @@ -699,7 +699,6 @@ export const __INTERNAL_VIEW_CONFIG: PartialViewConfig = { fontStyle: true, textShadowOffset: true, selectionColor: {process: require('../../StyleSheet/processColor').default}, - selection: true, placeholderTextColor: { process: require('../../StyleSheet/processColor').default, }, diff --git a/packages/react-native/Libraries/Components/TextInput/TextInput.js b/packages/react-native/Libraries/Components/TextInput/TextInput.js index 90d8e1022949d3..1ef9045bf5ae02 100644 --- a/packages/react-native/Libraries/Components/TextInput/TextInput.js +++ b/packages/react-native/Libraries/Components/TextInput/TextInput.js @@ -1095,27 +1095,19 @@ function InternalTextInput(props: Props): React.Node { tabIndex, rows, numberOfLines, + selection: propsSelection, ...otherProps } = props; const inputRef = useRef>>(null); - // Android sends a "onTextChanged" event followed by a "onSelectionChanged" event, for - // the same "most recent event count". - // For controlled selection, that means that immediately after text is updated, - // a controlled component will pass in the *previous* selection, even if the controlled - // component didn't mean to modify the selection at all. - // Therefore, we ignore selections and pass them through until the selection event has - // been sent. - // Note that this mitigation is NOT needed for Fabric. - // discovered when upgrading react-hooks // eslint-disable-next-line react-hooks/exhaustive-deps - let selection: ?Selection = - props.selection == null + const selection: ?Selection = + propsSelection == null ? null : { - start: props.selection.start, - end: props.selection.end ?? props.selection.start, + start: propsSelection.start, + end: propsSelection.end ?? propsSelection.start, }; const [mostRecentEventCount, setMostRecentEventCount] = useState(0); @@ -1127,12 +1119,6 @@ function InternalTextInput(props: Props): React.Node { |}>({selection, mostRecentEventCount}); const lastNativeSelection = lastNativeSelectionState.selection; - const lastNativeSelectionEventCount = - lastNativeSelectionState.mostRecentEventCount; - - if (lastNativeSelectionEventCount < mostRecentEventCount) { - selection = null; - } let viewCommands; if (AndroidTextInputCommands) { @@ -1533,7 +1519,6 @@ function InternalTextInput(props: Props): React.Node { onScroll={_onScroll} onSelectionChange={_onSelectionChange} placeholder={placeholder} - selection={selection} style={style} text={text} textBreakStrategy={props.textBreakStrategy} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextUpdate.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextUpdate.java index 82c9f5d75ac517..0802ec3622a1bd 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextUpdate.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextUpdate.java @@ -27,8 +27,6 @@ public class ReactTextUpdate { private final float mPaddingBottom; private final int mTextAlign; private final int mTextBreakStrategy; - private final int mSelectionStart; - private final int mSelectionEnd; private final int mJustificationMode; /** @@ -55,35 +53,7 @@ public ReactTextUpdate( paddingBottom, textAlign, Layout.BREAK_STRATEGY_HIGH_QUALITY, - Layout.JUSTIFICATION_MODE_NONE, - -1, - -1); - } - - public ReactTextUpdate( - Spannable text, - int jsEventCounter, - boolean containsImages, - float paddingStart, - float paddingTop, - float paddingEnd, - float paddingBottom, - int textAlign, - int textBreakStrategy, - int justificationMode) { - this( - text, - jsEventCounter, - containsImages, - paddingStart, - paddingTop, - paddingEnd, - paddingBottom, - textAlign, - textBreakStrategy, - justificationMode, - -1, - -1); + Layout.JUSTIFICATION_MODE_NONE); } public ReactTextUpdate( @@ -103,9 +73,7 @@ public ReactTextUpdate( UNSET, textAlign, textBreakStrategy, - justificationMode, - -1, - -1); + justificationMode); } public ReactTextUpdate( @@ -118,9 +86,7 @@ public ReactTextUpdate( float paddingBottom, int textAlign, int textBreakStrategy, - int justificationMode, - int selectionStart, - int selectionEnd) { + int justificationMode) { mText = text; mJsEventCounter = jsEventCounter; mContainsImages = containsImages; @@ -130,8 +96,6 @@ public ReactTextUpdate( mPaddingBottom = paddingBottom; mTextAlign = textAlign; mTextBreakStrategy = textBreakStrategy; - mSelectionStart = selectionStart; - mSelectionEnd = selectionEnd; mJustificationMode = justificationMode; } @@ -187,12 +151,4 @@ public int getTextBreakStrategy() { public int getJustificationMode() { return mJustificationMode; } - - public int getSelectionStart() { - return mSelectionStart; - } - - public int getSelectionEnd() { - return mSelectionEnd; - } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java index 365617d628b7c8..67d0b739568fc7 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java @@ -116,6 +116,7 @@ public class ReactEditText extends AppCompatEditText private int mFontStyle = UNSET; private boolean mAutoFocus = false; private boolean mDidAttachToWindow = false; + private @Nullable String mPlaceholder = null; private ReactViewBackgroundManager mReactBackgroundManager; @@ -502,6 +503,13 @@ public void setInputType(int type) { setKeyListener(mKeyListener); } + public void setPlaceholder(@Nullable String placeholder) { + if (!Objects.equals(placeholder, mPlaceholder)) { + mPlaceholder = placeholder; + setHint(placeholder); + } + } + public void setFontFamily(String fontFamily) { mFontFamily = fontFamily; mTypefaceDirty = true; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java index a4dc4a877a9a30..bdbeca32282dec 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java @@ -330,7 +330,7 @@ public void receiveCommand( if (!args.isNull(1)) { String text = args.getString(1); reactEditText.maybeSetTextFromJS( - getReactTextUpdate(text, mostRecentEventCount, start, end)); + getReactTextUpdate(text, mostRecentEventCount)); } reactEditText.maybeSetSelection(mostRecentEventCount, start, end); break; @@ -338,12 +338,12 @@ public void receiveCommand( } private ReactTextUpdate getReactTextUpdate( - String text, int mostRecentEventCount, int start, int end) { + String text, int mostRecentEventCount) { SpannableStringBuilder sb = new SpannableStringBuilder(); sb.append(TextTransform.apply(text, TextTransform.UNSET)); return new ReactTextUpdate( - sb, mostRecentEventCount, false, 0, 0, 0, 0, Gravity.NO_GRAVITY, 0, 0, start, end); + sb, mostRecentEventCount, false, 0, 0, 0, 0, Gravity.NO_GRAVITY, 0, 0); } @Override @@ -374,9 +374,9 @@ public void updateExtraData(ReactEditText view, Object extraData) { // Ensure that selection is handled correctly on text update boolean isCurrentSelectionEmpty = view.getSelectionStart() == view.getSelectionEnd(); - int selectionStart = update.getSelectionStart(); - int selectionEnd = update.getSelectionEnd(); - if ((selectionStart == UNSET || selectionEnd == UNSET) && isCurrentSelectionEmpty) { + int selectionStart = UNSET; + int selectionEnd = UNSET; + if (isCurrentSelectionEmpty) { // if selection is not set by state, shift current selection to ensure constant gap to // text end int textLength = view.getText() == null ? 0 : view.getText().length(); @@ -508,7 +508,7 @@ public void setAllowFontScaling(ReactEditText view, boolean allowFontScaling) { @ReactProp(name = "placeholder") public void setPlaceholder(ReactEditText view, String placeholder) { - view.setHint(placeholder); + view.setPlaceholder(placeholder); } @ReactProp(name = "placeholderTextColor", customType = "Color") diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputShadowNode.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputShadowNode.java index 26ecd718ca4f54..042f17346586af 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputShadowNode.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputShadowNode.java @@ -44,13 +44,10 @@ public class ReactTextInputShadowNode extends ReactBaseTextShadowNode @VisibleForTesting public static final String PROP_TEXT = "text"; @VisibleForTesting public static final String PROP_PLACEHOLDER = "placeholder"; - @VisibleForTesting public static final String PROP_SELECTION = "selection"; // Represents the {@code text} property only, not possible nested content. private @Nullable String mText = null; private @Nullable String mPlaceholder = null; - private int mSelectionStart = UNSET; - private int mSelectionEnd = UNSET; public ReactTextInputShadowNode( @Nullable ReactTextViewManagerCallback reactTextViewManagerCallback) { @@ -169,18 +166,6 @@ public void setMostRecentEventCount(int mostRecentEventCount) { @ReactProp(name = PROP_TEXT) public void setText(@Nullable String text) { mText = text; - if (text != null) { - // The selection shouldn't be bigger than the length of the text - if (mSelectionStart > text.length()) { - mSelectionStart = text.length(); - } - if (mSelectionEnd > text.length()) { - mSelectionEnd = text.length(); - } - } else { - mSelectionStart = UNSET; - mSelectionEnd = UNSET; - } markUpdated(); } @@ -198,18 +183,6 @@ public void setPlaceholder(@Nullable String placeholder) { return mPlaceholder; } - @ReactProp(name = PROP_SELECTION) - public void setSelection(@Nullable ReadableMap selection) { - mSelectionStart = mSelectionEnd = UNSET; - if (selection == null) return; - - if (selection.hasKey("start") && selection.hasKey("end")) { - mSelectionStart = selection.getInt("start"); - mSelectionEnd = selection.getInt("end"); - markUpdated(); - } - } - @Override public void setTextBreakStrategy(@Nullable String textBreakStrategy) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { @@ -249,9 +222,7 @@ public void onCollectExtraUpdates(UIViewOperationQueue uiViewOperationQueue) { getPadding(Spacing.BOTTOM), mTextAlign, mTextBreakStrategy, - mJustificationMode, - mSelectionStart, - mSelectionEnd); + mJustificationMode); uiViewOperationQueue.enqueueUpdateExtraData(getReactTag(), reactTextUpdate); } } diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputProps.cpp b/packages/react-native/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputProps.cpp index ccb14da3942346..6a8766826b3937 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputProps.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputProps.cpp @@ -138,8 +138,6 @@ AndroidTextInputProps::AndroidTextInputProps( "selectionColor", sourceProps.selectionColor, {})), - selection(CoreFeatures::enablePropIteratorSetter? sourceProps.selection : - convertRawProp(context, rawProps, "selection", sourceProps.selection, {})), value(CoreFeatures::enablePropIteratorSetter? sourceProps.value : convertRawProp(context, rawProps, "value", sourceProps.value, {})), defaultValue(CoreFeatures::enablePropIteratorSetter? sourceProps.defaultValue : convertRawProp(context, rawProps, "defaultValue", @@ -361,7 +359,6 @@ void AndroidTextInputProps::setProp( RAW_SET_PROP_SWITCH_CASE_BASIC(placeholderTextColor); RAW_SET_PROP_SWITCH_CASE_BASIC(secureTextEntry); RAW_SET_PROP_SWITCH_CASE_BASIC(selectionColor); - RAW_SET_PROP_SWITCH_CASE_BASIC(selection); RAW_SET_PROP_SWITCH_CASE_BASIC(defaultValue); RAW_SET_PROP_SWITCH_CASE_BASIC(selectTextOnFocus); RAW_SET_PROP_SWITCH_CASE_BASIC(submitBehavior); @@ -462,7 +459,6 @@ folly::dynamic AndroidTextInputProps::getDynamic() const { props["placeholderTextColor"] = toAndroidRepr(placeholderTextColor); props["secureTextEntry"] = secureTextEntry; props["selectionColor"] = toAndroidRepr(selectionColor); - props["selection"] = toDynamic(selection); props["value"] = value; props["defaultValue"] = defaultValue; props["selectTextOnFocus"] = selectTextOnFocus; diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputProps.h b/packages/react-native/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputProps.h index a24b68f0d036a6..ead28e3b1a1d97 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputProps.h +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputProps.h @@ -27,32 +27,6 @@ namespace facebook { namespace react { -struct AndroidTextInputSelectionStruct { - int start; - int end; -}; - -static inline void fromRawValue( - const PropsParserContext &context, - const RawValue &value, - AndroidTextInputSelectionStruct &result) { - auto map = (butter::map)value; - - auto start = map.find("start"); - if (start != map.end()) { - fromRawValue(context, start->second, result.start); - } - auto end = map.find("end"); - if (end != map.end()) { - fromRawValue(context, end->second, result.end); - } -} - -static inline std::string toString( - const AndroidTextInputSelectionStruct &value) { - return "[Object AndroidTextInputSelectionStruct]"; -} - struct AndroidTextInputTextShadowOffsetStruct { double width; double height; @@ -87,13 +61,6 @@ inline folly::dynamic toDynamic( dynamicValue["height"] = value.height; return dynamicValue; } - -inline folly::dynamic toDynamic(const AndroidTextInputSelectionStruct &value) { - folly::dynamic dynamicValue = folly::dynamic::object(); - dynamicValue["start"] = value.start; - dynamicValue["end"] = value.end; - return dynamicValue; -} #endif class AndroidTextInputProps final : public ViewProps, public BaseTextProps { @@ -139,7 +106,6 @@ class AndroidTextInputProps final : public ViewProps, public BaseTextProps { SharedColor placeholderTextColor{}; bool secureTextEntry{false}; SharedColor selectionColor{}; - AndroidTextInputSelectionStruct selection{}; std::string value{}; std::string defaultValue{}; bool selectTextOnFocus{false};