react-native-advanced-text 0.1.9 → 0.1.11
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.
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// File: AdvancedTextView.kt
|
|
2
1
|
package com.advancedtext
|
|
3
2
|
|
|
4
3
|
import android.content.Context
|
|
@@ -30,13 +29,14 @@ class AdvancedTextView : TextView {
|
|
|
30
29
|
private var menuOptions: List<String> = emptyList()
|
|
31
30
|
private var indicatorWordIndex: Int = -1
|
|
32
31
|
private var lastSelectedText: String = ""
|
|
32
|
+
private var isSelectionEnabled: Boolean = true
|
|
33
33
|
private var customActionMode: ActionMode? = null
|
|
34
34
|
|
|
35
35
|
constructor(context: Context?) : super(context) { init() }
|
|
36
36
|
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) { init() }
|
|
37
37
|
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { init() }
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
private fun init() {
|
|
40
40
|
Log.d(TAG, "AdvancedTextView initialized")
|
|
41
41
|
|
|
42
42
|
// Set default text appearance - DON'T set black color here
|
|
@@ -46,7 +46,6 @@ class AdvancedTextView : TextView {
|
|
|
46
46
|
movementMethod = LinkMovementMethod.getInstance()
|
|
47
47
|
setTextIsSelectable(true)
|
|
48
48
|
|
|
49
|
-
// Use custom action mode for selection menu
|
|
50
49
|
customSelectionActionModeCallback = object : ActionMode.Callback {
|
|
51
50
|
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
|
52
51
|
Log.d(TAG, "onCreateActionMode triggered")
|
|
@@ -96,8 +95,14 @@ class AdvancedTextView : TextView {
|
|
|
96
95
|
|
|
97
96
|
fun setAdvancedText(text: String) {
|
|
98
97
|
Log.d(TAG, "setAdvancedText: $text (length=${text.length})")
|
|
99
|
-
|
|
98
|
+
|
|
99
|
+
// Set the text first
|
|
100
|
+
super.setText(text, BufferType.SPANNABLE)
|
|
101
|
+
|
|
102
|
+
// Then apply highlights
|
|
100
103
|
updateTextWithHighlights()
|
|
104
|
+
|
|
105
|
+
// Force layout update
|
|
101
106
|
requestLayout()
|
|
102
107
|
invalidate()
|
|
103
108
|
}
|
|
@@ -132,59 +137,78 @@ class AdvancedTextView : TextView {
|
|
|
132
137
|
}
|
|
133
138
|
|
|
134
139
|
val spannableString = SpannableString(textValue)
|
|
135
|
-
|
|
140
|
+
|
|
141
|
+
// Split words while preserving spaces for accurate indexing
|
|
142
|
+
val words = textValue.split("\\s+".toRegex()).filter { it.isNotEmpty() }
|
|
136
143
|
|
|
137
144
|
var currentIndex = 0
|
|
138
145
|
words.forEachIndexed { wordIndex, word ->
|
|
139
146
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
|
160
169
|
}
|
|
170
|
+
Log.d(TAG, "Applying highlight to word '$word' at index $wordIndex with color ${highlightedWord.highlightColor}")
|
|
161
171
|
|
|
162
|
-
if (wordIndex == indicatorWordIndex) {
|
|
163
|
-
Log.d(TAG, "Applying indicator span to word '$word' at index $wordIndex")
|
|
164
|
-
// Apply red color to the indicator word
|
|
165
|
-
spannableString.setSpan(
|
|
166
|
-
ForegroundColorSpan(Color.RED),
|
|
167
|
-
wordStart,
|
|
168
|
-
wordEnd,
|
|
169
|
-
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
|
170
|
-
)
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Make words clickable
|
|
174
172
|
spannableString.setSpan(
|
|
175
|
-
|
|
173
|
+
BackgroundColorSpan(color),
|
|
176
174
|
wordStart,
|
|
177
175
|
wordEnd,
|
|
178
176
|
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
|
179
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")
|
|
180
183
|
|
|
181
|
-
|
|
184
|
+
spannableString.setSpan(
|
|
185
|
+
IndicatorSpan(),
|
|
186
|
+
wordStart,
|
|
187
|
+
wordEnd,
|
|
188
|
+
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
|
189
|
+
)
|
|
182
190
|
}
|
|
191
|
+
|
|
192
|
+
currentIndex = wordEnd
|
|
183
193
|
}
|
|
184
194
|
}
|
|
185
195
|
|
|
196
|
+
// Set the spannable text
|
|
186
197
|
setText(spannableString, BufferType.SPANNABLE)
|
|
187
|
-
|
|
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
|
|
188
212
|
}
|
|
189
213
|
|
|
190
214
|
private fun sendSelectionEvent(selectedText: String, eventType: String) {
|
|
@@ -205,23 +229,36 @@ class AdvancedTextView : TextView {
|
|
|
205
229
|
}
|
|
206
230
|
|
|
207
231
|
private inner class WordClickableSpan(
|
|
208
|
-
|
|
209
|
-
|
|
232
|
+
private val wordIndex: Int,
|
|
233
|
+
private val word: String
|
|
210
234
|
) : ClickableSpan() {
|
|
211
235
|
|
|
212
236
|
override fun onClick(widget: View) {
|
|
213
|
-
Log.d(TAG, "
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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)
|
|
218
242
|
}
|
|
219
|
-
sendWordPressEvent(word, wordIndex)
|
|
220
243
|
}
|
|
221
244
|
|
|
222
245
|
override fun updateDrawState(ds: TextPaint) {
|
|
223
|
-
super
|
|
224
|
-
//
|
|
246
|
+
// Don't call super to avoid default link styling (blue color, underline)
|
|
247
|
+
// Keep the original text appearance
|
|
248
|
+
ds.color = currentTextColor
|
|
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
|
|
225
262
|
ds.isUnderlineText = false
|
|
226
263
|
}
|
|
227
264
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// File: AdvancedTextViewManager.kt
|
|
2
|
+
// This should be the ONLY content in this file
|
|
2
3
|
package com.advancedtext
|
|
3
4
|
|
|
4
|
-
import android.graphics.Color
|
|
5
5
|
import android.view.ViewGroup
|
|
6
6
|
import com.facebook.react.bridge.ReadableArray
|
|
7
7
|
import com.facebook.react.module.annotations.ReactModule
|
|
@@ -31,10 +31,10 @@ class AdvancedTextViewManager : SimpleViewManager<AdvancedTextView>(),
|
|
|
31
31
|
|
|
32
32
|
public override fun createViewInstance(context: ThemedReactContext): AdvancedTextView {
|
|
33
33
|
val view = AdvancedTextView(context)
|
|
34
|
-
// Set layout params
|
|
34
|
+
// Set default layout params to ensure the view is visible
|
|
35
35
|
view.layoutParams = ViewGroup.LayoutParams(
|
|
36
36
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
37
|
-
ViewGroup.LayoutParams.WRAP_CONTENT
|
|
37
|
+
ViewGroup.LayoutParams.WRAP_CONTENT
|
|
38
38
|
)
|
|
39
39
|
return view
|
|
40
40
|
}
|
|
@@ -81,15 +81,7 @@ class AdvancedTextViewManager : SimpleViewManager<AdvancedTextView>(),
|
|
|
81
81
|
view?.setIndicatorWordIndex(index)
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
//
|
|
85
|
-
@ReactProp(name = "color", customType = "Color")
|
|
86
|
-
fun setColor(view: AdvancedTextView?, color: Int?) {
|
|
87
|
-
color?.let {
|
|
88
|
-
view?.setTextColor(it)
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Register custom events
|
|
84
|
+
// Add this method to register custom events
|
|
93
85
|
override fun getExportedCustomDirectEventTypeConstants(): Map<String, Any> {
|
|
94
86
|
return mapOf(
|
|
95
87
|
"onWordPress" to mapOf("registrationName" to "onWordPress"),
|
package/package.json
CHANGED