react-native-advanced-text 0.1.1 → 0.1.4
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.
|
@@ -9,6 +9,7 @@ import android.text.method.LinkMovementMethod
|
|
|
9
9
|
import android.text.style.ClickableSpan
|
|
10
10
|
import android.text.style.BackgroundColorSpan
|
|
11
11
|
import android.util.AttributeSet
|
|
12
|
+
import android.util.Log
|
|
12
13
|
import android.view.ContextMenu
|
|
13
14
|
import android.view.MenuItem
|
|
14
15
|
import android.view.View
|
|
@@ -19,77 +20,76 @@ import com.facebook.react.uimanager.events.RCTEventEmitter
|
|
|
19
20
|
import android.text.Selection
|
|
20
21
|
|
|
21
22
|
class AdvancedTextView : TextView, View.OnCreateContextMenuListener {
|
|
23
|
+
|
|
24
|
+
private val TAG = "AdvancedTextView"
|
|
25
|
+
|
|
22
26
|
private var highlightedWords: List<HighlightedWord> = emptyList()
|
|
23
27
|
private var menuOptions: List<String> = emptyList()
|
|
24
28
|
private var indicatorWordIndex: Int = -1
|
|
25
29
|
private var lastSelectedText: String = ""
|
|
26
30
|
private var isSelectionEnabled: Boolean = true
|
|
27
31
|
|
|
28
|
-
constructor(context: Context?) : super(context) {
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
|
|
33
|
-
init()
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
|
|
37
|
-
context,
|
|
38
|
-
attrs,
|
|
39
|
-
defStyleAttr
|
|
40
|
-
) {
|
|
41
|
-
init()
|
|
42
|
-
}
|
|
32
|
+
constructor(context: Context?) : super(context) { init() }
|
|
33
|
+
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) { init() }
|
|
34
|
+
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { init() }
|
|
43
35
|
|
|
44
36
|
private fun init() {
|
|
37
|
+
Log.d(TAG, "AdvancedTextView initialized")
|
|
45
38
|
movementMethod = LinkMovementMethod.getInstance()
|
|
46
39
|
setTextIsSelectable(true)
|
|
47
40
|
setOnCreateContextMenuListener(this)
|
|
48
|
-
|
|
49
|
-
setOnLongClickListener {
|
|
50
|
-
if (isSelectionEnabled) {
|
|
51
|
-
false
|
|
52
|
-
} else {
|
|
53
|
-
true
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
41
|
}
|
|
57
42
|
|
|
58
|
-
fun
|
|
59
|
-
|
|
43
|
+
fun setAdvancedText(text: String) {
|
|
44
|
+
Log.d(TAG, "setAdvancedText: $text")
|
|
45
|
+
this.text = text
|
|
60
46
|
updateTextWithHighlights()
|
|
61
47
|
}
|
|
62
48
|
|
|
63
49
|
fun setMenuOptions(menuOptions: List<String>) {
|
|
50
|
+
Log.d(TAG, "setMenuOptions received from RN: $menuOptions")
|
|
64
51
|
this.menuOptions = menuOptions
|
|
65
52
|
}
|
|
66
53
|
|
|
67
|
-
fun
|
|
68
|
-
|
|
54
|
+
fun setHighlightedWords(highlightedWords: List<HighlightedWord>) {
|
|
55
|
+
Log.d(TAG, "setHighlightedWords received from RN: $highlightedWords")
|
|
56
|
+
this.highlightedWords = highlightedWords
|
|
69
57
|
updateTextWithHighlights()
|
|
70
58
|
}
|
|
71
59
|
|
|
72
|
-
fun
|
|
73
|
-
|
|
60
|
+
fun setIndicatorWordIndex(index: Int) {
|
|
61
|
+
Log.d(TAG, "setIndicatorWordIndex received: $index")
|
|
62
|
+
this.indicatorWordIndex = index
|
|
74
63
|
updateTextWithHighlights()
|
|
75
64
|
}
|
|
76
65
|
|
|
77
66
|
private fun updateTextWithHighlights() {
|
|
78
|
-
val
|
|
79
|
-
|
|
67
|
+
val textValue = this.text.toString()
|
|
68
|
+
Log.d(TAG, "updateTextWithHighlights called")
|
|
69
|
+
Log.d(TAG, "Current text: $textValue")
|
|
70
|
+
Log.d(TAG, "Highlighted words: $highlightedWords")
|
|
71
|
+
Log.d(TAG, "Indicator index: $indicatorWordIndex")
|
|
72
|
+
|
|
73
|
+
if (textValue.isEmpty()) {
|
|
74
|
+
Log.d(TAG, "No text available, skipping")
|
|
75
|
+
return
|
|
76
|
+
}
|
|
80
77
|
|
|
81
|
-
val spannableString = SpannableString(
|
|
82
|
-
val words =
|
|
78
|
+
val spannableString = SpannableString(textValue)
|
|
79
|
+
val words = textValue.split("\\s+".toRegex())
|
|
83
80
|
|
|
84
81
|
var currentIndex = 0
|
|
85
82
|
words.forEachIndexed { wordIndex, word ->
|
|
83
|
+
|
|
86
84
|
if (word.isNotEmpty()) {
|
|
87
|
-
val wordStart =
|
|
85
|
+
val wordStart = textValue.indexOf(word, currentIndex)
|
|
88
86
|
if (wordStart >= 0) {
|
|
89
87
|
val wordEnd = wordStart + word.length
|
|
90
88
|
|
|
91
89
|
highlightedWords.find { it.index == wordIndex }?.let { highlightedWord ->
|
|
92
90
|
val color = Color.parseColor(highlightedWord.highlightColor)
|
|
91
|
+
Log.d(TAG, "Applying highlight to word '$word' at index $wordIndex with color ${highlightedWord.highlightColor}")
|
|
92
|
+
|
|
93
93
|
spannableString.setSpan(
|
|
94
94
|
BackgroundColorSpan(color),
|
|
95
95
|
wordStart,
|
|
@@ -99,6 +99,8 @@ class AdvancedTextView : TextView, View.OnCreateContextMenuListener {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
if (wordIndex == indicatorWordIndex) {
|
|
102
|
+
Log.d(TAG, "Applying indicator span to word '$word' at index $wordIndex")
|
|
103
|
+
|
|
102
104
|
spannableString.setSpan(
|
|
103
105
|
IndicatorSpan(),
|
|
104
106
|
wordStart,
|
|
@@ -107,6 +109,7 @@ class AdvancedTextView : TextView, View.OnCreateContextMenuListener {
|
|
|
107
109
|
)
|
|
108
110
|
}
|
|
109
111
|
|
|
112
|
+
// clickable span
|
|
110
113
|
spannableString.setSpan(
|
|
111
114
|
WordClickableSpan(wordIndex, word),
|
|
112
115
|
wordStart,
|
|
@@ -126,13 +129,19 @@ class AdvancedTextView : TextView, View.OnCreateContextMenuListener {
|
|
|
126
129
|
val selectionStart = selectionStart
|
|
127
130
|
val selectionEnd = selectionEnd
|
|
128
131
|
|
|
132
|
+
Log.d(TAG, "onCreateContextMenu triggered. selectionStart=$selectionStart selectionEnd=$selectionEnd")
|
|
133
|
+
|
|
129
134
|
if (selectionStart >= 0 && selectionEnd >= 0 && selectionStart != selectionEnd) {
|
|
130
135
|
lastSelectedText = text.subSequence(selectionStart, selectionEnd).toString()
|
|
131
136
|
|
|
137
|
+
Log.d(TAG, "User selected text: '$lastSelectedText'")
|
|
138
|
+
Log.d(TAG, "Menu options available: $menuOptions")
|
|
139
|
+
|
|
132
140
|
menu?.clear()
|
|
133
141
|
|
|
134
142
|
menuOptions.forEachIndexed { index, option ->
|
|
135
143
|
menu?.add(0, index, index, option)?.setOnMenuItemClickListener {
|
|
144
|
+
Log.d(TAG, "Menu item clicked: $option")
|
|
136
145
|
onMenuItemClick(it, lastSelectedText)
|
|
137
146
|
true
|
|
138
147
|
}
|
|
@@ -144,16 +153,20 @@ class AdvancedTextView : TextView, View.OnCreateContextMenuListener {
|
|
|
144
153
|
|
|
145
154
|
private fun onMenuItemClick(item: MenuItem, selectedText: String): Boolean {
|
|
146
155
|
val menuItemText = menuOptions[item.itemId]
|
|
156
|
+
Log.d(TAG, "onMenuItemClick: menuOption='$menuItemText', selectedText='$selectedText'")
|
|
147
157
|
sendSelectionEvent(selectedText, menuItemText)
|
|
148
158
|
return true
|
|
149
159
|
}
|
|
150
160
|
|
|
151
161
|
private fun sendSelectionEvent(selectedText: String, eventType: String) {
|
|
162
|
+
Log.d(TAG, "sendSelectionEvent -> eventType='$eventType' selectedText='$selectedText'")
|
|
163
|
+
|
|
152
164
|
val reactContext = context as ReactContext
|
|
153
165
|
val event = Arguments.createMap().apply {
|
|
154
166
|
putString("selectedText", selectedText)
|
|
155
167
|
putString("event", eventType)
|
|
156
168
|
}
|
|
169
|
+
|
|
157
170
|
reactContext.getJSModule(RCTEventEmitter::class.java)
|
|
158
171
|
.receiveEvent(id, "onSelection", event)
|
|
159
172
|
}
|
|
@@ -162,23 +175,25 @@ class AdvancedTextView : TextView, View.OnCreateContextMenuListener {
|
|
|
162
175
|
private val wordIndex: Int,
|
|
163
176
|
private val word: String
|
|
164
177
|
) : ClickableSpan() {
|
|
178
|
+
|
|
165
179
|
override fun onClick(widget: View) {
|
|
180
|
+
Log.d(TAG, "Word clicked: '$word' (index=$wordIndex)")
|
|
166
181
|
sendWordPressEvent(word, wordIndex)
|
|
167
182
|
}
|
|
168
183
|
|
|
169
184
|
override fun updateDrawState(ds: TextPaint) {
|
|
170
185
|
super.updateDrawState(ds)
|
|
186
|
+
ds.color = currentTextColor
|
|
171
187
|
ds.isUnderlineText = false
|
|
172
188
|
}
|
|
173
189
|
}
|
|
174
190
|
|
|
175
191
|
private inner class IndicatorSpan : ClickableSpan() {
|
|
176
192
|
override fun onClick(widget: View) {
|
|
177
|
-
|
|
193
|
+
Log.d(TAG, "IndicatorSpan clicked (shouldn't trigger action)")
|
|
178
194
|
}
|
|
179
195
|
|
|
180
196
|
override fun updateDrawState(ds: TextPaint) {
|
|
181
|
-
super.updateDrawState(ds)
|
|
182
197
|
ds.color = Color.RED
|
|
183
198
|
ds.isFakeBoldText = true
|
|
184
199
|
ds.isUnderlineText = false
|
|
@@ -186,16 +201,20 @@ class AdvancedTextView : TextView, View.OnCreateContextMenuListener {
|
|
|
186
201
|
}
|
|
187
202
|
|
|
188
203
|
private fun sendWordPressEvent(word: String, index: Int) {
|
|
204
|
+
Log.d(TAG, "sendWordPressEvent -> word='$word', index=$index")
|
|
205
|
+
|
|
189
206
|
val reactContext = context as ReactContext
|
|
190
207
|
val event = Arguments.createMap().apply {
|
|
191
208
|
putString("word", word)
|
|
192
209
|
putInt("index", index)
|
|
193
210
|
}
|
|
211
|
+
|
|
194
212
|
reactContext.getJSModule(RCTEventEmitter::class.java)
|
|
195
213
|
.receiveEvent(id, "onWordPress", event)
|
|
196
214
|
}
|
|
197
215
|
|
|
198
216
|
fun clearSelection() {
|
|
217
|
+
Log.d(TAG, "clearSelection called")
|
|
199
218
|
val spannable = this.text as? android.text.Spannable ?: return
|
|
200
219
|
Selection.removeSelection(spannable)
|
|
201
220
|
}
|
package/package.json
CHANGED