diff --git a/packages/react-native/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js b/packages/react-native/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js index e7c28912a0779e..55b770d26a35ef 100644 --- a/packages/react-native/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js +++ b/packages/react-native/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js @@ -692,7 +692,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 dce846b59369a3..df890974ece66c 100644 --- a/packages/react-native/Libraries/Components/TextInput/TextInput.js +++ b/packages/react-native/Libraries/Components/TextInput/TextInput.js @@ -1066,27 +1066,19 @@ function InternalTextInput(props: Props): React.Node { accessibilityState, id, tabIndex, + 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); @@ -1098,12 +1090,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) { @@ -1503,7 +1489,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 0084795ac5877d..448e94885dc21a 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 @@ -117,6 +117,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; @@ -497,6 +498,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 4c9ba17785b5cc..b27ace40cc98cf 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 @@ -328,21 +328,19 @@ public void receiveCommand( if (!args.isNull(1)) { String text = args.getString(1); - reactEditText.maybeSetTextFromJS( - getReactTextUpdate(text, mostRecentEventCount, start, end)); + reactEditText.maybeSetTextFromJS(getReactTextUpdate(text, mostRecentEventCount)); } reactEditText.maybeSetSelection(mostRecentEventCount, start, end); break; } } - private ReactTextUpdate getReactTextUpdate( - String text, int mostRecentEventCount, int start, int end) { + private ReactTextUpdate getReactTextUpdate(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 @@ -373,9 +371,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(); @@ -507,7 +505,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 75af518634c810..8c123d7545b81b 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 @@ -19,7 +19,6 @@ import com.facebook.common.logging.FLog; import com.facebook.infer.annotation.Assertions; import com.facebook.react.R; -import com.facebook.react.bridge.ReadableMap; import com.facebook.react.common.ReactConstants; import com.facebook.react.common.annotations.VisibleForTesting; import com.facebook.react.uimanager.Spacing; @@ -46,13 +45,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) { @@ -167,18 +163,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(); } @@ -196,18 +180,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) { @@ -247,9 +219,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 19cb290a3323f2..9953e2228ed24f 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 @@ -134,8 +134,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", @@ -349,7 +347,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); @@ -449,7 +446,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 66e89a77b0e37d..6c2996d99e07a6 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 @@ -26,32 +26,6 @@ namespace facebook::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; @@ -86,13 +60,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 { @@ -137,7 +104,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};