react-native-advanced-text 0.1.26 → 0.1.28
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/ios/AdvancedTextView.mm
CHANGED
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
using namespace facebook::react;
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
// Forward declaration
|
|
14
13
|
@class AdvancedTextView;
|
|
15
14
|
|
|
16
15
|
@interface AdvancedTextView () <RCTAdvancedTextViewViewProtocol, UIGestureRecognizerDelegate, UITextViewDelegate>
|
|
@@ -19,14 +18,17 @@ using namespace facebook::react;
|
|
|
19
18
|
@property (nonatomic, strong) NSMutableDictionary<NSNumber *, UIColor *> *highlightColors;
|
|
20
19
|
@property (nonatomic, strong) NSArray<NSString *> *menuOptions;
|
|
21
20
|
@property (nonatomic, assign) NSInteger indicatorWordIndex;
|
|
21
|
+
@property (nonatomic, assign) CGFloat fontSize;
|
|
22
|
+
@property (nonatomic, strong) NSString *fontWeight;
|
|
23
|
+
@property (nonatomic, strong) UIColor *textColor;
|
|
24
|
+
@property (nonatomic, strong) NSString *textAlign;
|
|
25
|
+
@property (nonatomic, strong) NSString *fontFamily;
|
|
22
26
|
|
|
23
|
-
// ✅ ADD THIS LINE
|
|
24
27
|
- (void)handleCustomMenuAction:(UIMenuItem *)sender;
|
|
25
28
|
|
|
26
29
|
@end
|
|
27
30
|
|
|
28
31
|
|
|
29
|
-
// Custom UITextView subclass to override menu behavior
|
|
30
32
|
@interface CustomTextView : UITextView
|
|
31
33
|
@property (nonatomic, weak) AdvancedTextView *parentView;
|
|
32
34
|
@end
|
|
@@ -37,22 +39,18 @@ using namespace facebook::react;
|
|
|
37
39
|
{
|
|
38
40
|
NSLog(@"[CustomTextView] canPerformAction: %@", NSStringFromSelector(action));
|
|
39
41
|
|
|
40
|
-
// Only allow our custom menu actions
|
|
41
42
|
if (action == @selector(handleCustomMenuAction:)) {
|
|
42
43
|
NSLog(@"[CustomTextView] ✅ Allowing custom action");
|
|
43
44
|
return YES;
|
|
44
45
|
}
|
|
45
46
|
|
|
46
|
-
// Block ALL system actions
|
|
47
47
|
NSLog(@"[CustomTextView] ❌ Blocking system action: %@", NSStringFromSelector(action));
|
|
48
48
|
return NO;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
- (void)handleCustomMenuAction:(UIMenuItem *)sender
|
|
52
52
|
{
|
|
53
|
-
// Forward to parent view
|
|
54
53
|
if (self.parentView) {
|
|
55
|
-
|
|
56
54
|
[self.parentView handleCustomMenuAction:sender];
|
|
57
55
|
}
|
|
58
56
|
}
|
|
@@ -86,6 +84,11 @@ using namespace facebook::react;
|
|
|
86
84
|
_wordRanges = [NSMutableArray array];
|
|
87
85
|
_highlightColors = [NSMutableDictionary dictionary];
|
|
88
86
|
_indicatorWordIndex = -1;
|
|
87
|
+
_fontSize = 16.0;
|
|
88
|
+
_fontWeight = @"normal";
|
|
89
|
+
_textColor = [UIColor labelColor];
|
|
90
|
+
_textAlign = @"left";
|
|
91
|
+
_fontFamily = @"System";
|
|
89
92
|
|
|
90
93
|
[self setupTextView];
|
|
91
94
|
[self setupGestureRecognizers];
|
|
@@ -127,7 +130,6 @@ using namespace facebook::react;
|
|
|
127
130
|
{
|
|
128
131
|
NSLog(@"[AdvancedTextView] setupGestureRecognizers called");
|
|
129
132
|
@try {
|
|
130
|
-
// Single tap for word selection
|
|
131
133
|
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]
|
|
132
134
|
initWithTarget:self
|
|
133
135
|
action:@selector(handleTap:)];
|
|
@@ -152,14 +154,44 @@ using namespace facebook::react;
|
|
|
152
154
|
BOOL highlightsChanged = NO;
|
|
153
155
|
BOOL menuChanged = NO;
|
|
154
156
|
BOOL indicatorChanged = NO;
|
|
157
|
+
BOOL styleChanged = NO;
|
|
158
|
+
|
|
159
|
+
if (oldViewProps.fontSize != newViewProps.fontSize && newViewProps.fontSize) {
|
|
160
|
+
NSLog(@"[AdvancedTextView] Updating fontSize to: %f", newViewProps.fontSize);
|
|
161
|
+
_fontSize = static_cast<CGFloat>(newViewProps.fontSize);
|
|
162
|
+
styleChanged = YES;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (oldViewProps.textAlign != newViewProps.textAlign && !newViewProps.textAlign.empty()) {
|
|
166
|
+
NSLog(@"[AdvancedTextView] Updating textAlign to: %s", newViewProps.textAlign.c_str());
|
|
167
|
+
_textAlign = [NSString stringWithUTF8String:newViewProps.textAlign.c_str()];
|
|
168
|
+
styleChanged = YES;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (oldViewProps.fontWeight != newViewProps.fontWeight && !newViewProps.fontWeight.empty()) {
|
|
172
|
+
NSLog(@"[AdvancedTextView] Updating fontWeight to: %s", newViewProps.fontWeight.c_str());
|
|
173
|
+
_fontWeight = [NSString stringWithUTF8String:newViewProps.fontWeight.c_str()];
|
|
174
|
+
styleChanged = YES;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (oldViewProps.fontFamily != newViewProps.fontFamily && !newViewProps.fontFamily.empty()) {
|
|
178
|
+
NSLog(@"[AdvancedTextView] Updating fontFamily to: %s", newViewProps.fontFamily.c_str());
|
|
179
|
+
_fontFamily = [NSString stringWithUTF8String:newViewProps.fontFamily.c_str()];
|
|
180
|
+
styleChanged = YES;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (oldViewProps.color != newViewProps.color && !newViewProps.color.empty()) {
|
|
184
|
+
NSLog(@"[AdvancedTextView] Updating color to: %s", newViewProps.color.c_str());
|
|
185
|
+
NSString *colorStr = [NSString stringWithUTF8String:newViewProps.color.c_str()];
|
|
186
|
+
_textColor = [self hexStringToColor:colorStr];
|
|
187
|
+
styleChanged = YES;
|
|
188
|
+
}
|
|
155
189
|
|
|
156
|
-
// Check text change
|
|
157
190
|
if (oldViewProps.text != newViewProps.text) {
|
|
158
191
|
textChanged = YES;
|
|
159
192
|
NSLog(@"[AdvancedTextView] Text changed");
|
|
160
193
|
}
|
|
161
194
|
|
|
162
|
-
// Check highlighted words change
|
|
163
195
|
if (oldViewProps.highlightedWords.size() != newViewProps.highlightedWords.size()) {
|
|
164
196
|
highlightsChanged = YES;
|
|
165
197
|
} else {
|
|
@@ -173,7 +205,6 @@ using namespace facebook::react;
|
|
|
173
205
|
}
|
|
174
206
|
}
|
|
175
207
|
|
|
176
|
-
// Check menu options change
|
|
177
208
|
if (oldViewProps.menuOptions.size() != newViewProps.menuOptions.size()) {
|
|
178
209
|
menuChanged = YES;
|
|
179
210
|
} else {
|
|
@@ -185,12 +216,10 @@ using namespace facebook::react;
|
|
|
185
216
|
}
|
|
186
217
|
}
|
|
187
218
|
|
|
188
|
-
// Check indicator change
|
|
189
219
|
if (oldViewProps.indicatorWordIndex != newViewProps.indicatorWordIndex) {
|
|
190
220
|
indicatorChanged = YES;
|
|
191
221
|
}
|
|
192
222
|
|
|
193
|
-
// Apply updates
|
|
194
223
|
if (textChanged) {
|
|
195
224
|
NSString *text = [NSString stringWithUTF8String:newViewProps.text.c_str()];
|
|
196
225
|
[self updateTextContent:text];
|
|
@@ -209,6 +238,11 @@ using namespace facebook::react;
|
|
|
209
238
|
[self updateTextAppearance];
|
|
210
239
|
}
|
|
211
240
|
|
|
241
|
+
if (styleChanged) {
|
|
242
|
+
NSLog(@"[AdvancedTextView] Style properties changed, updating appearance");
|
|
243
|
+
[self updateTextAppearance];
|
|
244
|
+
}
|
|
245
|
+
|
|
212
246
|
[super updateProps:props oldProps:oldProps];
|
|
213
247
|
NSLog(@"[AdvancedTextView] updateProps completed successfully");
|
|
214
248
|
} @catch (NSException *exception) {
|
|
@@ -229,7 +263,6 @@ using namespace facebook::react;
|
|
|
229
263
|
|
|
230
264
|
_textView.text = text;
|
|
231
265
|
|
|
232
|
-
// Parse text into words and their ranges
|
|
233
266
|
[_wordRanges removeAllObjects];
|
|
234
267
|
|
|
235
268
|
NSRange searchRange = NSMakeRange(0, text.length);
|
|
@@ -237,7 +270,6 @@ using namespace facebook::react;
|
|
|
237
270
|
|
|
238
271
|
NSInteger wordIndex = 0;
|
|
239
272
|
while (searchRange.location < text.length) {
|
|
240
|
-
// Skip whitespace
|
|
241
273
|
while (searchRange.location < text.length &&
|
|
242
274
|
[whitespaceSet characterIsMember:[text characterAtIndex:searchRange.location]]) {
|
|
243
275
|
searchRange.location++;
|
|
@@ -246,7 +278,6 @@ using namespace facebook::react;
|
|
|
246
278
|
|
|
247
279
|
if (searchRange.location >= text.length) break;
|
|
248
280
|
|
|
249
|
-
// Find word end
|
|
250
281
|
NSUInteger wordStart = searchRange.location;
|
|
251
282
|
while (searchRange.location < text.length &&
|
|
252
283
|
![whitespaceSet characterIsMember:[text characterAtIndex:searchRange.location]]) {
|
|
@@ -318,21 +349,41 @@ using namespace facebook::react;
|
|
|
318
349
|
- (void)updateTextAppearance
|
|
319
350
|
{
|
|
320
351
|
@try {
|
|
321
|
-
if (
|
|
352
|
+
if (!_textView.text || _textView.text.length == 0) {
|
|
322
353
|
return;
|
|
323
354
|
}
|
|
324
355
|
|
|
325
356
|
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc]
|
|
326
357
|
initWithString:_textView.text];
|
|
327
358
|
|
|
359
|
+
|
|
360
|
+
UIFont *font = nil;
|
|
361
|
+
|
|
362
|
+
if (_fontFamily && _fontFamily.length > 0) {
|
|
363
|
+
font = [UIFont fontWithName:_fontFamily size:_fontSize > 0 ? _fontSize : 16.0];
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (!font) {
|
|
367
|
+
if (_fontWeight && [_fontWeight.lowercaseString isEqualToString:@"bold"]) {
|
|
368
|
+
font = [UIFont boldSystemFontOfSize:_fontSize > 0 ? _fontSize : 16.0];
|
|
369
|
+
} else if (_fontWeight && [_fontWeight.lowercaseString isEqualToString:@"italic"]) {
|
|
370
|
+
font = [UIFont italicSystemFontOfSize:_fontSize > 0 ? _fontSize : 16.0];
|
|
371
|
+
} else {
|
|
372
|
+
font = [UIFont systemFontOfSize:_fontSize > 0 ? _fontSize : 16.0];
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
328
376
|
[attributedString addAttribute:NSFontAttributeName
|
|
329
|
-
value:
|
|
377
|
+
value:font
|
|
330
378
|
range:NSMakeRange(0, attributedString.length)];
|
|
331
379
|
|
|
380
|
+
|
|
381
|
+
UIColor *color = _textColor ?: [UIColor labelColor];
|
|
332
382
|
[attributedString addAttribute:NSForegroundColorAttributeName
|
|
333
|
-
value:
|
|
383
|
+
value:color
|
|
334
384
|
range:NSMakeRange(0, attributedString.length)];
|
|
335
385
|
|
|
386
|
+
|
|
336
387
|
for (NSDictionary *wordInfo in _wordRanges) {
|
|
337
388
|
NSNumber *index = wordInfo[@"index"];
|
|
338
389
|
NSValue *rangeValue = wordInfo[@"range"];
|
|
@@ -358,11 +409,25 @@ using namespace facebook::react;
|
|
|
358
409
|
}
|
|
359
410
|
|
|
360
411
|
_textView.attributedText = attributedString;
|
|
412
|
+
|
|
413
|
+
if (_textAlign) {
|
|
414
|
+
if ([_textAlign.lowercaseString isEqualToString:@"center"]) {
|
|
415
|
+
_textView.textAlignment = NSTextAlignmentCenter;
|
|
416
|
+
} else if ([_textAlign.lowercaseString isEqualToString:@"right"]) {
|
|
417
|
+
_textView.textAlignment = NSTextAlignmentRight;
|
|
418
|
+
} else {
|
|
419
|
+
_textView.textAlignment = NSTextAlignmentLeft;
|
|
420
|
+
}
|
|
421
|
+
} else {
|
|
422
|
+
_textView.textAlignment = NSTextAlignmentLeft;
|
|
423
|
+
}
|
|
424
|
+
|
|
361
425
|
} @catch (NSException *exception) {
|
|
362
426
|
NSLog(@"[AdvancedTextView] Exception in updateTextAppearance: %@", exception.reason);
|
|
363
427
|
}
|
|
364
428
|
}
|
|
365
429
|
|
|
430
|
+
|
|
366
431
|
- (void)handleTap:(UITapGestureRecognizer *)gesture
|
|
367
432
|
{
|
|
368
433
|
@try {
|
|
@@ -371,7 +436,6 @@ using namespace facebook::react;
|
|
|
371
436
|
CGPoint location = [gesture locationInView:_textView];
|
|
372
437
|
NSInteger wordIndex = [self wordIndexAtPoint:location];
|
|
373
438
|
|
|
374
|
-
// Dismiss any existing selection
|
|
375
439
|
_textView.selectedTextRange = nil;
|
|
376
440
|
|
|
377
441
|
if (wordIndex >= 0 && wordIndex < _wordRanges.count) {
|
|
@@ -464,6 +528,31 @@ using namespace facebook::react;
|
|
|
464
528
|
}
|
|
465
529
|
}
|
|
466
530
|
|
|
531
|
+
- (void)setFontSize:(CGFloat)fontSize {
|
|
532
|
+
_fontSize = fontSize;
|
|
533
|
+
[self updateTextAppearance];
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
- (void)setFontWeight:(NSString *)fontWeight {
|
|
537
|
+
_fontWeight = fontWeight;
|
|
538
|
+
[self updateTextAppearance];
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
- (void)setTextColor:(UIColor *)textColor {
|
|
542
|
+
_textColor = textColor;
|
|
543
|
+
[self updateTextAppearance];
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
- (void)setTextAlign:(NSString *)textAlign {
|
|
547
|
+
_textAlign = textAlign;
|
|
548
|
+
[self updateTextAppearance];
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
- (void)setFontFamily:(NSString *)fontFamily {
|
|
552
|
+
_fontFamily = fontFamily;
|
|
553
|
+
[self updateTextAppearance];
|
|
554
|
+
}
|
|
555
|
+
|
|
467
556
|
- (void)emitWordPressEvent:(NSString *)word index:(NSInteger)index
|
|
468
557
|
{
|
|
469
558
|
NSLog(@"[AdvancedTextView] emitWordPressEvent: %@ at index: %ld", word, (long)index);
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
/* eslint-disable prettier/prettier */
|
|
2
|
-
import type { ViewProps } from 'react-native';
|
|
3
|
-
import { codegenNativeComponent } from 'react-native';
|
|
4
|
-
// @ts-ignore
|
|
5
|
-
import type { DirectEventHandler, Int32 } from 'react-native/Libraries/Types/CodegenTypes';
|
|
6
|
-
|
|
7
|
-
interface HighlightedWord {
|
|
8
|
-
index: Int32;
|
|
9
|
-
highlightColor: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface NativeProps extends ViewProps {
|
|
13
|
-
text: string;
|
|
14
|
-
highlightedWords?: ReadonlyArray<HighlightedWord>;
|
|
15
|
-
menuOptions?: ReadonlyArray<string>;
|
|
16
|
-
onWordPress?: DirectEventHandler<{ word: string; index: Int32 }>;
|
|
17
|
-
onSelection?: DirectEventHandler<{ selectedText: string; event: string }>;
|
|
18
|
-
indicatorWordIndex?: Int32;
|
|
19
|
-
fontSize?: Int32;
|
|
20
|
-
fontWeight?: string;
|
|
21
|
-
color?: string;
|
|
22
|
-
textAlign?: string;
|
|
23
|
-
fontFamily?: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export default codegenNativeComponent<NativeProps>('AdvancedTextView');
|
|
1
|
+
/* eslint-disable prettier/prettier */
|
|
2
|
+
import type { ViewProps } from 'react-native';
|
|
3
|
+
import { codegenNativeComponent } from 'react-native';
|
|
4
|
+
// @ts-ignore
|
|
5
|
+
import type { DirectEventHandler, Int32 } from 'react-native/Libraries/Types/CodegenTypes';
|
|
6
|
+
|
|
7
|
+
interface HighlightedWord {
|
|
8
|
+
index: Int32;
|
|
9
|
+
highlightColor: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface NativeProps extends ViewProps {
|
|
13
|
+
text: string;
|
|
14
|
+
highlightedWords?: ReadonlyArray<HighlightedWord>;
|
|
15
|
+
menuOptions?: ReadonlyArray<string>;
|
|
16
|
+
onWordPress?: DirectEventHandler<{ word: string; index: Int32 }>;
|
|
17
|
+
onSelection?: DirectEventHandler<{ selectedText: string; event: string }>;
|
|
18
|
+
indicatorWordIndex?: Int32;
|
|
19
|
+
fontSize?: Int32;
|
|
20
|
+
fontWeight?: string;
|
|
21
|
+
color?: string;
|
|
22
|
+
textAlign?: string;
|
|
23
|
+
fontFamily?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default codegenNativeComponent<NativeProps>('AdvancedTextView');
|
package/package.json
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
/* eslint-disable prettier/prettier */
|
|
2
|
-
import type { ViewProps } from 'react-native';
|
|
3
|
-
import { codegenNativeComponent } from 'react-native';
|
|
4
|
-
// @ts-ignore
|
|
5
|
-
import type { DirectEventHandler, Int32 } from 'react-native/Libraries/Types/CodegenTypes';
|
|
6
|
-
|
|
7
|
-
interface HighlightedWord {
|
|
8
|
-
index: Int32;
|
|
9
|
-
highlightColor: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface NativeProps extends ViewProps {
|
|
13
|
-
text: string;
|
|
14
|
-
highlightedWords?: ReadonlyArray<HighlightedWord>;
|
|
15
|
-
menuOptions?: ReadonlyArray<string>;
|
|
16
|
-
onWordPress?: DirectEventHandler<{ word: string; index: Int32 }>;
|
|
17
|
-
onSelection?: DirectEventHandler<{ selectedText: string; event: string }>;
|
|
18
|
-
indicatorWordIndex?: Int32;
|
|
19
|
-
fontSize?: Int32;
|
|
20
|
-
fontWeight?: string;
|
|
21
|
-
color?: string;
|
|
22
|
-
textAlign?: string;
|
|
23
|
-
fontFamily?: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export default codegenNativeComponent<NativeProps>('AdvancedTextView');
|
|
1
|
+
/* eslint-disable prettier/prettier */
|
|
2
|
+
import type { ViewProps } from 'react-native';
|
|
3
|
+
import { codegenNativeComponent } from 'react-native';
|
|
4
|
+
// @ts-ignore
|
|
5
|
+
import type { DirectEventHandler, Int32 } from 'react-native/Libraries/Types/CodegenTypes';
|
|
6
|
+
|
|
7
|
+
interface HighlightedWord {
|
|
8
|
+
index: Int32;
|
|
9
|
+
highlightColor: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface NativeProps extends ViewProps {
|
|
13
|
+
text: string;
|
|
14
|
+
highlightedWords?: ReadonlyArray<HighlightedWord>;
|
|
15
|
+
menuOptions?: ReadonlyArray<string>;
|
|
16
|
+
onWordPress?: DirectEventHandler<{ word: string; index: Int32 }>;
|
|
17
|
+
onSelection?: DirectEventHandler<{ selectedText: string; event: string }>;
|
|
18
|
+
indicatorWordIndex?: Int32;
|
|
19
|
+
fontSize?: Int32;
|
|
20
|
+
fontWeight?: string;
|
|
21
|
+
color?: string;
|
|
22
|
+
textAlign?: string;
|
|
23
|
+
fontFamily?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default codegenNativeComponent<NativeProps>('AdvancedTextView');
|