@thiagobueno/rn-selectable-text 1.0.7 → 1.0.8

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.
@@ -239,42 +239,26 @@ static std::string safeStdString(NSString *str) {
239
239
  #pragma mark - UITextViewDelegate
240
240
 
241
241
  // ====================================================================
242
- // REAL-TIME CUSTOM MODE TRACKING (With Crash Prevention bounds check)
242
+ // TOUCH-UP DETECTION & DEBOUNCE LOGIC
243
243
  // ====================================================================
244
244
  - (void)textViewDidChangeSelection:(UITextView *)textView
245
245
  {
246
+ // CANCEL PREVIOUS TIMERS: If user is still dragging, reset the clock
247
+ [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(emitCustomModeEventOrShowMenu) object:nil];
248
+
246
249
  if (textView.selectedRange.location != NSNotFound && textView.selectedRange.length > 0) {
247
250
 
248
- // SAFE BOUNDS CHECK: Prevents iOS 15/17/18 out-of-bounds crash
249
- NSString *selectedText = @"";
250
- if (textView.selectedRange.location + textView.selectedRange.length <= textView.text.length) {
251
- selectedText = [textView.text substringWithRange:textView.selectedRange];
252
- }
253
-
254
- // INVISIBLE MODE (ALL IOS VERSIONS)
255
- if (_menuOptions.count == 0) {
256
- if (auto eventEmitter = std::static_pointer_cast<const SelectableTextViewEventEmitter>(_eventEmitter)) {
257
- SelectableTextViewEventEmitter::OnSelection selectionEvent = {
258
- .chosenOption = "CUSTOM_MODE",
259
- .highlightedText = safeStdString(selectedText)
260
- };
261
- eventEmitter->onSelection(selectionEvent);
262
- }
263
- return;
264
- }
265
-
266
- // STANDARD MODE
267
251
  if (@available(iOS 16.0, *)) {
252
+ // iOS 16+: Do nothing during drag. The event will fire perfectly on 'Touch Up' inside editMenuForTextInRange.
268
253
  return;
269
254
  } else {
270
- dispatch_async(dispatch_get_main_queue(), ^{
271
- [self showCustomMenu];
272
- });
255
+ // iOS 15: Debounce timer. Waits 0.4s after the user stops dragging before firing the event.
256
+ [self performSelector:@selector(emitCustomModeEventOrShowMenu) withObject:nil afterDelay:0.4];
273
257
  }
274
258
  } else {
275
259
  [[UIMenuController sharedMenuController] hideMenuFromView:_customTextView];
276
260
 
277
- // IF SELECTION IS CLEARED, NOTIFY JS TO HIDE THE BOTTOM SHEET
261
+ // IF SELECTION IS CLEARED, NOTIFY JS IMMEDIATELY
278
262
  if (_menuOptions.count == 0) {
279
263
  if (auto eventEmitter = std::static_pointer_cast<const SelectableTextViewEventEmitter>(_eventEmitter)) {
280
264
  SelectableTextViewEventEmitter::OnSelection selectionEvent = {
@@ -287,8 +271,33 @@ static std::string safeStdString(NSString *str) {
287
271
  }
288
272
  }
289
273
 
274
+ - (void)emitCustomModeEventOrShowMenu {
275
+ if (_customTextView.selectedRange.location == NSNotFound || _customTextView.selectedRange.length == 0) {
276
+ return;
277
+ }
278
+
279
+ if (_menuOptions.count == 0) {
280
+ // INVISIBLE MODE: Emit event on touch up
281
+ NSString *selectedText = @"";
282
+ if (_customTextView.selectedRange.location + _customTextView.selectedRange.length <= _customTextView.text.length) {
283
+ selectedText = [_customTextView.text substringWithRange:_customTextView.selectedRange];
284
+ }
285
+
286
+ if (auto eventEmitter = std::static_pointer_cast<const SelectableTextViewEventEmitter>(_eventEmitter)) {
287
+ SelectableTextViewEventEmitter::OnSelection selectionEvent = {
288
+ .chosenOption = "CUSTOM_MODE",
289
+ .highlightedText = safeStdString(selectedText)
290
+ };
291
+ eventEmitter->onSelection(selectionEvent);
292
+ }
293
+ } else {
294
+ // NATIVE MODE: Show the old UIMenuController
295
+ [self showCustomMenu];
296
+ }
297
+ }
298
+
290
299
  // ====================================================================
291
- // THE NEW IOS 16+ API
300
+ // THE NEW IOS 16+ API (FIRES ONLY ON TOUCH UP)
292
301
  // ====================================================================
293
302
  - (UIMenu *)textView:(UITextView *)textView editMenuForTextInRange:(NSRange)range suggestedActions:(NSArray<UIMenuElement *> *)suggestedActions API_AVAILABLE(ios(16.0)) {
294
303
 
@@ -349,7 +358,6 @@ static std::string safeStdString(NSString *str) {
349
358
 
350
359
  CGRect selectedRect = [_customTextView firstRectForRange:_customTextView.selectedTextRange];
351
360
 
352
- // CRASH PREVENTION: Validate rect before showing menu (iOS 15 safety)
353
361
  if (!CGRectIsEmpty(selectedRect) && !CGRectIsNull(selectedRect) && !CGRectIsInfinite(selectedRect)) {
354
362
  CGRect targetRect = [_customTextView convertRect:selectedRect toView:_customTextView];
355
363
  [menuController showMenuFromView:_customTextView rect:targetRect];
@@ -396,7 +404,6 @@ static std::string safeStdString(NSString *str) {
396
404
  NSString *selectorName = NSStringFromSelector(anInvocation.selector);
397
405
 
398
406
  if ([selectorName hasPrefix:@"customAction_"] && [selectorName hasSuffix:@":"]) {
399
- // CRASH PREVENTION: Validate selector length before cutting string
400
407
  if (selectorName.length > 14) {
401
408
  NSString *cleanedOption = [selectorName substringWithRange:NSMakeRange(13, selectorName.length - 14)];
402
409
 
@@ -426,7 +433,6 @@ static std::string safeStdString(NSString *str) {
426
433
  NSRange selectedRange = _customTextView.selectedRange;
427
434
  NSString *selectedText = @"";
428
435
 
429
- // SAFE BOUNDS CHECK
430
436
  if (selectedRange.location != NSNotFound && selectedRange.length > 0 && (selectedRange.location + selectedRange.length <= _customTextView.text.length)) {
431
437
  selectedText = [_customTextView.text substringWithRange:selectedRange];
432
438
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thiagobueno/rn-selectable-text",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "type": "module",
5
5
  "description": "A library for custom text selection menus",
6
6
  "main": "./lib/module/index.js",