react-native-typerich 1.1.1 → 2.2.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/README.md +2 -0
- package/ReactNativeTypeRich.podspec +41 -0
- package/android/src/main/java/com/typerich/TypeRichTextInputView.kt +32 -10
- package/android/src/main/java/com/typerich/TypeRichTextInputViewManager.kt +5 -0
- package/ios/TypeRichTextInputView.h +27 -7
- package/ios/TypeRichTextInputView.mm +809 -26
- package/ios/cpp/TypeRichTextInputViewComponentDescriptor.h +19 -0
- package/ios/cpp/TypeRichTextInputViewShadowNode.h +44 -0
- package/ios/cpp/TypeRichTextInputViewShadowNode.mm +110 -0
- package/ios/cpp/TypeRichTextInputViewState.cpp +10 -0
- package/ios/cpp/TypeRichTextInputViewState.h +22 -0
- package/ios/inputTextView/TypeRichUITextView.h +14 -0
- package/ios/inputTextView/TypeRichUITextView.mm +100 -0
- package/ios/modules/commands/TypeRichTextInputCommands.h +24 -0
- package/ios/modules/commands/TypeRichTextInputCommands.mm +392 -0
- package/ios/utils/StringUtils.h +19 -0
- package/ios/utils/StringUtils.mm +15 -0
- package/ios/utils/TextInputUtils.h +26 -0
- package/ios/utils/TextInputUtils.mm +58 -0
- package/lib/module/TypeRichTextInput.js +13 -36
- package/lib/module/TypeRichTextInput.js.map +1 -1
- package/lib/module/TypeRichTextInputNativeComponent.ts +266 -52
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/types/TypeRichTextInput.js +4 -0
- package/lib/module/types/TypeRichTextInput.js.map +1 -0
- package/lib/typescript/src/TypeRichTextInput.d.ts +2 -22
- package/lib/typescript/src/TypeRichTextInput.d.ts.map +1 -1
- package/lib/typescript/src/TypeRichTextInputNativeComponent.d.ts +200 -14
- package/lib/typescript/src/TypeRichTextInputNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/types/TypeRichTextInput.d.ts +95 -0
- package/lib/typescript/src/types/TypeRichTextInput.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/TypeRichTextInput.tsx +20 -70
- package/src/TypeRichTextInputNativeComponent.ts +266 -52
- package/src/index.tsx +1 -5
- package/src/types/TypeRichTextInput.tsx +116 -0
- package/TypeRichTextInput.podspec +0 -20
- package/ios/TypeRichTextInputViewManager.mm +0 -27
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
//
|
|
2
|
+
// TypeRichTextInputCommands.m
|
|
3
|
+
// ReactNativeTypeRich
|
|
4
|
+
//
|
|
5
|
+
// Created by Div on 29/12/25.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#import "TypeRichTextInputCommands.h"
|
|
9
|
+
#import "TypeRichTextInputView.h"
|
|
10
|
+
|
|
11
|
+
@implementation TypeRichTextInputCommands {
|
|
12
|
+
__weak UITextView *_textView;
|
|
13
|
+
__weak TypeRichTextInputView *_owner;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
- (instancetype)initWithTextView:(UITextView *)textView
|
|
17
|
+
owner:(TypeRichTextInputView *)owner {
|
|
18
|
+
if (self = [super init]) {
|
|
19
|
+
_textView = textView;
|
|
20
|
+
_owner = owner;
|
|
21
|
+
|
|
22
|
+
// Serial queue for commands
|
|
23
|
+
_commandQueue = [[NSOperationQueue alloc] init];
|
|
24
|
+
_commandQueue.maxConcurrentOperationCount = 1;
|
|
25
|
+
_commandQueue.qualityOfService = NSQualityOfServiceUserInteractive;
|
|
26
|
+
}
|
|
27
|
+
return self;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
#pragma mark - Focus / Blur
|
|
31
|
+
|
|
32
|
+
/// focus()
|
|
33
|
+
- (void)focus
|
|
34
|
+
{
|
|
35
|
+
UITextView *tv = _textView;
|
|
36
|
+
if (!tv || tv.isFirstResponder || !tv.editable) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
41
|
+
[tv becomeFirstResponder];
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/// blur()
|
|
46
|
+
- (void)blur
|
|
47
|
+
{
|
|
48
|
+
UITextView *tv = _textView;
|
|
49
|
+
if (!tv || !tv.isFirstResponder) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
54
|
+
[tv resignFirstResponder];
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
#pragma mark - Text Commands
|
|
59
|
+
|
|
60
|
+
/// setText(text) - diff-based with proper cursor tracking, non-undoable
|
|
61
|
+
- (void)setText:(NSString *)text
|
|
62
|
+
{
|
|
63
|
+
UITextView *tv = _textView;
|
|
64
|
+
TypeRichTextInputView *owner = _owner;
|
|
65
|
+
if (!tv || !owner) return;
|
|
66
|
+
|
|
67
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
68
|
+
// Never interrupt IME composition
|
|
69
|
+
if (tv.markedTextRange) return;
|
|
70
|
+
|
|
71
|
+
NSString *newText = text ?: @"";
|
|
72
|
+
NSString *currentText = tv.text ?: @"";
|
|
73
|
+
|
|
74
|
+
// commented for now as causing issues when textinput is blank
|
|
75
|
+
// if ([currentText isEqualToString:newText]) {
|
|
76
|
+
// return;
|
|
77
|
+
// }
|
|
78
|
+
|
|
79
|
+
owner.blockEmitting = YES;
|
|
80
|
+
|
|
81
|
+
NSRange oldSelection = tv.selectedRange;
|
|
82
|
+
|
|
83
|
+
// Calculate minimal diff range
|
|
84
|
+
NSRange diffRange = [self calculateDiffRange:currentText newText:newText];
|
|
85
|
+
|
|
86
|
+
// Calculate what text will be inserted
|
|
87
|
+
NSInteger newLength = newText.length - (currentText.length - diffRange.length);
|
|
88
|
+
NSString *replacementText = @"";
|
|
89
|
+
|
|
90
|
+
if (newLength > 0) {
|
|
91
|
+
replacementText = [newText substringWithRange:NSMakeRange(diffRange.location, newLength)];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Convert NSRange to UITextRange
|
|
95
|
+
UITextPosition *start = [tv positionFromPosition:tv.beginningOfDocument
|
|
96
|
+
offset:diffRange.location];
|
|
97
|
+
UITextPosition *end = [tv positionFromPosition:tv.beginningOfDocument
|
|
98
|
+
offset:NSMaxRange(diffRange)];
|
|
99
|
+
|
|
100
|
+
if (start && end) {
|
|
101
|
+
UITextRange *range = [tv textRangeFromPosition:start toPosition:end];
|
|
102
|
+
[tv replaceRange:range withText:replacementText];
|
|
103
|
+
|
|
104
|
+
// Calculate cursor adjustment
|
|
105
|
+
NSInteger delta = replacementText.length - diffRange.length;
|
|
106
|
+
NSInteger newCursorPos = oldSelection.location;
|
|
107
|
+
|
|
108
|
+
// If change happened before cursor, adjust cursor position
|
|
109
|
+
if (diffRange.location <= oldSelection.location) {
|
|
110
|
+
newCursorPos = oldSelection.location + delta;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Clamp to valid range
|
|
114
|
+
newCursorPos = MAX(0, MIN(newCursorPos, newText.length));
|
|
115
|
+
|
|
116
|
+
// Restore cursor at adjusted position
|
|
117
|
+
tv.selectedRange = NSMakeRange(newCursorPos, 0);
|
|
118
|
+
|
|
119
|
+
} else {
|
|
120
|
+
// Fallback: full replace with cursor clamping
|
|
121
|
+
tv.text = newText;
|
|
122
|
+
NSInteger safeLoc = MIN(oldSelection.location, newText.length);
|
|
123
|
+
tv.selectedRange = NSMakeRange(safeLoc, 0);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
owner.blockEmitting = NO;
|
|
127
|
+
|
|
128
|
+
[owner updatePlaceholderVisibilityFromCommand];
|
|
129
|
+
|
|
130
|
+
if (tv.scrollEnabled) {
|
|
131
|
+
[owner invalidateTextLayoutFromCommand];
|
|
132
|
+
|
|
133
|
+
// scroll to cursor
|
|
134
|
+
[tv scrollRangeToVisible:tv.selectedRange];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
[owner dispatchSelectionChangeIfNeeded];
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Helper: Calculate minimal diff range between two strings
|
|
142
|
+
- (NSRange)calculateDiffRange:(NSString *)oldText newText:(NSString *)newText {
|
|
143
|
+
NSInteger oldLen = oldText.length;
|
|
144
|
+
NSInteger newLen = newText.length;
|
|
145
|
+
|
|
146
|
+
// Find common prefix
|
|
147
|
+
NSInteger prefixLen = 0;
|
|
148
|
+
NSInteger minLen = MIN(oldLen, newLen);
|
|
149
|
+
|
|
150
|
+
while (prefixLen < minLen &&
|
|
151
|
+
[oldText characterAtIndex:prefixLen] == [newText characterAtIndex:prefixLen]) {
|
|
152
|
+
prefixLen++;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Find common suffix
|
|
156
|
+
NSInteger suffixLen = 0;
|
|
157
|
+
while (suffixLen < (minLen - prefixLen) &&
|
|
158
|
+
[oldText characterAtIndex:(oldLen - suffixLen - 1)] ==
|
|
159
|
+
[newText characterAtIndex:(newLen - suffixLen - 1)]) {
|
|
160
|
+
suffixLen++;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Return range in old text that needs to be replaced
|
|
164
|
+
NSInteger location = prefixLen;
|
|
165
|
+
NSInteger length = oldLen - prefixLen - suffixLen;
|
|
166
|
+
|
|
167
|
+
return NSMakeRange(location, length);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/// setSelection(start, end)
|
|
171
|
+
- (void)setSelectionStart:(NSInteger)start end:(NSInteger)end {
|
|
172
|
+
UITextView *tv = _textView;
|
|
173
|
+
TypeRichTextInputView *owner = _owner;
|
|
174
|
+
if (!tv || !owner) return;
|
|
175
|
+
|
|
176
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
177
|
+
if ([owner isTouchInProgress]) return;
|
|
178
|
+
|
|
179
|
+
owner.blockEmitting = YES;
|
|
180
|
+
|
|
181
|
+
NSInteger length = tv.text.length;
|
|
182
|
+
NSInteger s = MAX(0, MIN(start, length));
|
|
183
|
+
NSInteger e = MAX(s, MIN(end, length));
|
|
184
|
+
|
|
185
|
+
tv.selectedRange = NSMakeRange(s, e - s);
|
|
186
|
+
|
|
187
|
+
owner.blockEmitting = NO;
|
|
188
|
+
[owner dispatchSelectionChangeIfNeeded];
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/// insertTextAt(start, end, text)
|
|
193
|
+
- (void)insertTextAtStart:(NSInteger)start
|
|
194
|
+
end:(NSInteger)end
|
|
195
|
+
text:(NSString *)text
|
|
196
|
+
{
|
|
197
|
+
UITextView *tv = _textView;
|
|
198
|
+
TypeRichTextInputView *owner = _owner;
|
|
199
|
+
if (!tv || !owner || !text) return;
|
|
200
|
+
|
|
201
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
202
|
+
if ([owner isTouchInProgress]) return;
|
|
203
|
+
if (tv.markedTextRange) return;
|
|
204
|
+
// if ([owner isHandlingUserInput]) return;
|
|
205
|
+
|
|
206
|
+
owner.blockEmitting = YES;
|
|
207
|
+
|
|
208
|
+
UITextPosition *s =
|
|
209
|
+
[tv positionFromPosition:tv.beginningOfDocument offset:start];
|
|
210
|
+
UITextPosition *e =
|
|
211
|
+
[tv positionFromPosition:tv.beginningOfDocument offset:end];
|
|
212
|
+
|
|
213
|
+
if (!s || !e) {
|
|
214
|
+
owner.blockEmitting = NO;
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
UITextRange *range = [tv textRangeFromPosition:s toPosition:e];
|
|
219
|
+
|
|
220
|
+
// Preserve formatting
|
|
221
|
+
if (tv.typingAttributes) {
|
|
222
|
+
tv.typingAttributes = tv.typingAttributes;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
[tv replaceRange:range withText:text];
|
|
226
|
+
|
|
227
|
+
owner.blockEmitting = NO;
|
|
228
|
+
|
|
229
|
+
[owner updatePlaceholderVisibilityFromCommand];
|
|
230
|
+
[owner invalidateTextLayoutFromCommand];
|
|
231
|
+
[owner dispatchSelectionChangeIfNeeded];
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
- (NSDictionary *)baseAttributesForTextView:(UITextView *)tv {
|
|
236
|
+
if (tv.typingAttributes.count > 0) {
|
|
237
|
+
return tv.typingAttributes;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (tv.textStorage.length > 0) {
|
|
241
|
+
return [tv.textStorage attributesAtIndex:0 effectiveRange:nil];
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return @{
|
|
245
|
+
NSFontAttributeName: tv.font ?: [UIFont systemFontOfSize:14],
|
|
246
|
+
NSForegroundColorAttributeName: tv.textColor ?: UIColor.blackColor
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/// setText() - legacy, buggy and non undoable
|
|
251
|
+
//- (void)setText:(NSString *)text
|
|
252
|
+
//{
|
|
253
|
+
// UITextView *tv = _textView;
|
|
254
|
+
// TypeRichTextInputView *owner = _owner;
|
|
255
|
+
// if (!tv || !owner) return;
|
|
256
|
+
//
|
|
257
|
+
//
|
|
258
|
+
// dispatch_async(dispatch_get_main_queue(), ^{
|
|
259
|
+
// if (tv.markedTextRange) return;
|
|
260
|
+
// if (owner.isUserTyping) return;
|
|
261
|
+
//
|
|
262
|
+
// NSString *newText = text ?: @"";
|
|
263
|
+
//
|
|
264
|
+
// owner.blockEmitting = YES;
|
|
265
|
+
//
|
|
266
|
+
// NSTextStorage *storage = tv.textStorage;
|
|
267
|
+
//
|
|
268
|
+
// NSRange oldSelection = tv.selectedRange;
|
|
269
|
+
//
|
|
270
|
+
// NSDictionary *attrs = [self baseAttributesForTextView:tv];
|
|
271
|
+
//
|
|
272
|
+
// NSAttributedString *attrText =
|
|
273
|
+
// [[NSAttributedString alloc] initWithString:newText
|
|
274
|
+
// attributes:attrs];
|
|
275
|
+
//
|
|
276
|
+
// [storage beginEditing];
|
|
277
|
+
// [storage setAttributedString:attrText];
|
|
278
|
+
// [storage endEditing];
|
|
279
|
+
//
|
|
280
|
+
// // Clamp & restore selection
|
|
281
|
+
// NSInteger max = newText.length;
|
|
282
|
+
// NSInteger loc = MIN(oldSelection.location, max);
|
|
283
|
+
// NSInteger len = MIN(oldSelection.length, max - loc);
|
|
284
|
+
// tv.selectedRange = NSMakeRange(loc, len);
|
|
285
|
+
//
|
|
286
|
+
// owner.blockEmitting = NO;
|
|
287
|
+
//
|
|
288
|
+
// [owner updatePlaceholderVisibilityFromCommand];
|
|
289
|
+
// [owner invalidateTextLayoutFromCommand];
|
|
290
|
+
// [owner dispatchSelectionChangeIfNeeded];
|
|
291
|
+
////
|
|
292
|
+
//// // Restore cursor (clamped)
|
|
293
|
+
//// NSInteger safeOffset = MIN(cursorOffset, newText.length);
|
|
294
|
+
//// UITextPosition *pos =
|
|
295
|
+
//// [tv positionFromPosition:tv.beginningOfDocument
|
|
296
|
+
//// offset:safeOffset];
|
|
297
|
+
////
|
|
298
|
+
//// if (pos) {
|
|
299
|
+
//// tv.selectedTextRange =
|
|
300
|
+
//// [tv textRangeFromPosition:pos toPosition:pos];
|
|
301
|
+
//// }
|
|
302
|
+
////
|
|
303
|
+
//// owner.blockEmitting = NO;
|
|
304
|
+
////
|
|
305
|
+
//// [owner updatePlaceholderVisibilityFromCommand];
|
|
306
|
+
//// [owner invalidateTextLayoutFromCommand];
|
|
307
|
+
//// [owner dispatchSelectionChangeIfNeeded];
|
|
308
|
+
// });
|
|
309
|
+
//}
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
/// setText(text) — undoable, cursor-safe, IME-safe
|
|
313
|
+
//- (void)setText:(NSString *)text
|
|
314
|
+
//{
|
|
315
|
+
// UITextView *tv = _textView;
|
|
316
|
+
// TypeRichTextInputView *owner = _owner;
|
|
317
|
+
// if (!tv || !owner) return;
|
|
318
|
+
//
|
|
319
|
+
// dispatch_async(dispatch_get_main_queue(), ^{
|
|
320
|
+
// // Never touch text while IME composing
|
|
321
|
+
// if (tv.markedTextRange) return;
|
|
322
|
+
//
|
|
323
|
+
// NSString *newText = text ?: @"";
|
|
324
|
+
// NSString *oldText = tv.text ?: @"";
|
|
325
|
+
//
|
|
326
|
+
// // No-op fast path
|
|
327
|
+
// if ([oldText isEqualToString:newText]) {
|
|
328
|
+
// return;
|
|
329
|
+
// }
|
|
330
|
+
//
|
|
331
|
+
// owner.blockEmitting = YES;
|
|
332
|
+
//
|
|
333
|
+
// // Save selection (cursor)
|
|
334
|
+
// NSRange oldSelection = tv.selectedRange;
|
|
335
|
+
//
|
|
336
|
+
// // Compute minimal diff range
|
|
337
|
+
// NSRange replaceRange = DiffRange(oldText, newText);
|
|
338
|
+
//
|
|
339
|
+
// NSInteger insertStart = replaceRange.location;
|
|
340
|
+
// NSInteger insertLength =
|
|
341
|
+
// newText.length - (oldText.length - replaceRange.length);
|
|
342
|
+
//
|
|
343
|
+
// NSString *insertText =
|
|
344
|
+
// insertLength > 0
|
|
345
|
+
// ? [newText substringWithRange:
|
|
346
|
+
// NSMakeRange(insertStart, insertLength)]
|
|
347
|
+
// : @"";
|
|
348
|
+
//
|
|
349
|
+
// // Convert NSRange → UITextRange
|
|
350
|
+
// UITextPosition *start =
|
|
351
|
+
// [tv positionFromPosition:tv.beginningOfDocument
|
|
352
|
+
// offset:replaceRange.location];
|
|
353
|
+
// UITextPosition *end =
|
|
354
|
+
// [tv positionFromPosition:tv.beginningOfDocument
|
|
355
|
+
// offset:NSMaxRange(replaceRange)];
|
|
356
|
+
//
|
|
357
|
+
// if (!start || !end) {
|
|
358
|
+
// owner.blockEmitting = NO;
|
|
359
|
+
// return;
|
|
360
|
+
// }
|
|
361
|
+
//
|
|
362
|
+
// UITextRange *uiRange =
|
|
363
|
+
// [tv textRangeFromPosition:start toPosition:end];
|
|
364
|
+
//
|
|
365
|
+
// // THIS IS THE KEY LINE (undo-safe)
|
|
366
|
+
// [tv replaceRange:uiRange withText:insertText];
|
|
367
|
+
//
|
|
368
|
+
// // ---- Restore selection correctly ----
|
|
369
|
+
// NSInteger delta = insertText.length - replaceRange.length;
|
|
370
|
+
//
|
|
371
|
+
// NSInteger newLoc = oldSelection.location;
|
|
372
|
+
// NSInteger newLen = oldSelection.length;
|
|
373
|
+
//
|
|
374
|
+
// if (oldSelection.location > replaceRange.location) {
|
|
375
|
+
// newLoc = MAX(0, newLoc + delta);
|
|
376
|
+
// }
|
|
377
|
+
//
|
|
378
|
+
// NSInteger max = tv.text.length;
|
|
379
|
+
// newLoc = MIN(newLoc, max);
|
|
380
|
+
// newLen = MIN(newLen, max - newLoc);
|
|
381
|
+
//
|
|
382
|
+
// tv.selectedRange = NSMakeRange(newLoc, newLen);
|
|
383
|
+
//
|
|
384
|
+
// owner.blockEmitting = NO;
|
|
385
|
+
//
|
|
386
|
+
// [owner updatePlaceholderVisibilityFromCommand];
|
|
387
|
+
// [owner invalidateTextLayoutFromCommand];
|
|
388
|
+
// [owner dispatchSelectionChangeIfNeeded];
|
|
389
|
+
// });
|
|
390
|
+
//}
|
|
391
|
+
|
|
392
|
+
@end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//
|
|
2
|
+
// StringUtils.h
|
|
3
|
+
// Pods
|
|
4
|
+
//
|
|
5
|
+
// Created by Div on 27/12/25.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#import <Foundation/Foundation.h>
|
|
9
|
+
|
|
10
|
+
#ifdef __cplusplus
|
|
11
|
+
#include <string>
|
|
12
|
+
#endif
|
|
13
|
+
|
|
14
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
15
|
+
|
|
16
|
+
/// Convert C++ std::string → NSString
|
|
17
|
+
NSString *NSStringFromCppString(const std::string &str);
|
|
18
|
+
|
|
19
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//
|
|
2
|
+
// StringUtils.m
|
|
3
|
+
// ReactNativeTypeRich
|
|
4
|
+
//
|
|
5
|
+
// Created by Div on 27/12/25.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#import "StringUtils.h"
|
|
9
|
+
|
|
10
|
+
NSString *NSStringFromCppString(const std::string &str) {
|
|
11
|
+
if (str.empty()) {
|
|
12
|
+
return @"";
|
|
13
|
+
}
|
|
14
|
+
return [NSString stringWithUTF8String:str.c_str()];
|
|
15
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//
|
|
2
|
+
// TextUtils.h
|
|
3
|
+
// Pods
|
|
4
|
+
//
|
|
5
|
+
// Created by Div on 27/12/25.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#import <UIKit/UIKit.h>
|
|
9
|
+
#import <react/renderer/components/TypeRichTextInputViewSpec/Props.h>
|
|
10
|
+
|
|
11
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
12
|
+
|
|
13
|
+
/// Maps CSS-like fontWeight string → UIFontWeight
|
|
14
|
+
UIFontWeight FontWeightFromString(NSString *weight);
|
|
15
|
+
|
|
16
|
+
UITextAutocapitalizationType AutocapitalizeFromString(NSString *value);
|
|
17
|
+
|
|
18
|
+
UIKeyboardAppearance KeyboardAppearanceFromEnum(
|
|
19
|
+
facebook::react::TypeRichTextInputViewKeyboardAppearance value
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
static inline BOOL isComposingText(UITextView *textView) {
|
|
23
|
+
return textView.markedTextRange != nil;
|
|
24
|
+
}
|
|
25
|
+
NS_ASSUME_NONNULL_END
|
|
26
|
+
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
//
|
|
2
|
+
// TextUtils.m
|
|
3
|
+
// ReactNativeTypeRich
|
|
4
|
+
//
|
|
5
|
+
// Created by Div on 27/12/25.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#import "TextInputUtils.h"
|
|
9
|
+
|
|
10
|
+
UIFontWeight FontWeightFromString(NSString *weight) {
|
|
11
|
+
if (weight.length == 0) {
|
|
12
|
+
return UIFontWeightRegular;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if ([weight isEqualToString:@"100"]) return UIFontWeightUltraLight;
|
|
16
|
+
if ([weight isEqualToString:@"200"]) return UIFontWeightThin;
|
|
17
|
+
if ([weight isEqualToString:@"300"]) return UIFontWeightLight;
|
|
18
|
+
if ([weight isEqualToString:@"400"] || [weight isEqualToString:@"normal"])
|
|
19
|
+
return UIFontWeightRegular;
|
|
20
|
+
if ([weight isEqualToString:@"500"]) return UIFontWeightMedium;
|
|
21
|
+
if ([weight isEqualToString:@"600"]) return UIFontWeightSemibold;
|
|
22
|
+
if ([weight isEqualToString:@"700"] || [weight isEqualToString:@"bold"])
|
|
23
|
+
return UIFontWeightBold;
|
|
24
|
+
if ([weight isEqualToString:@"800"]) return UIFontWeightHeavy;
|
|
25
|
+
if ([weight isEqualToString:@"900"]) return UIFontWeightBlack;
|
|
26
|
+
|
|
27
|
+
return UIFontWeightRegular;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
UITextAutocapitalizationType AutocapitalizeFromString(NSString *value) {
|
|
31
|
+
if ([value isEqualToString:@"characters"]) {
|
|
32
|
+
return UITextAutocapitalizationTypeAllCharacters;
|
|
33
|
+
}
|
|
34
|
+
if ([value isEqualToString:@"words"]) {
|
|
35
|
+
return UITextAutocapitalizationTypeWords;
|
|
36
|
+
}
|
|
37
|
+
if ([value isEqualToString:@"sentences"]) {
|
|
38
|
+
return UITextAutocapitalizationTypeSentences;
|
|
39
|
+
}
|
|
40
|
+
return UITextAutocapitalizationTypeNone;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
UIKeyboardAppearance KeyboardAppearanceFromEnum(
|
|
44
|
+
facebook::react::TypeRichTextInputViewKeyboardAppearance value
|
|
45
|
+
) {
|
|
46
|
+
switch (value) {
|
|
47
|
+
case facebook::react::TypeRichTextInputViewKeyboardAppearance::Dark:
|
|
48
|
+
return UIKeyboardAppearanceDark;
|
|
49
|
+
|
|
50
|
+
case facebook::react::TypeRichTextInputViewKeyboardAppearance::Light:
|
|
51
|
+
return UIKeyboardAppearanceLight;
|
|
52
|
+
|
|
53
|
+
case facebook::react::TypeRichTextInputViewKeyboardAppearance::Default:
|
|
54
|
+
default:
|
|
55
|
+
return UIKeyboardAppearanceDefault;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
@@ -1,16 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
import { forwardRef, useImperativeHandle, useRef } from 'react';
|
|
4
|
-
import {
|
|
5
|
-
import { Commands } from './TypeRichTextInputNativeComponent';
|
|
4
|
+
import NativeTypeRichTextInput, { Commands } from './TypeRichTextInputNativeComponent';
|
|
6
5
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
7
|
-
let NativeTypeRichTextInput;
|
|
8
|
-
if (Platform.OS === 'android') {
|
|
9
|
-
NativeTypeRichTextInput = require('./TypeRichTextInputNativeComponent').default;
|
|
10
|
-
} else {
|
|
11
|
-
// iOS fallback (temporary)
|
|
12
|
-
NativeTypeRichTextInput = View;
|
|
13
|
-
}
|
|
14
6
|
export function normalizeEvent(event) {
|
|
15
7
|
if (event && typeof event === 'object' && 'nativeEvent' in event) {
|
|
16
8
|
return event.nativeEvent;
|
|
@@ -18,10 +10,6 @@ export function normalizeEvent(event) {
|
|
|
18
10
|
return event;
|
|
19
11
|
}
|
|
20
12
|
|
|
21
|
-
// Public facing props (same as NativeProps but events normalized)
|
|
22
|
-
|
|
23
|
-
const isAndroid = Platform.OS === 'android';
|
|
24
|
-
|
|
25
13
|
/**
|
|
26
14
|
* TypeRichTextInput
|
|
27
15
|
*
|
|
@@ -30,34 +18,37 @@ const isAndroid = Platform.OS === 'android';
|
|
|
30
18
|
* - Fabric-based rendering
|
|
31
19
|
* - custom ShadowNode on Android
|
|
32
20
|
*
|
|
33
|
-
* iOS support is currently
|
|
34
|
-
* we are planning to add support for ios too soon
|
|
21
|
+
* iOS support is currently in Beta Stage
|
|
35
22
|
*/
|
|
36
23
|
const TypeRichTextInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
37
24
|
const nativeRef = useRef(null);
|
|
25
|
+
const {
|
|
26
|
+
scrollEnabled = true,
|
|
27
|
+
...restProps
|
|
28
|
+
} = props;
|
|
38
29
|
useImperativeHandle(ref, () => ({
|
|
39
30
|
focus: () => {
|
|
40
|
-
if (
|
|
31
|
+
if (nativeRef.current) {
|
|
41
32
|
Commands.focus(nativeRef.current);
|
|
42
33
|
}
|
|
43
34
|
},
|
|
44
35
|
blur: () => {
|
|
45
|
-
if (
|
|
36
|
+
if (nativeRef.current) {
|
|
46
37
|
Commands.blur(nativeRef.current);
|
|
47
38
|
}
|
|
48
39
|
},
|
|
49
40
|
setText: text => {
|
|
50
|
-
if (
|
|
41
|
+
if (nativeRef.current) {
|
|
51
42
|
Commands.setText(nativeRef.current, text);
|
|
52
43
|
}
|
|
53
44
|
},
|
|
54
45
|
setSelection(start, end) {
|
|
55
|
-
if (
|
|
46
|
+
if (nativeRef.current) {
|
|
56
47
|
Commands.setSelection(nativeRef.current, start, end);
|
|
57
48
|
}
|
|
58
49
|
},
|
|
59
50
|
insertTextAt: (start, end, text) => {
|
|
60
|
-
if (
|
|
51
|
+
if (nativeRef.current) {
|
|
61
52
|
Commands.insertTextAt(nativeRef.current, start, end, text);
|
|
62
53
|
}
|
|
63
54
|
},
|
|
@@ -86,24 +77,10 @@ const TypeRichTextInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
86
77
|
text: e.text
|
|
87
78
|
});
|
|
88
79
|
}
|
|
89
|
-
|
|
90
|
-
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
91
|
-
const {
|
|
92
|
-
// native-only / android-only props we never want on <View />
|
|
93
|
-
androidExperimentalSynchronousEvents,
|
|
94
|
-
onChangeSelection,
|
|
95
|
-
onChangeText,
|
|
96
|
-
onPasteImageData,
|
|
97
|
-
onFocus,
|
|
98
|
-
onBlur,
|
|
99
|
-
// everything else
|
|
100
|
-
...restProps
|
|
101
|
-
} = props;
|
|
102
|
-
/* eslint-enable @typescript-eslint/no-unused-vars */
|
|
103
|
-
|
|
104
80
|
return /*#__PURE__*/_jsx(NativeTypeRichTextInput, {
|
|
105
81
|
ref: nativeRef,
|
|
106
|
-
...
|
|
82
|
+
...restProps,
|
|
83
|
+
scrollEnabled: scrollEnabled,
|
|
107
84
|
onInputFocus: () => props.onFocus?.(),
|
|
108
85
|
onInputBlur: () => props.onBlur?.(),
|
|
109
86
|
onChangeText: handleOnChangeTextEvent,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["forwardRef","useImperativeHandle","useRef","
|
|
1
|
+
{"version":3,"names":["forwardRef","useImperativeHandle","useRef","NativeTypeRichTextInput","Commands","jsx","_jsx","normalizeEvent","event","nativeEvent","TypeRichTextInput","props","ref","nativeRef","scrollEnabled","restProps","focus","current","blur","setText","text","setSelection","start","end","insertTextAt","getNativeRef","handlePasteImage","data","undefined","onPasteImageData","handleOnChangeTextEvent","e","onChangeText","value","handleonChangeSelectionEvent","onChangeSelection","onInputFocus","onFocus","onInputBlur","onBlur","onPasteImage"],"sourceRoot":"../../src","sources":["TypeRichTextInput.tsx"],"mappings":";;AAAA,SAASA,UAAU,EAAEC,mBAAmB,EAAEC,MAAM,QAAkB,OAAO;AAIzE,OAAOC,uBAAuB,IAC5BC,QAAQ,QACH,oCAAoC;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAc5C,OAAO,SAASC,cAAcA,CAAIC,KAA0B,EAAK;EAC/D,IAAIA,KAAK,IAAI,OAAOA,KAAK,KAAK,QAAQ,IAAI,aAAa,IAAIA,KAAK,EAAE;IAChE,OAAQA,KAAK,CAAwBC,WAAW;EAClD;EACA,OAAOD,KAAK;AACd;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAME,iBAAiB,gBAAGV,UAAU,CAClC,CAACW,KAA6B,EAAEC,GAA8B,KAAK;EACjE,MAAMC,SAAS,GAAGX,MAAM,CAAC,IAAI,CAAC;EAE9B,MAAM;IAAEY,aAAa,GAAG,IAAI;IAAE,GAAGC;EAAU,CAAC,GAAGJ,KAAK;EAEpDV,mBAAmB,CAACW,GAAG,EAAE,OAAO;IAC9BI,KAAK,EAAEA,CAAA,KAAM;MACX,IAAIH,SAAS,CAACI,OAAO,EAAE;QACrBb,QAAQ,CAACY,KAAK,CAACH,SAAS,CAACI,OAAO,CAAC;MACnC;IACF,CAAC;IACDC,IAAI,EAAEA,CAAA,KAAM;MACV,IAAIL,SAAS,CAACI,OAAO,EAAE;QACrBb,QAAQ,CAACc,IAAI,CAACL,SAAS,CAACI,OAAO,CAAC;MAClC;IACF,CAAC;IACDE,OAAO,EAAGC,IAAY,IAAK;MACzB,IAAIP,SAAS,CAACI,OAAO,EAAE;QACrBb,QAAQ,CAACe,OAAO,CAACN,SAAS,CAACI,OAAO,EAAEG,IAAI,CAAC;MAC3C;IACF,CAAC;IACDC,YAAYA,CAACC,KAAK,EAAEC,GAAG,EAAE;MACvB,IAAIV,SAAS,CAACI,OAAO,EAAE;QACrBb,QAAQ,CAACiB,YAAY,CAACR,SAAS,CAACI,OAAO,EAAEK,KAAK,EAAEC,GAAG,CAAC;MACtD;IACF,CAAC;IACDC,YAAY,EAAEA,CAACF,KAAa,EAAEC,GAAW,EAAEH,IAAY,KAAK;MAC1D,IAAIP,SAAS,CAACI,OAAO,EAAE;QACrBb,QAAQ,CAACoB,YAAY,CAACX,SAAS,CAACI,OAAO,EAAEK,KAAK,EAAEC,GAAG,EAAEH,IAAI,CAAC;MAC5D;IACF,CAAC;IACDK,YAAY,EAAEA,CAAA,KAAMZ,SAAS,CAACI;EAChC,CAAC,CAAC,CAAC;;EAEH;AACJ;AACA;EACI,SAASS,gBAAgBA,CACvBlB,KAIO,EACD;IACN;IACA,MAAMmB,IAAuC,GAC3CnB,KAAK,IAAI,OAAOA,KAAK,KAAK,QAAQ,GAC9BA,KAAK,CAACC,WAAW,IAAID,KAAK,GAC1BoB,SAAS;IAEf,IAAID,IAAI,EAAE;MACRhB,KAAK,CAACkB,gBAAgB,GAAGF,IAA6B,CAAC;IACzD;EACF;EAEA,SAASG,uBAAuBA,CAC9BtB,KAA6D,EAC7D;IACA,MAAMuB,CAAC,GAAGxB,cAAc,CAACC,KAAK,CAAC;IAC/BG,KAAK,CAACqB,YAAY,GAAGD,CAAC,CAACE,KAAK,CAAC;EAC/B;EAEA,SAASC,4BAA4BA,CACnC1B,KAAuE,EACvE;IACA,MAAMuB,CAAC,GAAGxB,cAAc,CAACC,KAAK,CAAC;IAC/BG,KAAK,CAACwB,iBAAiB,GAAG;MACxBb,KAAK,EAAES,CAAC,CAACT,KAAK;MACdC,GAAG,EAAEQ,CAAC,CAACR,GAAG;MACVH,IAAI,EAAEW,CAAC,CAACX;IACV,CAAC,CAAC;EACJ;EAEA,oBACEd,IAAA,CAACH,uBAAuB;IACtBS,GAAG,EAAEC,SAAU;IAAA,GACXE,SAAS;IACbD,aAAa,EAAEA,aAAc;IAC7BsB,YAAY,EAAEA,CAAA,KAAMzB,KAAK,CAAC0B,OAAO,GAAG,CAAE;IACtCC,WAAW,EAAEA,CAAA,KAAM3B,KAAK,CAAC4B,MAAM,GAAG,CAAE;IACpCP,YAAY,EAAEF,uBAAwB;IACtCK,iBAAiB,EAAED,4BAA6B;IAChDM,YAAY,EAAEd;EAAiB,CAChC,CAAC;AAEN,CACF,CAAC;AAED,eAAehB,iBAAiB","ignoreList":[]}
|