react-native-advanced-text 0.1.10 → 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
@@ -9,9 +8,11 @@ import android.text.TextPaint
9
8
  import android.text.method.LinkMovementMethod
10
9
  import android.text.style.ClickableSpan
11
10
  import android.text.style.BackgroundColorSpan
11
+ import android.text.style.ForegroundColorSpan
12
12
  import android.util.AttributeSet
13
13
  import android.util.Log
14
- import android.view.ContextMenu
14
+ import android.view.ActionMode
15
+ import android.view.Menu
15
16
  import android.view.MenuItem
16
17
  import android.view.View
17
18
  import android.widget.TextView
@@ -20,7 +21,7 @@ import com.facebook.react.bridge.ReactContext
20
21
  import com.facebook.react.uimanager.events.RCTEventEmitter
21
22
  import android.text.Selection
22
23
 
23
- class AdvancedTextView : TextView, View.OnCreateContextMenuListener {
24
+ class AdvancedTextView : TextView {
24
25
 
25
26
  private val TAG = "AdvancedTextView"
26
27
 
@@ -29,31 +30,78 @@ class AdvancedTextView : TextView, View.OnCreateContextMenuListener {
29
30
  private var indicatorWordIndex: Int = -1
30
31
  private var lastSelectedText: String = ""
31
32
  private var isSelectionEnabled: Boolean = true
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() }
35
37
  constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { init() }
36
38
 
37
- private fun init() {
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
- // Ensure minimum height for visibility during debugging
50
- minHeight = 100
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
+ }
51
94
  }
52
95
 
53
96
  fun setAdvancedText(text: String) {
54
97
  Log.d(TAG, "setAdvancedText: $text (length=${text.length})")
55
- this.text = text
98
+
99
+ // Set the text first
100
+ super.setText(text, BufferType.SPANNABLE)
101
+
102
+ // Then apply highlights
56
103
  updateTextWithHighlights()
104
+
57
105
  // Force layout update
58
106
  requestLayout()
59
107
  invalidate()
@@ -89,86 +137,72 @@ class AdvancedTextView : TextView, View.OnCreateContextMenuListener {
89
137
  }
90
138
 
91
139
  val spannableString = SpannableString(textValue)
92
- val words = textValue.split("\\s+".toRegex())
140
+
141
+ // Split words while preserving spaces for accurate indexing
142
+ val words = textValue.split("\\s+".toRegex()).filter { it.isNotEmpty() }
93
143
 
94
144
  var currentIndex = 0
95
145
  words.forEachIndexed { wordIndex, word ->
96
146
 
97
- if (word.isNotEmpty()) {
98
- val wordStart = textValue.indexOf(word, currentIndex)
99
- if (wordStart >= 0) {
100
- val wordEnd = wordStart + word.length
101
-
102
- highlightedWords.find { it.index == wordIndex }?.let { highlightedWord ->
103
- val color = try {
104
- Color.parseColor(highlightedWord.highlightColor)
105
- } catch (e: IllegalArgumentException) {
106
- Log.e(TAG, "Invalid color: ${highlightedWord.highlightColor}, using yellow")
107
- Color.YELLOW
108
- }
109
- Log.d(TAG, "Applying highlight to word '$word' at index $wordIndex with color ${highlightedWord.highlightColor}")
110
-
111
- spannableString.setSpan(
112
- BackgroundColorSpan(color),
113
- wordStart,
114
- wordEnd,
115
- Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
116
- )
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
117
169
  }
170
+ Log.d(TAG, "Applying highlight to word '$word' at index $wordIndex with color ${highlightedWord.highlightColor}")
118
171
 
119
- if (wordIndex == indicatorWordIndex) {
120
- Log.d(TAG, "Applying indicator span to word '$word' at index $wordIndex")
172
+ spannableString.setSpan(
173
+ BackgroundColorSpan(color),
174
+ wordStart,
175
+ wordEnd,
176
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
177
+ )
178
+ }
121
179
 
122
- spannableString.setSpan(
123
- IndicatorSpan(),
124
- wordStart,
125
- wordEnd,
126
- Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
127
- )
128
- }
180
+ // Then apply indicator span
181
+ if (wordIndex == indicatorWordIndex) {
182
+ Log.d(TAG, "Applying indicator span to word '$word' at index $wordIndex")
129
183
 
130
- // clickable span
131
184
  spannableString.setSpan(
132
- WordClickableSpan(wordIndex, word),
185
+ IndicatorSpan(),
133
186
  wordStart,
134
187
  wordEnd,
135
188
  Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
136
189
  )
137
-
138
- currentIndex = wordEnd
139
190
  }
191
+
192
+ currentIndex = wordEnd
140
193
  }
141
194
  }
142
195
 
196
+ // Set the spannable text
143
197
  setText(spannableString, BufferType.SPANNABLE)
144
- Log.d(TAG, "Text updated with spans")
145
- }
146
-
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
198
 
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")
199
+ // Ensure movement method is still set
200
+ movementMethod = LinkMovementMethod.getInstance()
158
201
 
159
- menu?.clear()
202
+ Log.d(TAG, "Text updated with spans, total spans")
203
+ }
160
204
 
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
205
 
169
- sendSelectionEvent(lastSelectedText, "selection")
170
- }
171
- }
172
206
 
173
207
  private fun onMenuItemClick(item: MenuItem, selectedText: String): Boolean {
174
208
  val menuItemText = menuOptions[item.itemId]
@@ -195,19 +229,25 @@ class AdvancedTextView : TextView, View.OnCreateContextMenuListener {
195
229
  }
196
230
 
197
231
  private inner class WordClickableSpan(
198
- private val wordIndex: Int,
199
- private val word: String
232
+ private val wordIndex: Int,
233
+ private val word: String
200
234
  ) : ClickableSpan() {
201
235
 
202
236
  override fun onClick(widget: View) {
203
- Log.d(TAG, "Word clicked: '$word' (index=$wordIndex)")
204
- sendWordPressEvent(word, wordIndex)
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
+ }
205
243
  }
206
244
 
207
245
  override fun updateDrawState(ds: TextPaint) {
208
- super.updateDrawState(ds)
246
+ // Don't call super to avoid default link styling (blue color, underline)
247
+ // Keep the original text appearance
209
248
  ds.color = currentTextColor
210
249
  ds.isUnderlineText = false
250
+ ds.bgColor = Color.TRANSPARENT
211
251
  }
212
252
  }
213
253
 
@@ -217,7 +257,7 @@ class AdvancedTextView : TextView, View.OnCreateContextMenuListener {
217
257
  }
218
258
 
219
259
  override fun updateDrawState(ds: TextPaint) {
220
- ds.color = Color.RED
260
+
221
261
  ds.isFakeBoldText = true
222
262
  ds.isUnderlineText = false
223
263
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-advanced-text",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "description": " Advanced text component for React Native with custom select options.",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",