react-native-advanced-text 0.1.28 → 0.1.29

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.
package/README.md CHANGED
@@ -1,37 +1,56 @@
1
- # react-native-advanced-text
2
-
3
- Advanced text component for React Native with custom select options.
4
-
5
- ## Installation
6
-
7
-
8
- ```sh
9
- npm install react-native-advanced-text
10
- ```
11
-
12
-
13
- ## Usage
14
-
15
-
16
- ```js
17
- import { AdvancedTextView } from "react-native-advanced-text";
18
-
19
- // ...
20
-
21
- <AdvancedTextView color="tomato" />
22
- ```
23
-
24
-
25
- ## Contributing
26
-
27
- - [Development workflow](CONTRIBUTING.md#development-workflow)
28
- - [Sending a pull request](CONTRIBUTING.md#sending-a-pull-request)
29
- - [Code of conduct](CODE_OF_CONDUCT.md)
30
-
31
- ## License
32
-
33
- MIT
34
-
35
- ---
36
-
37
- Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
1
+ # react-native-advanced-text
2
+
3
+ react-native-advanced-text is a powerful cross-platform text component for React Native that enables word-level interaction, dynamic highlighting, and custom selection actions.
4
+
5
+ ## Installation
6
+
7
+
8
+ ```sh
9
+ npm install react-native-advanced-text
10
+ ```
11
+
12
+
13
+ ## Usage
14
+
15
+
16
+ ```js
17
+ import { AdvancedTextView } from "react-native-advanced-text";
18
+
19
+ <AdvancedText
20
+ text={'This is an example of AdvancedText component. Tap on any word to see the event in action.'}
21
+ style={[styles.AdvancedText, { minHeight }]}
22
+ indicatorWordIndex={2}
23
+ onWordPress={(event) => {
24
+ console.log({event})
25
+ }}
26
+ menuOptions={['Highlight', 'Copy', 'Translate']}
27
+ onSelection={(event) => {
28
+ console.log({event})
29
+ }}
30
+ highlightedWords={[
31
+ {
32
+ index: 4,
33
+ highlightColor: '#6baeffb5',
34
+ },
35
+ ]}
36
+ fontSize={24}
37
+ color={'#FFFFFF'}
38
+ fontWeight="normal"
39
+ fontFamily={'monospace'}
40
+ />
41
+ ```
42
+
43
+
44
+ ## Contributing
45
+
46
+ - [Development workflow](CONTRIBUTING.md#development-workflow)
47
+ - [Sending a pull request](CONTRIBUTING.md#sending-a-pull-request)
48
+ - [Code of conduct](CODE_OF_CONDUCT.md)
49
+
50
+ ## License
51
+
52
+ MIT
53
+
54
+ ---
55
+
56
+ Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
@@ -2,10 +2,12 @@ package com.advancedtext
2
2
 
3
3
  import android.content.Context
4
4
  import android.graphics.Color
5
+ import android.graphics.Point
5
6
  import android.text.SpannableString
7
+ import android.text.Spannable
6
8
  import android.text.Spanned
7
9
  import android.text.TextPaint
8
- import android.text.method.LinkMovementMethod
10
+ import android.text.method.ArrowKeyMovementMethod
9
11
  import android.text.style.ClickableSpan
10
12
  import android.text.style.BackgroundColorSpan
11
13
  import android.text.style.ForegroundColorSpan
@@ -15,12 +17,14 @@ import android.view.ActionMode
15
17
  import android.view.Menu
16
18
  import android.view.MenuItem
17
19
  import android.view.View
20
+ import android.view.MotionEvent
18
21
  import android.widget.TextView
19
22
  import com.facebook.react.bridge.Arguments
20
23
  import com.facebook.react.bridge.ReactContext
21
24
  import com.facebook.react.uimanager.events.RCTEventEmitter
22
25
  import android.text.Selection
23
26
  import android.graphics.Typeface
27
+ import androidx.core.text.getSpans
24
28
 
25
29
  class AdvancedTextView : TextView {
26
30
 
@@ -49,9 +53,9 @@ class AdvancedTextView : TextView {
49
53
 
50
54
  textSize = 16f
51
55
  setPadding(16, 16, 16, 16)
52
- movementMethod = LinkMovementMethod.getInstance()
53
56
  setTextIsSelectable(true)
54
57
 
58
+ movementMethod = SmartMovementMethod
55
59
 
56
60
  customSelectionActionModeCallback = object : ActionMode.Callback {
57
61
  override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
@@ -94,8 +98,6 @@ class AdvancedTextView : TextView {
94
98
  }
95
99
  }
96
100
 
97
-
98
-
99
101
  fun setAdvancedText(text: String) {
100
102
  if (currentText == text) {
101
103
  Log.d(TAG, "Text unchanged, skipping update")
@@ -113,7 +115,6 @@ class AdvancedTextView : TextView {
113
115
  updateTextWithHighlights()
114
116
  }
115
117
 
116
-
117
118
  fun setAdvancedTextSize(size: Float) {
118
119
  if (fontSize == size) return
119
120
  fontSize = size
@@ -210,6 +211,7 @@ class AdvancedTextView : TextView {
210
211
  )
211
212
  }
212
213
 
214
+ // Add clickable span for word clicks
213
215
  spannableString.setSpan(
214
216
  WordClickableSpan(wordPos.index, wordPos.word),
215
217
  wordPos.start,
@@ -264,16 +266,13 @@ class AdvancedTextView : TextView {
264
266
  }
265
267
 
266
268
  private inner class WordClickableSpan(
267
- private val wordIndex: Int,
268
- private val word: String
269
+ private val wordIndex: Int,
270
+ private val word: String
269
271
  ) : ClickableSpan() {
270
272
 
271
273
  override fun onClick(widget: View) {
272
- Log.d(TAG, "WordClickableSpan onClick triggered: '$word' (index=$wordIndex)")
273
-
274
- widget.post {
275
- sendWordPressEvent(word, wordIndex)
276
- }
274
+ Log.d(TAG, "Word clicked: '$word' (index=$wordIndex)")
275
+ sendWordPressEvent(word, wordIndex)
277
276
  }
278
277
 
279
278
  override fun updateDrawState(ds: TextPaint) {
@@ -304,6 +303,56 @@ class AdvancedTextView : TextView {
304
303
  }
305
304
  }
306
305
 
306
+
307
+ private object SmartMovementMethod : ArrowKeyMovementMethod() {
308
+
309
+ override fun onTouchEvent(widget: TextView?, buffer: Spannable?, event: MotionEvent?): Boolean {
310
+ if (event != null && widget != null && buffer != null) {
311
+ if (handleMotion(event, widget, buffer)) {
312
+ return true
313
+ }
314
+ }
315
+ return super.onTouchEvent(widget, buffer, event)
316
+ }
317
+
318
+ private fun handleMotion(event: MotionEvent, widget: TextView, buffer: Spannable): Boolean {
319
+ var handled = false
320
+
321
+ if (event.action == MotionEvent.ACTION_DOWN || event.action == MotionEvent.ACTION_UP) {
322
+ val target = Point().apply {
323
+ x = event.x.toInt() - widget.totalPaddingLeft + widget.scrollX
324
+ y = event.y.toInt() - widget.totalPaddingTop + widget.scrollY
325
+ }
326
+
327
+ val line = widget.layout.getLineForVertical(target.y)
328
+ val offset = widget.layout.getOffsetForHorizontal(line, target.x.toFloat())
329
+
330
+ if (event.action == MotionEvent.ACTION_DOWN) {
331
+ handled = handled || buffer.execute<ClickableSpan>(offset) {
332
+ Selection.setSelection(buffer, buffer.getSpanStart(it), buffer.getSpanEnd(it))
333
+ }
334
+ }
335
+
336
+ if (event.action == MotionEvent.ACTION_UP) {
337
+ handled = handled || buffer.execute<ClickableSpan>(offset) {
338
+ it.onClick(widget)
339
+ }
340
+ }
341
+ }
342
+
343
+ return handled
344
+ }
345
+
346
+ private inline fun <reified T : Any> Spannable.execute(offset: Int, fn: (T) -> Unit): Boolean {
347
+ val spans = this.getSpans<T>(offset, offset)
348
+ if (spans.isNotEmpty()) {
349
+ spans.forEach(fn)
350
+ return true
351
+ }
352
+ return false
353
+ }
354
+ }
355
+
307
356
  data class WordPosition(
308
357
  val index: Int,
309
358
  val start: Int,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-advanced-text",
3
- "version": "0.1.28",
3
+ "version": "0.1.29",
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",