@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.
Files changed (108) hide show
  1. package/.circleci/config.yml +130 -0
  2. package/LICENSE +21 -0
  3. package/README.md +73 -0
  4. package/android/.project +17 -0
  5. package/android/.settings/org.eclipse.buildship.core.prefs +13 -0
  6. package/android/build.gradle +94 -0
  7. package/android/generated/java/com/facebook/react/viewmanagers/PasteTextInputManagerDelegate.java +236 -0
  8. package/android/generated/java/com/facebook/react/viewmanagers/PasteTextInputManagerInterface.java +84 -0
  9. package/android/generated/jni/CMakeLists.txt +36 -0
  10. package/android/generated/jni/PasteTextInputSpecs-generated.cpp +27 -0
  11. package/android/generated/jni/PasteTextInputSpecs.h +28 -0
  12. package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/ComponentDescriptors.cpp +22 -0
  13. package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/ComponentDescriptors.h +166 -0
  14. package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/EventEmitters.cpp +183 -0
  15. package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/EventEmitters.h +148 -0
  16. package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/PasteTextInputSpecsJSI-generated.cpp +17 -0
  17. package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/PasteTextInputSpecsJSI.h +19 -0
  18. package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/Props.cpp +640 -0
  19. package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/Props.h +144 -0
  20. package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/ShadowNodes.cpp +247 -0
  21. package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/ShadowNodes.h +95 -0
  22. package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/States.cpp +14 -0
  23. package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/States.h +19 -0
  24. package/android/gradle.properties +6 -0
  25. package/android/src/main/java/com/mattermost/pasteinputtext/IPasteInputListener.kt +8 -0
  26. package/android/src/main/java/com/mattermost/pasteinputtext/PasteInputActionCallback.kt +72 -0
  27. package/android/src/main/java/com/mattermost/pasteinputtext/PasteInputEditText.kt +60 -0
  28. package/android/src/main/java/com/mattermost/pasteinputtext/PasteInputFileFromUrl.kt +50 -0
  29. package/android/src/main/java/com/mattermost/pasteinputtext/PasteInputListener.kt +84 -0
  30. package/android/src/main/java/com/mattermost/pasteinputtext/PasteTextInputManager.kt +72 -0
  31. package/android/src/main/java/com/mattermost/pasteinputtext/PasteTextInputPackage.kt +14 -0
  32. package/android/src/main/java/com/mattermost/pasteinputtext/PasteTextInputPasteEvent.java +41 -0
  33. package/android/src/main/java/com/mattermost/pasteinputtext/RealPathUtil.kt +174 -0
  34. package/ios/NSData+MimeType.h +20 -0
  35. package/ios/NSData+MimeType.m +59 -0
  36. package/ios/PasteInput-Bridging-Header.h +1 -0
  37. package/ios/PasteInput.xcodeproj/project.pbxproj +319 -0
  38. package/ios/PasteInputTextView.h +20 -0
  39. package/ios/PasteInputTextView.m +68 -0
  40. package/ios/PasteInputView.h +18 -0
  41. package/ios/PasteInputView.m +96 -0
  42. package/ios/PasteTextInput.h +13 -0
  43. package/ios/PasteTextInput.mm +726 -0
  44. package/ios/PasteTextInputManager.mm +171 -0
  45. package/ios/Swime/MimeType.h +70 -0
  46. package/ios/Swime/MimeType.m +701 -0
  47. package/ios/Swime/Swime.h +14 -0
  48. package/ios/Swime/Swime.m +28 -0
  49. package/ios/Swime/SwimeProxy.h +18 -0
  50. package/ios/Swime/SwimeProxy.m +66 -0
  51. package/ios/Swime/SwimeUtils.h +12 -0
  52. package/ios/Swime/SwimeUtils.m +23 -0
  53. package/ios/UIImage+ImageEffects.h +112 -0
  54. package/ios/UIImage+ImageEffects.m +310 -0
  55. package/ios/UIImage+vImageScaling.h +16 -0
  56. package/ios/UIImage+vImageScaling.m +48 -0
  57. package/ios/UIPasteboard+GetImageInfo.h +19 -0
  58. package/ios/UIPasteboard+GetImageInfo.m +98 -0
  59. package/ios/generated/PasteTextInputSpecs/ComponentDescriptors.cpp +22 -0
  60. package/ios/generated/PasteTextInputSpecs/ComponentDescriptors.h +42 -0
  61. package/ios/generated/PasteTextInputSpecs/EventEmitters.cpp +41 -0
  62. package/ios/generated/PasteTextInputSpecs/EventEmitters.h +37 -0
  63. package/ios/generated/PasteTextInputSpecs/PasteTextInputSpecs-generated.mm +16 -0
  64. package/ios/generated/PasteTextInputSpecs/PasteTextInputSpecs.h +38 -0
  65. package/ios/generated/PasteTextInputSpecs/Props.cpp +142 -0
  66. package/ios/generated/PasteTextInputSpecs/Props.h +81 -0
  67. package/ios/generated/PasteTextInputSpecs/RCTComponentViewHelpers.h +106 -0
  68. package/ios/generated/PasteTextInputSpecs/ShadowNodes.cpp +127 -0
  69. package/ios/generated/PasteTextInputSpecs/ShadowNodes.h +85 -0
  70. package/ios/generated/PasteTextInputSpecs/States.cpp +16 -0
  71. package/ios/generated/PasteTextInputSpecs/States.h +53 -0
  72. package/ios/generated/PasteTextInputSpecsJSI-generated.cpp +17 -0
  73. package/ios/generated/PasteTextInputSpecsJSI.h +19 -0
  74. package/lib/commonjs/PasteTextInput.js +446 -0
  75. package/lib/commonjs/PasteTextInput.js.map +1 -0
  76. package/lib/commonjs/PasteTextInputNativeComponent.ts +277 -0
  77. package/lib/commonjs/index.js +25 -0
  78. package/lib/commonjs/index.js.map +1 -0
  79. package/lib/commonjs/module.d.js +2 -0
  80. package/lib/commonjs/module.d.js.map +1 -0
  81. package/lib/commonjs/package.json +1 -0
  82. package/lib/commonjs/types.js +6 -0
  83. package/lib/commonjs/types.js.map +1 -0
  84. package/lib/module/PasteTextInput.js +440 -0
  85. package/lib/module/PasteTextInput.js.map +1 -0
  86. package/lib/module/PasteTextInputNativeComponent.ts +277 -0
  87. package/lib/module/index.js +9 -0
  88. package/lib/module/index.js.map +1 -0
  89. package/lib/module/module.d.js +2 -0
  90. package/lib/module/module.d.js.map +1 -0
  91. package/lib/module/types.js +4 -0
  92. package/lib/module/types.js.map +1 -0
  93. package/lib/typescript/src/PasteTextInput.d.ts +5 -0
  94. package/lib/typescript/src/PasteTextInput.d.ts.map +1 -0
  95. package/lib/typescript/src/PasteTextInputNativeComponent.d.ts +168 -0
  96. package/lib/typescript/src/PasteTextInputNativeComponent.d.ts.map +1 -0
  97. package/lib/typescript/src/index.d.ts +6 -0
  98. package/lib/typescript/src/index.d.ts.map +1 -0
  99. package/lib/typescript/src/types.d.ts +56 -0
  100. package/lib/typescript/src/types.d.ts.map +1 -0
  101. package/package.json +140 -0
  102. package/react-native-paste-input.podspec +20 -0
  103. package/react-native.config.js +13 -0
  104. package/src/PasteTextInput.tsx +615 -0
  105. package/src/PasteTextInputNativeComponent.ts +277 -0
  106. package/src/index.ts +13 -0
  107. package/src/module.d.ts +4 -0
  108. 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