react-native-advanced-text 0.1.23 → 0.1.25
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/android/src/main/java/com/advancedtext/AdvancedTextView.kt +54 -5
- package/android/src/main/java/com/advancedtext/AdvancedTextViewManager.kt +25 -1
- package/ios/AdvancedTextView.mm +64 -107
- package/lib/module/AdvancedText.js.map +1 -1
- package/lib/module/AdvancedTextViewNativeComponent.ts +7 -2
- package/lib/typescript/src/AdvancedText.d.ts +1 -23
- package/lib/typescript/src/AdvancedText.d.ts.map +1 -1
- package/lib/typescript/src/AdvancedTextViewNativeComponent.d.ts +6 -1
- package/lib/typescript/src/AdvancedTextViewNativeComponent.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/AdvancedText.tsx +3 -32
- package/src/AdvancedTextViewNativeComponent.ts +7 -2
|
@@ -32,6 +32,10 @@ class AdvancedTextView : TextView {
|
|
|
32
32
|
private var customActionMode: ActionMode? = null
|
|
33
33
|
private var currentText: String = ""
|
|
34
34
|
private var textColor: String = "#000000"
|
|
35
|
+
private var fontSize: Float = 16f
|
|
36
|
+
private var fontWeight: String = "normal"
|
|
37
|
+
private var textAlign: String = "left"
|
|
38
|
+
private var fontFamily: String = "sans-serif"
|
|
35
39
|
|
|
36
40
|
private var wordPositions: List<WordPosition> = emptyList()
|
|
37
41
|
|
|
@@ -46,7 +50,7 @@ class AdvancedTextView : TextView {
|
|
|
46
50
|
setPadding(16, 16, 16, 16)
|
|
47
51
|
movementMethod = LinkMovementMethod.getInstance()
|
|
48
52
|
setTextIsSelectable(true)
|
|
49
|
-
|
|
53
|
+
|
|
50
54
|
|
|
51
55
|
customSelectionActionModeCallback = object : ActionMode.Callback {
|
|
52
56
|
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
|
@@ -90,10 +94,6 @@ class AdvancedTextView : TextView {
|
|
|
90
94
|
}
|
|
91
95
|
|
|
92
96
|
|
|
93
|
-
fun setAdvancedTextColor(colorInt: Int) {
|
|
94
|
-
textColor = String.format("#%06X", 0xFFFFFF and colorInt)
|
|
95
|
-
updateTextWithHighlights()
|
|
96
|
-
}
|
|
97
97
|
|
|
98
98
|
fun setAdvancedText(text: String) {
|
|
99
99
|
if (currentText == text) {
|
|
@@ -107,6 +107,40 @@ class AdvancedTextView : TextView {
|
|
|
107
107
|
updateTextWithHighlights()
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
fun setAdvancedTextColor(colorInt: Int) {
|
|
111
|
+
textColor = String.format("#%06X", 0xFFFFFF and colorInt)
|
|
112
|
+
updateTextWithHighlights()
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
fun setAdvancedTextSize(size: Float) {
|
|
117
|
+
if (fontSize == size) return
|
|
118
|
+
fontSize = size
|
|
119
|
+
updateTextWithHighlights() // ensures size change is applied with highlights
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
fun setAdvancedFontWeight(weight: String) {
|
|
123
|
+
if (fontWeight == weight) return
|
|
124
|
+
fontWeight = weight
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
fun setAdvancedTextAlign(align: String) {
|
|
128
|
+
if (textAlign == align) return
|
|
129
|
+
textAlign = align
|
|
130
|
+
when (align) {
|
|
131
|
+
"left" -> textAlignment = View.TEXT_ALIGNMENT_TEXT_START
|
|
132
|
+
"center" -> textAlignment = View.TEXT_ALIGNMENT_CENTER
|
|
133
|
+
"right" -> textAlignment = View.TEXT_ALIGNMENT_TEXT_END
|
|
134
|
+
else -> textAlignment = View.TEXT_ALIGNMENT_TEXT_START
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
fun setAdvancedFontFamily(family: String) {
|
|
139
|
+
if (fontFamily == family) return
|
|
140
|
+
fontFamily = family
|
|
141
|
+
typeface = Typeface.create(family, Typeface.NORMAL)
|
|
142
|
+
}
|
|
143
|
+
|
|
110
144
|
fun setMenuOptions(menuOptions: List<String>) {
|
|
111
145
|
if (this.menuOptions == menuOptions) return
|
|
112
146
|
this.menuOptions = menuOptions
|
|
@@ -182,6 +216,21 @@ class AdvancedTextView : TextView {
|
|
|
182
216
|
)
|
|
183
217
|
}
|
|
184
218
|
|
|
219
|
+
textAlignment = when (textAlign) {
|
|
220
|
+
"left" -> View.TEXT_ALIGNMENT_TEXT_START
|
|
221
|
+
"center" -> View.TEXT_ALIGNMENT_CENTER
|
|
222
|
+
"right" -> View.TEXT_ALIGNMENT_TEXT_END
|
|
223
|
+
else -> View.TEXT_ALIGNMENT_TEXT_START
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
setTextSize(fontSize)
|
|
227
|
+
|
|
228
|
+
typeface = when (fontWeight) {
|
|
229
|
+
"bold" -> Typeface.create(fontFamily, Typeface.BOLD)
|
|
230
|
+
"italic" -> Typeface.create(fontFamily, Typeface.ITALIC)
|
|
231
|
+
else -> Typeface.create(fontFamily, Typeface.NORMAL)
|
|
232
|
+
}
|
|
233
|
+
|
|
185
234
|
post {
|
|
186
235
|
setText(spannableString, BufferType.SPANNABLE)
|
|
187
236
|
Log.d(TAG, "Text updated with ${wordPositions.size} spans")
|
|
@@ -95,7 +95,31 @@ class AdvancedTextViewManager : SimpleViewManager<AdvancedTextView>() {
|
|
|
95
95
|
fun setFontSize(view: AdvancedTextView?, fontSize: Float) {
|
|
96
96
|
android.util.Log.d(NAME, "setFontSize called with: $fontSize")
|
|
97
97
|
if (fontSize > 0) {
|
|
98
|
-
view?.
|
|
98
|
+
view?.setAdvancedTextSize(fontSize)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
@ReactProp(name = "fontWeight")
|
|
103
|
+
fun setFontWeight(view: AdvancedTextView?, fontWeight: String?) {
|
|
104
|
+
android.util.Log.d(NAME, "setFontWeight called with: $fontWeight")
|
|
105
|
+
if (fontWeight != null) {
|
|
106
|
+
view?.setAdvancedFontWeight(fontWeight)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
@ReactProp(name = "textAlign")
|
|
111
|
+
fun setTextAlign(view: AdvancedTextView?, textAlign: String?) {
|
|
112
|
+
android.util.Log.d(NAME, "setTextAlign called with: $textAlign")
|
|
113
|
+
if (textAlign != null) {
|
|
114
|
+
view?.setAdvancedTextAlign(textAlign)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
@ReactProp(name = "fontFamily")
|
|
119
|
+
fun setFontFamily(view: AdvancedTextView?, fontFamily: String?) {
|
|
120
|
+
android.util.Log.d(NAME, "setFontFamily called with: $fontFamily")
|
|
121
|
+
if (fontFamily != null) {
|
|
122
|
+
view?.setAdvancedFontFamily(fontFamily)
|
|
99
123
|
}
|
|
100
124
|
}
|
|
101
125
|
|
package/ios/AdvancedTextView.mm
CHANGED
|
@@ -9,14 +9,62 @@
|
|
|
9
9
|
|
|
10
10
|
using namespace facebook::react;
|
|
11
11
|
|
|
12
|
+
|
|
13
|
+
// Forward declaration
|
|
14
|
+
@class AdvancedTextView;
|
|
15
|
+
|
|
12
16
|
@interface AdvancedTextView () <RCTAdvancedTextViewViewProtocol, UIGestureRecognizerDelegate, UITextViewDelegate>
|
|
13
17
|
|
|
14
|
-
@property (nonatomic, strong) UITextView *textView;
|
|
15
18
|
@property (nonatomic, strong) NSMutableArray<NSDictionary *> *wordRanges;
|
|
16
19
|
@property (nonatomic, strong) NSMutableDictionary<NSNumber *, UIColor *> *highlightColors;
|
|
17
20
|
@property (nonatomic, strong) NSArray<NSString *> *menuOptions;
|
|
18
21
|
@property (nonatomic, assign) NSInteger indicatorWordIndex;
|
|
19
22
|
|
|
23
|
+
// ✅ ADD THIS LINE
|
|
24
|
+
- (void)handleCustomMenuAction:(UIMenuItem *)sender;
|
|
25
|
+
|
|
26
|
+
@end
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
// Custom UITextView subclass to override menu behavior
|
|
30
|
+
@interface CustomTextView : UITextView
|
|
31
|
+
@property (nonatomic, weak) AdvancedTextView *parentView;
|
|
32
|
+
@end
|
|
33
|
+
|
|
34
|
+
@implementation CustomTextView
|
|
35
|
+
|
|
36
|
+
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
|
|
37
|
+
{
|
|
38
|
+
NSLog(@"[CustomTextView] canPerformAction: %@", NSStringFromSelector(action));
|
|
39
|
+
|
|
40
|
+
// Only allow our custom menu actions
|
|
41
|
+
if (action == @selector(handleCustomMenuAction:)) {
|
|
42
|
+
NSLog(@"[CustomTextView] ✅ Allowing custom action");
|
|
43
|
+
return YES;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Block ALL system actions
|
|
47
|
+
NSLog(@"[CustomTextView] ❌ Blocking system action: %@", NSStringFromSelector(action));
|
|
48
|
+
return NO;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
- (void)handleCustomMenuAction:(UIMenuItem *)sender
|
|
52
|
+
{
|
|
53
|
+
// Forward to parent view
|
|
54
|
+
if (self.parentView) {
|
|
55
|
+
|
|
56
|
+
[self.parentView handleCustomMenuAction:sender];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@end
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@interface AdvancedTextView () <RCTAdvancedTextViewViewProtocol, UIGestureRecognizerDelegate, UITextViewDelegate>
|
|
65
|
+
|
|
66
|
+
@property (nonatomic, strong) CustomTextView *textView;
|
|
67
|
+
|
|
20
68
|
@end
|
|
21
69
|
|
|
22
70
|
@implementation AdvancedTextView
|
|
@@ -56,9 +104,10 @@ using namespace facebook::react;
|
|
|
56
104
|
{
|
|
57
105
|
NSLog(@"[AdvancedTextView] setupTextView called");
|
|
58
106
|
@try {
|
|
59
|
-
_textView = [[
|
|
107
|
+
_textView = [[CustomTextView alloc] initWithFrame:self.bounds];
|
|
108
|
+
_textView.parentView = self;
|
|
60
109
|
_textView.editable = NO;
|
|
61
|
-
_textView.selectable = YES;
|
|
110
|
+
_textView.selectable = YES;
|
|
62
111
|
_textView.scrollEnabled = YES;
|
|
63
112
|
_textView.backgroundColor = [UIColor clearColor];
|
|
64
113
|
_textView.textContainerInset = UIEdgeInsetsMake(8, 8, 8, 8);
|
|
@@ -107,21 +156,18 @@ using namespace facebook::react;
|
|
|
107
156
|
// Check text change
|
|
108
157
|
if (oldViewProps.text != newViewProps.text) {
|
|
109
158
|
textChanged = YES;
|
|
110
|
-
NSLog(@"[AdvancedTextView] Text changed
|
|
159
|
+
NSLog(@"[AdvancedTextView] Text changed");
|
|
111
160
|
}
|
|
112
161
|
|
|
113
162
|
// Check highlighted words change
|
|
114
163
|
if (oldViewProps.highlightedWords.size() != newViewProps.highlightedWords.size()) {
|
|
115
164
|
highlightsChanged = YES;
|
|
116
|
-
NSLog(@"[AdvancedTextView] Highlights size changed: %zu -> %zu",
|
|
117
|
-
oldViewProps.highlightedWords.size(), newViewProps.highlightedWords.size());
|
|
118
165
|
} else {
|
|
119
166
|
for (size_t i = 0; i < oldViewProps.highlightedWords.size(); i++) {
|
|
120
167
|
const auto &oldHW = oldViewProps.highlightedWords[i];
|
|
121
168
|
const auto &newHW = newViewProps.highlightedWords[i];
|
|
122
169
|
if (oldHW.index != newHW.index || oldHW.highlightColor != newHW.highlightColor) {
|
|
123
170
|
highlightsChanged = YES;
|
|
124
|
-
NSLog(@"[AdvancedTextView] Highlight changed at index %zu", i);
|
|
125
171
|
break;
|
|
126
172
|
}
|
|
127
173
|
}
|
|
@@ -130,13 +176,10 @@ using namespace facebook::react;
|
|
|
130
176
|
// Check menu options change
|
|
131
177
|
if (oldViewProps.menuOptions.size() != newViewProps.menuOptions.size()) {
|
|
132
178
|
menuChanged = YES;
|
|
133
|
-
NSLog(@"[AdvancedTextView] Menu options size changed: %zu -> %zu",
|
|
134
|
-
oldViewProps.menuOptions.size(), newViewProps.menuOptions.size());
|
|
135
179
|
} else {
|
|
136
180
|
for (size_t i = 0; i < oldViewProps.menuOptions.size(); i++) {
|
|
137
181
|
if (oldViewProps.menuOptions[i] != newViewProps.menuOptions[i]) {
|
|
138
182
|
menuChanged = YES;
|
|
139
|
-
NSLog(@"[AdvancedTextView] Menu option changed at index %zu", i);
|
|
140
183
|
break;
|
|
141
184
|
}
|
|
142
185
|
}
|
|
@@ -145,8 +188,6 @@ using namespace facebook::react;
|
|
|
145
188
|
// Check indicator change
|
|
146
189
|
if (oldViewProps.indicatorWordIndex != newViewProps.indicatorWordIndex) {
|
|
147
190
|
indicatorChanged = YES;
|
|
148
|
-
NSLog(@"[AdvancedTextView] Indicator changed: %d -> %d",
|
|
149
|
-
oldViewProps.indicatorWordIndex, newViewProps.indicatorWordIndex);
|
|
150
191
|
}
|
|
151
192
|
|
|
152
193
|
// Apply updates
|
|
@@ -171,8 +212,7 @@ using namespace facebook::react;
|
|
|
171
212
|
[super updateProps:props oldProps:oldProps];
|
|
172
213
|
NSLog(@"[AdvancedTextView] updateProps completed successfully");
|
|
173
214
|
} @catch (NSException *exception) {
|
|
174
|
-
NSLog(@"[AdvancedTextView] Exception in updateProps:
|
|
175
|
-
exception.name, exception.reason);
|
|
215
|
+
NSLog(@"[AdvancedTextView] Exception in updateProps: %@", exception.reason);
|
|
176
216
|
@throw;
|
|
177
217
|
}
|
|
178
218
|
}
|
|
@@ -187,9 +227,7 @@ using namespace facebook::react;
|
|
|
187
227
|
return;
|
|
188
228
|
}
|
|
189
229
|
|
|
190
|
-
// IMPORTANT: Set the text first!
|
|
191
230
|
_textView.text = text;
|
|
192
|
-
NSLog(@"[AdvancedTextView] Set textView.text with length: %lu", (unsigned long)_textView.text.length);
|
|
193
231
|
|
|
194
232
|
// Parse text into words and their ranges
|
|
195
233
|
[_wordRanges removeAllObjects];
|
|
@@ -250,10 +288,6 @@ using namespace facebook::react;
|
|
|
250
288
|
|
|
251
289
|
if (color) {
|
|
252
290
|
_highlightColors[@(index)] = color;
|
|
253
|
-
NSLog(@"[AdvancedTextView] Added highlight for word %ld with color %@",
|
|
254
|
-
(long)index, colorString);
|
|
255
|
-
} else {
|
|
256
|
-
NSLog(@"[AdvancedTextView] Failed to parse color: %@", colorString);
|
|
257
291
|
}
|
|
258
292
|
}
|
|
259
293
|
|
|
@@ -283,22 +317,14 @@ using namespace facebook::react;
|
|
|
283
317
|
|
|
284
318
|
- (void)updateTextAppearance
|
|
285
319
|
{
|
|
286
|
-
NSLog(@"[AdvancedTextView] updateTextAppearance called");
|
|
287
320
|
@try {
|
|
288
|
-
if (_wordRanges.count == 0) {
|
|
289
|
-
NSLog(@"[AdvancedTextView] No word ranges, skipping appearance update");
|
|
290
|
-
return;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
if (!_textView.text || _textView.text.length == 0) {
|
|
294
|
-
NSLog(@"[AdvancedTextView] TextView has no text");
|
|
321
|
+
if (_wordRanges.count == 0 || !_textView.text || _textView.text.length == 0) {
|
|
295
322
|
return;
|
|
296
323
|
}
|
|
297
324
|
|
|
298
325
|
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc]
|
|
299
326
|
initWithString:_textView.text];
|
|
300
327
|
|
|
301
|
-
// Apply default attributes
|
|
302
328
|
[attributedString addAttribute:NSFontAttributeName
|
|
303
329
|
value:[UIFont systemFontOfSize:16]
|
|
304
330
|
range:NSMakeRange(0, attributedString.length)];
|
|
@@ -307,17 +333,12 @@ using namespace facebook::react;
|
|
|
307
333
|
value:[UIColor labelColor]
|
|
308
334
|
range:NSMakeRange(0, attributedString.length)];
|
|
309
335
|
|
|
310
|
-
// Apply highlights
|
|
311
336
|
for (NSDictionary *wordInfo in _wordRanges) {
|
|
312
337
|
NSNumber *index = wordInfo[@"index"];
|
|
313
338
|
NSValue *rangeValue = wordInfo[@"range"];
|
|
314
339
|
NSRange range = [rangeValue rangeValue];
|
|
315
340
|
|
|
316
|
-
// Validate range
|
|
317
341
|
if (range.location + range.length > attributedString.length) {
|
|
318
|
-
NSLog(@"[AdvancedTextView] Invalid range at index %@: {%lu, %lu} for string length %lu",
|
|
319
|
-
index, (unsigned long)range.location, (unsigned long)range.length,
|
|
320
|
-
(unsigned long)attributedString.length);
|
|
321
342
|
continue;
|
|
322
343
|
}
|
|
323
344
|
|
|
@@ -328,9 +349,7 @@ using namespace facebook::react;
|
|
|
328
349
|
range:range];
|
|
329
350
|
}
|
|
330
351
|
|
|
331
|
-
// Add background color indicator for indicated word
|
|
332
352
|
if (_indicatorWordIndex >= 0 && [index integerValue] == _indicatorWordIndex) {
|
|
333
|
-
// Use a semi-transparent blue background for the current word
|
|
334
353
|
UIColor *indicatorColor = [[UIColor systemBlueColor] colorWithAlphaComponent:0.3];
|
|
335
354
|
[attributedString addAttribute:NSBackgroundColorAttributeName
|
|
336
355
|
value:indicatorColor
|
|
@@ -339,26 +358,19 @@ using namespace facebook::react;
|
|
|
339
358
|
}
|
|
340
359
|
|
|
341
360
|
_textView.attributedText = attributedString;
|
|
342
|
-
NSLog(@"[AdvancedTextView] Text appearance updated successfully");
|
|
343
361
|
} @catch (NSException *exception) {
|
|
344
|
-
NSLog(@"[AdvancedTextView] Exception in updateTextAppearance:
|
|
345
|
-
exception.name, exception.reason);
|
|
346
|
-
@throw;
|
|
362
|
+
NSLog(@"[AdvancedTextView] Exception in updateTextAppearance: %@", exception.reason);
|
|
347
363
|
}
|
|
348
364
|
}
|
|
349
365
|
|
|
350
366
|
- (void)handleTap:(UITapGestureRecognizer *)gesture
|
|
351
367
|
{
|
|
352
|
-
NSLog(@"[AdvancedTextView] handleTap called");
|
|
353
368
|
@try {
|
|
354
369
|
if (gesture.state != UIGestureRecognizerStateEnded) return;
|
|
355
370
|
|
|
356
371
|
CGPoint location = [gesture locationInView:_textView];
|
|
357
372
|
NSInteger wordIndex = [self wordIndexAtPoint:location];
|
|
358
373
|
|
|
359
|
-
NSLog(@"[AdvancedTextView] Tap at point: {%.2f, %.2f}, word index: %ld",
|
|
360
|
-
location.x, location.y, (long)wordIndex);
|
|
361
|
-
|
|
362
374
|
// Dismiss any existing selection
|
|
363
375
|
_textView.selectedTextRange = nil;
|
|
364
376
|
|
|
@@ -366,7 +378,6 @@ using namespace facebook::react;
|
|
|
366
378
|
NSDictionary *wordInfo = _wordRanges[wordIndex];
|
|
367
379
|
NSString *word = wordInfo[@"word"];
|
|
368
380
|
|
|
369
|
-
NSLog(@"[AdvancedTextView] Word pressed: %@", word);
|
|
370
381
|
[self emitWordPressEvent:word index:wordIndex];
|
|
371
382
|
}
|
|
372
383
|
} @catch (NSException *exception) {
|
|
@@ -376,22 +387,13 @@ using namespace facebook::react;
|
|
|
376
387
|
|
|
377
388
|
#pragma mark - UITextViewDelegate
|
|
378
389
|
|
|
379
|
-
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange interaction:(UITextItemInteraction)interaction
|
|
380
|
-
{
|
|
381
|
-
return YES;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
390
|
- (void)textViewDidChangeSelection:(UITextView *)textView
|
|
385
391
|
{
|
|
386
|
-
NSLog(@"[AdvancedTextView] Selection changed");
|
|
387
|
-
|
|
388
|
-
// Get selected text
|
|
389
392
|
NSString *selectedText = [textView.text substringWithRange:textView.selectedRange];
|
|
390
393
|
|
|
391
394
|
if (selectedText.length > 0) {
|
|
392
395
|
NSLog(@"[AdvancedTextView] Selected text: %@", selectedText);
|
|
393
396
|
|
|
394
|
-
// Add custom menu items
|
|
395
397
|
if (_menuOptions && _menuOptions.count > 0) {
|
|
396
398
|
[self setupCustomMenuItems];
|
|
397
399
|
}
|
|
@@ -400,7 +402,7 @@ using namespace facebook::react;
|
|
|
400
402
|
|
|
401
403
|
- (void)setupCustomMenuItems
|
|
402
404
|
{
|
|
403
|
-
NSLog(@"[AdvancedTextView] Setting up custom menu items");
|
|
405
|
+
NSLog(@"[AdvancedTextView] Setting up %lu custom menu items", (unsigned long)_menuOptions.count);
|
|
404
406
|
|
|
405
407
|
UIMenuController *menuController = [UIMenuController sharedMenuController];
|
|
406
408
|
NSMutableArray *customItems = [NSMutableArray array];
|
|
@@ -409,53 +411,34 @@ using namespace facebook::react;
|
|
|
409
411
|
UIMenuItem *item = [[UIMenuItem alloc] initWithTitle:option
|
|
410
412
|
action:@selector(handleCustomMenuAction:)];
|
|
411
413
|
[customItems addObject:item];
|
|
414
|
+
NSLog(@"[AdvancedTextView] Created menu item: %@", option);
|
|
412
415
|
}
|
|
413
416
|
|
|
414
417
|
menuController.menuItems = customItems;
|
|
415
418
|
}
|
|
416
419
|
|
|
420
|
+
|
|
417
421
|
- (void)handleCustomMenuAction:(UIMenuItem *)sender
|
|
418
422
|
{
|
|
419
423
|
NSLog(@"[AdvancedTextView] Custom menu action: %@", sender.title);
|
|
420
424
|
|
|
421
425
|
NSString *selectedText = [_textView.text substringWithRange:_textView.selectedRange];
|
|
422
|
-
[self emitSelectionEvent:selectedText menuOption:sender.title];
|
|
423
|
-
|
|
424
|
-
// Clear selection after action
|
|
425
|
-
_textView.selectedTextRange = nil;
|
|
426
|
-
}
|
|
427
426
|
|
|
428
|
-
|
|
429
|
-
{
|
|
430
|
-
// Allow custom menu items
|
|
431
|
-
if (action == @selector(handleCustomMenuAction:)) {
|
|
432
|
-
return YES;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
// Allow default actions (copy, etc.)
|
|
436
|
-
if (action == @selector(copy:) ||
|
|
437
|
-
action == @selector(selectAll:) ||
|
|
438
|
-
action == @selector(select:)) {
|
|
439
|
-
return [super canPerformAction:action withSender:sender];
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
return NO;
|
|
443
|
-
}
|
|
427
|
+
[self emitSelectionEvent:selectedText menuOption:sender.title];
|
|
444
428
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
429
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
430
|
+
self->_textView.selectedTextRange = nil;
|
|
431
|
+
NSLog(@"[AdvancedTextView] Selection cleared");
|
|
432
|
+
});
|
|
448
433
|
}
|
|
449
434
|
|
|
450
435
|
- (NSInteger)wordIndexAtPoint:(CGPoint)point
|
|
451
436
|
{
|
|
452
437
|
@try {
|
|
453
438
|
if (!_textView.layoutManager || !_textView.textContainer) {
|
|
454
|
-
NSLog(@"[AdvancedTextView] Layout manager or text container is nil");
|
|
455
439
|
return -1;
|
|
456
440
|
}
|
|
457
441
|
|
|
458
|
-
// Adjust point for text container insets
|
|
459
442
|
point.x -= _textView.textContainerInset.left;
|
|
460
443
|
point.y -= _textView.textContainerInset.top;
|
|
461
444
|
|
|
@@ -466,9 +449,6 @@ using namespace facebook::react;
|
|
|
466
449
|
inTextContainer:textContainer
|
|
467
450
|
fractionOfDistanceBetweenInsertionPoints:nil];
|
|
468
451
|
|
|
469
|
-
NSLog(@"[AdvancedTextView] Character index at point: %lu", (unsigned long)characterIndex);
|
|
470
|
-
|
|
471
|
-
// Find which word this character belongs to
|
|
472
452
|
for (NSDictionary *wordInfo in _wordRanges) {
|
|
473
453
|
NSValue *rangeValue = wordInfo[@"range"];
|
|
474
454
|
NSRange range = [rangeValue rangeValue];
|
|
@@ -480,22 +460,10 @@ using namespace facebook::react;
|
|
|
480
460
|
|
|
481
461
|
return -1;
|
|
482
462
|
} @catch (NSException *exception) {
|
|
483
|
-
NSLog(@"[AdvancedTextView] Exception in wordIndexAtPoint: %@", exception);
|
|
484
463
|
return -1;
|
|
485
464
|
}
|
|
486
465
|
}
|
|
487
466
|
|
|
488
|
-
- (void)showContextMenuForWord:(NSString *)word atIndex:(NSInteger)index location:(CGPoint)location
|
|
489
|
-
{
|
|
490
|
-
// Removed - using native iOS text selection menu instead
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
- (UIViewController *)findViewController
|
|
494
|
-
{
|
|
495
|
-
// Removed - no longer needed
|
|
496
|
-
return nil;
|
|
497
|
-
}
|
|
498
|
-
|
|
499
467
|
- (void)emitWordPressEvent:(NSString *)word index:(NSInteger)index
|
|
500
468
|
{
|
|
501
469
|
NSLog(@"[AdvancedTextView] emitWordPressEvent: %@ at index: %ld", word, (long)index);
|
|
@@ -508,9 +476,6 @@ using namespace facebook::react;
|
|
|
508
476
|
event.index = static_cast<int>(index);
|
|
509
477
|
|
|
510
478
|
emitter->onWordPress(event);
|
|
511
|
-
NSLog(@"[AdvancedTextView] Word press event emitted successfully");
|
|
512
|
-
} else {
|
|
513
|
-
NSLog(@"[AdvancedTextView] Event emitter is null");
|
|
514
479
|
}
|
|
515
480
|
} @catch (NSException *exception) {
|
|
516
481
|
NSLog(@"[AdvancedTextView] Exception in emitWordPressEvent: %@", exception);
|
|
@@ -529,9 +494,6 @@ using namespace facebook::react;
|
|
|
529
494
|
event.event = [option UTF8String];
|
|
530
495
|
|
|
531
496
|
emitter->onSelection(event);
|
|
532
|
-
NSLog(@"[AdvancedTextView] Selection event emitted successfully");
|
|
533
|
-
} else {
|
|
534
|
-
NSLog(@"[AdvancedTextView] Event emitter is null");
|
|
535
497
|
}
|
|
536
498
|
} @catch (NSException *exception) {
|
|
537
499
|
NSLog(@"[AdvancedTextView] Exception in emitSelectionEvent: %@", exception);
|
|
@@ -540,7 +502,6 @@ using namespace facebook::react;
|
|
|
540
502
|
|
|
541
503
|
- (void)layoutSubviews
|
|
542
504
|
{
|
|
543
|
-
NSLog(@"[AdvancedTextView] layoutSubviews called");
|
|
544
505
|
@try {
|
|
545
506
|
[super layoutSubviews];
|
|
546
507
|
_textView.frame = self.bounds;
|
|
@@ -553,7 +514,6 @@ using namespace facebook::react;
|
|
|
553
514
|
{
|
|
554
515
|
@try {
|
|
555
516
|
if (!stringToConvert || [stringToConvert length] == 0) {
|
|
556
|
-
NSLog(@"[AdvancedTextView] Empty color string");
|
|
557
517
|
return nil;
|
|
558
518
|
}
|
|
559
519
|
|
|
@@ -562,7 +522,6 @@ using namespace facebook::react;
|
|
|
562
522
|
|
|
563
523
|
unsigned hex;
|
|
564
524
|
if (![stringScanner scanHexInt:&hex]) {
|
|
565
|
-
NSLog(@"[AdvancedTextView] Failed to parse hex color: %@", stringToConvert);
|
|
566
525
|
return nil;
|
|
567
526
|
}
|
|
568
527
|
|
|
@@ -572,14 +531,12 @@ using namespace facebook::react;
|
|
|
572
531
|
|
|
573
532
|
return [UIColor colorWithRed:r / 255.0f green:g / 255.0f blue:b / 255.0f alpha:1.0f];
|
|
574
533
|
} @catch (NSException *exception) {
|
|
575
|
-
NSLog(@"[AdvancedTextView] Exception in hexStringToColor: %@", exception);
|
|
576
534
|
return nil;
|
|
577
535
|
}
|
|
578
536
|
}
|
|
579
537
|
|
|
580
538
|
Class<RCTComponentViewProtocol> AdvancedTextViewCls(void)
|
|
581
539
|
{
|
|
582
|
-
NSLog(@"[AdvancedTextView] AdvancedTextViewCls called");
|
|
583
540
|
return AdvancedTextView.class;
|
|
584
541
|
}
|
|
585
542
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","AdvancedTextViewNativeComponent","jsx","_jsx","AdvancedText","text","style","highlightedWords","menuOptions","onWordPress","onSelection","indicatorWordIndex","restProps"],"sourceRoot":"..\\..\\src","sources":["AdvancedText.tsx"],"mappings":";;AAAA,OAAOA,KAAK,MAAM,OAAO;
|
|
1
|
+
{"version":3,"names":["React","AdvancedTextViewNativeComponent","jsx","_jsx","AdvancedText","text","style","highlightedWords","menuOptions","onWordPress","onSelection","indicatorWordIndex","restProps"],"sourceRoot":"..\\..\\src","sources":["AdvancedText.tsx"],"mappings":";;AAAA,OAAOA,KAAK,MAAM,OAAO;AACzB,OAAOC,+BAA+B,MAE/B,mCAAmC;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAE3C,OAAO,MAAMC,YAAmC,GAAGA,CAAC;EAClDC,IAAI;EACJC,KAAK;EACLC,gBAAgB;EAChBC,WAAW;EACXC,WAAW;EACXC,WAAW;EACXC,kBAAkB;EAClB,GAAGC;AACL,CAAC,KAAK;EACJ,oBACET,IAAA,CAACF,+BAA+B;IAAA,GAC1BW,SAAS;IACbN,KAAK,EAAEA,KAAM;IACbD,IAAI,EAAEA,IAAK;IACXE,gBAAgB,EAAEA,gBAAiB;IACnCC,WAAW,EAAEA,WAAY;IACzBC,WAAW,EAAEA,WAAY;IACzBC,WAAW,EAAEA,WAAY;IACzBC,kBAAkB,EAAEA;EAAmB,CACxC,CAAC;AAEN,CAAC","ignoreList":[]}
|
|
@@ -2,20 +2,25 @@
|
|
|
2
2
|
import type { ViewProps } from 'react-native';
|
|
3
3
|
import { codegenNativeComponent } from 'react-native';
|
|
4
4
|
// @ts-ignore
|
|
5
|
-
import type { DirectEventHandler, Int32} from 'react-native/Libraries/Types/CodegenTypes';
|
|
5
|
+
import type { DirectEventHandler, Int32 } from 'react-native/Libraries/Types/CodegenTypes';
|
|
6
6
|
|
|
7
7
|
interface HighlightedWord {
|
|
8
8
|
index: Int32;
|
|
9
9
|
highlightColor: string;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
interface NativeProps extends ViewProps {
|
|
12
|
+
export interface NativeProps extends ViewProps {
|
|
13
13
|
text: string;
|
|
14
14
|
highlightedWords?: ReadonlyArray<HighlightedWord>;
|
|
15
15
|
menuOptions?: ReadonlyArray<string>;
|
|
16
16
|
onWordPress?: DirectEventHandler<{ word: string; index: Int32 }>;
|
|
17
17
|
onSelection?: DirectEventHandler<{ selectedText: string; event: string }>;
|
|
18
18
|
indicatorWordIndex?: Int32;
|
|
19
|
+
fontSize?: Int32;
|
|
20
|
+
fontWeight?: string;
|
|
21
|
+
color?: string;
|
|
22
|
+
textAlign?: string;
|
|
23
|
+
fontFamily?: string;
|
|
19
24
|
}
|
|
20
25
|
|
|
21
26
|
export default codegenNativeComponent<NativeProps>('AdvancedTextView');
|
|
@@ -1,26 +1,4 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import
|
|
3
|
-
interface HighlightedWord {
|
|
4
|
-
index: number;
|
|
5
|
-
highlightColor: string;
|
|
6
|
-
}
|
|
7
|
-
interface WordPressEvent {
|
|
8
|
-
word: string;
|
|
9
|
-
index: number;
|
|
10
|
-
}
|
|
11
|
-
interface SelectionEvent {
|
|
12
|
-
selectedText: string;
|
|
13
|
-
event: string;
|
|
14
|
-
}
|
|
15
|
-
interface NativeProps extends ViewProps {
|
|
16
|
-
text: string;
|
|
17
|
-
style?: StyleProp<ViewStyle>;
|
|
18
|
-
highlightedWords?: ReadonlyArray<HighlightedWord>;
|
|
19
|
-
menuOptions?: ReadonlyArray<string>;
|
|
20
|
-
onWordPress?: (event: NativeSyntheticEvent<WordPressEvent>) => void;
|
|
21
|
-
onSelection?: (event: NativeSyntheticEvent<SelectionEvent>) => void;
|
|
22
|
-
indicatorWordIndex?: number;
|
|
23
|
-
}
|
|
2
|
+
import { type NativeProps } from './AdvancedTextViewNativeComponent';
|
|
24
3
|
export declare const AdvancedText: React.FC<NativeProps>;
|
|
25
|
-
export {};
|
|
26
4
|
//# sourceMappingURL=AdvancedText.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdvancedText.d.ts","sourceRoot":"","sources":["../../../src/AdvancedText.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,
|
|
1
|
+
{"version":3,"file":"AdvancedText.d.ts","sourceRoot":"","sources":["../../../src/AdvancedText.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAwC,EACtC,KAAK,WAAW,EACjB,MAAM,mCAAmC,CAAC;AAE3C,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CAsB9C,CAAC"}
|
|
@@ -4,7 +4,7 @@ interface HighlightedWord {
|
|
|
4
4
|
index: Int32;
|
|
5
5
|
highlightColor: string;
|
|
6
6
|
}
|
|
7
|
-
interface NativeProps extends ViewProps {
|
|
7
|
+
export interface NativeProps extends ViewProps {
|
|
8
8
|
text: string;
|
|
9
9
|
highlightedWords?: ReadonlyArray<HighlightedWord>;
|
|
10
10
|
menuOptions?: ReadonlyArray<string>;
|
|
@@ -17,6 +17,11 @@ interface NativeProps extends ViewProps {
|
|
|
17
17
|
event: string;
|
|
18
18
|
}>;
|
|
19
19
|
indicatorWordIndex?: Int32;
|
|
20
|
+
fontSize?: Int32;
|
|
21
|
+
fontWeight?: string;
|
|
22
|
+
color?: string;
|
|
23
|
+
textAlign?: string;
|
|
24
|
+
fontFamily?: string;
|
|
20
25
|
}
|
|
21
26
|
declare const _default: import("react-native/types_generated/Libraries/Utilities/codegenNativeComponent").NativeComponentType<NativeProps>;
|
|
22
27
|
export default _default;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdvancedTextViewNativeComponent.d.ts","sourceRoot":"","sources":["../../../src/AdvancedTextViewNativeComponent.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAG9C,OAAO,KAAK,EAAE,kBAAkB,EAAE,KAAK,
|
|
1
|
+
{"version":3,"file":"AdvancedTextViewNativeComponent.d.ts","sourceRoot":"","sources":["../../../src/AdvancedTextViewNativeComponent.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAG9C,OAAO,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,2CAA2C,CAAC;AAE3F,UAAU,eAAe;IACvB,KAAK,EAAE,KAAK,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,WAAY,SAAQ,SAAS;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,CAAC,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IAClD,WAAW,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACpC,WAAW,CAAC,EAAE,kBAAkB,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,KAAK,CAAA;KAAE,CAAC,CAAC;IACjE,WAAW,CAAC,EAAE,kBAAkB,CAAC;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1E,kBAAkB,CAAC,EAAE,KAAK,CAAC;IAC3B,QAAQ,CAAC,EAAE,KAAK,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;;AAED,wBAAuE"}
|
package/package.json
CHANGED
package/src/AdvancedText.tsx
CHANGED
|
@@ -1,36 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
ViewProps,
|
|
6
|
-
ViewStyle,
|
|
7
|
-
} from 'react-native';
|
|
8
|
-
import AdvancedTextViewNativeComponent from './AdvancedTextViewNativeComponent';
|
|
9
|
-
|
|
10
|
-
interface HighlightedWord {
|
|
11
|
-
index: number;
|
|
12
|
-
highlightColor: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
interface WordPressEvent {
|
|
16
|
-
word: string;
|
|
17
|
-
index: number;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
interface SelectionEvent {
|
|
21
|
-
selectedText: string;
|
|
22
|
-
event: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
interface NativeProps extends ViewProps {
|
|
26
|
-
text: string;
|
|
27
|
-
style?: StyleProp<ViewStyle>;
|
|
28
|
-
highlightedWords?: ReadonlyArray<HighlightedWord>;
|
|
29
|
-
menuOptions?: ReadonlyArray<string>;
|
|
30
|
-
onWordPress?: (event: NativeSyntheticEvent<WordPressEvent>) => void;
|
|
31
|
-
onSelection?: (event: NativeSyntheticEvent<SelectionEvent>) => void;
|
|
32
|
-
indicatorWordIndex?: number;
|
|
33
|
-
}
|
|
2
|
+
import AdvancedTextViewNativeComponent, {
|
|
3
|
+
type NativeProps,
|
|
4
|
+
} from './AdvancedTextViewNativeComponent';
|
|
34
5
|
|
|
35
6
|
export const AdvancedText: React.FC<NativeProps> = ({
|
|
36
7
|
text,
|
|
@@ -2,20 +2,25 @@
|
|
|
2
2
|
import type { ViewProps } from 'react-native';
|
|
3
3
|
import { codegenNativeComponent } from 'react-native';
|
|
4
4
|
// @ts-ignore
|
|
5
|
-
import type { DirectEventHandler, Int32} from 'react-native/Libraries/Types/CodegenTypes';
|
|
5
|
+
import type { DirectEventHandler, Int32 } from 'react-native/Libraries/Types/CodegenTypes';
|
|
6
6
|
|
|
7
7
|
interface HighlightedWord {
|
|
8
8
|
index: Int32;
|
|
9
9
|
highlightColor: string;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
interface NativeProps extends ViewProps {
|
|
12
|
+
export interface NativeProps extends ViewProps {
|
|
13
13
|
text: string;
|
|
14
14
|
highlightedWords?: ReadonlyArray<HighlightedWord>;
|
|
15
15
|
menuOptions?: ReadonlyArray<string>;
|
|
16
16
|
onWordPress?: DirectEventHandler<{ word: string; index: Int32 }>;
|
|
17
17
|
onSelection?: DirectEventHandler<{ selectedText: string; event: string }>;
|
|
18
18
|
indicatorWordIndex?: Int32;
|
|
19
|
+
fontSize?: Int32;
|
|
20
|
+
fontWeight?: string;
|
|
21
|
+
color?: string;
|
|
22
|
+
textAlign?: string;
|
|
23
|
+
fontFamily?: string;
|
|
19
24
|
}
|
|
20
25
|
|
|
21
26
|
export default codegenNativeComponent<NativeProps>('AdvancedTextView');
|