react-native-advanced-text 0.1.16 → 0.1.17
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 +303 -303
- package/android/src/main/java/com/advancedtext/AdvancedTextViewManager.kt +95 -95
- package/lib/module/AdvancedTextViewNativeComponent.ts +1 -1
- package/lib/typescript/src/AdvancedTextViewNativeComponent.d.ts.map +1 -1
- package/package.json +175 -175
- package/src/AdvancedTextViewNativeComponent.ts +1 -1
|
@@ -1,303 +1,303 @@
|
|
|
1
|
-
package com.advancedtext
|
|
2
|
-
|
|
3
|
-
import android.content.Context
|
|
4
|
-
import android.graphics.Color
|
|
5
|
-
import android.text.SpannableString
|
|
6
|
-
import android.text.Spanned
|
|
7
|
-
import android.text.TextPaint
|
|
8
|
-
import android.text.method.LinkMovementMethod
|
|
9
|
-
import android.text.style.ClickableSpan
|
|
10
|
-
import android.text.style.BackgroundColorSpan
|
|
11
|
-
import android.text.style.ForegroundColorSpan
|
|
12
|
-
import android.util.AttributeSet
|
|
13
|
-
import android.util.Log
|
|
14
|
-
import android.view.ActionMode
|
|
15
|
-
import android.view.Menu
|
|
16
|
-
import android.view.MenuItem
|
|
17
|
-
import android.view.View
|
|
18
|
-
import android.widget.TextView
|
|
19
|
-
import com.facebook.react.bridge.Arguments
|
|
20
|
-
import com.facebook.react.bridge.ReactContext
|
|
21
|
-
import com.facebook.react.uimanager.events.RCTEventEmitter
|
|
22
|
-
import android.text.Selection
|
|
23
|
-
|
|
24
|
-
class AdvancedTextView : TextView {
|
|
25
|
-
|
|
26
|
-
private val TAG = "AdvancedTextView"
|
|
27
|
-
|
|
28
|
-
private var highlightedWords: List<HighlightedWord> = emptyList()
|
|
29
|
-
private var menuOptions: List<String> = emptyList()
|
|
30
|
-
private var indicatorWordIndex: Int = -1
|
|
31
|
-
private var lastSelectedText: String = ""
|
|
32
|
-
private var isSelectionEnabled: Boolean = true
|
|
33
|
-
private var customActionMode: ActionMode? = null
|
|
34
|
-
|
|
35
|
-
constructor(context: Context?) : super(context) { init() }
|
|
36
|
-
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) { init() }
|
|
37
|
-
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { init() }
|
|
38
|
-
|
|
39
|
-
private fun init() {
|
|
40
|
-
Log.d(TAG, "AdvancedTextView initialized")
|
|
41
|
-
|
|
42
|
-
// Set default text appearance - DON'T set black color here
|
|
43
|
-
textSize = 16f
|
|
44
|
-
setPadding(16, 16, 16, 16)
|
|
45
|
-
|
|
46
|
-
movementMethod = LinkMovementMethod.getInstance()
|
|
47
|
-
setTextIsSelectable(true)
|
|
48
|
-
|
|
49
|
-
customSelectionActionModeCallback = object : ActionMode.Callback {
|
|
50
|
-
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
|
51
|
-
Log.d(TAG, "onCreateActionMode triggered")
|
|
52
|
-
customActionMode = mode
|
|
53
|
-
return true
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
|
57
|
-
Log.d(TAG, "onPrepareActionMode triggered")
|
|
58
|
-
menu?.clear()
|
|
59
|
-
|
|
60
|
-
val selectionStart = selectionStart
|
|
61
|
-
val selectionEnd = selectionEnd
|
|
62
|
-
|
|
63
|
-
if (selectionStart >= 0 && selectionEnd >= 0 && selectionStart != selectionEnd) {
|
|
64
|
-
lastSelectedText = text.subSequence(selectionStart, selectionEnd).toString()
|
|
65
|
-
Log.d(TAG, "User selected text: '$lastSelectedText'")
|
|
66
|
-
Log.d(TAG, "Menu options available: $menuOptions")
|
|
67
|
-
|
|
68
|
-
menuOptions.forEachIndexed { index, option ->
|
|
69
|
-
menu?.add(0, index, index, option)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
sendSelectionEvent(lastSelectedText, "selection")
|
|
73
|
-
return true
|
|
74
|
-
}
|
|
75
|
-
return false
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean {
|
|
79
|
-
item?.let {
|
|
80
|
-
val menuItemText = it.title.toString()
|
|
81
|
-
Log.d(TAG, "Menu item clicked: $menuItemText")
|
|
82
|
-
sendSelectionEvent(lastSelectedText, menuItemText)
|
|
83
|
-
mode?.finish()
|
|
84
|
-
return true
|
|
85
|
-
}
|
|
86
|
-
return false
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
override fun onDestroyActionMode(mode: ActionMode?) {
|
|
90
|
-
Log.d(TAG, "onDestroyActionMode")
|
|
91
|
-
customActionMode = null
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
fun setAdvancedText(text: String) {
|
|
97
|
-
Log.d(TAG, "setAdvancedText: $text (length=${text.length})")
|
|
98
|
-
|
|
99
|
-
// Set the text first
|
|
100
|
-
super.setText(text, BufferType.SPANNABLE)
|
|
101
|
-
|
|
102
|
-
// Then apply highlights
|
|
103
|
-
updateTextWithHighlights()
|
|
104
|
-
|
|
105
|
-
// Force layout update
|
|
106
|
-
requestLayout()
|
|
107
|
-
invalidate()
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
fun setMenuOptions(menuOptions: List<String>) {
|
|
111
|
-
Log.d(TAG, "setMenuOptions received from RN: $menuOptions")
|
|
112
|
-
this.menuOptions = menuOptions
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
fun setHighlightedWords(highlightedWords: List<HighlightedWord>) {
|
|
116
|
-
Log.d(TAG, "setHighlightedWords received from RN: $highlightedWords")
|
|
117
|
-
this.highlightedWords = highlightedWords
|
|
118
|
-
updateTextWithHighlights()
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
fun setIndicatorWordIndex(index: Int) {
|
|
122
|
-
Log.d(TAG, "setIndicatorWordIndex received: $index")
|
|
123
|
-
this.indicatorWordIndex = index
|
|
124
|
-
updateTextWithHighlights()
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
private fun updateTextWithHighlights() {
|
|
128
|
-
val textValue = this.text?.toString() ?: ""
|
|
129
|
-
Log.d(TAG, "updateTextWithHighlights called")
|
|
130
|
-
Log.d(TAG, "Current text: $textValue")
|
|
131
|
-
Log.d(TAG, "Highlighted words: $highlightedWords")
|
|
132
|
-
Log.d(TAG, "Indicator index: $indicatorWordIndex")
|
|
133
|
-
|
|
134
|
-
if (textValue.isEmpty()) {
|
|
135
|
-
Log.d(TAG, "No text available, skipping")
|
|
136
|
-
return
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
val spannableString = SpannableString(textValue)
|
|
140
|
-
|
|
141
|
-
// Split words while preserving spaces for accurate indexing
|
|
142
|
-
val words = textValue.split("\\s+".toRegex()).filter { it.isNotEmpty() }
|
|
143
|
-
|
|
144
|
-
var currentIndex = 0
|
|
145
|
-
words.forEachIndexed { wordIndex, word ->
|
|
146
|
-
|
|
147
|
-
// Find the actual position of the word in the text
|
|
148
|
-
val wordStart = textValue.indexOf(word, currentIndex)
|
|
149
|
-
if (wordStart >= 0) {
|
|
150
|
-
val wordEnd = wordStart + word.length
|
|
151
|
-
|
|
152
|
-
Log.d(TAG, "Processing word '$word' at position $wordStart-$wordEnd, index $wordIndex")
|
|
153
|
-
|
|
154
|
-
// Apply clickable span FIRST (this is important)
|
|
155
|
-
spannableString.setSpan(
|
|
156
|
-
WordClickableSpan(wordIndex, word),
|
|
157
|
-
wordStart,
|
|
158
|
-
wordEnd,
|
|
159
|
-
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
|
160
|
-
)
|
|
161
|
-
|
|
162
|
-
// Then apply background color for highlighted words
|
|
163
|
-
highlightedWords.find { it.index == wordIndex }?.let { highlightedWord ->
|
|
164
|
-
val color = try {
|
|
165
|
-
Color.parseColor(highlightedWord.highlightColor)
|
|
166
|
-
} catch (e: IllegalArgumentException) {
|
|
167
|
-
Log.e(TAG, "Invalid color: ${highlightedWord.highlightColor}, using yellow")
|
|
168
|
-
Color.YELLOW
|
|
169
|
-
}
|
|
170
|
-
Log.d(TAG, "Applying highlight to word '$word' at index $wordIndex with color ${highlightedWord.highlightColor}")
|
|
171
|
-
|
|
172
|
-
spannableString.setSpan(
|
|
173
|
-
BackgroundColorSpan(color),
|
|
174
|
-
wordStart,
|
|
175
|
-
wordEnd,
|
|
176
|
-
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
|
177
|
-
)
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// Then apply indicator span
|
|
181
|
-
if (wordIndex == indicatorWordIndex) {
|
|
182
|
-
Log.d(TAG, "Applying indicator span to word '$word' at index $wordIndex")
|
|
183
|
-
|
|
184
|
-
spannableString.setSpan(
|
|
185
|
-
IndicatorSpan(),
|
|
186
|
-
wordStart,
|
|
187
|
-
wordEnd,
|
|
188
|
-
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
|
189
|
-
)
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
currentIndex = wordEnd
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// Set the spannable text
|
|
197
|
-
setText(spannableString, BufferType.SPANNABLE)
|
|
198
|
-
|
|
199
|
-
// Ensure movement method is still set
|
|
200
|
-
movementMethod = LinkMovementMethod.getInstance()
|
|
201
|
-
|
|
202
|
-
Log.d(TAG, "Text updated with spans, total spans")
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
private fun onMenuItemClick(item: MenuItem, selectedText: String): Boolean {
|
|
208
|
-
val menuItemText = menuOptions[item.itemId]
|
|
209
|
-
Log.d(TAG, "onMenuItemClick: menuOption='$menuItemText', selectedText='$selectedText'")
|
|
210
|
-
sendSelectionEvent(selectedText, menuItemText)
|
|
211
|
-
return true
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
private fun sendSelectionEvent(selectedText: String, eventType: String) {
|
|
215
|
-
Log.d(TAG, "sendSelectionEvent -> eventType='$eventType' selectedText='$selectedText'")
|
|
216
|
-
|
|
217
|
-
try {
|
|
218
|
-
val reactContext = context as? ReactContext ?: return
|
|
219
|
-
val event = Arguments.createMap().apply {
|
|
220
|
-
putString("selectedText", selectedText)
|
|
221
|
-
putString("event", eventType)
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
reactContext.getJSModule(RCTEventEmitter::class.java)
|
|
225
|
-
.receiveEvent(id, "onSelection", event)
|
|
226
|
-
} catch (e: Exception) {
|
|
227
|
-
Log.e(TAG, "Error sending selection event", e)
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
private inner class WordClickableSpan(
|
|
232
|
-
private val wordIndex: Int,
|
|
233
|
-
private val word: String
|
|
234
|
-
) : ClickableSpan() {
|
|
235
|
-
|
|
236
|
-
override fun onClick(widget: View) {
|
|
237
|
-
Log.d(TAG, "WordClickableSpan onClick triggered: '$word' (index=$wordIndex)")
|
|
238
|
-
|
|
239
|
-
// Small delay to ensure the click is processed
|
|
240
|
-
widget.post {
|
|
241
|
-
sendWordPressEvent(word, wordIndex)
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
override fun updateDrawState(ds: TextPaint) {
|
|
246
|
-
// Don't call super to avoid default link styling (blue color, underline)
|
|
247
|
-
// Keep the original text appearance
|
|
248
|
-
|
|
249
|
-
ds.isUnderlineText = false
|
|
250
|
-
ds.bgColor = Color.TRANSPARENT
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
private inner class IndicatorSpan : ClickableSpan() {
|
|
255
|
-
override fun onClick(widget: View) {
|
|
256
|
-
Log.d(TAG, "IndicatorSpan clicked (shouldn't trigger action)")
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
override fun updateDrawState(ds: TextPaint) {
|
|
260
|
-
|
|
261
|
-
ds.isFakeBoldText = true
|
|
262
|
-
ds.isUnderlineText = false
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
private fun sendWordPressEvent(word: String, index: Int) {
|
|
267
|
-
Log.d(TAG, "sendWordPressEvent -> word='$word', index=$index")
|
|
268
|
-
|
|
269
|
-
try {
|
|
270
|
-
val reactContext = context as? ReactContext ?: return
|
|
271
|
-
val event = Arguments.createMap().apply {
|
|
272
|
-
putString("word", word)
|
|
273
|
-
putInt("index", index)
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
reactContext.getJSModule(RCTEventEmitter::class.java)
|
|
277
|
-
.receiveEvent(id, "onWordPress", event)
|
|
278
|
-
} catch (e: Exception) {
|
|
279
|
-
Log.e(TAG, "Error sending word press event", e)
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
fun clearSelection() {
|
|
284
|
-
Log.d(TAG, "clearSelection called")
|
|
285
|
-
val spannable = this.text as? android.text.Spannable ?: return
|
|
286
|
-
Selection.removeSelection(spannable)
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
290
|
-
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
|
291
|
-
Log.d(TAG, "onMeasure: width=${measuredWidth}, height=${measuredHeight}")
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
|
295
|
-
super.onLayout(changed, left, top, right, bottom)
|
|
296
|
-
Log.d(TAG, "onLayout: changed=$changed, bounds=[$left,$top,$right,$bottom]")
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
data class HighlightedWord(
|
|
301
|
-
val index: Int,
|
|
302
|
-
val highlightColor: String
|
|
303
|
-
)
|
|
1
|
+
package com.advancedtext
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.graphics.Color
|
|
5
|
+
import android.text.SpannableString
|
|
6
|
+
import android.text.Spanned
|
|
7
|
+
import android.text.TextPaint
|
|
8
|
+
import android.text.method.LinkMovementMethod
|
|
9
|
+
import android.text.style.ClickableSpan
|
|
10
|
+
import android.text.style.BackgroundColorSpan
|
|
11
|
+
import android.text.style.ForegroundColorSpan
|
|
12
|
+
import android.util.AttributeSet
|
|
13
|
+
import android.util.Log
|
|
14
|
+
import android.view.ActionMode
|
|
15
|
+
import android.view.Menu
|
|
16
|
+
import android.view.MenuItem
|
|
17
|
+
import android.view.View
|
|
18
|
+
import android.widget.TextView
|
|
19
|
+
import com.facebook.react.bridge.Arguments
|
|
20
|
+
import com.facebook.react.bridge.ReactContext
|
|
21
|
+
import com.facebook.react.uimanager.events.RCTEventEmitter
|
|
22
|
+
import android.text.Selection
|
|
23
|
+
|
|
24
|
+
class AdvancedTextView : TextView {
|
|
25
|
+
|
|
26
|
+
private val TAG = "AdvancedTextView"
|
|
27
|
+
|
|
28
|
+
private var highlightedWords: List<HighlightedWord> = emptyList()
|
|
29
|
+
private var menuOptions: List<String> = emptyList()
|
|
30
|
+
private var indicatorWordIndex: Int = -1
|
|
31
|
+
private var lastSelectedText: String = ""
|
|
32
|
+
private var isSelectionEnabled: Boolean = true
|
|
33
|
+
private var customActionMode: ActionMode? = null
|
|
34
|
+
|
|
35
|
+
constructor(context: Context?) : super(context) { init() }
|
|
36
|
+
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) { init() }
|
|
37
|
+
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { init() }
|
|
38
|
+
|
|
39
|
+
private fun init() {
|
|
40
|
+
Log.d(TAG, "AdvancedTextView initialized")
|
|
41
|
+
|
|
42
|
+
// Set default text appearance - DON'T set black color here
|
|
43
|
+
textSize = 16f
|
|
44
|
+
setPadding(16, 16, 16, 16)
|
|
45
|
+
|
|
46
|
+
movementMethod = LinkMovementMethod.getInstance()
|
|
47
|
+
setTextIsSelectable(true)
|
|
48
|
+
|
|
49
|
+
customSelectionActionModeCallback = object : ActionMode.Callback {
|
|
50
|
+
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
|
51
|
+
Log.d(TAG, "onCreateActionMode triggered")
|
|
52
|
+
customActionMode = mode
|
|
53
|
+
return true
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
|
57
|
+
Log.d(TAG, "onPrepareActionMode triggered")
|
|
58
|
+
menu?.clear()
|
|
59
|
+
|
|
60
|
+
val selectionStart = selectionStart
|
|
61
|
+
val selectionEnd = selectionEnd
|
|
62
|
+
|
|
63
|
+
if (selectionStart >= 0 && selectionEnd >= 0 && selectionStart != selectionEnd) {
|
|
64
|
+
lastSelectedText = text.subSequence(selectionStart, selectionEnd).toString()
|
|
65
|
+
Log.d(TAG, "User selected text: '$lastSelectedText'")
|
|
66
|
+
Log.d(TAG, "Menu options available: $menuOptions")
|
|
67
|
+
|
|
68
|
+
menuOptions.forEachIndexed { index, option ->
|
|
69
|
+
menu?.add(0, index, index, option)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
sendSelectionEvent(lastSelectedText, "selection")
|
|
73
|
+
return true
|
|
74
|
+
}
|
|
75
|
+
return false
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean {
|
|
79
|
+
item?.let {
|
|
80
|
+
val menuItemText = it.title.toString()
|
|
81
|
+
Log.d(TAG, "Menu item clicked: $menuItemText")
|
|
82
|
+
sendSelectionEvent(lastSelectedText, menuItemText)
|
|
83
|
+
mode?.finish()
|
|
84
|
+
return true
|
|
85
|
+
}
|
|
86
|
+
return false
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
override fun onDestroyActionMode(mode: ActionMode?) {
|
|
90
|
+
Log.d(TAG, "onDestroyActionMode")
|
|
91
|
+
customActionMode = null
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
fun setAdvancedText(text: String) {
|
|
97
|
+
Log.d(TAG, "setAdvancedText: $text (length=${text.length})")
|
|
98
|
+
|
|
99
|
+
// Set the text first
|
|
100
|
+
super.setText(text, BufferType.SPANNABLE)
|
|
101
|
+
|
|
102
|
+
// Then apply highlights
|
|
103
|
+
updateTextWithHighlights()
|
|
104
|
+
|
|
105
|
+
// Force layout update
|
|
106
|
+
requestLayout()
|
|
107
|
+
invalidate()
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
fun setMenuOptions(menuOptions: List<String>) {
|
|
111
|
+
Log.d(TAG, "setMenuOptions received from RN: $menuOptions")
|
|
112
|
+
this.menuOptions = menuOptions
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
fun setHighlightedWords(highlightedWords: List<HighlightedWord>) {
|
|
116
|
+
Log.d(TAG, "setHighlightedWords received from RN: $highlightedWords")
|
|
117
|
+
this.highlightedWords = highlightedWords
|
|
118
|
+
updateTextWithHighlights()
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
fun setIndicatorWordIndex(index: Int) {
|
|
122
|
+
Log.d(TAG, "setIndicatorWordIndex received: $index")
|
|
123
|
+
this.indicatorWordIndex = index
|
|
124
|
+
updateTextWithHighlights()
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private fun updateTextWithHighlights() {
|
|
128
|
+
val textValue = this.text?.toString() ?: ""
|
|
129
|
+
Log.d(TAG, "updateTextWithHighlights called")
|
|
130
|
+
Log.d(TAG, "Current text: $textValue")
|
|
131
|
+
Log.d(TAG, "Highlighted words: $highlightedWords")
|
|
132
|
+
Log.d(TAG, "Indicator index: $indicatorWordIndex")
|
|
133
|
+
|
|
134
|
+
if (textValue.isEmpty()) {
|
|
135
|
+
Log.d(TAG, "No text available, skipping")
|
|
136
|
+
return
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
val spannableString = SpannableString(textValue)
|
|
140
|
+
|
|
141
|
+
// Split words while preserving spaces for accurate indexing
|
|
142
|
+
val words = textValue.split("\\s+".toRegex()).filter { it.isNotEmpty() }
|
|
143
|
+
|
|
144
|
+
var currentIndex = 0
|
|
145
|
+
words.forEachIndexed { wordIndex, word ->
|
|
146
|
+
|
|
147
|
+
// Find the actual position of the word in the text
|
|
148
|
+
val wordStart = textValue.indexOf(word, currentIndex)
|
|
149
|
+
if (wordStart >= 0) {
|
|
150
|
+
val wordEnd = wordStart + word.length
|
|
151
|
+
|
|
152
|
+
Log.d(TAG, "Processing word '$word' at position $wordStart-$wordEnd, index $wordIndex")
|
|
153
|
+
|
|
154
|
+
// Apply clickable span FIRST (this is important)
|
|
155
|
+
spannableString.setSpan(
|
|
156
|
+
WordClickableSpan(wordIndex, word),
|
|
157
|
+
wordStart,
|
|
158
|
+
wordEnd,
|
|
159
|
+
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
// Then apply background color for highlighted words
|
|
163
|
+
highlightedWords.find { it.index == wordIndex }?.let { highlightedWord ->
|
|
164
|
+
val color = try {
|
|
165
|
+
Color.parseColor(highlightedWord.highlightColor)
|
|
166
|
+
} catch (e: IllegalArgumentException) {
|
|
167
|
+
Log.e(TAG, "Invalid color: ${highlightedWord.highlightColor}, using yellow")
|
|
168
|
+
Color.YELLOW
|
|
169
|
+
}
|
|
170
|
+
Log.d(TAG, "Applying highlight to word '$word' at index $wordIndex with color ${highlightedWord.highlightColor}")
|
|
171
|
+
|
|
172
|
+
spannableString.setSpan(
|
|
173
|
+
BackgroundColorSpan(color),
|
|
174
|
+
wordStart,
|
|
175
|
+
wordEnd,
|
|
176
|
+
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Then apply indicator span
|
|
181
|
+
if (wordIndex == indicatorWordIndex) {
|
|
182
|
+
Log.d(TAG, "Applying indicator span to word '$word' at index $wordIndex")
|
|
183
|
+
|
|
184
|
+
spannableString.setSpan(
|
|
185
|
+
IndicatorSpan(),
|
|
186
|
+
wordStart,
|
|
187
|
+
wordEnd,
|
|
188
|
+
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
|
189
|
+
)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
currentIndex = wordEnd
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Set the spannable text
|
|
197
|
+
setText(spannableString, BufferType.SPANNABLE)
|
|
198
|
+
|
|
199
|
+
// Ensure movement method is still set
|
|
200
|
+
movementMethod = LinkMovementMethod.getInstance()
|
|
201
|
+
|
|
202
|
+
Log.d(TAG, "Text updated with spans, total spans")
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
private fun onMenuItemClick(item: MenuItem, selectedText: String): Boolean {
|
|
208
|
+
val menuItemText = menuOptions[item.itemId]
|
|
209
|
+
Log.d(TAG, "onMenuItemClick: menuOption='$menuItemText', selectedText='$selectedText'")
|
|
210
|
+
sendSelectionEvent(selectedText, menuItemText)
|
|
211
|
+
return true
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
private fun sendSelectionEvent(selectedText: String, eventType: String) {
|
|
215
|
+
Log.d(TAG, "sendSelectionEvent -> eventType='$eventType' selectedText='$selectedText'")
|
|
216
|
+
|
|
217
|
+
try {
|
|
218
|
+
val reactContext = context as? ReactContext ?: return
|
|
219
|
+
val event = Arguments.createMap().apply {
|
|
220
|
+
putString("selectedText", selectedText)
|
|
221
|
+
putString("event", eventType)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
reactContext.getJSModule(RCTEventEmitter::class.java)
|
|
225
|
+
.receiveEvent(id, "onSelection", event)
|
|
226
|
+
} catch (e: Exception) {
|
|
227
|
+
Log.e(TAG, "Error sending selection event", e)
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
private inner class WordClickableSpan(
|
|
232
|
+
private val wordIndex: Int,
|
|
233
|
+
private val word: String
|
|
234
|
+
) : ClickableSpan() {
|
|
235
|
+
|
|
236
|
+
override fun onClick(widget: View) {
|
|
237
|
+
Log.d(TAG, "WordClickableSpan onClick triggered: '$word' (index=$wordIndex)")
|
|
238
|
+
|
|
239
|
+
// Small delay to ensure the click is processed
|
|
240
|
+
widget.post {
|
|
241
|
+
sendWordPressEvent(word, wordIndex)
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
override fun updateDrawState(ds: TextPaint) {
|
|
246
|
+
// Don't call super to avoid default link styling (blue color, underline)
|
|
247
|
+
// Keep the original text appearance
|
|
248
|
+
|
|
249
|
+
ds.isUnderlineText = false
|
|
250
|
+
ds.bgColor = Color.TRANSPARENT
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
private inner class IndicatorSpan : ClickableSpan() {
|
|
255
|
+
override fun onClick(widget: View) {
|
|
256
|
+
Log.d(TAG, "IndicatorSpan clicked (shouldn't trigger action)")
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
override fun updateDrawState(ds: TextPaint) {
|
|
260
|
+
|
|
261
|
+
ds.isFakeBoldText = true
|
|
262
|
+
ds.isUnderlineText = false
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
private fun sendWordPressEvent(word: String, index: Int) {
|
|
267
|
+
Log.d(TAG, "sendWordPressEvent -> word='$word', index=$index")
|
|
268
|
+
|
|
269
|
+
try {
|
|
270
|
+
val reactContext = context as? ReactContext ?: return
|
|
271
|
+
val event = Arguments.createMap().apply {
|
|
272
|
+
putString("word", word)
|
|
273
|
+
putInt("index", index)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
reactContext.getJSModule(RCTEventEmitter::class.java)
|
|
277
|
+
.receiveEvent(id, "onWordPress", event)
|
|
278
|
+
} catch (e: Exception) {
|
|
279
|
+
Log.e(TAG, "Error sending word press event", e)
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
fun clearSelection() {
|
|
284
|
+
Log.d(TAG, "clearSelection called")
|
|
285
|
+
val spannable = this.text as? android.text.Spannable ?: return
|
|
286
|
+
Selection.removeSelection(spannable)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
290
|
+
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
|
291
|
+
Log.d(TAG, "onMeasure: width=${measuredWidth}, height=${measuredHeight}")
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
|
295
|
+
super.onLayout(changed, left, top, right, bottom)
|
|
296
|
+
Log.d(TAG, "onLayout: changed=$changed, bounds=[$left,$top,$right,$bottom]")
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
data class HighlightedWord(
|
|
301
|
+
val index: Int,
|
|
302
|
+
val highlightColor: String
|
|
303
|
+
)
|
|
@@ -1,95 +1,95 @@
|
|
|
1
|
-
// File: AdvancedTextViewManager.kt
|
|
2
|
-
// This should be the ONLY content in this file
|
|
3
|
-
package com.advancedtext
|
|
4
|
-
|
|
5
|
-
import android.view.ViewGroup
|
|
6
|
-
import com.facebook.react.bridge.ReadableArray
|
|
7
|
-
import com.facebook.react.module.annotations.ReactModule
|
|
8
|
-
import com.facebook.react.uimanager.SimpleViewManager
|
|
9
|
-
import com.facebook.react.uimanager.ThemedReactContext
|
|
10
|
-
import com.facebook.react.uimanager.ViewManagerDelegate
|
|
11
|
-
import com.facebook.react.uimanager.annotations.ReactProp
|
|
12
|
-
import com.facebook.react.viewmanagers.AdvancedTextViewManagerInterface
|
|
13
|
-
import com.facebook.react.viewmanagers.AdvancedTextViewManagerDelegate
|
|
14
|
-
|
|
15
|
-
@ReactModule(name = AdvancedTextViewManager.NAME)
|
|
16
|
-
class AdvancedTextViewManager : SimpleViewManager<AdvancedTextView>(),
|
|
17
|
-
AdvancedTextViewManagerInterface<AdvancedTextView> {
|
|
18
|
-
private val mDelegate: ViewManagerDelegate<AdvancedTextView>
|
|
19
|
-
|
|
20
|
-
init {
|
|
21
|
-
mDelegate = AdvancedTextViewManagerDelegate(this)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
override fun getDelegate(): ViewManagerDelegate<AdvancedTextView>? {
|
|
25
|
-
return mDelegate
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
override fun getName(): String {
|
|
29
|
-
return NAME
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
public override fun createViewInstance(context: ThemedReactContext): AdvancedTextView {
|
|
33
|
-
val view = AdvancedTextView(context)
|
|
34
|
-
// Set default layout params to ensure the view is visible
|
|
35
|
-
view.layoutParams = ViewGroup.LayoutParams(
|
|
36
|
-
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
37
|
-
ViewGroup.LayoutParams.WRAP_CONTENT
|
|
38
|
-
)
|
|
39
|
-
return view
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
@ReactProp(name = "text")
|
|
43
|
-
override fun setText(view: AdvancedTextView?, text: String?) {
|
|
44
|
-
view?.setAdvancedText(text ?: "")
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
@ReactProp(name = "highlightedWords")
|
|
48
|
-
override fun setHighlightedWords(view: AdvancedTextView?, highlightedWords: ReadableArray?) {
|
|
49
|
-
val words = mutableListOf<HighlightedWord>()
|
|
50
|
-
highlightedWords?.let {
|
|
51
|
-
for (i in 0 until it.size()) {
|
|
52
|
-
val map = it.getMap(i)
|
|
53
|
-
map?.let { wordMap ->
|
|
54
|
-
words.add(
|
|
55
|
-
HighlightedWord(
|
|
56
|
-
index = wordMap.getInt("index"),
|
|
57
|
-
highlightColor = wordMap.getString("highlightColor") ?: "#FFFF00"
|
|
58
|
-
)
|
|
59
|
-
)
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
view?.setHighlightedWords(words)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
@ReactProp(name = "menuOptions")
|
|
67
|
-
override fun setMenuOptions(view: AdvancedTextView?, menuOptions: ReadableArray?) {
|
|
68
|
-
val options = mutableListOf<String>()
|
|
69
|
-
menuOptions?.let {
|
|
70
|
-
for (i in 0 until it.size()) {
|
|
71
|
-
it.getString(i)?.let { option ->
|
|
72
|
-
options.add(option)
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
view?.setMenuOptions(options)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
@ReactProp(name = "indicatorWordIndex")
|
|
80
|
-
override fun setIndicatorWordIndex(view: AdvancedTextView?, index: Int) {
|
|
81
|
-
view?.setIndicatorWordIndex(index)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Add this method to register custom events
|
|
85
|
-
override fun getExportedCustomDirectEventTypeConstants(): Map<String, Any> {
|
|
86
|
-
return mapOf(
|
|
87
|
-
"onWordPress" to mapOf("registrationName" to "onWordPress"),
|
|
88
|
-
"onSelection" to mapOf("registrationName" to "onSelection")
|
|
89
|
-
)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
companion object {
|
|
93
|
-
const val NAME = "AdvancedTextView"
|
|
94
|
-
}
|
|
95
|
-
}
|
|
1
|
+
// File: AdvancedTextViewManager.kt
|
|
2
|
+
// This should be the ONLY content in this file
|
|
3
|
+
package com.advancedtext
|
|
4
|
+
|
|
5
|
+
import android.view.ViewGroup
|
|
6
|
+
import com.facebook.react.bridge.ReadableArray
|
|
7
|
+
import com.facebook.react.module.annotations.ReactModule
|
|
8
|
+
import com.facebook.react.uimanager.SimpleViewManager
|
|
9
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
10
|
+
import com.facebook.react.uimanager.ViewManagerDelegate
|
|
11
|
+
import com.facebook.react.uimanager.annotations.ReactProp
|
|
12
|
+
import com.facebook.react.viewmanagers.AdvancedTextViewManagerInterface
|
|
13
|
+
import com.facebook.react.viewmanagers.AdvancedTextViewManagerDelegate
|
|
14
|
+
|
|
15
|
+
@ReactModule(name = AdvancedTextViewManager.NAME)
|
|
16
|
+
class AdvancedTextViewManager : SimpleViewManager<AdvancedTextView>(),
|
|
17
|
+
AdvancedTextViewManagerInterface<AdvancedTextView> {
|
|
18
|
+
private val mDelegate: ViewManagerDelegate<AdvancedTextView>
|
|
19
|
+
|
|
20
|
+
init {
|
|
21
|
+
mDelegate = AdvancedTextViewManagerDelegate(this)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
override fun getDelegate(): ViewManagerDelegate<AdvancedTextView>? {
|
|
25
|
+
return mDelegate
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
override fun getName(): String {
|
|
29
|
+
return NAME
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public override fun createViewInstance(context: ThemedReactContext): AdvancedTextView {
|
|
33
|
+
val view = AdvancedTextView(context)
|
|
34
|
+
// Set default layout params to ensure the view is visible
|
|
35
|
+
view.layoutParams = ViewGroup.LayoutParams(
|
|
36
|
+
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
37
|
+
ViewGroup.LayoutParams.WRAP_CONTENT
|
|
38
|
+
)
|
|
39
|
+
return view
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@ReactProp(name = "text")
|
|
43
|
+
override fun setText(view: AdvancedTextView?, text: String?) {
|
|
44
|
+
view?.setAdvancedText(text ?: "")
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@ReactProp(name = "highlightedWords")
|
|
48
|
+
override fun setHighlightedWords(view: AdvancedTextView?, highlightedWords: ReadableArray?) {
|
|
49
|
+
val words = mutableListOf<HighlightedWord>()
|
|
50
|
+
highlightedWords?.let {
|
|
51
|
+
for (i in 0 until it.size()) {
|
|
52
|
+
val map = it.getMap(i)
|
|
53
|
+
map?.let { wordMap ->
|
|
54
|
+
words.add(
|
|
55
|
+
HighlightedWord(
|
|
56
|
+
index = wordMap.getInt("index"),
|
|
57
|
+
highlightColor = wordMap.getString("highlightColor") ?: "#FFFF00"
|
|
58
|
+
)
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
view?.setHighlightedWords(words)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@ReactProp(name = "menuOptions")
|
|
67
|
+
override fun setMenuOptions(view: AdvancedTextView?, menuOptions: ReadableArray?) {
|
|
68
|
+
val options = mutableListOf<String>()
|
|
69
|
+
menuOptions?.let {
|
|
70
|
+
for (i in 0 until it.size()) {
|
|
71
|
+
it.getString(i)?.let { option ->
|
|
72
|
+
options.add(option)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
view?.setMenuOptions(options)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@ReactProp(name = "indicatorWordIndex")
|
|
80
|
+
override fun setIndicatorWordIndex(view: AdvancedTextView?, index: Int) {
|
|
81
|
+
view?.setIndicatorWordIndex(index)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Add this method to register custom events
|
|
85
|
+
override fun getExportedCustomDirectEventTypeConstants(): Map<String, Any> {
|
|
86
|
+
return mapOf(
|
|
87
|
+
"onWordPress" to mapOf("registrationName" to "onWordPress"),
|
|
88
|
+
"onSelection" to mapOf("registrationName" to "onSelection")
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
companion object {
|
|
93
|
+
const val NAME = "AdvancedTextView"
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { codegenNativeComponent } from 'react-native';
|
|
2
1
|
import type { ViewProps } from 'react-native';
|
|
2
|
+
import { codegenNativeComponent } from 'react-native';
|
|
3
3
|
// @ts-ignore
|
|
4
4
|
// eslint-disable-next-line prettier/prettier
|
|
5
5
|
import type { DirectEventHandler, Int32 } from 'react-native/Libraries/Types/CodegenTypes';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdvancedTextViewNativeComponent.d.ts","sourceRoot":"","sources":["../../../src/AdvancedTextViewNativeComponent.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AdvancedTextViewNativeComponent.d.ts","sourceRoot":"","sources":["../../../src/AdvancedTextViewNativeComponent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAI9C,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,UAAU,WAAY,SAAQ,SAAS;IACrC,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,CAAA;KAAE,CAAC,CAAC;IACnD,WAAW,CAAC,EAAE,kBAAkB,CAAC;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9E,kBAAkB,CAAC,EAAE,KAAK,CAAC;CAC5B;;AAED,wBAAuE"}
|
package/package.json
CHANGED
|
@@ -1,175 +1,175 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "react-native-advanced-text",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": " Advanced text component for React Native with custom select options.",
|
|
5
|
-
"main": "./lib/module/index.js",
|
|
6
|
-
"types": "./lib/typescript/src/index.d.ts",
|
|
7
|
-
"exports": {
|
|
8
|
-
".": {
|
|
9
|
-
"source": "./src/index.tsx",
|
|
10
|
-
"types": "./lib/typescript/src/index.d.ts",
|
|
11
|
-
"default": "./lib/module/index.js"
|
|
12
|
-
},
|
|
13
|
-
"./package.json": "./package.json"
|
|
14
|
-
},
|
|
15
|
-
"files": [
|
|
16
|
-
"src",
|
|
17
|
-
"lib",
|
|
18
|
-
"android",
|
|
19
|
-
"ios",
|
|
20
|
-
"cpp",
|
|
21
|
-
"*.podspec",
|
|
22
|
-
"react-native.config.js",
|
|
23
|
-
"!ios/build",
|
|
24
|
-
"!android/build",
|
|
25
|
-
"!android/gradle",
|
|
26
|
-
"!android/gradlew",
|
|
27
|
-
"!android/gradlew.bat",
|
|
28
|
-
"!android/local.properties",
|
|
29
|
-
"!**/__tests__",
|
|
30
|
-
"!**/__fixtures__",
|
|
31
|
-
"!**/__mocks__",
|
|
32
|
-
"!**/.*"
|
|
33
|
-
],
|
|
34
|
-
"scripts": {
|
|
35
|
-
"example": "yarn workspace react-native-advanced-text-example",
|
|
36
|
-
"clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
|
|
37
|
-
"prepare": "bob build",
|
|
38
|
-
"typecheck": "tsc",
|
|
39
|
-
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
|
40
|
-
"release": "release-it --only-version",
|
|
41
|
-
"test": "jest"
|
|
42
|
-
},
|
|
43
|
-
"keywords": [
|
|
44
|
-
"react-native",
|
|
45
|
-
"ios",
|
|
46
|
-
"android"
|
|
47
|
-
],
|
|
48
|
-
"repository": {
|
|
49
|
-
"type": "git",
|
|
50
|
-
"url": "git+https://github.com/AminAllahham/react-native-advanced-text.git"
|
|
51
|
-
},
|
|
52
|
-
"author": "Amin Al-lahham <amin.allahham9@gmail.com> (https://github.com/AminAllahham)",
|
|
53
|
-
"license": "MIT",
|
|
54
|
-
"bugs": {
|
|
55
|
-
"url": "https://github.com/AminAllahham/react-native-advanced-text/issues"
|
|
56
|
-
},
|
|
57
|
-
"homepage": "https://github.com/AminAllahham/react-native-advanced-text#readme",
|
|
58
|
-
"publishConfig": {
|
|
59
|
-
"registry": "https://registry.npmjs.org/"
|
|
60
|
-
},
|
|
61
|
-
"devDependencies": {
|
|
62
|
-
"@commitlint/config-conventional": "^19.8.1",
|
|
63
|
-
"@eslint/compat": "^1.3.2",
|
|
64
|
-
"@eslint/eslintrc": "^3.3.1",
|
|
65
|
-
"@eslint/js": "^9.35.0",
|
|
66
|
-
"@react-native-community/cli": "20.0.1",
|
|
67
|
-
"@react-native/babel-preset": "0.81.1",
|
|
68
|
-
"@react-native/eslint-config": "^0.81.1",
|
|
69
|
-
"@release-it/conventional-changelog": "^10.0.1",
|
|
70
|
-
"@types/jest": "^29.5.14",
|
|
71
|
-
"@types/react": "^19.1.0",
|
|
72
|
-
"commitlint": "^19.8.1",
|
|
73
|
-
"del-cli": "^6.0.0",
|
|
74
|
-
"eslint": "^9.35.0",
|
|
75
|
-
"eslint-config-prettier": "^10.1.8",
|
|
76
|
-
"eslint-plugin-prettier": "^5.5.4",
|
|
77
|
-
"jest": "^29.7.0",
|
|
78
|
-
"lefthook": "^2.0.3",
|
|
79
|
-
"prettier": "^2.8.8",
|
|
80
|
-
"react": "19.1.0",
|
|
81
|
-
"react-native": "0.81.1",
|
|
82
|
-
"react-native-builder-bob": "^0.40.16",
|
|
83
|
-
"release-it": "^19.0.4",
|
|
84
|
-
"turbo": "^2.5.6",
|
|
85
|
-
"typescript": "^5.9.2"
|
|
86
|
-
},
|
|
87
|
-
"peerDependencies": {
|
|
88
|
-
"react": "*",
|
|
89
|
-
"react-native": "*"
|
|
90
|
-
},
|
|
91
|
-
"workspaces": [
|
|
92
|
-
"example"
|
|
93
|
-
],
|
|
94
|
-
"packageManager": "yarn@4.11.0",
|
|
95
|
-
"react-native-builder-bob": {
|
|
96
|
-
"source": "src",
|
|
97
|
-
"output": "lib",
|
|
98
|
-
"targets": [
|
|
99
|
-
[
|
|
100
|
-
"module",
|
|
101
|
-
{
|
|
102
|
-
"esm": true
|
|
103
|
-
}
|
|
104
|
-
],
|
|
105
|
-
[
|
|
106
|
-
"typescript",
|
|
107
|
-
{
|
|
108
|
-
"project": "tsconfig.build.json"
|
|
109
|
-
}
|
|
110
|
-
]
|
|
111
|
-
]
|
|
112
|
-
},
|
|
113
|
-
"codegenConfig": {
|
|
114
|
-
"name": "AdvancedTextViewSpec",
|
|
115
|
-
"type": "all",
|
|
116
|
-
"jsSrcsDir": "src",
|
|
117
|
-
"android": {
|
|
118
|
-
"javaPackageName": "com.advancedtext"
|
|
119
|
-
},
|
|
120
|
-
"ios": {
|
|
121
|
-
"componentProvider": {
|
|
122
|
-
"AdvancedTextView": "AdvancedTextView"
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
},
|
|
126
|
-
"prettier": {
|
|
127
|
-
"quoteProps": "consistent",
|
|
128
|
-
"singleQuote": true,
|
|
129
|
-
"tabWidth": 2,
|
|
130
|
-
"trailingComma": "es5",
|
|
131
|
-
"useTabs": false
|
|
132
|
-
},
|
|
133
|
-
"commitlint": {
|
|
134
|
-
"extends": [
|
|
135
|
-
"@commitlint/config-conventional"
|
|
136
|
-
]
|
|
137
|
-
},
|
|
138
|
-
"release-it": {
|
|
139
|
-
"git": {
|
|
140
|
-
"commitMessage": "chore: release ${version}",
|
|
141
|
-
"tagName": "v${version}"
|
|
142
|
-
},
|
|
143
|
-
"npm": {
|
|
144
|
-
"publish": true
|
|
145
|
-
},
|
|
146
|
-
"github": {
|
|
147
|
-
"release": true
|
|
148
|
-
},
|
|
149
|
-
"plugins": {
|
|
150
|
-
"@release-it/conventional-changelog": {
|
|
151
|
-
"preset": {
|
|
152
|
-
"name": "angular"
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
},
|
|
157
|
-
"jest": {
|
|
158
|
-
"preset": "react-native",
|
|
159
|
-
"modulePathIgnorePatterns": [
|
|
160
|
-
"<rootDir>/example/node_modules",
|
|
161
|
-
"<rootDir>/lib/"
|
|
162
|
-
]
|
|
163
|
-
},
|
|
164
|
-
"create-react-native-library": {
|
|
165
|
-
"languages": "kotlin-objc",
|
|
166
|
-
"type": "fabric-view",
|
|
167
|
-
"tools": [
|
|
168
|
-
"eslint",
|
|
169
|
-
"lefthook",
|
|
170
|
-
"release-it",
|
|
171
|
-
"jest"
|
|
172
|
-
],
|
|
173
|
-
"version": "0.55.1"
|
|
174
|
-
}
|
|
175
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "react-native-advanced-text",
|
|
3
|
+
"version": "0.1.17",
|
|
4
|
+
"description": " Advanced text component for React Native with custom select options.",
|
|
5
|
+
"main": "./lib/module/index.js",
|
|
6
|
+
"types": "./lib/typescript/src/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"source": "./src/index.tsx",
|
|
10
|
+
"types": "./lib/typescript/src/index.d.ts",
|
|
11
|
+
"default": "./lib/module/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./package.json": "./package.json"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"src",
|
|
17
|
+
"lib",
|
|
18
|
+
"android",
|
|
19
|
+
"ios",
|
|
20
|
+
"cpp",
|
|
21
|
+
"*.podspec",
|
|
22
|
+
"react-native.config.js",
|
|
23
|
+
"!ios/build",
|
|
24
|
+
"!android/build",
|
|
25
|
+
"!android/gradle",
|
|
26
|
+
"!android/gradlew",
|
|
27
|
+
"!android/gradlew.bat",
|
|
28
|
+
"!android/local.properties",
|
|
29
|
+
"!**/__tests__",
|
|
30
|
+
"!**/__fixtures__",
|
|
31
|
+
"!**/__mocks__",
|
|
32
|
+
"!**/.*"
|
|
33
|
+
],
|
|
34
|
+
"scripts": {
|
|
35
|
+
"example": "yarn workspace react-native-advanced-text-example",
|
|
36
|
+
"clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
|
|
37
|
+
"prepare": "bob build",
|
|
38
|
+
"typecheck": "tsc",
|
|
39
|
+
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
|
40
|
+
"release": "release-it --only-version",
|
|
41
|
+
"test": "jest"
|
|
42
|
+
},
|
|
43
|
+
"keywords": [
|
|
44
|
+
"react-native",
|
|
45
|
+
"ios",
|
|
46
|
+
"android"
|
|
47
|
+
],
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "git",
|
|
50
|
+
"url": "git+https://github.com/AminAllahham/react-native-advanced-text.git"
|
|
51
|
+
},
|
|
52
|
+
"author": "Amin Al-lahham <amin.allahham9@gmail.com> (https://github.com/AminAllahham)",
|
|
53
|
+
"license": "MIT",
|
|
54
|
+
"bugs": {
|
|
55
|
+
"url": "https://github.com/AminAllahham/react-native-advanced-text/issues"
|
|
56
|
+
},
|
|
57
|
+
"homepage": "https://github.com/AminAllahham/react-native-advanced-text#readme",
|
|
58
|
+
"publishConfig": {
|
|
59
|
+
"registry": "https://registry.npmjs.org/"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@commitlint/config-conventional": "^19.8.1",
|
|
63
|
+
"@eslint/compat": "^1.3.2",
|
|
64
|
+
"@eslint/eslintrc": "^3.3.1",
|
|
65
|
+
"@eslint/js": "^9.35.0",
|
|
66
|
+
"@react-native-community/cli": "20.0.1",
|
|
67
|
+
"@react-native/babel-preset": "0.81.1",
|
|
68
|
+
"@react-native/eslint-config": "^0.81.1",
|
|
69
|
+
"@release-it/conventional-changelog": "^10.0.1",
|
|
70
|
+
"@types/jest": "^29.5.14",
|
|
71
|
+
"@types/react": "^19.1.0",
|
|
72
|
+
"commitlint": "^19.8.1",
|
|
73
|
+
"del-cli": "^6.0.0",
|
|
74
|
+
"eslint": "^9.35.0",
|
|
75
|
+
"eslint-config-prettier": "^10.1.8",
|
|
76
|
+
"eslint-plugin-prettier": "^5.5.4",
|
|
77
|
+
"jest": "^29.7.0",
|
|
78
|
+
"lefthook": "^2.0.3",
|
|
79
|
+
"prettier": "^2.8.8",
|
|
80
|
+
"react": "19.1.0",
|
|
81
|
+
"react-native": "0.81.1",
|
|
82
|
+
"react-native-builder-bob": "^0.40.16",
|
|
83
|
+
"release-it": "^19.0.4",
|
|
84
|
+
"turbo": "^2.5.6",
|
|
85
|
+
"typescript": "^5.9.2"
|
|
86
|
+
},
|
|
87
|
+
"peerDependencies": {
|
|
88
|
+
"react": "*",
|
|
89
|
+
"react-native": "*"
|
|
90
|
+
},
|
|
91
|
+
"workspaces": [
|
|
92
|
+
"example"
|
|
93
|
+
],
|
|
94
|
+
"packageManager": "yarn@4.11.0",
|
|
95
|
+
"react-native-builder-bob": {
|
|
96
|
+
"source": "src",
|
|
97
|
+
"output": "lib",
|
|
98
|
+
"targets": [
|
|
99
|
+
[
|
|
100
|
+
"module",
|
|
101
|
+
{
|
|
102
|
+
"esm": true
|
|
103
|
+
}
|
|
104
|
+
],
|
|
105
|
+
[
|
|
106
|
+
"typescript",
|
|
107
|
+
{
|
|
108
|
+
"project": "tsconfig.build.json"
|
|
109
|
+
}
|
|
110
|
+
]
|
|
111
|
+
]
|
|
112
|
+
},
|
|
113
|
+
"codegenConfig": {
|
|
114
|
+
"name": "AdvancedTextViewSpec",
|
|
115
|
+
"type": "all",
|
|
116
|
+
"jsSrcsDir": "src",
|
|
117
|
+
"android": {
|
|
118
|
+
"javaPackageName": "com.advancedtext"
|
|
119
|
+
},
|
|
120
|
+
"ios": {
|
|
121
|
+
"componentProvider": {
|
|
122
|
+
"AdvancedTextView": "AdvancedTextView"
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
"prettier": {
|
|
127
|
+
"quoteProps": "consistent",
|
|
128
|
+
"singleQuote": true,
|
|
129
|
+
"tabWidth": 2,
|
|
130
|
+
"trailingComma": "es5",
|
|
131
|
+
"useTabs": false
|
|
132
|
+
},
|
|
133
|
+
"commitlint": {
|
|
134
|
+
"extends": [
|
|
135
|
+
"@commitlint/config-conventional"
|
|
136
|
+
]
|
|
137
|
+
},
|
|
138
|
+
"release-it": {
|
|
139
|
+
"git": {
|
|
140
|
+
"commitMessage": "chore: release ${version}",
|
|
141
|
+
"tagName": "v${version}"
|
|
142
|
+
},
|
|
143
|
+
"npm": {
|
|
144
|
+
"publish": true
|
|
145
|
+
},
|
|
146
|
+
"github": {
|
|
147
|
+
"release": true
|
|
148
|
+
},
|
|
149
|
+
"plugins": {
|
|
150
|
+
"@release-it/conventional-changelog": {
|
|
151
|
+
"preset": {
|
|
152
|
+
"name": "angular"
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
"jest": {
|
|
158
|
+
"preset": "react-native",
|
|
159
|
+
"modulePathIgnorePatterns": [
|
|
160
|
+
"<rootDir>/example/node_modules",
|
|
161
|
+
"<rootDir>/lib/"
|
|
162
|
+
]
|
|
163
|
+
},
|
|
164
|
+
"create-react-native-library": {
|
|
165
|
+
"languages": "kotlin-objc",
|
|
166
|
+
"type": "fabric-view",
|
|
167
|
+
"tools": [
|
|
168
|
+
"eslint",
|
|
169
|
+
"lefthook",
|
|
170
|
+
"release-it",
|
|
171
|
+
"jest"
|
|
172
|
+
],
|
|
173
|
+
"version": "0.55.1"
|
|
174
|
+
}
|
|
175
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { codegenNativeComponent } from 'react-native';
|
|
2
1
|
import type { ViewProps } from 'react-native';
|
|
2
|
+
import { codegenNativeComponent } from 'react-native';
|
|
3
3
|
// @ts-ignore
|
|
4
4
|
// eslint-disable-next-line prettier/prettier
|
|
5
5
|
import type { DirectEventHandler, Int32 } from 'react-native/Libraries/Types/CodegenTypes';
|