Skip to content

Commit e46d81e

Browse files
committed
fix: disable ios automatic extra space behavior when pasting
1 parent dd5fbb6 commit e46d81e

2 files changed

Lines changed: 173 additions & 0 deletions

File tree

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
diff --git a/node_modules/react-native/Libraries/Components/TextInput/RCTTextInputViewConfig.js b/node_modules/react-native/Libraries/Components/TextInput/RCTTextInputViewConfig.js
2+
index 52134db..6545ae0 100644
3+
--- a/node_modules/react-native/Libraries/Components/TextInput/RCTTextInputViewConfig.js
4+
+++ b/node_modules/react-native/Libraries/Components/TextInput/RCTTextInputViewConfig.js
5+
@@ -148,6 +148,7 @@ const RCTTextInputViewConfig = {
6+
clearTextOnFocus: true,
7+
showSoftInputOnFocus: true,
8+
autoFocus: true,
9+
+ smartInsertDelete: true,
10+
...ConditionallyIgnoredEventHandlers({
11+
onChange: true,
12+
onSelectionChange: true,
13+
diff --git a/node_modules/react-native/Libraries/Components/TextInput/TextInput.js b/node_modules/react-native/Libraries/Components/TextInput/TextInput.js
14+
index 8fa1171..0a39140 100644
15+
--- a/node_modules/react-native/Libraries/Components/TextInput/TextInput.js
16+
+++ b/node_modules/react-native/Libraries/Components/TextInput/TextInput.js
17+
@@ -301,6 +301,14 @@ type IOSProps = $ReadOnly<{|
18+
* @platform ios
19+
*/
20+
textContentType?: ?TextContentType,
21+
+
22+
+ /**
23+
+ * If `false`, the iOS system will not insert an extra space after a paste operation
24+
+ * neither delete one or two spaces after a cut or delete operation.
25+
+ *
26+
+ * The default value is `true`.
27+
+ */
28+
+ smartInsertDelete?: boolean | undefined;
29+
|}>;
30+
31+
type AndroidProps = $ReadOnly<{|
32+
diff --git a/node_modules/react-native/Libraries/Text/RCTConvert+Text.h b/node_modules/react-native/Libraries/Text/RCTConvert+Text.h
33+
index b7c411a..4425cc2 100644
34+
--- a/node_modules/react-native/Libraries/Text/RCTConvert+Text.h
35+
+++ b/node_modules/react-native/Libraries/Text/RCTConvert+Text.h
36+
@@ -16,6 +16,7 @@ NS_ASSUME_NONNULL_BEGIN
37+
+ (UITextAutocorrectionType)UITextAutocorrectionType:(nullable id)json;
38+
+ (UITextSpellCheckingType)UITextSpellCheckingType:(nullable id)json;
39+
+ (RCTTextTransform)RCTTextTransform:(nullable id)json;
40+
++ (UITextSmartInsertDeleteType)UITextSmartInsertDeleteType:(nullable id)json;
41+
42+
@end
43+
44+
diff --git a/node_modules/react-native/Libraries/Text/RCTConvert+Text.m b/node_modules/react-native/Libraries/Text/RCTConvert+Text.m
45+
index da9fb7c..5857677 100644
46+
--- a/node_modules/react-native/Libraries/Text/RCTConvert+Text.m
47+
+++ b/node_modules/react-native/Libraries/Text/RCTConvert+Text.m
48+
@@ -32,4 +32,11 @@ + (UITextSpellCheckingType)UITextSpellCheckingType:(id)json
49+
@"lowercase": @(RCTTextTransformLowercase),
50+
}), RCTTextTransformUndefined, integerValue)
51+
52+
++ (UITextSmartInsertDeleteType)UITextSmartInsertDeleteType:(id)json
53+
+{
54+
+ return json == nil ? UITextSmartInsertDeleteTypeDefault
55+
+ : [RCTConvert BOOL:json] ? UITextSmartInsertDeleteTypeYes
56+
+ : UITextSmartInsertDeleteTypeNo;
57+
+}
58+
+
59+
@end
60+
diff --git a/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputViewManager.m b/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputViewManager.m
61+
index b1ecf85..447286e 100644
62+
--- a/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputViewManager.m
63+
+++ b/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputViewManager.m
64+
@@ -48,6 +48,7 @@ @implementation RCTBaseTextInputViewManager
65+
RCT_REMAP_VIEW_PROPERTY(clearButtonMode, backedTextInputView.clearButtonMode, UITextFieldViewMode)
66+
RCT_REMAP_VIEW_PROPERTY(scrollEnabled, backedTextInputView.scrollEnabled, BOOL)
67+
RCT_REMAP_VIEW_PROPERTY(secureTextEntry, backedTextInputView.secureTextEntry, BOOL)
68+
+RCT_REMAP_VIEW_PROPERTY(smartInsertDelete, backedTextInputView.smartInsertDeleteType, UITextSmartInsertDeleteType)
69+
RCT_EXPORT_VIEW_PROPERTY(autoFocus, BOOL)
70+
RCT_EXPORT_VIEW_PROPERTY(blurOnSubmit, BOOL)
71+
RCT_EXPORT_VIEW_PROPERTY(clearTextOnFocus, BOOL)
72+
diff --git a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm
73+
index 3631fb4..9f793c5 100644
74+
--- a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm
75+
+++ b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm
76+
@@ -185,6 +185,21 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &
77+
}
78+
}
79+
80+
+ /*
81+
+ * When updating component's props, we compare if the new value is different from the old one.
82+
+ * If it is different, we update the native view with the new value.
83+
+ *
84+
+ * `RCTUITextSmartInsertDeleteTypeFromOptionalBool` is used to convert the boolean value coming
85+
+ * from JS side to the appropriate `UITextSmartInsertDeleteType` value, that is required for
86+
+ * this `smartInsertDeleteType` attribute.
87+
+ */
88+
+ if (newTextInputProps.traits.smartInsertDelete != oldTextInputProps.traits.smartInsertDelete) {
89+
+ if (@available(iOS 11.0, *)) {
90+
+ _backedTextInputView.smartInsertDeleteType =
91+
+ RCTUITextSmartInsertDeleteTypeFromOptionalBool(newTextInputProps.traits.smartInsertDelete);
92+
+ }
93+
+ }
94+
+
95+
// Traits `blurOnSubmit`, `clearTextOnFocus`, and `selectTextOnFocus` were omitted intentially here
96+
// because they are being checked on-demand.
97+
98+
diff --git a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputUtils.h b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputUtils.h
99+
index ffaac13..6f77538 100644
100+
--- a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputUtils.h
101+
+++ b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputUtils.h
102+
@@ -41,4 +41,7 @@ UITextContentType RCTUITextContentTypeFromString(std::string const &contentType)
103+
API_AVAILABLE(ios(12.0))
104+
UITextInputPasswordRules *RCTUITextInputPasswordRulesFromString(std::string const &passwordRules);
105+
106+
+API_AVAILABLE(ios(11.0))
107+
+UITextSmartInsertDeleteType RCTUITextSmartInsertDeleteTypeFromOptionalBool(std::optional<bool> smartInsertDelete);
108+
+
109+
NS_ASSUME_NONNULL_END
110+
diff --git a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputUtils.mm b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputUtils.mm
111+
index 99f4681..41343e1 100644
112+
--- a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputUtils.mm
113+
+++ b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputUtils.mm
114+
@@ -44,6 +44,10 @@ void RCTCopyBackedTextInput(
115+
toTextInput.keyboardType = fromTextInput.keyboardType;
116+
toTextInput.textContentType = fromTextInput.textContentType;
117+
118+
+ if (@available(iOS 11.0, *)) {
119+
+ toTextInput.smartInsertDeleteType = fromTextInput.smartInsertDeleteType;
120+
+ }
121+
+
122+
if (@available(iOS 12.0, *)) {
123+
toTextInput.passwordRules = fromTextInput.passwordRules;
124+
}
125+
@@ -232,3 +236,10 @@ UITextContentType RCTUITextContentTypeFromString(std::string const &contentType)
126+
{
127+
return [UITextInputPasswordRules passwordRulesWithDescriptor:RCTNSStringFromStringNilIfEmpty(passwordRules)];
128+
}
129+
+
130+
+API_AVAILABLE(ios(11.0))
131+
+UITextSmartInsertDeleteType RCTUITextSmartInsertDeleteTypeFromOptionalBool(std::optional<bool> smartInsertDelete)
132+
+{
133+
+ return smartInsertDelete.has_value() ? (*smartInsertDelete ? UITextSmartInsertDeleteTypeYes : UITextSmartInsertDeleteTypeNo)
134+
+ : UITextSmartInsertDeleteTypeDefault;
135+
+}
136+
diff --git a/node_modules/react-native/ReactCommon/react/renderer/components/textinput/iostextinput/primitives.h b/node_modules/react-native/ReactCommon/react/renderer/components/textinput/iostextinput/primitives.h
137+
index 1747a98..f9bb580 100644
138+
--- a/node_modules/react-native/ReactCommon/react/renderer/components/textinput/iostextinput/primitives.h
139+
+++ b/node_modules/react-native/ReactCommon/react/renderer/components/textinput/iostextinput/primitives.h
140+
@@ -215,6 +215,15 @@ class TextInputTraits final {
141+
* Default value: `<empty string>` (no rules).
142+
*/
143+
std::string passwordRules{};
144+
+
145+
+ /*
146+
+ * If `false`, the iOS system will not insert an extra space after a paste operation
147+
+ * neither delete one or two spaces after a cut or delete operation.
148+
+ * iOS-only (inherently iOS-specific)
149+
+ * Can be empty (`null` in JavaScript) which means `default`.
150+
+ * Default value: `empty` (`null`).
151+
+ */
152+
+ std::optional<bool> smartInsertDelete{};
153+
};
154+
155+
} // namespace react
156+
diff --git a/node_modules/react-native/ReactCommon/react/renderer/components/textinput/iostextinput/propsConversions.h b/node_modules/react-native/ReactCommon/react/renderer/components/textinput/iostextinput/propsConversions.h
157+
index 84c52ee..9e29840 100644
158+
--- a/node_modules/react-native/ReactCommon/react/renderer/components/textinput/iostextinput/propsConversions.h
159+
+++ b/node_modules/react-native/ReactCommon/react/renderer/components/textinput/iostextinput/propsConversions.h
160+
@@ -141,6 +141,12 @@ static TextInputTraits convertRawProp(
161+
"passwordRules",
162+
sourceTraits.passwordRules,
163+
defaultTraits.passwordRules);
164+
+ traits.smartInsertDelete = convertRawProp(
165+
+ context,
166+
+ rawProps,
167+
+ "smartInsertDelete",
168+
+ sourceTraits.smartInsertDelete,
169+
+ defaultTraits.smartInsertDelete);
170+
171+
return traits;
172+
}

src/components/Composer/index.ios.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ class Composer extends React.Component {
105105
onContentSizeChange={(e) => ComposerUtils.updateNumberOfLines(this.props, e)}
106106
rejectResponderTermination={false}
107107
textAlignVertical="center"
108+
smartInsertDelete={false}
108109
style={this.state.propStyles}
109110
/* eslint-disable-next-line react/jsx-props-no-spreading */
110111
{...propsToPass}

0 commit comments

Comments
 (0)