react-native-advanced-text 0.1.1 → 0.1.3
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,23 @@ 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
|
-
super.updateDrawState(ds)
|
|
171
185
|
ds.isUnderlineText = false
|
|
172
186
|
}
|
|
173
187
|
}
|
|
174
188
|
|
|
175
189
|
private inner class IndicatorSpan : ClickableSpan() {
|
|
176
190
|
override fun onClick(widget: View) {
|
|
177
|
-
|
|
191
|
+
Log.d(TAG, "IndicatorSpan clicked (shouldn't trigger action)")
|
|
178
192
|
}
|
|
179
193
|
|
|
180
194
|
override fun updateDrawState(ds: TextPaint) {
|
|
181
|
-
super.updateDrawState(ds)
|
|
182
195
|
ds.color = Color.RED
|
|
183
196
|
ds.isFakeBoldText = true
|
|
184
197
|
ds.isUnderlineText = false
|
|
@@ -186,16 +199,20 @@ class AdvancedTextView : TextView, View.OnCreateContextMenuListener {
|
|
|
186
199
|
}
|
|
187
200
|
|
|
188
201
|
private fun sendWordPressEvent(word: String, index: Int) {
|
|
202
|
+
Log.d(TAG, "sendWordPressEvent -> word='$word', index=$index")
|
|
203
|
+
|
|
189
204
|
val reactContext = context as ReactContext
|
|
190
205
|
val event = Arguments.createMap().apply {
|
|
191
206
|
putString("word", word)
|
|
192
207
|
putInt("index", index)
|
|
193
208
|
}
|
|
209
|
+
|
|
194
210
|
reactContext.getJSModule(RCTEventEmitter::class.java)
|
|
195
211
|
.receiveEvent(id, "onWordPress", event)
|
|
196
212
|
}
|
|
197
213
|
|
|
198
214
|
fun clearSelection() {
|
|
215
|
+
Log.d(TAG, "clearSelection called")
|
|
199
216
|
val spannable = this.text as? android.text.Spannable ?: return
|
|
200
217
|
Selection.removeSelection(spannable)
|
|
201
218
|
}
|
package/package.json
CHANGED