react-native-enriched 0.1.5 → 0.2.0

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.
Files changed (80) hide show
  1. package/README.md +3 -9
  2. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerDelegate.java +4 -1
  3. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerInterface.java +2 -1
  4. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/Props.cpp +5 -0
  5. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/Props.h +1 -45
  6. package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt +53 -12
  7. package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewLayoutManager.kt +7 -56
  8. package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewManager.kt +19 -22
  9. package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewPackage.kt +2 -0
  10. package/android/src/main/java/com/swmansion/enriched/MeasurementStore.kt +158 -0
  11. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedCodeBlockSpan.kt +36 -1
  12. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedImageSpan.kt +132 -11
  13. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedSpans.kt +65 -46
  14. package/android/src/main/java/com/swmansion/enriched/spans/utils/ForceRedrawSpan.kt +13 -0
  15. package/android/src/main/java/com/swmansion/enriched/styles/HtmlStyle.kt +2 -9
  16. package/android/src/main/java/com/swmansion/enriched/styles/InlineStyles.kt +1 -0
  17. package/android/src/main/java/com/swmansion/enriched/styles/ParagraphStyles.kt +110 -3
  18. package/android/src/main/java/com/swmansion/enriched/styles/ParametrizedStyles.kt +75 -32
  19. package/android/src/main/java/com/swmansion/enriched/utils/AsyncDrawable.kt +91 -0
  20. package/android/src/main/java/com/swmansion/enriched/utils/EnrichedParser.java +38 -15
  21. package/android/src/main/java/com/swmansion/enriched/utils/ResourceManager.kt +26 -0
  22. package/android/src/main/java/com/swmansion/enriched/watchers/EnrichedSpanWatcher.kt +3 -1
  23. package/android/src/main/java/com/swmansion/enriched/watchers/EnrichedTextWatcher.kt +1 -1
  24. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputMeasurementManager.cpp +15 -2
  25. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputMeasurementManager.h +1 -0
  26. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputShadowNode.cpp +1 -2
  27. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/conversions.h +27 -0
  28. package/android/src/main/res/drawable/broken_image.xml +10 -0
  29. package/ios/EnrichedTextInputView.h +3 -1
  30. package/ios/EnrichedTextInputView.mm +167 -68
  31. package/ios/config/InputConfig.h +6 -0
  32. package/ios/config/InputConfig.mm +32 -0
  33. package/ios/generated/RNEnrichedTextInputViewSpec/Props.cpp +5 -0
  34. package/ios/generated/RNEnrichedTextInputViewSpec/Props.h +1 -45
  35. package/ios/generated/RNEnrichedTextInputViewSpec/RCTComponentViewHelpers.h +20 -4
  36. package/ios/inputParser/InputParser.mm +179 -31
  37. package/ios/inputTextView/InputTextView.mm +3 -5
  38. package/ios/internals/EnrichedTextInputViewShadowNode.h +1 -0
  39. package/ios/internals/EnrichedTextInputViewShadowNode.mm +29 -17
  40. package/ios/styles/BlockQuoteStyle.mm +5 -26
  41. package/ios/styles/BoldStyle.mm +2 -0
  42. package/ios/styles/CodeBlockStyle.mm +228 -0
  43. package/ios/styles/H1Style.mm +1 -0
  44. package/ios/styles/H2Style.mm +1 -0
  45. package/ios/styles/H3Style.mm +1 -0
  46. package/ios/styles/ImageStyle.mm +158 -0
  47. package/ios/styles/InlineCodeStyle.mm +2 -0
  48. package/ios/styles/ItalicStyle.mm +2 -0
  49. package/ios/styles/LinkStyle.mm +15 -7
  50. package/ios/styles/MentionStyle.mm +133 -36
  51. package/ios/styles/OrderedListStyle.mm +5 -8
  52. package/ios/styles/StrikethroughStyle.mm +2 -0
  53. package/ios/styles/UnderlineStyle.mm +2 -0
  54. package/ios/styles/UnorderedListStyle.mm +5 -8
  55. package/ios/utils/BaseStyleProtocol.h +1 -0
  56. package/ios/utils/ImageData.h +10 -0
  57. package/ios/utils/ImageData.mm +4 -0
  58. package/ios/utils/LayoutManagerExtension.mm +118 -3
  59. package/ios/utils/OccurenceUtils.h +4 -0
  60. package/ios/utils/OccurenceUtils.mm +47 -0
  61. package/ios/utils/ParagraphAttributesUtils.h +1 -0
  62. package/ios/utils/ParagraphAttributesUtils.mm +87 -20
  63. package/ios/utils/StringExtension.h +1 -1
  64. package/ios/utils/StringExtension.mm +17 -8
  65. package/ios/utils/StyleHeaders.h +12 -0
  66. package/ios/utils/ZeroWidthSpaceUtils.mm +22 -10
  67. package/lib/module/EnrichedTextInput.js +4 -2
  68. package/lib/module/EnrichedTextInput.js.map +1 -1
  69. package/lib/module/EnrichedTextInputNativeComponent.ts +7 -5
  70. package/lib/module/normalizeHtmlStyle.js +0 -4
  71. package/lib/module/normalizeHtmlStyle.js.map +1 -1
  72. package/lib/typescript/src/EnrichedTextInput.d.ts +3 -6
  73. package/lib/typescript/src/EnrichedTextInput.d.ts.map +1 -1
  74. package/lib/typescript/src/EnrichedTextInputNativeComponent.d.ts +2 -5
  75. package/lib/typescript/src/EnrichedTextInputNativeComponent.d.ts.map +1 -1
  76. package/lib/typescript/src/normalizeHtmlStyle.d.ts.map +1 -1
  77. package/package.json +1 -1
  78. package/src/EnrichedTextInput.tsx +6 -7
  79. package/src/EnrichedTextInputNativeComponent.ts +7 -5
  80. package/src/normalizeHtmlStyle.ts +0 -4
@@ -26,14 +26,13 @@ using namespace facebook::react;
26
26
  EnrichedTextInputViewShadowNode::ConcreteState::Shared _state;
27
27
  int _componentViewHeightUpdateCounter;
28
28
  NSMutableSet<NSNumber *> *_activeStyles;
29
- NSDictionary<NSNumber *, NSArray<NSNumber *> *> *_conflictingStyles;
30
- NSDictionary<NSNumber *, NSArray<NSNumber *> *> *_blockingStyles;
31
29
  LinkData *_recentlyActiveLinkData;
32
30
  NSRange _recentlyActiveLinkRange;
33
31
  NSString *_recentlyEmittedString;
34
32
  MentionParams *_recentlyActiveMentionParams;
35
33
  NSRange _recentlyActiveMentionRange;
36
34
  NSString *_recentlyEmittedHtml;
35
+ BOOL _emitHtml;
37
36
  UILabel *_placeholderLabel;
38
37
  UIColor *_placeholderColor;
39
38
  BOOL _emitFocusBlur;
@@ -74,8 +73,8 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
74
73
  _recentlyActiveMentionRange = NSMakeRange(0, 0);
75
74
  recentlyChangedRange = NSMakeRange(0, 0);
76
75
  _recentlyEmittedString = @"";
77
- _recentlyEmittedHtml = @"";
78
- emitHtml = NO;
76
+ _recentlyEmittedHtml = @"<html>\n<p></p>\n</html>";
77
+ _emitHtml = NO;
79
78
  blockEmitting = NO;
80
79
  _emitFocusBlur = YES;
81
80
 
@@ -94,10 +93,12 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
94
93
  @([H3Style getStyleType]): [[H3Style alloc] initWithInput:self],
95
94
  @([UnorderedListStyle getStyleType]): [[UnorderedListStyle alloc] initWithInput:self],
96
95
  @([OrderedListStyle getStyleType]): [[OrderedListStyle alloc] initWithInput:self],
97
- @([BlockQuoteStyle getStyleType]): [[BlockQuoteStyle alloc] initWithInput:self]
96
+ @([BlockQuoteStyle getStyleType]): [[BlockQuoteStyle alloc] initWithInput:self],
97
+ @([CodeBlockStyle getStyleType]): [[CodeBlockStyle alloc] initWithInput:self],
98
+ @([ImageStyle getStyleType]): [[ImageStyle alloc] initWithInput:self]
98
99
  };
99
100
 
100
- _conflictingStyles = @{
101
+ conflictingStyles = @{
101
102
  @([BoldStyle getStyleType]) : @[],
102
103
  @([ItalicStyle getStyleType]) : @[],
103
104
  @([UnderlineStyle getStyleType]) : @[],
@@ -105,28 +106,33 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
105
106
  @([InlineCodeStyle getStyleType]) : @[@([LinkStyle getStyleType]), @([MentionStyle getStyleType])],
106
107
  @([LinkStyle getStyleType]): @[@([InlineCodeStyle getStyleType]), @([LinkStyle getStyleType]), @([MentionStyle getStyleType])],
107
108
  @([MentionStyle getStyleType]): @[@([InlineCodeStyle getStyleType]), @([LinkStyle getStyleType])],
108
- @([H1Style getStyleType]): @[@([H2Style getStyleType]), @([H3Style getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType])],
109
- @([H2Style getStyleType]): @[@([H1Style getStyleType]), @([H3Style getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType])],
110
- @([H3Style getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType])],
111
- @([UnorderedListStyle getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([H3Style getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType])],
112
- @([OrderedListStyle getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([H3Style getStyleType]), @([UnorderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType])],
113
- @([BlockQuoteStyle getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([H3Style getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType])]
109
+ @([H1Style getStyleType]): @[@([H2Style getStyleType]), @([H3Style getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]), @([CodeBlockStyle getStyleType])],
110
+ @([H2Style getStyleType]): @[@([H1Style getStyleType]), @([H3Style getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]), @([CodeBlockStyle getStyleType])],
111
+ @([H3Style getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]), @([CodeBlockStyle getStyleType])],
112
+ @([UnorderedListStyle getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([H3Style getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]), @([CodeBlockStyle getStyleType])],
113
+ @([OrderedListStyle getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([H3Style getStyleType]), @([UnorderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]), @([CodeBlockStyle getStyleType])],
114
+ @([BlockQuoteStyle getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([H3Style getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]), @([CodeBlockStyle getStyleType])],
115
+ @([CodeBlockStyle getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([H3Style getStyleType]),
116
+ @([BoldStyle getStyleType]), @([ItalicStyle getStyleType]), @([UnderlineStyle getStyleType]), @([StrikethroughStyle getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]), @([InlineCodeStyle getStyleType]), @([MentionStyle getStyleType]), @([LinkStyle getStyleType])],
117
+ @([ImageStyle getStyleType]) : @[@([LinkStyle getStyleType]), @([MentionStyle getStyleType])]
114
118
  };
115
119
 
116
- _blockingStyles = @{
117
- @([BoldStyle getStyleType]) : @[],
118
- @([ItalicStyle getStyleType]) : @[],
119
- @([UnderlineStyle getStyleType]) : @[],
120
- @([StrikethroughStyle getStyleType]) : @[],
121
- @([InlineCodeStyle getStyleType]) : @[],
122
- @([LinkStyle getStyleType]): @[],
123
- @([MentionStyle getStyleType]): @[],
120
+ blockingStyles = @{
121
+ @([BoldStyle getStyleType]) : @[@([CodeBlockStyle getStyleType])],
122
+ @([ItalicStyle getStyleType]) : @[@([CodeBlockStyle getStyleType])],
123
+ @([UnderlineStyle getStyleType]) : @[@([CodeBlockStyle getStyleType])],
124
+ @([StrikethroughStyle getStyleType]) : @[@([CodeBlockStyle getStyleType])],
125
+ @([InlineCodeStyle getStyleType]) : @[@([CodeBlockStyle getStyleType]), @([ImageStyle getStyleType])],
126
+ @([LinkStyle getStyleType]): @[@([CodeBlockStyle getStyleType]), @([ImageStyle getStyleType])],
127
+ @([MentionStyle getStyleType]): @[@([CodeBlockStyle getStyleType]), @([ImageStyle getStyleType])],
124
128
  @([H1Style getStyleType]): @[],
125
129
  @([H2Style getStyleType]): @[],
126
130
  @([H3Style getStyleType]): @[],
127
131
  @([UnorderedListStyle getStyleType]): @[],
128
132
  @([OrderedListStyle getStyleType]): @[],
129
133
  @([BlockQuoteStyle getStyleType]): @[],
134
+ @([CodeBlockStyle getStyleType]): @[],
135
+ @([ImageStyle getStyleType]) : @[@([InlineCodeStyle getStyleType])]
130
136
  };
131
137
 
132
138
  parser = [[InputParser alloc] initWithInput:self];
@@ -346,6 +352,25 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
346
352
  }
347
353
  }
348
354
 
355
+ if(newViewProps.htmlStyle.codeblock.color != oldViewProps.htmlStyle.codeblock.color) {
356
+ if(isColorMeaningful(newViewProps.htmlStyle.codeblock.color)) {
357
+ [newConfig setCodeBlockFgColor:RCTUIColorFromSharedColor(newViewProps.htmlStyle.codeblock.color)];
358
+ stylePropChanged = YES;
359
+ }
360
+ }
361
+
362
+ if(newViewProps.htmlStyle.codeblock.backgroundColor != oldViewProps.htmlStyle.codeblock.backgroundColor) {
363
+ if(isColorMeaningful(newViewProps.htmlStyle.codeblock.backgroundColor)) {
364
+ [newConfig setCodeBlockBgColor:RCTUIColorFromSharedColor(newViewProps.htmlStyle.codeblock.backgroundColor)];
365
+ stylePropChanged = YES;
366
+ }
367
+ }
368
+
369
+ if(newViewProps.htmlStyle.codeblock.borderRadius != oldViewProps.htmlStyle.codeblock.borderRadius) {
370
+ [newConfig setCodeBlockBorderRadius:newViewProps.htmlStyle.codeblock.borderRadius];
371
+ stylePropChanged = YES;
372
+ }
373
+
349
374
  if(newViewProps.htmlStyle.a.textDecorationLine != oldViewProps.htmlStyle.a.textDecorationLine) {
350
375
  NSString *objcString = [NSString fromCppString:newViewProps.htmlStyle.a.textDecorationLine];
351
376
  if([objcString isEqualToString:DecorationUnderline]) {
@@ -357,6 +382,10 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
357
382
  stylePropChanged = YES;
358
383
  }
359
384
 
385
+ if(newViewProps.scrollEnabled != oldViewProps.scrollEnabled || textView.scrollEnabled != newViewProps.scrollEnabled) {
386
+ [textView setScrollEnabled:newViewProps.scrollEnabled];
387
+ }
388
+
360
389
  folly::dynamic oldMentionStyle = oldViewProps.htmlStyle.mention;
361
390
  folly::dynamic newMentionStyle = newViewProps.htmlStyle.mention;
362
391
  if(oldMentionStyle != newMentionStyle) {
@@ -393,22 +422,17 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
393
422
 
394
423
  // now set the new config
395
424
  config = newConfig;
396
-
397
- // we don't want to emit these html changes in here
398
- BOOL prevEmitHtml = emitHtml;
399
- if(prevEmitHtml) {
400
- emitHtml = NO;
401
- }
402
-
425
+
426
+ // no emitting during styles reload
427
+ blockEmitting = YES;
428
+
403
429
  // make sure everything is sound in the html
404
430
  NSString *initiallyProcessedHtml = [parser initiallyProcessHtml:currentHtml];
405
431
  if(initiallyProcessedHtml != nullptr) {
406
432
  [parser replaceWholeFromHtml:initiallyProcessedHtml];
407
433
  }
408
434
 
409
- if(prevEmitHtml) {
410
- emitHtml = YES;
411
- }
435
+ blockEmitting = NO;
412
436
 
413
437
  // fill the typing attributes with style props
414
438
  defaultTypingAttributes[NSForegroundColorAttributeName] = [config primaryColor];
@@ -509,12 +533,11 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
509
533
  }
510
534
 
511
535
  // isOnChangeHtmlSet
512
- emitHtml = newViewProps.isOnChangeHtmlSet;
536
+ _emitHtml = newViewProps.isOnChangeHtmlSet;
513
537
 
514
538
  [super updateProps:props oldProps:oldProps];
515
- // mandatory text and height checks
539
+ // run the changes callback
516
540
  [self anyTextMayHaveBeenModified];
517
- [self tryUpdatingHeight];
518
541
 
519
542
  // autofocus - needs to be done at the very end
520
543
  if(isFirstMount && newViewProps.autoFocus) {
@@ -575,7 +598,7 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
575
598
  options: NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
576
599
  context: nullptr
577
600
  ];
578
-
601
+
579
602
  return CGSizeMake(maxWidth, ceil(boundingBox.size.height));
580
603
  }
581
604
 
@@ -605,6 +628,9 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
605
628
  // style updates are emitted only if something differs from the previously active styles
606
629
  BOOL updateNeeded = NO;
607
630
 
631
+ // active styles are kept in a separate set until we're sure they can be emitted
632
+ NSMutableSet *newActiveStyles = [_activeStyles mutableCopy];
633
+
608
634
  // data for onLinkDetected event
609
635
  LinkData *detectedLinkData;
610
636
  NSRange detectedLinkRange = NSMakeRange(0, 0);
@@ -615,14 +641,14 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
615
641
 
616
642
  for (NSNumber* type in stylesDict) {
617
643
  id<BaseStyleProtocol> style = stylesDict[type];
618
- BOOL wasActive = [_activeStyles containsObject: type];
644
+ BOOL wasActive = [newActiveStyles containsObject: type];
619
645
  BOOL isActive = [style detectStyle:textView.selectedRange];
620
646
  if(wasActive != isActive) {
621
647
  updateNeeded = YES;
622
648
  if(isActive) {
623
- [_activeStyles addObject:type];
649
+ [newActiveStyles addObject:type];
624
650
  } else {
625
- [_activeStyles removeObject:type];
651
+ [newActiveStyles removeObject:type];
626
652
  }
627
653
  }
628
654
 
@@ -682,6 +708,9 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
682
708
  if(updateNeeded) {
683
709
  auto emitter = [self getEventEmitter];
684
710
  if(emitter != nullptr) {
711
+ // update activeStyles only if emitter is available
712
+ _activeStyles = newActiveStyles;
713
+
685
714
  emitter->onChangeState({
686
715
  .isBold = [_activeStyles containsObject: @([BoldStyle getStyleType])],
687
716
  .isItalic = [_activeStyles containsObject: @([ItalicStyle getStyleType])],
@@ -696,8 +725,8 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
696
725
  .isUnorderedList = [_activeStyles containsObject: @([UnorderedListStyle getStyleType])],
697
726
  .isOrderedList = [_activeStyles containsObject: @([OrderedListStyle getStyleType])],
698
727
  .isBlockQuote = [_activeStyles containsObject: @([BlockQuoteStyle getStyleType])],
699
- .isCodeBlock = NO, // [_activeStyles containsObject: @([CodeBlockStyle getStyleType])],
700
- .isImage = NO // [_activeStyles containsObject: @([ImageStyle getStyleType]])],
728
+ .isCodeBlock = [_activeStyles containsObject: @([CodeBlockStyle getStyleType])],
729
+ .isImage = [_activeStyles containsObject: @([ImageStyle getStyleType])],
701
730
  });
702
731
  }
703
732
  }
@@ -705,9 +734,6 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
705
734
  if(detectedLinkData != nullptr) {
706
735
  // emit onLinkeDetected event
707
736
  [self emitOnLinkDetectedEvent:detectedLinkData.text url:detectedLinkData.url range:detectedLinkRange];
708
-
709
- _recentlyActiveLinkData = detectedLinkData;
710
- _recentlyActiveLinkRange = detectedLinkRange;
711
737
  }
712
738
 
713
739
  if(detectedMentionParams != nullptr) {
@@ -768,6 +794,14 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
768
794
  [self toggleParagraphStyle:[OrderedListStyle getStyleType]];
769
795
  } else if([commandName isEqualToString:@"toggleBlockQuote"]) {
770
796
  [self toggleParagraphStyle:[BlockQuoteStyle getStyleType]];
797
+ } else if([commandName isEqualToString:@"toggleCodeBlock"]) {
798
+ [self toggleParagraphStyle:[CodeBlockStyle getStyleType]];
799
+ } else if([commandName isEqualToString:@"addImage"]) {
800
+ NSString *uri = (NSString *)args[0];
801
+ CGFloat imgWidth = [(NSNumber*)args[1] floatValue];
802
+ CGFloat imgHeight = [(NSNumber*)args[2] floatValue];
803
+
804
+ [self addImage:uri width:imgWidth height:imgHeight];
771
805
  }
772
806
  }
773
807
 
@@ -806,6 +840,13 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
806
840
  - (void)emitOnLinkDetectedEvent:(NSString *)text url:(NSString *)url range:(NSRange)range {
807
841
  auto emitter = [self getEventEmitter];
808
842
  if(emitter != nullptr) {
843
+ // update recently active link info
844
+ LinkData *newLinkData = [[LinkData alloc] init];
845
+ newLinkData.text = text;
846
+ newLinkData.url = url;
847
+ _recentlyActiveLinkData = newLinkData;
848
+ _recentlyActiveLinkRange = range;
849
+
809
850
  emitter->onLinkDetected({
810
851
  .text = [text toCppString],
811
852
  .url = [url toCppString],
@@ -846,7 +887,7 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
846
887
  }
847
888
 
848
889
  - (void)tryEmittingOnChangeHtmlEvent {
849
- if(!emitHtml || textView.markedTextRange != nullptr) {
890
+ if(!_emitHtml || textView.markedTextRange != nullptr) {
850
891
  return;
851
892
  }
852
893
  auto emitter = [self getEventEmitter];
@@ -907,11 +948,22 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
907
948
  }
908
949
  }
909
950
 
951
+ - (void)addImage:(NSString *)uri width:(float)width height:(float)height
952
+ {
953
+ ImageStyle *imageStyleClass = (ImageStyle *)stylesDict[@([ImageStyle getStyleType])];
954
+ if(imageStyleClass == nullptr) { return; }
955
+
956
+ if([self handleStyleBlocksAndConflicts:[ImageStyle getStyleType] range:textView.selectedRange]) {
957
+ [imageStyleClass addImage:uri width:width height:height];
958
+ [self anyTextMayHaveBeenModified];
959
+ }
960
+ }
961
+
910
962
  - (void)startMentionWithIndicator:(NSString *)indicator {
911
963
  MentionStyle *mentionStyleClass = (MentionStyle *)stylesDict[@([MentionStyle getStyleType])];
912
964
  if(mentionStyleClass == nullptr) { return; }
913
965
 
914
- if([self handleStyleBlocksAndConflicts:[MentionStyle getStyleType] range:[[mentionStyleClass getActiveMentionRange] rangeValue]]) {
966
+ if([self handleStyleBlocksAndConflicts:[MentionStyle getStyleType] range:textView.selectedRange]) {
915
967
  [mentionStyleClass startMentionWithIndicator:indicator];
916
968
  [self anyTextMayHaveBeenModified];
917
969
  }
@@ -920,13 +972,13 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
920
972
  // returns false when style shouldn't be applied and true when it can be
921
973
  - (BOOL)handleStyleBlocksAndConflicts:(StyleType)type range:(NSRange)range {
922
974
  // handle blocking styles: if any is present we do not apply the toggled style
923
- NSArray<NSNumber *> *blocking = [self getPresentStyleTypesFrom: _blockingStyles[@(type)] range:range];
975
+ NSArray<NSNumber *> *blocking = [self getPresentStyleTypesFrom: blockingStyles[@(type)] range:range];
924
976
  if(blocking.count != 0) {
925
977
  return NO;
926
978
  }
927
979
 
928
980
  // handle conflicting styles: all of their occurences have to be removed
929
- NSArray<NSNumber *> *conflicting = [self getPresentStyleTypesFrom: _conflictingStyles[@(type)] range:range];
981
+ NSArray<NSNumber *> *conflicting = [self getPresentStyleTypesFrom: conflictingStyles[@(type)] range:range];
930
982
  if(conflicting.count != 0) {
931
983
  for(NSNumber *style in conflicting) {
932
984
  id<BaseStyleProtocol> styleClass = stylesDict[style];
@@ -976,7 +1028,12 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
976
1028
  MentionStyle *mentionStyleClass = (MentionStyle *)stylesDict[@([MentionStyle getStyleType])];
977
1029
  if(mentionStyleClass != nullptr) {
978
1030
  [mentionStyleClass manageMentionTypingAttributes];
979
- [mentionStyleClass manageMentionEditing];
1031
+
1032
+ // mention editing runs if only a selection was done (no text change)
1033
+ // otherwise we would double-emit with a second call in the anyTextMayHaveBeenModified method
1034
+ if([_recentlyEmittedString isEqualToString:[textView.textStorage.string copy]]) {
1035
+ [mentionStyleClass manageMentionEditing];
1036
+ }
980
1037
  }
981
1038
 
982
1039
  // typing attributes for empty lines selection reset
@@ -994,11 +1051,15 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
994
1051
  textView.typingAttributes = defaultTypingAttributes;
995
1052
  }
996
1053
  }
1054
+
1055
+ // update active styles as well
1056
+ [self tryUpdatingActiveStyles];
997
1057
  }
998
1058
 
999
1059
  - (void)handleWordModificationBasedChanges:(NSString*)word inRange:(NSRange)range {
1000
1060
  // manual links refreshing and automatic links detection handling
1001
1061
  LinkStyle* linkStyle = [stylesDict objectForKey:@([LinkStyle getStyleType])];
1062
+
1002
1063
  if(linkStyle != nullptr) {
1003
1064
  // manual links need to be handled first because they can block automatic links after being refreshed
1004
1065
  [linkStyle handleManualLinks:word inRange:range];
@@ -1033,6 +1094,12 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
1033
1094
  [bqStyle manageBlockquoteColor];
1034
1095
  }
1035
1096
 
1097
+ // codeblock font and color management
1098
+ CodeBlockStyle *codeBlockStyle = stylesDict[@([CodeBlockStyle getStyleType])];
1099
+ if(codeBlockStyle != nullptr) {
1100
+ [codeBlockStyle manageCodeBlockFontAndColor];
1101
+ }
1102
+
1036
1103
  // improper headings fix
1037
1104
  H1Style *h1Style = stylesDict[@([H1Style getStyleType])];
1038
1105
  H2Style *h2Style = stylesDict[@([H2Style getStyleType])];
@@ -1043,6 +1110,13 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
1043
1110
  [h3Style handleImproperHeadings];
1044
1111
  }
1045
1112
 
1113
+ // mentions management: removal and editing
1114
+ MentionStyle *mentionStyleClass = (MentionStyle *)stylesDict[@([MentionStyle getStyleType])];
1115
+ if(mentionStyleClass != nullptr) {
1116
+ [mentionStyleClass handleExistingMentions];
1117
+ [mentionStyleClass manageMentionEditing];
1118
+ }
1119
+
1046
1120
  // placholder management
1047
1121
  if(!_placeholderLabel.hidden && textView.textStorage.string.length > 0) {
1048
1122
  [self setPlaceholderLabelShown:NO];
@@ -1051,12 +1125,6 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
1051
1125
  }
1052
1126
 
1053
1127
  if(![textView.textStorage.string isEqualToString:_recentlyEmittedString]) {
1054
- // mentions removal management
1055
- MentionStyle *mentionStyleClass = (MentionStyle *)stylesDict[@([MentionStyle getStyleType])];
1056
- if(mentionStyleClass != nullptr) {
1057
- [mentionStyleClass handleExistingMentions];
1058
- }
1059
-
1060
1128
  // modified words handling
1061
1129
  NSArray *modifiedWords = [WordsUtils getAffectedWordsFromText:textView.textStorage.string modificationRange:recentlyChangedRange];
1062
1130
  if(modifiedWords != nullptr) {
@@ -1078,28 +1146,55 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
1078
1146
  // emit onChangeText event
1079
1147
  auto emitter = [self getEventEmitter];
1080
1148
  if(emitter != nullptr) {
1149
+ // set the recently emitted string only if the emitter is defined
1150
+ _recentlyEmittedString = stringToBeEmitted;
1151
+
1081
1152
  emitter->onChangeText({
1082
1153
  .value = [stringToBeEmitted toCppString]
1083
1154
  });
1084
1155
  }
1085
-
1086
- // set the recently emitted string
1087
- _recentlyEmittedString = stringToBeEmitted;
1088
1156
  }
1089
1157
 
1090
1158
  // update height on each character change
1091
1159
  [self tryUpdatingHeight];
1092
1160
  // update active styles as well
1093
1161
  [self tryUpdatingActiveStyles];
1162
+ // update drawing - schedule debounced relayout
1163
+ [self scheduleRelayoutIfNeeded];
1164
+ }
1165
+
1166
+ // Debounced relayout helper - coalesces multiple requests into one per runloop tick
1167
+ - (void)scheduleRelayoutIfNeeded
1168
+ {
1169
+ // Cancel any previously scheduled invocation to debounce
1170
+ [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_performRelayout) object:nil];
1171
+ // Schedule on next runloop cycle
1172
+ [self performSelector:@selector(_performRelayout) withObject:nil afterDelay:0];
1173
+ }
1174
+
1175
+ - (void)_performRelayout
1176
+ {
1177
+ if (!textView) { return; }
1178
+
1179
+ dispatch_async(dispatch_get_main_queue(), ^{
1180
+ NSRange wholeRange = NSMakeRange(0, self->textView.textStorage.string.length);
1181
+ NSRange actualRange = NSMakeRange(0, 0);
1182
+ [self->textView.layoutManager invalidateLayoutForCharacterRange:wholeRange actualCharacterRange:&actualRange];
1183
+ [self->textView.layoutManager ensureLayoutForCharacterRange:actualRange];
1184
+ [self->textView.layoutManager invalidateDisplayForCharacterRange:wholeRange];
1185
+
1186
+ // We have to explicitly set contentSize
1187
+ // That way textView knows if content overflows and if should be scrollable
1188
+ // We recall measureSize here because value returned from previous measureSize may not be up-to date at that point
1189
+ CGSize measuredSize = [self measureSize:self->textView.frame.size.width];
1190
+ self->textView.contentSize = measuredSize;
1191
+ });
1192
+ }
1094
1193
 
1095
- // update drawing
1096
- dispatch_async(dispatch_get_main_queue(), ^{
1097
- NSRange wholeRange = NSMakeRange(0, textView.textStorage.string.length);
1098
- NSRange actualRange = NSMakeRange(0, 0);
1099
- [textView.layoutManager invalidateLayoutForCharacterRange:wholeRange actualCharacterRange:&actualRange];
1100
- [textView.layoutManager ensureLayoutForCharacterRange:actualRange];
1101
- [textView.layoutManager invalidateDisplayForCharacterRange:wholeRange];
1102
- });
1194
+ - (void)didMoveToWindow {
1195
+ [super didMoveToWindow];
1196
+ // used to run all lifecycle callbacks
1197
+ [self anyTextMayHaveBeenModified];
1103
1198
  }
1104
1199
 
1105
1200
  // MARK: - UITextView delegate methods
@@ -1137,6 +1232,7 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
1137
1232
  UnorderedListStyle *uStyle = stylesDict[@([UnorderedListStyle getStyleType])];
1138
1233
  OrderedListStyle *oStyle = stylesDict[@([OrderedListStyle getStyleType])];
1139
1234
  BlockQuoteStyle *bqStyle = stylesDict[@([BlockQuoteStyle getStyleType])];
1235
+ CodeBlockStyle *cbStyle = stylesDict[@([CodeBlockStyle getStyleType])];
1140
1236
  LinkStyle *linkStyle = stylesDict[@([LinkStyle getStyleType])];
1141
1237
  MentionStyle *mentionStyle = stylesDict[@([MentionStyle getStyleType])];
1142
1238
  H1Style *h1Style = stylesDict[@([H1Style getStyleType])];
@@ -1152,13 +1248,19 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
1152
1248
  [oStyle handleBackspaceInRange:range replacementText:text] ||
1153
1249
  [oStyle tryHandlingListShorcutInRange:range replacementText:text] ||
1154
1250
  [bqStyle handleBackspaceInRange:range replacementText:text] ||
1251
+ [cbStyle handleBackspaceInRange:range replacementText:text] ||
1155
1252
  [linkStyle handleLeadingLinkReplacement:range replacementText:text] ||
1156
1253
  [mentionStyle handleLeadingMentionReplacement:range replacementText:text] ||
1157
1254
  [h1Style handleNewlinesInRange:range replacementText:text] ||
1158
1255
  [h2Style handleNewlinesInRange:range replacementText:text] ||
1159
1256
  [h3Style handleNewlinesInRange:range replacementText:text] ||
1160
1257
  [ZeroWidthSpaceUtils handleBackspaceInRange:range replacementText:text input:self] ||
1161
- [ParagraphAttributesUtils handleBackspaceInRange:range replacementText:text input:self]
1258
+ [ParagraphAttributesUtils handleBackspaceInRange:range replacementText:text input:self] ||
1259
+ // CRITICAL: This callback HAS TO be always evaluated last.
1260
+ //
1261
+ // This function is the "Generic Fallback": if no specific style claims the backspace action
1262
+ // to change its state, only then do we proceed to physically delete the newline and merge paragraphs.
1263
+ [ParagraphAttributesUtils handleNewlineBackspaceInRange:range replacementText:text input:self]
1162
1264
  ) {
1163
1265
  [self anyTextMayHaveBeenModified];
1164
1266
  return NO;
@@ -1184,9 +1286,6 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
1184
1286
 
1185
1287
  // manage selection changes
1186
1288
  [self manageSelectionBasedChanges];
1187
-
1188
- // update active styles
1189
- [self tryUpdatingActiveStyles];
1190
1289
  }
1191
1290
 
1192
1291
  // this function isn't called always when some text changes (for example setting link or starting mention with indicator doesn't fire it)
@@ -64,4 +64,10 @@
64
64
  - (void)setLinkDecorationLine:(TextDecorationLineEnum)newValue;
65
65
  - (void)setMentionStyleProps:(NSDictionary *)newValue;
66
66
  - (MentionStyleProps *)mentionStylePropsForIndicator:(NSString *)indicator;
67
+ - (UIColor *)codeBlockFgColor;
68
+ - (void)setCodeBlockFgColor:(UIColor *)newValue;
69
+ - (UIColor *)codeBlockBgColor;
70
+ - (void)setCodeBlockBgColor:(UIColor *)newValue;
71
+ - (CGFloat)codeBlockBorderRadius;
72
+ - (void)setCodeBlockBorderRadius:(CGFloat)newValue;
67
73
  @end
@@ -36,6 +36,11 @@
36
36
  UIColor *_linkColor;
37
37
  TextDecorationLineEnum _linkDecorationLine;
38
38
  NSDictionary *_mentionProperties;
39
+ UIColor *_codeBlockFgColor;
40
+ CGFloat _codeBlockBorderRadius;
41
+ UIColor *_codeBlockBgColor;
42
+ CGFloat _imageWidth;
43
+ CGFloat _imageHeight;
39
44
  }
40
45
 
41
46
  - (instancetype) init {
@@ -79,6 +84,9 @@
79
84
  copy->_linkColor = [_linkColor copy];
80
85
  copy->_linkDecorationLine = [_linkDecorationLine copy];
81
86
  copy->_mentionProperties = [_mentionProperties mutableCopy];
87
+ copy->_codeBlockFgColor = [_codeBlockFgColor copy];
88
+ copy->_codeBlockBgColor = [_codeBlockBgColor copy];
89
+ copy->_codeBlockBorderRadius = _codeBlockBorderRadius;
82
90
  return copy;
83
91
  }
84
92
 
@@ -379,4 +387,28 @@
379
387
  return fallbackProps;
380
388
  }
381
389
 
390
+ - (UIColor *)codeBlockFgColor {
391
+ return _codeBlockFgColor;
392
+ }
393
+
394
+ - (void)setCodeBlockFgColor:(UIColor *)newValue {
395
+ _codeBlockFgColor = newValue;
396
+ }
397
+
398
+ - (UIColor *)codeBlockBgColor {
399
+ return _codeBlockBgColor;
400
+ }
401
+
402
+ - (void)setCodeBlockBgColor:(UIColor *)newValue {
403
+ _codeBlockBgColor = newValue;
404
+ }
405
+
406
+ - (CGFloat)codeBlockBorderRadius {
407
+ return _codeBlockBorderRadius;
408
+ }
409
+
410
+ - (void)setCodeBlockBorderRadius:(CGFloat)newValue {
411
+ _codeBlockBorderRadius = newValue;
412
+ }
413
+
382
414
  @end
@@ -30,6 +30,7 @@ EnrichedTextInputViewProps::EnrichedTextInputViewProps(
30
30
  selectionColor(convertRawProp(context, rawProps, "selectionColor", sourceProps.selectionColor, {})),
31
31
  autoCapitalize(convertRawProp(context, rawProps, "autoCapitalize", sourceProps.autoCapitalize, {})),
32
32
  htmlStyle(convertRawProp(context, rawProps, "htmlStyle", sourceProps.htmlStyle, {})),
33
+ scrollEnabled(convertRawProp(context, rawProps, "scrollEnabled", sourceProps.scrollEnabled, {false})),
33
34
  color(convertRawProp(context, rawProps, "color", sourceProps.color, {})),
34
35
  fontSize(convertRawProp(context, rawProps, "fontSize", sourceProps.fontSize, {0.0})),
35
36
  fontFamily(convertRawProp(context, rawProps, "fontFamily", sourceProps.fontFamily, {})),
@@ -94,6 +95,10 @@ folly::dynamic EnrichedTextInputViewProps::getDiffProps(
94
95
  result["htmlStyle"] = toDynamic(htmlStyle);
95
96
  }
96
97
 
98
+ if (scrollEnabled != oldProps->scrollEnabled) {
99
+ result["scrollEnabled"] = scrollEnabled;
100
+ }
101
+
97
102
  if (color != oldProps->color) {
98
103
  result["color"] = *color;
99
104
  }
@@ -309,45 +309,6 @@ static inline folly::dynamic toDynamic(const EnrichedTextInputViewHtmlStyleAStru
309
309
  }
310
310
  #endif
311
311
 
312
- struct EnrichedTextInputViewHtmlStyleImgStruct {
313
- Float width{0.0};
314
- Float height{0.0};
315
-
316
- #ifdef RN_SERIALIZABLE_STATE
317
- bool operator==(const EnrichedTextInputViewHtmlStyleImgStruct&) const = default;
318
-
319
- folly::dynamic toDynamic() const {
320
- folly::dynamic result = folly::dynamic::object();
321
- result["width"] = width;
322
- result["height"] = height;
323
- return result;
324
- }
325
- #endif
326
- };
327
-
328
- static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, EnrichedTextInputViewHtmlStyleImgStruct &result) {
329
- auto map = (std::unordered_map<std::string, RawValue>)value;
330
-
331
- auto tmp_width = map.find("width");
332
- if (tmp_width != map.end()) {
333
- fromRawValue(context, tmp_width->second, result.width);
334
- }
335
- auto tmp_height = map.find("height");
336
- if (tmp_height != map.end()) {
337
- fromRawValue(context, tmp_height->second, result.height);
338
- }
339
- }
340
-
341
- static inline std::string toString(const EnrichedTextInputViewHtmlStyleImgStruct &value) {
342
- return "[Object EnrichedTextInputViewHtmlStyleImgStruct]";
343
- }
344
-
345
- #ifdef RN_SERIALIZABLE_STATE
346
- static inline folly::dynamic toDynamic(const EnrichedTextInputViewHtmlStyleImgStruct &value) {
347
- return value.toDynamic();
348
- }
349
- #endif
350
-
351
312
  struct EnrichedTextInputViewHtmlStyleOlStruct {
352
313
  Float gapWidth{0.0};
353
314
  Float marginLeft{0.0};
@@ -459,7 +420,6 @@ struct EnrichedTextInputViewHtmlStyleStruct {
459
420
  EnrichedTextInputViewHtmlStyleCodeStruct code{};
460
421
  EnrichedTextInputViewHtmlStyleAStruct a{};
461
422
  folly::dynamic mention{};
462
- EnrichedTextInputViewHtmlStyleImgStruct img{};
463
423
  EnrichedTextInputViewHtmlStyleOlStruct ol{};
464
424
  EnrichedTextInputViewHtmlStyleUlStruct ul{};
465
425
 
@@ -476,7 +436,6 @@ struct EnrichedTextInputViewHtmlStyleStruct {
476
436
  result["code"] = ::facebook::react::toDynamic(code);
477
437
  result["a"] = ::facebook::react::toDynamic(a);
478
438
  result["mention"] = mention;
479
- result["img"] = ::facebook::react::toDynamic(img);
480
439
  result["ol"] = ::facebook::react::toDynamic(ol);
481
440
  result["ul"] = ::facebook::react::toDynamic(ul);
482
441
  return result;
@@ -519,10 +478,6 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu
519
478
  if (tmp_mention != map.end()) {
520
479
  fromRawValue(context, tmp_mention->second, result.mention);
521
480
  }
522
- auto tmp_img = map.find("img");
523
- if (tmp_img != map.end()) {
524
- fromRawValue(context, tmp_img->second, result.img);
525
- }
526
481
  auto tmp_ol = map.find("ol");
527
482
  if (tmp_ol != map.end()) {
528
483
  fromRawValue(context, tmp_ol->second, result.ol);
@@ -559,6 +514,7 @@ class EnrichedTextInputViewProps final : public ViewProps {
559
514
  SharedColor selectionColor{};
560
515
  std::string autoCapitalize{};
561
516
  EnrichedTextInputViewHtmlStyleStruct htmlStyle{};
517
+ bool scrollEnabled{false};
562
518
  SharedColor color{};
563
519
  Float fontSize{0.0};
564
520
  std::string fontFamily{};