react-native-advanced-text 0.2.14 → 0.2.16
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 +12 -0
- package/android/src/main/java/com/advancedtext/AdvancedTextView.kt +118 -68
- package/android/src/main/java/com/advancedtext/AdvancedTextViewManager.kt +4 -1
- package/ios/AdvancedTextView.mm +34 -2
- package/lib/module/AdvancedTextViewNativeComponent.ts +3 -0
- package/lib/typescript/src/AdvancedTextViewNativeComponent.d.ts +1 -0
- package/lib/typescript/src/AdvancedTextViewNativeComponent.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/AdvancedTextViewNativeComponent.ts +3 -0
package/README.md
CHANGED
|
@@ -38,6 +38,11 @@ import { AdvancedText } from "react-native-advanced-text";
|
|
|
38
38
|
highlightColor: '#6baeffb5',
|
|
39
39
|
borderRadius: 6,
|
|
40
40
|
},
|
|
41
|
+
{
|
|
42
|
+
index: 7,
|
|
43
|
+
highlightColor: '#818CF8',
|
|
44
|
+
underline: true, // a line under the word instead of a background
|
|
45
|
+
},
|
|
41
46
|
]}
|
|
42
47
|
fontSize={24}
|
|
43
48
|
color={'#FFFFFF'}
|
|
@@ -47,6 +52,13 @@ import { AdvancedText } from "react-native-advanced-text";
|
|
|
47
52
|
/>
|
|
48
53
|
```
|
|
49
54
|
|
|
55
|
+
## Underlined words
|
|
56
|
+
|
|
57
|
+
An entry in `highlightedWords` with `underline: true` draws a line under the
|
|
58
|
+
word in `highlightColor` instead of filling its background (`borderRadius` is
|
|
59
|
+
ignored). A word can be both highlighted and underlined: list it twice, once
|
|
60
|
+
with `underline: true` and once without.
|
|
61
|
+
|
|
50
62
|
## Sizing
|
|
51
63
|
|
|
52
64
|
`AdvancedText` measures its own height, like React Native's built‑in `<Text>`.
|
|
@@ -3,9 +3,12 @@ package com.advancedtext
|
|
|
3
3
|
import android.content.Context
|
|
4
4
|
import android.graphics.Canvas
|
|
5
5
|
import android.graphics.Color
|
|
6
|
+
import android.graphics.Paint
|
|
7
|
+
import android.graphics.Path
|
|
6
8
|
import android.graphics.Point
|
|
7
9
|
import android.graphics.RectF
|
|
8
10
|
import android.os.Build
|
|
11
|
+
import android.text.Layout
|
|
9
12
|
import android.text.SpannableString
|
|
10
13
|
import android.text.Spannable
|
|
11
14
|
import android.text.Spanned
|
|
@@ -58,6 +61,13 @@ class AdvancedTextView : TextView {
|
|
|
58
61
|
// text. setText() would clear any selection the user is making.
|
|
59
62
|
private var wordColorSpans: Array<ForegroundColorSpan?> = emptyArray()
|
|
60
63
|
|
|
64
|
+
// Reused on every draw for the highlight backgrounds.
|
|
65
|
+
private val highlightPaint = Paint(Paint.ANTI_ALIAS_FLAG)
|
|
66
|
+
private val rangePath = Path()
|
|
67
|
+
private val lineClipPath = Path()
|
|
68
|
+
private val rangeBounds = RectF()
|
|
69
|
+
private val underlineBounds = RectF()
|
|
70
|
+
|
|
61
71
|
constructor(context: Context?) : super(context) { init() }
|
|
62
72
|
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) { init() }
|
|
63
73
|
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { init() }
|
|
@@ -382,43 +392,27 @@ class AdvancedTextView : TextView {
|
|
|
382
392
|
private fun drawHighlightedWordBackgrounds(canvas: Canvas) {
|
|
383
393
|
val currentLayout = layout ?: return
|
|
384
394
|
val density = resources.displayMetrics.density
|
|
385
|
-
val originalColor = paint.color
|
|
386
395
|
|
|
387
396
|
highlightedWords.forEach { highlightedWord ->
|
|
388
397
|
val wordPos = wordPositions.find { it.index == highlightedWord.index } ?: return@forEach
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
val isLastWordOnLine = lineEndOffset >= currentLayout.getLineEnd(line)
|
|
407
|
-
val right = if (isLastWordOnLine) {
|
|
408
|
-
currentLayout.getLineRight(line)
|
|
409
|
-
} else {
|
|
410
|
-
currentLayout.getPrimaryHorizontal(lineEndOffset)
|
|
411
|
-
}
|
|
412
|
-
val top = currentLayout.getLineTop(line).toFloat()
|
|
413
|
-
val bottom = currentLayout.getLineBottom(line).toFloat()
|
|
414
|
-
val rect = RectF(
|
|
415
|
-
totalPaddingLeft + left,
|
|
416
|
-
totalPaddingTop + top,
|
|
417
|
-
totalPaddingLeft + right,
|
|
418
|
-
totalPaddingTop + bottom
|
|
398
|
+
if (highlightedWord.underline) {
|
|
399
|
+
// The word, but not the space after it.
|
|
400
|
+
drawRangeUnderline(
|
|
401
|
+
canvas,
|
|
402
|
+
currentLayout,
|
|
403
|
+
wordPos.start,
|
|
404
|
+
wordPos.end.coerceAtMost(currentText.length),
|
|
405
|
+
parseColor(highlightedWord.highlightColor)
|
|
406
|
+
)
|
|
407
|
+
} else {
|
|
408
|
+
drawRangeBackground(
|
|
409
|
+
canvas,
|
|
410
|
+
currentLayout,
|
|
411
|
+
wordPos.start,
|
|
412
|
+
wordPos.extendedEnd.coerceAtMost(currentText.length),
|
|
413
|
+
parseColor(highlightedWord.highlightColor),
|
|
414
|
+
highlightedWord.borderRadius * density
|
|
419
415
|
)
|
|
420
|
-
val radius = highlightedWord.borderRadius * density
|
|
421
|
-
canvas.drawRoundRect(rect, radius, radius, paint)
|
|
422
416
|
}
|
|
423
417
|
}
|
|
424
418
|
|
|
@@ -426,43 +420,94 @@ class AdvancedTextView : TextView {
|
|
|
426
420
|
if (indicatorMode != "fill" && indicatorWordIndex >= 0 && indicatorColor.isNotEmpty()) {
|
|
427
421
|
val wordPos = wordPositions.find { it.index == indicatorWordIndex }
|
|
428
422
|
if (wordPos != null) {
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
paint.color = parseColor(indicatorColor)
|
|
438
|
-
|
|
439
|
-
for (line in startLine..endLine) {
|
|
440
|
-
val lineStartOffset = maxOf(start, currentLayout.getLineStart(line))
|
|
441
|
-
val lineEndOffset = minOf(end, currentLayout.getLineEnd(line))
|
|
442
|
-
if (lineStartOffset >= lineEndOffset) continue
|
|
443
|
-
|
|
444
|
-
val left = currentLayout.getPrimaryHorizontal(lineStartOffset)
|
|
445
|
-
val isLastWordOnLine = lineEndOffset >= currentLayout.getLineEnd(line)
|
|
446
|
-
val right = if (isLastWordOnLine) {
|
|
447
|
-
currentLayout.getLineRight(line)
|
|
448
|
-
} else {
|
|
449
|
-
currentLayout.getPrimaryHorizontal(lineEndOffset)
|
|
450
|
-
}
|
|
451
|
-
val top = currentLayout.getLineTop(line).toFloat()
|
|
452
|
-
val bottom = currentLayout.getLineBottom(line).toFloat()
|
|
453
|
-
val rect = RectF(
|
|
454
|
-
totalPaddingLeft + left,
|
|
455
|
-
totalPaddingTop + top,
|
|
456
|
-
totalPaddingLeft + right,
|
|
457
|
-
totalPaddingTop + bottom
|
|
458
|
-
)
|
|
459
|
-
canvas.drawRoundRect(rect, 0f, 0f, paint)
|
|
460
|
-
}
|
|
461
|
-
}
|
|
423
|
+
drawRangeBackground(
|
|
424
|
+
canvas,
|
|
425
|
+
currentLayout,
|
|
426
|
+
wordPos.start,
|
|
427
|
+
wordPos.end.coerceAtMost(currentText.length),
|
|
428
|
+
parseColor(indicatorColor),
|
|
429
|
+
0f
|
|
430
|
+
)
|
|
462
431
|
}
|
|
463
432
|
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Fills the background of text[start, end), one box per line.
|
|
436
|
+
private fun drawRangeBackground(
|
|
437
|
+
canvas: Canvas,
|
|
438
|
+
layout: Layout,
|
|
439
|
+
start: Int,
|
|
440
|
+
end: Int,
|
|
441
|
+
color: Int,
|
|
442
|
+
radius: Float
|
|
443
|
+
) {
|
|
444
|
+
highlightPaint.color = color
|
|
445
|
+
forEachLineOfRange(layout, start, end) {
|
|
446
|
+
canvas.drawRoundRect(rangeBounds, radius, radius, highlightPaint)
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Draws a line under text[start, end), one per line it spans, just below
|
|
451
|
+
// the baseline like a text underline.
|
|
452
|
+
private fun drawRangeUnderline(
|
|
453
|
+
canvas: Canvas,
|
|
454
|
+
layout: Layout,
|
|
455
|
+
start: Int,
|
|
456
|
+
end: Int,
|
|
457
|
+
color: Int
|
|
458
|
+
) {
|
|
459
|
+
val density = resources.displayMetrics.density
|
|
460
|
+
val thickness = UNDERLINE_THICKNESS_DP * density
|
|
461
|
+
val gap = UNDERLINE_GAP_DP * density
|
|
462
|
+
highlightPaint.color = color
|
|
463
|
+
forEachLineOfRange(layout, start, end) { line ->
|
|
464
|
+
val top = layout.getLineBaseline(line) + totalPaddingTop + gap
|
|
465
|
+
underlineBounds.set(rangeBounds.left, top, rangeBounds.right, top + thickness)
|
|
466
|
+
canvas.drawRoundRect(underlineBounds, thickness / 2, thickness / 2, highlightPaint)
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Calls draw for each line text[start, end) spans, with rangeBounds set to
|
|
471
|
+
// that line's part of the range in view coordinates. The edges come from
|
|
472
|
+
// Layout.getSelectionPath, which follows the text's direction: a
|
|
473
|
+
// right-to-left word (Arabic) starts at its right edge, so treating
|
|
474
|
+
// getPrimaryHorizontal(start) as the left edge put boxes in the wrong place.
|
|
475
|
+
private inline fun forEachLineOfRange(
|
|
476
|
+
layout: Layout,
|
|
477
|
+
start: Int,
|
|
478
|
+
end: Int,
|
|
479
|
+
draw: (line: Int) -> Unit
|
|
480
|
+
) {
|
|
481
|
+
if (start >= end) return
|
|
482
|
+
|
|
483
|
+
val startLine = layout.getLineForOffset(start)
|
|
484
|
+
val endLine = layout.getLineForOffset(end - 1)
|
|
485
|
+
for (line in startLine..endLine) {
|
|
486
|
+
val segmentStart = maxOf(start, layout.getLineStart(line))
|
|
487
|
+
val segmentEnd = minOf(end, layout.getLineEnd(line))
|
|
488
|
+
if (segmentStart >= segmentEnd) continue
|
|
489
|
+
|
|
490
|
+
rangePath.reset()
|
|
491
|
+
layout.getSelectionPath(segmentStart, segmentEnd, rangePath)
|
|
492
|
+
|
|
493
|
+
// A range reaching the end of a line counts as running onto the
|
|
494
|
+
// next one, so the selection path fills out to the view's edge.
|
|
495
|
+
// Clip to this line's text to stop the box at the last word.
|
|
496
|
+
lineClipPath.reset()
|
|
497
|
+
lineClipPath.addRect(
|
|
498
|
+
layout.getLineLeft(line),
|
|
499
|
+
layout.getLineTop(line).toFloat(),
|
|
500
|
+
layout.getLineRight(line),
|
|
501
|
+
layout.getLineBottom(line).toFloat(),
|
|
502
|
+
Path.Direction.CW
|
|
503
|
+
)
|
|
504
|
+
rangePath.op(lineClipPath, Path.Op.INTERSECT)
|
|
505
|
+
rangePath.computeBounds(rangeBounds, true)
|
|
506
|
+
if (rangeBounds.isEmpty) continue
|
|
464
507
|
|
|
465
|
-
|
|
508
|
+
rangeBounds.offset(totalPaddingLeft.toFloat(), totalPaddingTop.toFloat())
|
|
509
|
+
draw(line)
|
|
510
|
+
}
|
|
466
511
|
}
|
|
467
512
|
|
|
468
513
|
private fun parseColor(colorString: String): Int {
|
|
@@ -589,5 +634,10 @@ class AdvancedTextView : TextView {
|
|
|
589
634
|
data class HighlightedWord(
|
|
590
635
|
val index: Int,
|
|
591
636
|
val highlightColor: String,
|
|
592
|
-
val borderRadius: Float = 0f
|
|
637
|
+
val borderRadius: Float = 0f,
|
|
638
|
+
// Draws a line under the word instead of filling its background.
|
|
639
|
+
val underline: Boolean = false
|
|
593
640
|
)
|
|
641
|
+
|
|
642
|
+
private const val UNDERLINE_THICKNESS_DP = 1.5f
|
|
643
|
+
private const val UNDERLINE_GAP_DP = 2.5f
|
|
@@ -54,7 +54,10 @@ class AdvancedTextViewManager : SimpleViewManager<AdvancedTextView>() {
|
|
|
54
54
|
wordMap.getDouble("borderRadius").toFloat()
|
|
55
55
|
} else {
|
|
56
56
|
0f
|
|
57
|
-
}
|
|
57
|
+
},
|
|
58
|
+
underline = wordMap.hasKey("underline") &&
|
|
59
|
+
!wordMap.isNull("underline") &&
|
|
60
|
+
wordMap.getBoolean("underline")
|
|
58
61
|
)
|
|
59
62
|
)
|
|
60
63
|
}
|
package/ios/AdvancedTextView.mm
CHANGED
|
@@ -31,6 +31,9 @@ static UIFont *ATResolveFont(NSString *family, CGFloat size)
|
|
|
31
31
|
|
|
32
32
|
@property (nonatomic, strong) NSMutableArray<NSDictionary *> *wordRanges;
|
|
33
33
|
@property (nonatomic, strong) NSMutableDictionary<NSNumber *, NSDictionary *> *highlightStyles;
|
|
34
|
+
// Underline colors by word index, kept apart from highlightStyles so a word
|
|
35
|
+
// can be both highlighted and underlined.
|
|
36
|
+
@property (nonatomic, strong) NSMutableDictionary<NSNumber *, UIColor *> *underlineStyles;
|
|
34
37
|
@property (nonatomic, strong) NSArray<NSString *> *menuOptions;
|
|
35
38
|
@property (nonatomic, assign) CGFloat fontSize;
|
|
36
39
|
@property (nonatomic, strong) NSString *fontWeight;
|
|
@@ -109,6 +112,7 @@ static UIFont *ATResolveFont(NSString *family, CGFloat size)
|
|
|
109
112
|
|
|
110
113
|
_wordRanges = [NSMutableArray array];
|
|
111
114
|
_highlightStyles = [NSMutableDictionary dictionary];
|
|
115
|
+
_underlineStyles = [NSMutableDictionary dictionary];
|
|
112
116
|
_fontSize = 16.0;
|
|
113
117
|
_fontWeight = @"normal";
|
|
114
118
|
_textColor = [UIColor labelColor];
|
|
@@ -237,7 +241,8 @@ static UIFont *ATResolveFont(NSString *family, CGFloat size)
|
|
|
237
241
|
const auto &newHW = newViewProps.highlightedWords[i];
|
|
238
242
|
if (oldHW.index != newHW.index ||
|
|
239
243
|
oldHW.highlightColor != newHW.highlightColor ||
|
|
240
|
-
oldHW.borderRadius != newHW.borderRadius
|
|
244
|
+
oldHW.borderRadius != newHW.borderRadius ||
|
|
245
|
+
oldHW.underline != newHW.underline) {
|
|
241
246
|
highlightsChanged = YES;
|
|
242
247
|
break;
|
|
243
248
|
}
|
|
@@ -410,6 +415,7 @@ static UIFont *ATResolveFont(NSString *family, CGFloat size)
|
|
|
410
415
|
highlightedWords.size());
|
|
411
416
|
@try {
|
|
412
417
|
[_highlightStyles removeAllObjects];
|
|
418
|
+
[_underlineStyles removeAllObjects];
|
|
413
419
|
|
|
414
420
|
for (const auto &hw : highlightedWords) {
|
|
415
421
|
NSInteger index = hw.index;
|
|
@@ -417,7 +423,9 @@ static UIFont *ATResolveFont(NSString *family, CGFloat size)
|
|
|
417
423
|
UIColor *color = [self hexStringToColor:colorString];
|
|
418
424
|
CGFloat borderRadius = static_cast<CGFloat>(hw.borderRadius);
|
|
419
425
|
|
|
420
|
-
if (color) {
|
|
426
|
+
if (color && hw.underline) {
|
|
427
|
+
_underlineStyles[@(index)] = color;
|
|
428
|
+
} else if (color) {
|
|
421
429
|
_highlightStyles[@(index)] = @{
|
|
422
430
|
@"color": color,
|
|
423
431
|
@"borderRadius": @(MAX(borderRadius, 0.0))
|
|
@@ -499,6 +507,7 @@ static UIFont *ATResolveFont(NSString *family, CGFloat size)
|
|
|
499
507
|
|
|
500
508
|
|
|
501
509
|
[self applyWordColorsToString:attributedString];
|
|
510
|
+
[self applyUnderlinesToString:attributedString];
|
|
502
511
|
|
|
503
512
|
_textView.attributedText = attributedString;
|
|
504
513
|
[_textView setNeedsDisplay];
|
|
@@ -545,6 +554,29 @@ static UIFont *ATResolveFont(NSString *family, CGFloat size)
|
|
|
545
554
|
}
|
|
546
555
|
}
|
|
547
556
|
|
|
557
|
+
// A text underline under each underlined word, but not the space after it.
|
|
558
|
+
- (void)applyUnderlinesToString:(NSMutableAttributedString *)string
|
|
559
|
+
{
|
|
560
|
+
if (_underlineStyles.count == 0) {
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
for (NSDictionary *wordInfo in _wordRanges) {
|
|
565
|
+
UIColor *color = _underlineStyles[wordInfo[@"index"]];
|
|
566
|
+
if (!color) {
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
NSRange wordRange = [wordInfo[@"range"] rangeValue];
|
|
570
|
+
if (NSMaxRange(wordRange) > string.length) {
|
|
571
|
+
break;
|
|
572
|
+
}
|
|
573
|
+
[string addAttributes:@{
|
|
574
|
+
NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle),
|
|
575
|
+
NSUnderlineColorAttributeName: color
|
|
576
|
+
} range:wordRange];
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
548
580
|
// Recolors the live textStorage instead of reassigning attributedText, so the
|
|
549
581
|
// indicator can advance while the user is selecting text: attribute-only
|
|
550
582
|
// edits keep selectedRange and the edit menu, assigning attributedText resets them.
|
|
@@ -8,6 +8,9 @@ interface HighlightedWord {
|
|
|
8
8
|
index: Int32;
|
|
9
9
|
highlightColor: string;
|
|
10
10
|
borderRadius?: Float;
|
|
11
|
+
// Underlines the word in highlightColor instead of filling its background.
|
|
12
|
+
// A word can have both: list it once with and once without.
|
|
13
|
+
underline?: boolean;
|
|
11
14
|
}
|
|
12
15
|
|
|
13
16
|
export interface NativeProps extends ViewProps {
|
|
@@ -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,EAAG,KAAK,EAAG,MAAM,2CAA2C,CAAC;AAEpG,UAAU,eAAe;IACvB,KAAK,EAAE,KAAK,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,KAAK,CAAC;
|
|
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,EAAG,KAAK,EAAG,MAAM,2CAA2C,CAAC;AAEpG,UAAU,eAAe;IACvB,KAAK,EAAE,KAAK,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,KAAK,CAAC;IAGrB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;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,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;IACpB,UAAU,CAAC,EAAE,KAAK,CAAC;IACnB,aAAa,CAAC,EAAE,KAAK,CAAC;IACtB,kBAAkB,CAAC,EAAE,KAAK,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;;AAED,wBAAuE"}
|
package/package.json
CHANGED
|
@@ -8,6 +8,9 @@ interface HighlightedWord {
|
|
|
8
8
|
index: Int32;
|
|
9
9
|
highlightColor: string;
|
|
10
10
|
borderRadius?: Float;
|
|
11
|
+
// Underlines the word in highlightColor instead of filling its background.
|
|
12
|
+
// A word can have both: list it once with and once without.
|
|
13
|
+
underline?: boolean;
|
|
11
14
|
}
|
|
12
15
|
|
|
13
16
|
export interface NativeProps extends ViewProps {
|