@tbvjaos510/react-native-paste-input 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.circleci/config.yml +130 -0
- package/LICENSE +21 -0
- package/README.md +73 -0
- package/android/.project +17 -0
- package/android/.settings/org.eclipse.buildship.core.prefs +13 -0
- package/android/build.gradle +94 -0
- package/android/generated/java/com/facebook/react/viewmanagers/PasteTextInputManagerDelegate.java +236 -0
- package/android/generated/java/com/facebook/react/viewmanagers/PasteTextInputManagerInterface.java +84 -0
- package/android/generated/jni/CMakeLists.txt +36 -0
- package/android/generated/jni/PasteTextInputSpecs-generated.cpp +27 -0
- package/android/generated/jni/PasteTextInputSpecs.h +28 -0
- package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/ComponentDescriptors.cpp +22 -0
- package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/ComponentDescriptors.h +166 -0
- package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/EventEmitters.cpp +183 -0
- package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/EventEmitters.h +148 -0
- package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/PasteTextInputSpecsJSI-generated.cpp +17 -0
- package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/PasteTextInputSpecsJSI.h +19 -0
- package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/Props.cpp +640 -0
- package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/Props.h +144 -0
- package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/ShadowNodes.cpp +247 -0
- package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/ShadowNodes.h +95 -0
- package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/States.cpp +14 -0
- package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/States.h +19 -0
- package/android/gradle.properties +6 -0
- package/android/src/main/java/com/mattermost/pasteinputtext/IPasteInputListener.kt +8 -0
- package/android/src/main/java/com/mattermost/pasteinputtext/PasteInputActionCallback.kt +72 -0
- package/android/src/main/java/com/mattermost/pasteinputtext/PasteInputEditText.kt +60 -0
- package/android/src/main/java/com/mattermost/pasteinputtext/PasteInputFileFromUrl.kt +50 -0
- package/android/src/main/java/com/mattermost/pasteinputtext/PasteInputListener.kt +84 -0
- package/android/src/main/java/com/mattermost/pasteinputtext/PasteTextInputManager.kt +72 -0
- package/android/src/main/java/com/mattermost/pasteinputtext/PasteTextInputPackage.kt +14 -0
- package/android/src/main/java/com/mattermost/pasteinputtext/PasteTextInputPasteEvent.java +41 -0
- package/android/src/main/java/com/mattermost/pasteinputtext/RealPathUtil.kt +174 -0
- package/ios/NSData+MimeType.h +20 -0
- package/ios/NSData+MimeType.m +59 -0
- package/ios/PasteInput-Bridging-Header.h +1 -0
- package/ios/PasteInput.xcodeproj/project.pbxproj +319 -0
- package/ios/PasteInputTextView.h +20 -0
- package/ios/PasteInputTextView.m +68 -0
- package/ios/PasteInputView.h +18 -0
- package/ios/PasteInputView.m +96 -0
- package/ios/PasteTextInput.h +13 -0
- package/ios/PasteTextInput.mm +726 -0
- package/ios/PasteTextInputManager.mm +171 -0
- package/ios/Swime/MimeType.h +70 -0
- package/ios/Swime/MimeType.m +701 -0
- package/ios/Swime/Swime.h +14 -0
- package/ios/Swime/Swime.m +28 -0
- package/ios/Swime/SwimeProxy.h +18 -0
- package/ios/Swime/SwimeProxy.m +66 -0
- package/ios/Swime/SwimeUtils.h +12 -0
- package/ios/Swime/SwimeUtils.m +23 -0
- package/ios/UIImage+ImageEffects.h +112 -0
- package/ios/UIImage+ImageEffects.m +310 -0
- package/ios/UIImage+vImageScaling.h +16 -0
- package/ios/UIImage+vImageScaling.m +48 -0
- package/ios/UIPasteboard+GetImageInfo.h +19 -0
- package/ios/UIPasteboard+GetImageInfo.m +98 -0
- package/ios/generated/PasteTextInputSpecs/ComponentDescriptors.cpp +22 -0
- package/ios/generated/PasteTextInputSpecs/ComponentDescriptors.h +42 -0
- package/ios/generated/PasteTextInputSpecs/EventEmitters.cpp +41 -0
- package/ios/generated/PasteTextInputSpecs/EventEmitters.h +37 -0
- package/ios/generated/PasteTextInputSpecs/PasteTextInputSpecs-generated.mm +16 -0
- package/ios/generated/PasteTextInputSpecs/PasteTextInputSpecs.h +38 -0
- package/ios/generated/PasteTextInputSpecs/Props.cpp +142 -0
- package/ios/generated/PasteTextInputSpecs/Props.h +81 -0
- package/ios/generated/PasteTextInputSpecs/RCTComponentViewHelpers.h +106 -0
- package/ios/generated/PasteTextInputSpecs/ShadowNodes.cpp +127 -0
- package/ios/generated/PasteTextInputSpecs/ShadowNodes.h +85 -0
- package/ios/generated/PasteTextInputSpecs/States.cpp +16 -0
- package/ios/generated/PasteTextInputSpecs/States.h +53 -0
- package/ios/generated/PasteTextInputSpecsJSI-generated.cpp +17 -0
- package/ios/generated/PasteTextInputSpecsJSI.h +19 -0
- package/lib/commonjs/PasteTextInput.js +446 -0
- package/lib/commonjs/PasteTextInput.js.map +1 -0
- package/lib/commonjs/PasteTextInputNativeComponent.ts +277 -0
- package/lib/commonjs/index.js +25 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/module.d.js +2 -0
- package/lib/commonjs/module.d.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/types.js +6 -0
- package/lib/commonjs/types.js.map +1 -0
- package/lib/module/PasteTextInput.js +440 -0
- package/lib/module/PasteTextInput.js.map +1 -0
- package/lib/module/PasteTextInputNativeComponent.ts +277 -0
- package/lib/module/index.js +9 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/module.d.js +2 -0
- package/lib/module/module.d.js.map +1 -0
- package/lib/module/types.js +4 -0
- package/lib/module/types.js.map +1 -0
- package/lib/typescript/src/PasteTextInput.d.ts +5 -0
- package/lib/typescript/src/PasteTextInput.d.ts.map +1 -0
- package/lib/typescript/src/PasteTextInputNativeComponent.d.ts +168 -0
- package/lib/typescript/src/PasteTextInputNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +6 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +56 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/package.json +140 -0
- package/react-native-paste-input.podspec +20 -0
- package/react-native.config.js +13 -0
- package/src/PasteTextInput.tsx +615 -0
- package/src/PasteTextInputNativeComponent.ts +277 -0
- package/src/index.ts +13 -0
- package/src/module.d.ts +4 -0
- package/src/types.ts +71 -0
|
@@ -0,0 +1,726 @@
|
|
|
1
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
2
|
+
#import "PasteTextInput.h"
|
|
3
|
+
#import "PasteInputTextView.h"
|
|
4
|
+
|
|
5
|
+
#import <React/RCTBackedTextInputViewProtocol.h>
|
|
6
|
+
#import <React/RCTUITextView.h>
|
|
7
|
+
|
|
8
|
+
#import "generated/PasteTextInputSpecs/ComponentDescriptors.h"
|
|
9
|
+
#import "generated/PasteTextInputSpecs/EventEmitters.h"
|
|
10
|
+
#import "generated/PasteTextInputSpecs/Props.h"
|
|
11
|
+
#import "generated/PasteTextInputSpecs/RCTComponentViewHelpers.h"
|
|
12
|
+
#import "generated/PasteTextInputSpecs/ShadowNodes.h"
|
|
13
|
+
|
|
14
|
+
#import <react/renderer/textlayoutmanager/RCTAttributedTextUtils.h>
|
|
15
|
+
|
|
16
|
+
#import "RCTConversions.h"
|
|
17
|
+
#import "RCTTextInputUtils.h"
|
|
18
|
+
|
|
19
|
+
#import "RCTFabricComponentsPlugins.h"
|
|
20
|
+
|
|
21
|
+
using namespace facebook::react;
|
|
22
|
+
|
|
23
|
+
@interface PasteTextInput () <RCTBackedTextInputDelegate, RCTPasteTextInputViewProtocol>
|
|
24
|
+
@end
|
|
25
|
+
|
|
26
|
+
@implementation PasteTextInput {
|
|
27
|
+
PasteTextInputShadowNode::ConcreteState::Shared _state;
|
|
28
|
+
PasteInputTextView *_backedTextInputView;
|
|
29
|
+
BOOL _ignoreNextTextInputCall;
|
|
30
|
+
NSUInteger _mostRecentEventCount;
|
|
31
|
+
NSAttributedString *_lastStringStateWasUpdatedWith;
|
|
32
|
+
|
|
33
|
+
/*
|
|
34
|
+
* A flag that when set to true, `_mostRecentEventCount` won't be incremented when `[self _updateState]`
|
|
35
|
+
* and delegate methods `textInputDidChange` and `textInputDidChangeSelection` will exit early.
|
|
36
|
+
*
|
|
37
|
+
* Setting `_backedTextInputView.attributedText` triggers delegate methods `textInputDidChange` and
|
|
38
|
+
* `textInputDidChangeSelection` for multiline text input only.
|
|
39
|
+
* In multiline text input this is undesirable as we don't want to be sending events for changes that JS triggered.
|
|
40
|
+
*/
|
|
41
|
+
BOOL _comingFromJS;
|
|
42
|
+
BOOL _didMoveToWindow;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
+(ComponentDescriptorProvider)componentDescriptorProvider
|
|
46
|
+
{
|
|
47
|
+
return concreteComponentDescriptorProvider<PasteTextInputComponentDescriptor>();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
- (std::shared_ptr<const PasteTextInputEventEmitter>)getEventEmitter
|
|
51
|
+
{
|
|
52
|
+
if (!self->_eventEmitter) {
|
|
53
|
+
return nullptr;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
assert(std::dynamic_pointer_cast<PasteTextInputEventEmitter const>(self->_eventEmitter));
|
|
57
|
+
return std::static_pointer_cast<PasteTextInputEventEmitter const>(self->_eventEmitter);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
std::string convertNSDictionaryValueToStdString(NSDictionary *dictionary, NSString *key) {
|
|
61
|
+
id obj = [dictionary objectForKey:key];
|
|
62
|
+
if (obj != nil) {
|
|
63
|
+
if ([obj isKindOfClass:[NSString class]]) {
|
|
64
|
+
NSString *nsStringObj = (NSString *)obj;
|
|
65
|
+
const char *cString = [nsStringObj UTF8String];
|
|
66
|
+
return std::string(cString);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return std::string();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
std::int32_t convertNSDictionaryValueToStdInt(NSDictionary *dictionary, NSString *key) {
|
|
74
|
+
id obj = [dictionary objectForKey:key];
|
|
75
|
+
if (obj != nil) {
|
|
76
|
+
if ([obj isKindOfClass:[NSNumber class]]) {
|
|
77
|
+
NSNumber *numberObj = (NSNumber *)obj;
|
|
78
|
+
return [numberObj intValue];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return 0;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
- (instancetype)initWithFrame:(CGRect)frame {
|
|
86
|
+
if (self = [super initWithFrame:frame]) {
|
|
87
|
+
static const auto defaultProps = std::make_shared<const PasteTextInputProps>();
|
|
88
|
+
_props = defaultProps;
|
|
89
|
+
|
|
90
|
+
_backedTextInputView = [[PasteInputTextView alloc] initWithFrame:self.bounds];
|
|
91
|
+
_backedTextInputView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
92
|
+
_backedTextInputView.textInputDelegate = self;
|
|
93
|
+
[self _setOnPaste];
|
|
94
|
+
_ignoreNextTextInputCall = NO;
|
|
95
|
+
_comingFromJS = NO;
|
|
96
|
+
_didMoveToWindow = NO;
|
|
97
|
+
|
|
98
|
+
[self addSubview:_backedTextInputView];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return self;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
- (void)didMoveToWindow
|
|
105
|
+
{
|
|
106
|
+
[super didMoveToWindow];
|
|
107
|
+
|
|
108
|
+
if (self.window && !_didMoveToWindow) {
|
|
109
|
+
const auto &props = static_cast<const PasteTextInputProps &>(*_props);
|
|
110
|
+
if (props.autoFocus) {
|
|
111
|
+
[_backedTextInputView becomeFirstResponder];
|
|
112
|
+
}
|
|
113
|
+
_didMoveToWindow = YES;
|
|
114
|
+
}
|
|
115
|
+
[self _restoreTextSelection];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
- (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &)oldProps
|
|
120
|
+
{
|
|
121
|
+
const auto &oldTextInputProps = static_cast<const PasteTextInputProps &>(*_props);
|
|
122
|
+
const auto &newTextInputProps = static_cast<const PasteTextInputProps &>(*props);
|
|
123
|
+
|
|
124
|
+
// Traits:
|
|
125
|
+
if (newTextInputProps.multiline != oldTextInputProps.multiline) {
|
|
126
|
+
[self _setMultiline:newTextInputProps.multiline];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (newTextInputProps.traits.autocapitalizationType != oldTextInputProps.traits.autocapitalizationType) {
|
|
130
|
+
_backedTextInputView.autocapitalizationType =
|
|
131
|
+
RCTUITextAutocapitalizationTypeFromAutocapitalizationType(newTextInputProps.traits.autocapitalizationType);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (newTextInputProps.traits.autoCorrect != oldTextInputProps.traits.autoCorrect) {
|
|
135
|
+
_backedTextInputView.autocorrectionType =
|
|
136
|
+
RCTUITextAutocorrectionTypeFromOptionalBool(newTextInputProps.traits.autoCorrect);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (newTextInputProps.traits.contextMenuHidden != oldTextInputProps.traits.contextMenuHidden) {
|
|
140
|
+
_backedTextInputView.contextMenuHidden = newTextInputProps.traits.contextMenuHidden;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (newTextInputProps.traits.editable != oldTextInputProps.traits.editable) {
|
|
144
|
+
_backedTextInputView.editable = newTextInputProps.traits.editable;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (newTextInputProps.traits.enablesReturnKeyAutomatically !=
|
|
148
|
+
oldTextInputProps.traits.enablesReturnKeyAutomatically) {
|
|
149
|
+
_backedTextInputView.enablesReturnKeyAutomatically = newTextInputProps.traits.enablesReturnKeyAutomatically;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (newTextInputProps.traits.keyboardAppearance != oldTextInputProps.traits.keyboardAppearance) {
|
|
153
|
+
_backedTextInputView.keyboardAppearance =
|
|
154
|
+
RCTUIKeyboardAppearanceFromKeyboardAppearance(newTextInputProps.traits.keyboardAppearance);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (newTextInputProps.traits.spellCheck != oldTextInputProps.traits.spellCheck) {
|
|
158
|
+
_backedTextInputView.spellCheckingType =
|
|
159
|
+
RCTUITextSpellCheckingTypeFromOptionalBool(newTextInputProps.traits.spellCheck);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (newTextInputProps.traits.caretHidden != oldTextInputProps.traits.caretHidden) {
|
|
163
|
+
_backedTextInputView.caretHidden = newTextInputProps.traits.caretHidden;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (newTextInputProps.traits.clearButtonMode != oldTextInputProps.traits.clearButtonMode) {
|
|
167
|
+
_backedTextInputView.clearButtonMode =
|
|
168
|
+
RCTUITextFieldViewModeFromTextInputAccessoryVisibilityMode(newTextInputProps.traits.clearButtonMode);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (newTextInputProps.traits.scrollEnabled != oldTextInputProps.traits.scrollEnabled) {
|
|
172
|
+
_backedTextInputView.scrollEnabled = newTextInputProps.traits.scrollEnabled;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (newTextInputProps.traits.secureTextEntry != oldTextInputProps.traits.secureTextEntry) {
|
|
176
|
+
_backedTextInputView.secureTextEntry = newTextInputProps.traits.secureTextEntry;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (newTextInputProps.traits.keyboardType != oldTextInputProps.traits.keyboardType) {
|
|
180
|
+
_backedTextInputView.keyboardType = RCTUIKeyboardTypeFromKeyboardType(newTextInputProps.traits.keyboardType);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (newTextInputProps.traits.returnKeyType != oldTextInputProps.traits.returnKeyType) {
|
|
184
|
+
_backedTextInputView.returnKeyType = RCTUIReturnKeyTypeFromReturnKeyType(newTextInputProps.traits.returnKeyType);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (newTextInputProps.traits.textContentType != oldTextInputProps.traits.textContentType) {
|
|
188
|
+
_backedTextInputView.textContentType = RCTUITextContentTypeFromString(newTextInputProps.traits.textContentType);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (newTextInputProps.traits.passwordRules != oldTextInputProps.traits.passwordRules) {
|
|
192
|
+
_backedTextInputView.passwordRules = RCTUITextInputPasswordRulesFromString(newTextInputProps.traits.passwordRules);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (newTextInputProps.traits.smartInsertDelete != oldTextInputProps.traits.smartInsertDelete) {
|
|
196
|
+
_backedTextInputView.smartInsertDeleteType =
|
|
197
|
+
RCTUITextSmartInsertDeleteTypeFromOptionalBool(newTextInputProps.traits.smartInsertDelete);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Traits `blurOnSubmit`, `clearTextOnFocus`, and `selectTextOnFocus` were omitted intentionally here
|
|
201
|
+
// because they are being checked on-demand.
|
|
202
|
+
|
|
203
|
+
// Other props:
|
|
204
|
+
if (newTextInputProps.placeholder != oldTextInputProps.placeholder) {
|
|
205
|
+
_backedTextInputView.placeholder = RCTNSStringFromString(newTextInputProps.placeholder);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (newTextInputProps.placeholderTextColor != oldTextInputProps.placeholderTextColor) {
|
|
209
|
+
_backedTextInputView.placeholderColor = RCTUIColorFromSharedColor(newTextInputProps.placeholderTextColor);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (newTextInputProps.textAttributes != oldTextInputProps.textAttributes) {
|
|
213
|
+
_backedTextInputView.defaultTextAttributes =
|
|
214
|
+
RCTNSTextAttributesFromTextAttributes(newTextInputProps.getEffectiveTextAttributes(RCTFontSizeMultiplier()));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (newTextInputProps.selectionColor != oldTextInputProps.selectionColor) {
|
|
218
|
+
_backedTextInputView.tintColor = RCTUIColorFromSharedColor(newTextInputProps.selectionColor);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (newTextInputProps.inputAccessoryViewID != oldTextInputProps.inputAccessoryViewID) {
|
|
222
|
+
_backedTextInputView.inputAccessoryViewID = RCTNSStringFromString(newTextInputProps.inputAccessoryViewID);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (newTextInputProps.smartPunctuation != oldTextInputProps.smartPunctuation) {
|
|
226
|
+
[self _setSmartPunctuation:[[NSString alloc] initWithCString:newTextInputProps.smartPunctuation.c_str() encoding:NSASCIIStringEncoding]];
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (newTextInputProps.disableCopyPaste != oldTextInputProps.disableCopyPaste) {
|
|
230
|
+
_backedTextInputView.disableCopyPaste = newTextInputProps.disableCopyPaste;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
[super updateProps:props oldProps:oldProps];
|
|
234
|
+
|
|
235
|
+
[self setDefaultInputAccessoryView];
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
- (void)updateState:(const State::Shared &)state oldState:(const State::Shared &)oldState
|
|
239
|
+
{
|
|
240
|
+
_state = std::static_pointer_cast<PasteTextInputShadowNode::ConcreteState const>(state);
|
|
241
|
+
|
|
242
|
+
if (!_state) {
|
|
243
|
+
assert(false && "State is `null` for <TextInput> component.");
|
|
244
|
+
_backedTextInputView.attributedText = nil;
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
auto data = _state->getData();
|
|
249
|
+
|
|
250
|
+
if (!oldState) {
|
|
251
|
+
_mostRecentEventCount = _state->getData().mostRecentEventCount;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (_mostRecentEventCount == _state->getData().mostRecentEventCount) {
|
|
255
|
+
_comingFromJS = YES;
|
|
256
|
+
[self _setAttributedString:RCTNSAttributedStringFromAttributedStringBox(data.attributedStringBox)];
|
|
257
|
+
_comingFromJS = NO;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
- (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics
|
|
262
|
+
oldLayoutMetrics:(const LayoutMetrics &)oldLayoutMetrics
|
|
263
|
+
{
|
|
264
|
+
[super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:oldLayoutMetrics];
|
|
265
|
+
|
|
266
|
+
_backedTextInputView.frame =
|
|
267
|
+
UIEdgeInsetsInsetRect(self.bounds, RCTUIEdgeInsetsFromEdgeInsets(layoutMetrics.borderWidth));
|
|
268
|
+
_backedTextInputView.textContainerInset =
|
|
269
|
+
RCTUIEdgeInsetsFromEdgeInsets(layoutMetrics.contentInsets - layoutMetrics.borderWidth);
|
|
270
|
+
|
|
271
|
+
if (_eventEmitter) {
|
|
272
|
+
const auto eventEmitter = [self getEventEmitter];
|
|
273
|
+
eventEmitter->onContentSizeChange([self _textInputMetrics]);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
- (void)prepareForRecycle
|
|
278
|
+
{
|
|
279
|
+
[super prepareForRecycle];
|
|
280
|
+
_state.reset();
|
|
281
|
+
_backedTextInputView.attributedText = nil;
|
|
282
|
+
_mostRecentEventCount = 0;
|
|
283
|
+
_comingFromJS = NO;
|
|
284
|
+
_lastStringStateWasUpdatedWith = nil;
|
|
285
|
+
_ignoreNextTextInputCall = NO;
|
|
286
|
+
_didMoveToWindow = NO;
|
|
287
|
+
[_backedTextInputView resignFirstResponder];
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
#pragma mark - RCTBackedTextInputDelegate
|
|
291
|
+
|
|
292
|
+
- (BOOL)textInputShouldBeginEditing
|
|
293
|
+
{
|
|
294
|
+
return YES;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
- (void)textInputDidBeginEditing
|
|
298
|
+
{
|
|
299
|
+
const auto &props = static_cast<const PasteTextInputProps &>(*_props);
|
|
300
|
+
|
|
301
|
+
if (props.traits.clearTextOnFocus) {
|
|
302
|
+
_backedTextInputView.attributedText = nil;
|
|
303
|
+
[self textInputDidChange];
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (props.traits.selectTextOnFocus) {
|
|
307
|
+
[_backedTextInputView selectAll:nil];
|
|
308
|
+
[self textInputDidChangeSelection];
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (_eventEmitter) {
|
|
312
|
+
const auto eventEmitter = [self getEventEmitter];
|
|
313
|
+
eventEmitter->onFocus([self _textInputMetrics]);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
- (BOOL)textInputShouldEndEditing
|
|
318
|
+
{
|
|
319
|
+
return YES;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
- (void)textInputDidEndEditing
|
|
323
|
+
{
|
|
324
|
+
if (_eventEmitter) {
|
|
325
|
+
const auto eventEmitter = [self getEventEmitter];
|
|
326
|
+
eventEmitter->onEndEditing([self _textInputMetrics]);
|
|
327
|
+
eventEmitter->onBlur([self _textInputMetrics]);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
- (BOOL)textInputShouldSubmitOnReturn
|
|
332
|
+
{
|
|
333
|
+
const SubmitBehavior submitBehavior = [self getSubmitBehavior];
|
|
334
|
+
const BOOL shouldSubmit = submitBehavior == SubmitBehavior::Submit || submitBehavior == SubmitBehavior::BlurAndSubmit;
|
|
335
|
+
// We send `submit` event here, in `textInputShouldSubmitOnReturn`
|
|
336
|
+
// (not in `textInputDidReturn)`, because of semantic of the event:
|
|
337
|
+
// `onSubmitEditing` is called when "Submit" button
|
|
338
|
+
// (the blue key on onscreen keyboard) did pressed
|
|
339
|
+
// (no connection to any specific "submitting" process).
|
|
340
|
+
|
|
341
|
+
if (_eventEmitter && shouldSubmit) {
|
|
342
|
+
const auto eventEmitter = [self getEventEmitter];
|
|
343
|
+
eventEmitter->onSubmitEditing([self _textInputMetrics]);
|
|
344
|
+
}
|
|
345
|
+
return shouldSubmit;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
- (BOOL)textInputShouldReturn
|
|
349
|
+
{
|
|
350
|
+
return [self getSubmitBehavior] == SubmitBehavior::BlurAndSubmit;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
- (void)textInputDidReturn
|
|
354
|
+
{
|
|
355
|
+
// Does nothing.
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
- (NSString *)textInputShouldChangeText:(NSString *)text inRange:(NSRange)range
|
|
359
|
+
{
|
|
360
|
+
const auto &props = static_cast<const PasteTextInputProps &>(*_props);
|
|
361
|
+
|
|
362
|
+
if (!_backedTextInputView.textWasPasted) {
|
|
363
|
+
if (_eventEmitter) {
|
|
364
|
+
const auto &textInputEventEmitter = static_cast<const TextInputEventEmitter &>(*_eventEmitter);
|
|
365
|
+
textInputEventEmitter.onKeyPress({
|
|
366
|
+
.text = RCTStringFromNSString(text),
|
|
367
|
+
.eventCount = static_cast<int>(_mostRecentEventCount),
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (props.maxLength) {
|
|
373
|
+
NSInteger allowedLength = props.maxLength - _backedTextInputView.attributedText.string.length + range.length;
|
|
374
|
+
|
|
375
|
+
if (allowedLength > 0 && text.length > allowedLength) {
|
|
376
|
+
// make sure unicode characters that are longer than 16 bits (such as emojis) are not cut off
|
|
377
|
+
NSRange cutOffCharacterRange = [text rangeOfComposedCharacterSequenceAtIndex:allowedLength - 1];
|
|
378
|
+
if (cutOffCharacterRange.location + cutOffCharacterRange.length > allowedLength) {
|
|
379
|
+
// the character at the length limit takes more than 16bits, truncation should end at the character before
|
|
380
|
+
allowedLength = cutOffCharacterRange.location;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (allowedLength <= 0) {
|
|
385
|
+
return nil;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return allowedLength > text.length ? text : [text substringToIndex:allowedLength];
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return text;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
- (BOOL)textInputShouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
|
|
395
|
+
{
|
|
396
|
+
return YES;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
- (void)textInputDidChange
|
|
400
|
+
{
|
|
401
|
+
if (_comingFromJS) {
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (_ignoreNextTextInputCall && [_lastStringStateWasUpdatedWith isEqual:_backedTextInputView.attributedText]) {
|
|
406
|
+
_ignoreNextTextInputCall = NO;
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
[self _updateState];
|
|
411
|
+
|
|
412
|
+
if (_eventEmitter) {
|
|
413
|
+
const auto &textInputEventEmitter = static_cast<const TextInputEventEmitter &>(*_eventEmitter);
|
|
414
|
+
textInputEventEmitter.onChange([self _textInputMetrics]);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
- (void)textInputDidChangeSelection
|
|
419
|
+
{
|
|
420
|
+
if (_comingFromJS) {
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
const auto &props = static_cast<const PasteTextInputProps &>(*_props);
|
|
424
|
+
if (props.multiline && ![_lastStringStateWasUpdatedWith isEqual:_backedTextInputView.attributedText]) {
|
|
425
|
+
[self textInputDidChange];
|
|
426
|
+
_ignoreNextTextInputCall = YES;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (_eventEmitter) {
|
|
430
|
+
const auto eventEmitter = [self getEventEmitter];
|
|
431
|
+
eventEmitter->onSelectionChange([self _textInputMetrics]);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
#pragma mark - RCTBackedTextInputDelegate (UIScrollViewDelegate)
|
|
436
|
+
|
|
437
|
+
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
|
|
438
|
+
{
|
|
439
|
+
if (_eventEmitter) {
|
|
440
|
+
const auto eventEmitter = [self getEventEmitter];
|
|
441
|
+
eventEmitter->onScroll([self _textInputMetrics]);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
#pragma mark - Native Commands
|
|
446
|
+
|
|
447
|
+
- (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args
|
|
448
|
+
{
|
|
449
|
+
RCTComponentViewHelpers(self, commandName, args);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
- (void)focus
|
|
453
|
+
{
|
|
454
|
+
[_backedTextInputView becomeFirstResponder];
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
- (void)blur
|
|
458
|
+
{
|
|
459
|
+
[_backedTextInputView resignFirstResponder];
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
- (void)setTextAndSelection:(NSInteger)eventCount
|
|
463
|
+
value:(NSString *__nullable)value
|
|
464
|
+
start:(NSInteger)start
|
|
465
|
+
end:(NSInteger)end
|
|
466
|
+
{
|
|
467
|
+
if (_mostRecentEventCount != eventCount) {
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
_comingFromJS = YES;
|
|
471
|
+
if (value && ![value isEqualToString:_backedTextInputView.attributedText.string]) {
|
|
472
|
+
NSAttributedString *attributedString =
|
|
473
|
+
[[NSAttributedString alloc] initWithString:value attributes:_backedTextInputView.defaultTextAttributes];
|
|
474
|
+
[self _setAttributedString:attributedString];
|
|
475
|
+
[self _updateState];
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
UITextPosition *startPosition = [_backedTextInputView positionFromPosition:_backedTextInputView.beginningOfDocument
|
|
479
|
+
offset:start];
|
|
480
|
+
UITextPosition *endPosition = [_backedTextInputView positionFromPosition:_backedTextInputView.beginningOfDocument
|
|
481
|
+
offset:end];
|
|
482
|
+
|
|
483
|
+
if (startPosition && endPosition) {
|
|
484
|
+
UITextRange *range = [_backedTextInputView textRangeFromPosition:startPosition toPosition:endPosition];
|
|
485
|
+
[_backedTextInputView setSelectedTextRange:range notifyDelegate:NO];
|
|
486
|
+
}
|
|
487
|
+
_comingFromJS = NO;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
#pragma mark - Default input accessory view
|
|
491
|
+
|
|
492
|
+
- (void)setDefaultInputAccessoryView
|
|
493
|
+
{
|
|
494
|
+
// InputAccessoryView component sets the inputAccessoryView when inputAccessoryViewID exists
|
|
495
|
+
if (_backedTextInputView.inputAccessoryViewID) {
|
|
496
|
+
if (_backedTextInputView.isFirstResponder) {
|
|
497
|
+
[_backedTextInputView reloadInputViews];
|
|
498
|
+
}
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
UIKeyboardType keyboardType = _backedTextInputView.keyboardType;
|
|
503
|
+
|
|
504
|
+
// These keyboard types (all are number pads) don't have a "Done" button by default,
|
|
505
|
+
// so we create an `inputAccessoryView` with this button for them.
|
|
506
|
+
BOOL shouldHaveInputAccessoryView =
|
|
507
|
+
(keyboardType == UIKeyboardTypeNumberPad || keyboardType == UIKeyboardTypePhonePad ||
|
|
508
|
+
keyboardType == UIKeyboardTypeDecimalPad || keyboardType == UIKeyboardTypeASCIICapableNumberPad) &&
|
|
509
|
+
_backedTextInputView.returnKeyType == UIReturnKeyDone;
|
|
510
|
+
|
|
511
|
+
if ((_backedTextInputView.inputAccessoryView != nil) == shouldHaveInputAccessoryView) {
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if (shouldHaveInputAccessoryView) {
|
|
516
|
+
UIToolbar *toolbarView = [UIToolbar new];
|
|
517
|
+
[toolbarView sizeToFit];
|
|
518
|
+
UIBarButtonItem *flexibleSpace =
|
|
519
|
+
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
|
|
520
|
+
UIBarButtonItem *doneButton =
|
|
521
|
+
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone
|
|
522
|
+
target:self
|
|
523
|
+
action:@selector(handleInputAccessoryDoneButton)];
|
|
524
|
+
toolbarView.items = @[ flexibleSpace, doneButton ];
|
|
525
|
+
_backedTextInputView.inputAccessoryView = toolbarView;
|
|
526
|
+
} else {
|
|
527
|
+
_backedTextInputView.inputAccessoryView = nil;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
if (_backedTextInputView.isFirstResponder) {
|
|
531
|
+
[_backedTextInputView reloadInputViews];
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
- (void)handleInputAccessoryDoneButton
|
|
536
|
+
{
|
|
537
|
+
if ([self textInputShouldReturn]) {
|
|
538
|
+
[_backedTextInputView endEditing:YES];
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
#pragma mark - Other
|
|
543
|
+
|
|
544
|
+
- (TextInputEventEmitter::Metrics)_textInputMetrics
|
|
545
|
+
{
|
|
546
|
+
return {
|
|
547
|
+
.text = RCTStringFromNSString(_backedTextInputView.attributedText.string),
|
|
548
|
+
.selectionRange = [self _selectionRange],
|
|
549
|
+
.eventCount = static_cast<int>(_mostRecentEventCount),
|
|
550
|
+
.contentOffset = RCTPointFromCGPoint(_backedTextInputView.contentOffset),
|
|
551
|
+
.contentInset = RCTEdgeInsetsFromUIEdgeInsets(_backedTextInputView.contentInset),
|
|
552
|
+
.contentSize = RCTSizeFromCGSize(_backedTextInputView.contentSize),
|
|
553
|
+
.layoutMeasurement = RCTSizeFromCGSize(_backedTextInputView.bounds.size),
|
|
554
|
+
.zoomScale = _backedTextInputView.zoomScale,
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
- (void)_updateState
|
|
559
|
+
{
|
|
560
|
+
if (!_state) {
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
NSAttributedString *attributedString = _backedTextInputView.attributedText;
|
|
564
|
+
auto data = _state->getData();
|
|
565
|
+
_lastStringStateWasUpdatedWith = attributedString;
|
|
566
|
+
data.attributedStringBox = RCTAttributedStringBoxFromNSAttributedString(attributedString);
|
|
567
|
+
_mostRecentEventCount += _comingFromJS ? 0 : 1;
|
|
568
|
+
data.mostRecentEventCount = _mostRecentEventCount;
|
|
569
|
+
_state->updateState(std::move(data));
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
- (void)_restoreTextSelection
|
|
573
|
+
{
|
|
574
|
+
const auto &selection = static_cast<const PasteTextInputProps &>(*_props).selection;
|
|
575
|
+
if (!selection.has_value()) {
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
auto start = [_backedTextInputView positionFromPosition:_backedTextInputView.beginningOfDocument
|
|
579
|
+
offset:selection->start];
|
|
580
|
+
auto end = [_backedTextInputView positionFromPosition:_backedTextInputView.beginningOfDocument offset:selection->end];
|
|
581
|
+
auto range = [_backedTextInputView textRangeFromPosition:start toPosition:end];
|
|
582
|
+
[_backedTextInputView setSelectedTextRange:range notifyDelegate:YES];
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
- (AttributedString::Range)_selectionRange
|
|
586
|
+
{
|
|
587
|
+
UITextRange *selectedTextRange = _backedTextInputView.selectedTextRange;
|
|
588
|
+
NSInteger start = [_backedTextInputView offsetFromPosition:_backedTextInputView.beginningOfDocument
|
|
589
|
+
toPosition:selectedTextRange.start];
|
|
590
|
+
NSInteger end = [_backedTextInputView offsetFromPosition:_backedTextInputView.beginningOfDocument
|
|
591
|
+
toPosition:selectedTextRange.end];
|
|
592
|
+
return AttributedString::Range{(int)start, (int)(end - start)};
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
- (void)_setAttributedString:(NSAttributedString *)attributedString
|
|
596
|
+
{
|
|
597
|
+
if ([self _textOf:attributedString equals:_backedTextInputView.attributedText]) {
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
UITextRange *selectedRange = _backedTextInputView.selectedTextRange;
|
|
601
|
+
NSInteger oldTextLength = _backedTextInputView.attributedText.string.length;
|
|
602
|
+
_backedTextInputView.attributedText = attributedString;
|
|
603
|
+
if (selectedRange.empty) {
|
|
604
|
+
// Maintaining a cursor position relative to the end of the old text.
|
|
605
|
+
NSInteger offsetStart = [_backedTextInputView offsetFromPosition:_backedTextInputView.beginningOfDocument
|
|
606
|
+
toPosition:selectedRange.start];
|
|
607
|
+
NSInteger offsetFromEnd = oldTextLength - offsetStart;
|
|
608
|
+
NSInteger newOffset = attributedString.string.length - offsetFromEnd;
|
|
609
|
+
UITextPosition *position = [_backedTextInputView positionFromPosition:_backedTextInputView.beginningOfDocument
|
|
610
|
+
offset:newOffset];
|
|
611
|
+
[_backedTextInputView setSelectedTextRange:[_backedTextInputView textRangeFromPosition:position toPosition:position]
|
|
612
|
+
notifyDelegate:YES];
|
|
613
|
+
}
|
|
614
|
+
[self _restoreTextSelection];
|
|
615
|
+
_lastStringStateWasUpdatedWith = attributedString;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
-(void)_setOnPaste{
|
|
619
|
+
_backedTextInputView.onPaste = ^(NSDictionary *body) {
|
|
620
|
+
const auto eventEmitter = [self getEventEmitter];
|
|
621
|
+
if (eventEmitter) {
|
|
622
|
+
std::vector<PasteTextInputEventEmitter::OnPasteData> eventDataVector;
|
|
623
|
+
NSArray<NSDictionary *> *files = body[@"data"];
|
|
624
|
+
if (files != nil && files.count > 0) {
|
|
625
|
+
for (int i = 0; i < files.count; i++) {
|
|
626
|
+
NSDictionary *file = files[i];
|
|
627
|
+
id obj = [file objectForKey:@"fileName"]; // Get object from NSDictionary
|
|
628
|
+
if (obj != nil) {
|
|
629
|
+
PasteTextInputEventEmitter::OnPasteData data = PasteTextInputEventEmitter::OnPasteData{
|
|
630
|
+
.fileName = convertNSDictionaryValueToStdString(file, @"fileName"),
|
|
631
|
+
.fileSize = convertNSDictionaryValueToStdInt(file, @"fileSize"),
|
|
632
|
+
.type = convertNSDictionaryValueToStdString(file, @"type"),
|
|
633
|
+
.uri = convertNSDictionaryValueToStdString(file, @"uri"),
|
|
634
|
+
};
|
|
635
|
+
eventDataVector.push_back(data);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
eventEmitter->onPaste(PasteTextInputEventEmitter::OnPaste{
|
|
641
|
+
.data = eventDataVector
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
- (void)_setMultiline:(BOOL)multiline
|
|
648
|
+
{
|
|
649
|
+
[_backedTextInputView removeFromSuperview];
|
|
650
|
+
PasteInputTextView *backedTextInputView = [[PasteInputTextView alloc] initWithFrame:self.bounds];
|
|
651
|
+
backedTextInputView.frame = _backedTextInputView.frame;
|
|
652
|
+
RCTCopyBackedTextInput(_backedTextInputView, backedTextInputView);
|
|
653
|
+
_backedTextInputView = backedTextInputView;
|
|
654
|
+
[self _setOnPaste];
|
|
655
|
+
[self addSubview:_backedTextInputView];
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
- (void)_setSmartPunctuation:(NSString *)smartPunctuation {
|
|
659
|
+
if ([smartPunctuation isEqualToString:@"enable"]) {
|
|
660
|
+
[_backedTextInputView setSmartDashesType:UITextSmartDashesTypeYes];
|
|
661
|
+
[_backedTextInputView setSmartQuotesType:UITextSmartQuotesTypeYes];
|
|
662
|
+
[_backedTextInputView setSmartInsertDeleteType:UITextSmartInsertDeleteTypeYes];
|
|
663
|
+
} else if ([smartPunctuation isEqualToString:@"disable"]) {
|
|
664
|
+
[_backedTextInputView setSmartDashesType:UITextSmartDashesTypeNo];
|
|
665
|
+
[_backedTextInputView setSmartQuotesType:UITextSmartQuotesTypeNo];
|
|
666
|
+
[_backedTextInputView setSmartInsertDeleteType:UITextSmartInsertDeleteTypeNo];
|
|
667
|
+
} else {
|
|
668
|
+
[_backedTextInputView setSmartDashesType:UITextSmartDashesTypeDefault];
|
|
669
|
+
[_backedTextInputView setSmartQuotesType:UITextSmartQuotesTypeDefault];
|
|
670
|
+
[_backedTextInputView setSmartInsertDeleteType:UITextSmartInsertDeleteTypeDefault];
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
- (BOOL)_textOf:(NSAttributedString *)newText equals:(NSAttributedString *)oldText
|
|
675
|
+
{
|
|
676
|
+
// When the dictation is running we can't update the attributed text on the backed up text view
|
|
677
|
+
// because setting the attributed string will kill the dictation. This means that we can't impose
|
|
678
|
+
// the settings on a dictation.
|
|
679
|
+
// Similarly, when the user is in the middle of inputting some text in Japanese/Chinese, there will be styling on the
|
|
680
|
+
// text that we should disregard. See
|
|
681
|
+
// https://developer.apple.com/documentation/uikit/uitextinput/1614489-markedtextrange?language=objc for more info.
|
|
682
|
+
// Also, updating the attributed text while inputting Korean language will break input mechanism.
|
|
683
|
+
// If the user added an emoji, the system adds a font attribute for the emoji and stores the original font in
|
|
684
|
+
// NSOriginalFont. Lastly, when entering a password, etc., there will be additional styling on the field as the native
|
|
685
|
+
// text view handles showing the last character for a split second.
|
|
686
|
+
__block BOOL fontHasBeenUpdatedBySystem = false;
|
|
687
|
+
[oldText enumerateAttribute:@"NSOriginalFont"
|
|
688
|
+
inRange:NSMakeRange(0, oldText.length)
|
|
689
|
+
options:0
|
|
690
|
+
usingBlock:^(id value, NSRange range, BOOL *stop) {
|
|
691
|
+
if (value) {
|
|
692
|
+
fontHasBeenUpdatedBySystem = true;
|
|
693
|
+
}
|
|
694
|
+
}];
|
|
695
|
+
|
|
696
|
+
BOOL shouldFallbackToBareTextComparison =
|
|
697
|
+
[_backedTextInputView.textInputMode.primaryLanguage isEqualToString:@"dictation"] ||
|
|
698
|
+
[_backedTextInputView.textInputMode.primaryLanguage isEqualToString:@"ko-KR"] ||
|
|
699
|
+
_backedTextInputView.markedTextRange || _backedTextInputView.isSecureTextEntry || fontHasBeenUpdatedBySystem;
|
|
700
|
+
|
|
701
|
+
if (shouldFallbackToBareTextComparison) {
|
|
702
|
+
return ([newText.string isEqualToString:oldText.string]);
|
|
703
|
+
} else {
|
|
704
|
+
return ([newText isEqualToAttributedString:oldText]);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
- (SubmitBehavior)getSubmitBehavior
|
|
709
|
+
{
|
|
710
|
+
const auto &props = static_cast<const PasteTextInputProps &>(*_props);
|
|
711
|
+
const SubmitBehavior submitBehaviorDefaultable = props.submitBehavior;
|
|
712
|
+
|
|
713
|
+
// We should always have a non-default `submitBehavior`, but in case we don't, set it based on multiline.
|
|
714
|
+
if (submitBehaviorDefaultable == SubmitBehavior::Default) {
|
|
715
|
+
return props.multiline ? SubmitBehavior::Newline : SubmitBehavior::BlurAndSubmit;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
return submitBehaviorDefaultable;
|
|
719
|
+
}
|
|
720
|
+
@end
|
|
721
|
+
|
|
722
|
+
Class<RCTComponentViewProtocol> PasteTextInputCls(void)
|
|
723
|
+
{
|
|
724
|
+
return PasteTextInput.class;
|
|
725
|
+
}
|
|
726
|
+
#endif
|