react-native-advanced-text 0.1.8 → 0.1.9
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,9 +9,11 @@ import android.text.TextPaint
|
|
|
9
9
|
import android.text.method.LinkMovementMethod
|
|
10
10
|
import android.text.style.ClickableSpan
|
|
11
11
|
import android.text.style.BackgroundColorSpan
|
|
12
|
+
import android.text.style.ForegroundColorSpan
|
|
12
13
|
import android.util.AttributeSet
|
|
13
14
|
import android.util.Log
|
|
14
|
-
import android.view.
|
|
15
|
+
import android.view.ActionMode
|
|
16
|
+
import android.view.Menu
|
|
15
17
|
import android.view.MenuItem
|
|
16
18
|
import android.view.View
|
|
17
19
|
import android.widget.TextView
|
|
@@ -20,7 +22,7 @@ import com.facebook.react.bridge.ReactContext
|
|
|
20
22
|
import com.facebook.react.uimanager.events.RCTEventEmitter
|
|
21
23
|
import android.text.Selection
|
|
22
24
|
|
|
23
|
-
class AdvancedTextView : TextView
|
|
25
|
+
class AdvancedTextView : TextView {
|
|
24
26
|
|
|
25
27
|
private val TAG = "AdvancedTextView"
|
|
26
28
|
|
|
@@ -28,7 +30,7 @@ class AdvancedTextView : TextView, View.OnCreateContextMenuListener {
|
|
|
28
30
|
private var menuOptions: List<String> = emptyList()
|
|
29
31
|
private var indicatorWordIndex: Int = -1
|
|
30
32
|
private var lastSelectedText: String = ""
|
|
31
|
-
private var
|
|
33
|
+
private var customActionMode: ActionMode? = null
|
|
32
34
|
|
|
33
35
|
constructor(context: Context?) : super(context) { init() }
|
|
34
36
|
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) { init() }
|
|
@@ -37,24 +39,65 @@ class AdvancedTextView : TextView, View.OnCreateContextMenuListener {
|
|
|
37
39
|
private fun init() {
|
|
38
40
|
Log.d(TAG, "AdvancedTextView initialized")
|
|
39
41
|
|
|
40
|
-
// Set default text appearance
|
|
41
|
-
setTextColor(Color.BLACK)
|
|
42
|
+
// Set default text appearance - DON'T set black color here
|
|
42
43
|
textSize = 16f
|
|
43
44
|
setPadding(16, 16, 16, 16)
|
|
44
45
|
|
|
45
46
|
movementMethod = LinkMovementMethod.getInstance()
|
|
46
47
|
setTextIsSelectable(true)
|
|
47
|
-
setOnCreateContextMenuListener(this)
|
|
48
48
|
|
|
49
|
-
//
|
|
50
|
-
|
|
49
|
+
// Use custom action mode for selection menu
|
|
50
|
+
customSelectionActionModeCallback = object : ActionMode.Callback {
|
|
51
|
+
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
|
52
|
+
Log.d(TAG, "onCreateActionMode triggered")
|
|
53
|
+
customActionMode = mode
|
|
54
|
+
return true
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
|
58
|
+
Log.d(TAG, "onPrepareActionMode triggered")
|
|
59
|
+
menu?.clear()
|
|
60
|
+
|
|
61
|
+
val selectionStart = selectionStart
|
|
62
|
+
val selectionEnd = selectionEnd
|
|
63
|
+
|
|
64
|
+
if (selectionStart >= 0 && selectionEnd >= 0 && selectionStart != selectionEnd) {
|
|
65
|
+
lastSelectedText = text.subSequence(selectionStart, selectionEnd).toString()
|
|
66
|
+
Log.d(TAG, "User selected text: '$lastSelectedText'")
|
|
67
|
+
Log.d(TAG, "Menu options available: $menuOptions")
|
|
68
|
+
|
|
69
|
+
menuOptions.forEachIndexed { index, option ->
|
|
70
|
+
menu?.add(0, index, index, option)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
sendSelectionEvent(lastSelectedText, "selection")
|
|
74
|
+
return true
|
|
75
|
+
}
|
|
76
|
+
return false
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean {
|
|
80
|
+
item?.let {
|
|
81
|
+
val menuItemText = it.title.toString()
|
|
82
|
+
Log.d(TAG, "Menu item clicked: $menuItemText")
|
|
83
|
+
sendSelectionEvent(lastSelectedText, menuItemText)
|
|
84
|
+
mode?.finish()
|
|
85
|
+
return true
|
|
86
|
+
}
|
|
87
|
+
return false
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
override fun onDestroyActionMode(mode: ActionMode?) {
|
|
91
|
+
Log.d(TAG, "onDestroyActionMode")
|
|
92
|
+
customActionMode = null
|
|
93
|
+
}
|
|
94
|
+
}
|
|
51
95
|
}
|
|
52
96
|
|
|
53
97
|
fun setAdvancedText(text: String) {
|
|
54
98
|
Log.d(TAG, "setAdvancedText: $text (length=${text.length})")
|
|
55
99
|
this.text = text
|
|
56
100
|
updateTextWithHighlights()
|
|
57
|
-
// Force layout update
|
|
58
101
|
requestLayout()
|
|
59
102
|
invalidate()
|
|
60
103
|
}
|
|
@@ -118,16 +161,16 @@ class AdvancedTextView : TextView, View.OnCreateContextMenuListener {
|
|
|
118
161
|
|
|
119
162
|
if (wordIndex == indicatorWordIndex) {
|
|
120
163
|
Log.d(TAG, "Applying indicator span to word '$word' at index $wordIndex")
|
|
121
|
-
|
|
164
|
+
// Apply red color to the indicator word
|
|
122
165
|
spannableString.setSpan(
|
|
123
|
-
|
|
166
|
+
ForegroundColorSpan(Color.RED),
|
|
124
167
|
wordStart,
|
|
125
168
|
wordEnd,
|
|
126
169
|
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
|
127
170
|
)
|
|
128
171
|
}
|
|
129
172
|
|
|
130
|
-
// clickable
|
|
173
|
+
// Make words clickable
|
|
131
174
|
spannableString.setSpan(
|
|
132
175
|
WordClickableSpan(wordIndex, word),
|
|
133
176
|
wordStart,
|
|
@@ -144,39 +187,6 @@ class AdvancedTextView : TextView, View.OnCreateContextMenuListener {
|
|
|
144
187
|
Log.d(TAG, "Text updated with spans")
|
|
145
188
|
}
|
|
146
189
|
|
|
147
|
-
override fun onCreateContextMenu(menu: ContextMenu?, v: View?, menuInfo: ContextMenu.ContextMenuInfo?) {
|
|
148
|
-
val selectionStart = selectionStart
|
|
149
|
-
val selectionEnd = selectionEnd
|
|
150
|
-
|
|
151
|
-
Log.d(TAG, "onCreateContextMenu triggered. selectionStart=$selectionStart selectionEnd=$selectionEnd")
|
|
152
|
-
|
|
153
|
-
if (selectionStart >= 0 && selectionEnd >= 0 && selectionStart != selectionEnd) {
|
|
154
|
-
lastSelectedText = text.subSequence(selectionStart, selectionEnd).toString()
|
|
155
|
-
|
|
156
|
-
Log.d(TAG, "User selected text: '$lastSelectedText'")
|
|
157
|
-
Log.d(TAG, "Menu options available: $menuOptions")
|
|
158
|
-
|
|
159
|
-
menu?.clear()
|
|
160
|
-
|
|
161
|
-
menuOptions.forEachIndexed { index, option ->
|
|
162
|
-
menu?.add(0, index, index, option)?.setOnMenuItemClickListener {
|
|
163
|
-
Log.d(TAG, "Menu item clicked: $option")
|
|
164
|
-
onMenuItemClick(it, lastSelectedText)
|
|
165
|
-
true
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
sendSelectionEvent(lastSelectedText, "selection")
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
private fun onMenuItemClick(item: MenuItem, selectedText: String): Boolean {
|
|
174
|
-
val menuItemText = menuOptions[item.itemId]
|
|
175
|
-
Log.d(TAG, "onMenuItemClick: menuOption='$menuItemText', selectedText='$selectedText'")
|
|
176
|
-
sendSelectionEvent(selectedText, menuItemText)
|
|
177
|
-
return true
|
|
178
|
-
}
|
|
179
|
-
|
|
180
190
|
private fun sendSelectionEvent(selectedText: String, eventType: String) {
|
|
181
191
|
Log.d(TAG, "sendSelectionEvent -> eventType='$eventType' selectedText='$selectedText'")
|
|
182
192
|
|
|
@@ -201,24 +211,17 @@ class AdvancedTextView : TextView, View.OnCreateContextMenuListener {
|
|
|
201
211
|
|
|
202
212
|
override fun onClick(widget: View) {
|
|
203
213
|
Log.d(TAG, "Word clicked: '$word' (index=$wordIndex)")
|
|
214
|
+
// Clear any selection first to prevent interference
|
|
215
|
+
val spannable = widget as? TextView
|
|
216
|
+
spannable?.let {
|
|
217
|
+
Selection.removeSelection(it.text as? android.text.Spannable)
|
|
218
|
+
}
|
|
204
219
|
sendWordPressEvent(word, wordIndex)
|
|
205
220
|
}
|
|
206
221
|
|
|
207
222
|
override fun updateDrawState(ds: TextPaint) {
|
|
208
223
|
super.updateDrawState(ds)
|
|
209
|
-
|
|
210
|
-
ds.isUnderlineText = false
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
private inner class IndicatorSpan : ClickableSpan() {
|
|
215
|
-
override fun onClick(widget: View) {
|
|
216
|
-
Log.d(TAG, "IndicatorSpan clicked (shouldn't trigger action)")
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
override fun updateDrawState(ds: TextPaint) {
|
|
220
|
-
ds.color = Color.RED
|
|
221
|
-
ds.isFakeBoldText = true
|
|
224
|
+
// Don't change the color - let ForegroundColorSpan handle it
|
|
222
225
|
ds.isUnderlineText = false
|
|
223
226
|
}
|
|
224
227
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// File: AdvancedTextViewManager.kt
|
|
2
|
-
// This should be the ONLY content in this file
|
|
3
2
|
package com.advancedtext
|
|
4
3
|
|
|
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
|
|
34
|
+
// Set layout params for proper sizing
|
|
35
35
|
view.layoutParams = ViewGroup.LayoutParams(
|
|
36
36
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
37
|
-
ViewGroup.LayoutParams.WRAP_CONTENT
|
|
37
|
+
ViewGroup.LayoutParams.WRAP_CONTENT // Changed to WRAP_CONTENT for auto height
|
|
38
38
|
)
|
|
39
39
|
return view
|
|
40
40
|
}
|
|
@@ -81,7 +81,15 @@ class AdvancedTextViewManager : SimpleViewManager<AdvancedTextView>(),
|
|
|
81
81
|
view?.setIndicatorWordIndex(index)
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
//
|
|
84
|
+
// Handle color prop from React Native style
|
|
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
|
|
85
93
|
override fun getExportedCustomDirectEventTypeConstants(): Map<String, Any> {
|
|
86
94
|
return mapOf(
|
|
87
95
|
"onWordPress" to mapOf("registrationName" to "onWordPress"),
|
package/package.json
CHANGED