react-native-advanced-text 0.1.32 → 0.1.34

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,56 +1,56 @@
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)
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)
@@ -177,14 +177,20 @@ class AdvancedTextView : TextView {
177
177
 
178
178
  val positions = mutableListOf<WordPosition>()
179
179
  val regex = "\\S+".toRegex()
180
-
181
- regex.findAll(text).forEachIndexed { index, match ->
182
- positions.add(WordPosition(
183
- index = index,
184
- start = match.range.first,
185
- end = match.range.last + 1,
186
- word = match.value
187
- ))
180
+ val matches = regex.findAll(text).toList()
181
+
182
+ matches.forEachIndexed { index, match ->
183
+ val wordEnd = match.range.last + 1
184
+ val extendedEnd = if (index + 1 < matches.size) matches[index + 1].range.first else text.length
185
+ positions.add(
186
+ WordPosition(
187
+ index = index,
188
+ start = match.range.first,
189
+ end = wordEnd,
190
+ extendedEnd = extendedEnd,
191
+ word = match.value
192
+ )
193
+ )
188
194
  }
189
195
 
190
196
  wordPositions = positions
@@ -205,7 +211,7 @@ class AdvancedTextView : TextView {
205
211
  spannableString.setSpan(
206
212
  BackgroundColorSpan(color),
207
213
  wordPos.start,
208
- wordPos.end,
214
+ wordPos.extendedEnd,
209
215
  Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
210
216
  )
211
217
  }
@@ -219,7 +225,6 @@ class AdvancedTextView : TextView {
219
225
  )
220
226
  }
221
227
 
222
- // Add clickable span for word clicks
223
228
  spannableString.setSpan(
224
229
  WordClickableSpan(wordPos.index, wordPos.word),
225
230
  wordPos.start,
@@ -369,6 +374,7 @@ class AdvancedTextView : TextView {
369
374
  val index: Int,
370
375
  val start: Int,
371
376
  val end: Int,
377
+ val extendedEnd: Int,
372
378
  val word: String
373
379
  )
374
380
  }
@@ -277,9 +277,11 @@ using namespace facebook::react;
277
277
  NSCharacterSet *whitespaceSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
278
278
 
279
279
  NSInteger wordIndex = 0;
280
+ NSMutableArray *allMatches = [NSMutableArray array];
281
+
280
282
  while (searchRange.location < text.length) {
281
283
  while (searchRange.location < text.length &&
282
- [whitespaceSet characterIsMember:[text characterAtIndex:searchRange.location]]) {
284
+ [whitespaceSet characterIsMember:[text characterAtIndex:searchRange.location]]) {
283
285
  searchRange.location++;
284
286
  searchRange.length = text.length - searchRange.location;
285
287
  }
@@ -288,14 +290,14 @@ using namespace facebook::react;
288
290
 
289
291
  NSUInteger wordStart = searchRange.location;
290
292
  while (searchRange.location < text.length &&
291
- ![whitespaceSet characterIsMember:[text characterAtIndex:searchRange.location]]) {
293
+ ![whitespaceSet characterIsMember:[text characterAtIndex:searchRange.location]]) {
292
294
  searchRange.location++;
293
295
  }
294
296
 
295
297
  NSRange wordRange = NSMakeRange(wordStart, searchRange.location - wordStart);
296
298
  NSString *word = [text substringWithRange:wordRange];
297
299
 
298
- [_wordRanges addObject:@{
300
+ [allMatches addObject:@{
299
301
  @"word": word,
300
302
  @"range": [NSValue valueWithRange:wordRange],
301
303
  @"index": @(wordIndex)
@@ -305,6 +307,25 @@ using namespace facebook::react;
305
307
  searchRange.length = text.length - searchRange.location;
306
308
  }
307
309
 
310
+ for (NSInteger i = 0; i < allMatches.count; i++) {
311
+ NSMutableDictionary *wordInfo = [allMatches[i] mutableCopy];
312
+ NSRange wordRange = [wordInfo[@"range"] rangeValue];
313
+
314
+ NSUInteger extendedEnd;
315
+ if (i + 1 < allMatches.count) {
316
+ NSRange nextRange = [allMatches[i + 1][@"range"] rangeValue];
317
+ extendedEnd = nextRange.location;
318
+ } else {
319
+ extendedEnd = text.length;
320
+ }
321
+
322
+ NSRange extendedRange = NSMakeRange(wordRange.location, extendedEnd - wordRange.location);
323
+ wordInfo[@"extendedRange"] = [NSValue valueWithRange:extendedRange];
324
+
325
+ [_wordRanges addObject:[wordInfo copy]];
326
+ }
327
+
328
+
308
329
  NSLog(@"[AdvancedTextView] Parsed %ld words", (long)_wordRanges.count);
309
330
  [self updateTextAppearance];
310
331
  } @catch (NSException *exception) {
@@ -382,7 +403,7 @@ using namespace facebook::react;
382
403
  value:paragraphStyle
383
404
  range:NSMakeRange(0, attributedString.length)];
384
405
  }
385
-
406
+
386
407
  UIFont *font = nil;
387
408
 
388
409
  if (_fontFamily && _fontFamily.length > 0) {
@@ -421,16 +442,21 @@ using namespace facebook::react;
421
442
 
422
443
  UIColor *highlightColor = _highlightColors[index];
423
444
  if (highlightColor) {
424
- [attributedString addAttribute:NSBackgroundColorAttributeName
425
- value:highlightColor
426
- range:range];
445
+ NSValue *extendedRangeValue = wordInfo[@"extendedRange"];
446
+ NSRange extendedRange = extendedRangeValue ? [extendedRangeValue rangeValue] : range;
447
+
448
+ if (extendedRange.location + extendedRange.length <= attributedString.length) {
449
+ [attributedString addAttribute:NSBackgroundColorAttributeName
450
+ value:highlightColor
451
+ range:extendedRange];
452
+ }
427
453
  }
428
454
 
429
455
  if (_indicatorWordIndex >= 0 && [index integerValue] == _indicatorWordIndex) {
430
456
  UIColor *indicatorColor = [[UIColor systemBlueColor] colorWithAlphaComponent:0.3];
431
457
  [attributedString addAttribute:NSBackgroundColorAttributeName
432
- value:indicatorColor
433
- range:range];
458
+ value:indicatorColor
459
+ range:range];
434
460
  }
435
461
  }
436
462
 
@@ -1 +1 @@
1
- {"version":3,"names":["React","AdvancedTextViewNativeComponent","jsx","_jsx","AdvancedText","text","style","highlightedWords","menuOptions","onWordPress","onSelection","indicatorWordIndex","restProps"],"sourceRoot":"..\\..\\src","sources":["AdvancedText.tsx"],"mappings":";;AAAA,OAAOA,KAAK,MAAM,OAAO;AACzB,OAAOC,+BAA+B,MAE/B,mCAAmC;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAE3C,OAAO,MAAMC,YAAmC,GAAGA,CAAC;EAClDC,IAAI;EACJC,KAAK;EACLC,gBAAgB;EAChBC,WAAW;EACXC,WAAW;EACXC,WAAW;EACXC,kBAAkB;EAClB,GAAGC;AACL,CAAC,KAAK;EACJ,oBACET,IAAA,CAACF,+BAA+B;IAAA,GAC1BW,SAAS;IACbN,KAAK,EAAEA,KAAM;IACbD,IAAI,EAAEA,IAAK;IACXE,gBAAgB,EAAEA,gBAAiB;IACnCC,WAAW,EAAEA,WAAY;IACzBC,WAAW,EAAEA,WAAY;IACzBC,WAAW,EAAEA,WAAY;IACzBC,kBAAkB,EAAEA;EAAmB,CACxC,CAAC;AAEN,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["React","AdvancedTextViewNativeComponent","jsx","_jsx","AdvancedText","text","style","highlightedWords","menuOptions","onWordPress","onSelection","indicatorWordIndex","restProps"],"sourceRoot":"../../src","sources":["AdvancedText.tsx"],"mappings":";;AAAA,OAAOA,KAAK,MAAM,OAAO;AACzB,OAAOC,+BAA+B,MAE/B,mCAAmC;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAE3C,OAAO,MAAMC,YAAmC,GAAGA,CAAC;EAClDC,IAAI;EACJC,KAAK;EACLC,gBAAgB;EAChBC,WAAW;EACXC,WAAW;EACXC,WAAW;EACXC,kBAAkB;EAClB,GAAGC;AACL,CAAC,KAAK;EACJ,oBACET,IAAA,CAACF,+BAA+B;IAAA,GAC1BW,SAAS;IACbN,KAAK,EAAEA,KAAM;IACbD,IAAI,EAAEA,IAAK;IACXE,gBAAgB,EAAEA,gBAAiB;IACnCC,WAAW,EAAEA,WAAY;IACzBC,WAAW,EAAEA,WAAY;IACzBC,WAAW,EAAEA,WAAY;IACzBC,kBAAkB,EAAEA;EAAmB,CACxC,CAAC;AAEN,CAAC","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":["AdvancedText"],"sourceRoot":"..\\..\\src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,YAAY,QAAQ,mBAAgB","ignoreList":[]}
1
+ {"version":3,"names":["AdvancedText"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,YAAY,QAAQ,mBAAgB","ignoreList":[]}
package/package.json CHANGED
@@ -1,175 +1,175 @@
1
- {
2
- "name": "react-native-advanced-text",
3
- "version": "0.1.32",
4
- "description": " Advanced text component for React Native with custom select options.",
5
- "main": "./lib/module/index.js",
6
- "types": "./lib/typescript/src/index.d.ts",
7
- "exports": {
8
- ".": {
9
- "source": "./src/index.tsx",
10
- "types": "./lib/typescript/src/index.d.ts",
11
- "default": "./lib/module/index.js"
12
- },
13
- "./package.json": "./package.json"
14
- },
15
- "files": [
16
- "src",
17
- "lib",
18
- "android",
19
- "ios",
20
- "cpp",
21
- "*.podspec",
22
- "react-native.config.js",
23
- "!ios/build",
24
- "!android/build",
25
- "!android/gradle",
26
- "!android/gradlew",
27
- "!android/gradlew.bat",
28
- "!android/local.properties",
29
- "!**/__tests__",
30
- "!**/__fixtures__",
31
- "!**/__mocks__",
32
- "!**/.*"
33
- ],
34
- "scripts": {
35
- "example": "yarn workspace react-native-advanced-text-example",
36
- "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
37
- "prepare": "bob build",
38
- "typecheck": "tsc",
39
- "lint": "eslint \"**/*.{js,ts,tsx}\"",
40
- "release": "release-it --only-version",
41
- "test": "jest"
42
- },
43
- "keywords": [
44
- "react-native",
45
- "ios",
46
- "android"
47
- ],
48
- "repository": {
49
- "type": "git",
50
- "url": "git+https://github.com/AminAllahham/react-native-advanced-text.git"
51
- },
52
- "author": "Amin Al-lahham <amin.allahham9@gmail.com> (https://github.com/AminAllahham)",
53
- "license": "MIT",
54
- "bugs": {
55
- "url": "https://github.com/AminAllahham/react-native-advanced-text/issues"
56
- },
57
- "homepage": "https://github.com/AminAllahham/react-native-advanced-text#readme",
58
- "publishConfig": {
59
- "registry": "https://registry.npmjs.org/"
60
- },
61
- "devDependencies": {
62
- "@commitlint/config-conventional": "^19.8.1",
63
- "@eslint/compat": "^1.3.2",
64
- "@eslint/eslintrc": "^3.3.1",
65
- "@eslint/js": "^9.35.0",
66
- "@react-native-community/cli": "20.0.1",
67
- "@react-native/babel-preset": "0.81.1",
68
- "@react-native/eslint-config": "^0.81.1",
69
- "@release-it/conventional-changelog": "^10.0.1",
70
- "@types/jest": "^29.5.14",
71
- "@types/react": "^19.1.0",
72
- "commitlint": "^19.8.1",
73
- "del-cli": "^6.0.0",
74
- "eslint": "^9.35.0",
75
- "eslint-config-prettier": "^10.1.8",
76
- "eslint-plugin-prettier": "^5.5.4",
77
- "jest": "^29.7.0",
78
- "lefthook": "^2.0.3",
79
- "prettier": "^2.8.8",
80
- "react": "19.1.0",
81
- "react-native": "0.81.1",
82
- "react-native-builder-bob": "^0.40.16",
83
- "release-it": "^19.0.4",
84
- "turbo": "^2.5.6",
85
- "typescript": "^5.9.2"
86
- },
87
- "peerDependencies": {
88
- "react": "*",
89
- "react-native": "*"
90
- },
91
- "workspaces": [
92
- "example"
93
- ],
94
- "packageManager": "yarn@4.11.0",
95
- "react-native-builder-bob": {
96
- "source": "src",
97
- "output": "lib",
98
- "targets": [
99
- [
100
- "module",
101
- {
102
- "esm": true
103
- }
104
- ],
105
- [
106
- "typescript",
107
- {
108
- "project": "tsconfig.build.json"
109
- }
110
- ]
111
- ]
112
- },
113
- "codegenConfig": {
114
- "name": "AdvancedTextViewSpec",
115
- "type": "all",
116
- "jsSrcsDir": "src",
117
- "android": {
118
- "javaPackageName": "com.advancedtext"
119
- },
120
- "ios": {
121
- "componentProvider": {
122
- "AdvancedTextView": "AdvancedTextView"
123
- }
124
- }
125
- },
126
- "prettier": {
127
- "quoteProps": "consistent",
128
- "singleQuote": true,
129
- "tabWidth": 2,
130
- "trailingComma": "es5",
131
- "useTabs": false
132
- },
133
- "commitlint": {
134
- "extends": [
135
- "@commitlint/config-conventional"
136
- ]
137
- },
138
- "release-it": {
139
- "git": {
140
- "commitMessage": "chore: release ${version}",
141
- "tagName": "v${version}"
142
- },
143
- "npm": {
144
- "publish": true
145
- },
146
- "github": {
147
- "release": true
148
- },
149
- "plugins": {
150
- "@release-it/conventional-changelog": {
151
- "preset": {
152
- "name": "angular"
153
- }
154
- }
155
- }
156
- },
157
- "jest": {
158
- "preset": "react-native",
159
- "modulePathIgnorePatterns": [
160
- "<rootDir>/example/node_modules",
161
- "<rootDir>/lib/"
162
- ]
163
- },
164
- "create-react-native-library": {
165
- "languages": "kotlin-objc",
166
- "type": "fabric-view",
167
- "tools": [
168
- "eslint",
169
- "lefthook",
170
- "release-it",
171
- "jest"
172
- ],
173
- "version": "0.55.1"
174
- }
175
- }
1
+ {
2
+ "name": "react-native-advanced-text",
3
+ "version": "0.1.34",
4
+ "description": " Advanced text component for React Native with custom select options.",
5
+ "main": "./lib/module/index.js",
6
+ "types": "./lib/typescript/src/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "source": "./src/index.tsx",
10
+ "types": "./lib/typescript/src/index.d.ts",
11
+ "default": "./lib/module/index.js"
12
+ },
13
+ "./package.json": "./package.json"
14
+ },
15
+ "files": [
16
+ "src",
17
+ "lib",
18
+ "android",
19
+ "ios",
20
+ "cpp",
21
+ "*.podspec",
22
+ "react-native.config.js",
23
+ "!ios/build",
24
+ "!android/build",
25
+ "!android/gradle",
26
+ "!android/gradlew",
27
+ "!android/gradlew.bat",
28
+ "!android/local.properties",
29
+ "!**/__tests__",
30
+ "!**/__fixtures__",
31
+ "!**/__mocks__",
32
+ "!**/.*"
33
+ ],
34
+ "scripts": {
35
+ "example": "yarn workspace react-native-advanced-text-example",
36
+ "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
37
+ "prepare": "bob build",
38
+ "typecheck": "tsc",
39
+ "lint": "eslint \"**/*.{js,ts,tsx}\"",
40
+ "release": "release-it --only-version",
41
+ "test": "jest"
42
+ },
43
+ "keywords": [
44
+ "react-native",
45
+ "ios",
46
+ "android"
47
+ ],
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "git+https://github.com/AminAllahham/react-native-advanced-text.git"
51
+ },
52
+ "author": "Amin Al-lahham <amin.allahham9@gmail.com> (https://github.com/AminAllahham)",
53
+ "license": "MIT",
54
+ "bugs": {
55
+ "url": "https://github.com/AminAllahham/react-native-advanced-text/issues"
56
+ },
57
+ "homepage": "https://github.com/AminAllahham/react-native-advanced-text#readme",
58
+ "publishConfig": {
59
+ "registry": "https://registry.npmjs.org/"
60
+ },
61
+ "devDependencies": {
62
+ "@commitlint/config-conventional": "^19.8.1",
63
+ "@eslint/compat": "^1.3.2",
64
+ "@eslint/eslintrc": "^3.3.1",
65
+ "@eslint/js": "^9.35.0",
66
+ "@react-native-community/cli": "20.0.1",
67
+ "@react-native/babel-preset": "0.81.1",
68
+ "@react-native/eslint-config": "^0.81.1",
69
+ "@release-it/conventional-changelog": "^10.0.1",
70
+ "@types/jest": "^29.5.14",
71
+ "@types/react": "^19.1.0",
72
+ "commitlint": "^19.8.1",
73
+ "del-cli": "^6.0.0",
74
+ "eslint": "^9.35.0",
75
+ "eslint-config-prettier": "^10.1.8",
76
+ "eslint-plugin-prettier": "^5.5.4",
77
+ "jest": "^29.7.0",
78
+ "lefthook": "^2.0.3",
79
+ "prettier": "^2.8.8",
80
+ "react": "19.1.0",
81
+ "react-native": "0.81.1",
82
+ "react-native-builder-bob": "^0.40.16",
83
+ "release-it": "^19.0.4",
84
+ "turbo": "^2.5.6",
85
+ "typescript": "^5.9.2"
86
+ },
87
+ "peerDependencies": {
88
+ "react": "*",
89
+ "react-native": "*"
90
+ },
91
+ "workspaces": [
92
+ "example"
93
+ ],
94
+ "packageManager": "yarn@4.11.0",
95
+ "react-native-builder-bob": {
96
+ "source": "src",
97
+ "output": "lib",
98
+ "targets": [
99
+ [
100
+ "module",
101
+ {
102
+ "esm": true
103
+ }
104
+ ],
105
+ [
106
+ "typescript",
107
+ {
108
+ "project": "tsconfig.build.json"
109
+ }
110
+ ]
111
+ ]
112
+ },
113
+ "codegenConfig": {
114
+ "name": "AdvancedTextViewSpec",
115
+ "type": "all",
116
+ "jsSrcsDir": "src",
117
+ "android": {
118
+ "javaPackageName": "com.advancedtext"
119
+ },
120
+ "ios": {
121
+ "componentProvider": {
122
+ "AdvancedTextView": "AdvancedTextView"
123
+ }
124
+ }
125
+ },
126
+ "prettier": {
127
+ "quoteProps": "consistent",
128
+ "singleQuote": true,
129
+ "tabWidth": 2,
130
+ "trailingComma": "es5",
131
+ "useTabs": false
132
+ },
133
+ "commitlint": {
134
+ "extends": [
135
+ "@commitlint/config-conventional"
136
+ ]
137
+ },
138
+ "release-it": {
139
+ "git": {
140
+ "commitMessage": "chore: release ${version}",
141
+ "tagName": "v${version}"
142
+ },
143
+ "npm": {
144
+ "publish": true
145
+ },
146
+ "github": {
147
+ "release": true
148
+ },
149
+ "plugins": {
150
+ "@release-it/conventional-changelog": {
151
+ "preset": {
152
+ "name": "angular"
153
+ }
154
+ }
155
+ }
156
+ },
157
+ "jest": {
158
+ "preset": "react-native",
159
+ "modulePathIgnorePatterns": [
160
+ "<rootDir>/example/node_modules",
161
+ "<rootDir>/lib/"
162
+ ]
163
+ },
164
+ "create-react-native-library": {
165
+ "languages": "kotlin-objc",
166
+ "type": "fabric-view",
167
+ "tools": [
168
+ "eslint",
169
+ "lefthook",
170
+ "release-it",
171
+ "jest"
172
+ ],
173
+ "version": "0.55.1"
174
+ }
175
+ }