react-native-enriched 0.2.0 → 0.3.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 (186) hide show
  1. package/README.md +16 -17
  2. package/android/build.gradle +77 -72
  3. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerDelegate.java +21 -0
  4. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerInterface.java +7 -0
  5. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/EventEmitters.cpp +156 -0
  6. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/EventEmitters.h +147 -0
  7. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/Props.cpp +10 -0
  8. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/Props.h +194 -0
  9. package/android/lint.gradle +70 -0
  10. package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputConnectionWrapper.kt +140 -0
  11. package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt +304 -83
  12. package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewLayoutManager.kt +3 -1
  13. package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewManager.kt +166 -51
  14. package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewPackage.kt +1 -3
  15. package/android/src/main/java/com/swmansion/enriched/MeasurementStore.kt +70 -21
  16. package/android/src/main/java/com/swmansion/enriched/events/MentionHandler.kt +21 -11
  17. package/android/src/main/java/com/swmansion/enriched/events/OnChangeHtmlEvent.kt +8 -9
  18. package/android/src/main/java/com/swmansion/enriched/events/OnChangeSelectionEvent.kt +10 -9
  19. package/android/src/main/java/com/swmansion/enriched/events/OnChangeStateDeprecatedEvent.kt +21 -0
  20. package/android/src/main/java/com/swmansion/enriched/events/OnChangeStateEvent.kt +9 -12
  21. package/android/src/main/java/com/swmansion/enriched/events/OnChangeTextEvent.kt +10 -10
  22. package/android/src/main/java/com/swmansion/enriched/events/OnInputBlurEvent.kt +7 -9
  23. package/android/src/main/java/com/swmansion/enriched/events/OnInputFocusEvent.kt +7 -9
  24. package/android/src/main/java/com/swmansion/enriched/events/OnInputKeyPressEvent.kt +27 -0
  25. package/android/src/main/java/com/swmansion/enriched/events/OnLinkDetectedEvent.kt +13 -11
  26. package/android/src/main/java/com/swmansion/enriched/events/OnMentionDetectedEvent.kt +10 -9
  27. package/android/src/main/java/com/swmansion/enriched/events/OnMentionEvent.kt +9 -8
  28. package/android/src/main/java/com/swmansion/enriched/events/OnRequestHtmlResultEvent.kt +32 -0
  29. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedBlockQuoteSpan.kt +24 -5
  30. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedBoldSpan.kt +8 -1
  31. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedCodeBlockSpan.kt +10 -2
  32. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH1Span.kt +8 -1
  33. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH2Span.kt +8 -1
  34. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH3Span.kt +8 -1
  35. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH4Span.kt +24 -0
  36. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH5Span.kt +24 -0
  37. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH6Span.kt +24 -0
  38. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedImageSpan.kt +34 -17
  39. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedInlineCodeSpan.kt +8 -1
  40. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedItalicSpan.kt +7 -1
  41. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedLinkSpan.kt +10 -4
  42. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedMentionSpan.kt +14 -11
  43. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedOrderedListSpan.kt +18 -11
  44. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedSpans.kt +174 -72
  45. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedStrikeThroughSpan.kt +7 -1
  46. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedUnderlineSpan.kt +7 -1
  47. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedUnorderedListSpan.kt +11 -5
  48. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedBlockSpan.kt +3 -2
  49. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedHeadingSpan.kt +1 -2
  50. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedInlineSpan.kt +1 -2
  51. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedParagraphSpan.kt +3 -2
  52. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedSpan.kt +5 -0
  53. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedZeroWidthSpaceSpan.kt +1 -2
  54. package/android/src/main/java/com/swmansion/enriched/spans/utils/ForceRedrawSpan.kt +2 -1
  55. package/android/src/main/java/com/swmansion/enriched/styles/HtmlStyle.kt +155 -20
  56. package/android/src/main/java/com/swmansion/enriched/styles/InlineStyles.kt +25 -8
  57. package/android/src/main/java/com/swmansion/enriched/styles/ListStyles.kt +60 -20
  58. package/android/src/main/java/com/swmansion/enriched/styles/ParagraphStyles.kt +161 -25
  59. package/android/src/main/java/com/swmansion/enriched/styles/ParametrizedStyles.kt +128 -52
  60. package/android/src/main/java/com/swmansion/enriched/utils/AsyncDrawable.kt +10 -7
  61. package/android/src/main/java/com/swmansion/enriched/utils/EnrichedConstants.kt +11 -0
  62. package/android/src/main/java/com/swmansion/enriched/utils/EnrichedEditableFactory.kt +17 -0
  63. package/android/src/main/java/com/swmansion/enriched/utils/EnrichedParser.java +136 -87
  64. package/android/src/main/java/com/swmansion/enriched/utils/EnrichedSelection.kt +71 -42
  65. package/android/src/main/java/com/swmansion/enriched/utils/EnrichedSpanState.kt +183 -48
  66. package/android/src/main/java/com/swmansion/enriched/utils/EnrichedSpannable.kt +82 -0
  67. package/android/src/main/java/com/swmansion/enriched/utils/EnrichedSpannableStringBuilder.kt +15 -0
  68. package/android/src/main/java/com/swmansion/enriched/utils/Utils.kt +0 -70
  69. package/android/src/main/java/com/swmansion/enriched/watchers/EnrichedSpanWatcher.kt +46 -14
  70. package/android/src/main/java/com/swmansion/enriched/watchers/EnrichedTextWatcher.kt +34 -11
  71. package/android/src/main/new_arch/CMakeLists.txt +6 -0
  72. package/android/src/main/new_arch/RNEnrichedTextInputViewSpec.cpp +6 -6
  73. package/android/src/main/new_arch/RNEnrichedTextInputViewSpec.h +6 -6
  74. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputComponentDescriptor.h +19 -19
  75. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputMeasurementManager.cpp +40 -51
  76. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputMeasurementManager.h +13 -15
  77. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputShadowNode.cpp +23 -21
  78. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputShadowNode.h +35 -36
  79. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputState.cpp +4 -4
  80. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputState.h +13 -14
  81. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/conversions.h +33 -14
  82. package/ios/EnrichedTextInputView.h +26 -14
  83. package/ios/EnrichedTextInputView.mm +1209 -586
  84. package/ios/config/InputConfig.h +24 -6
  85. package/ios/config/InputConfig.mm +154 -38
  86. package/ios/{utils → extensions}/ColorExtension.mm +7 -5
  87. package/ios/extensions/FontExtension.mm +106 -0
  88. package/ios/{utils → extensions}/LayoutManagerExtension.h +1 -1
  89. package/ios/extensions/LayoutManagerExtension.mm +396 -0
  90. package/ios/{utils → extensions}/StringExtension.mm +19 -16
  91. package/ios/generated/RNEnrichedTextInputViewSpec/EventEmitters.cpp +156 -0
  92. package/ios/generated/RNEnrichedTextInputViewSpec/EventEmitters.h +147 -0
  93. package/ios/generated/RNEnrichedTextInputViewSpec/Props.cpp +10 -0
  94. package/ios/generated/RNEnrichedTextInputViewSpec/Props.h +194 -0
  95. package/ios/generated/RNEnrichedTextInputViewSpec/RCTComponentViewHelpers.h +95 -0
  96. package/ios/inputParser/InputParser.h +5 -5
  97. package/ios/inputParser/InputParser.mm +864 -380
  98. package/ios/inputTextView/InputTextView.h +1 -1
  99. package/ios/inputTextView/InputTextView.mm +100 -59
  100. package/ios/{utils → interfaces}/BaseStyleProtocol.h +2 -2
  101. package/ios/interfaces/ImageAttachment.h +10 -0
  102. package/ios/interfaces/ImageAttachment.mm +36 -0
  103. package/ios/interfaces/LinkRegexConfig.h +19 -0
  104. package/ios/interfaces/LinkRegexConfig.mm +37 -0
  105. package/ios/interfaces/MediaAttachment.h +23 -0
  106. package/ios/interfaces/MediaAttachment.mm +31 -0
  107. package/ios/{utils → interfaces}/MentionParams.h +0 -1
  108. package/ios/{utils → interfaces}/MentionStyleProps.mm +27 -20
  109. package/ios/{utils → interfaces}/StyleHeaders.h +37 -15
  110. package/ios/{utils → interfaces}/StyleTypeEnum.h +3 -0
  111. package/ios/internals/EnrichedTextInputViewComponentDescriptor.h +11 -9
  112. package/ios/internals/EnrichedTextInputViewShadowNode.h +28 -25
  113. package/ios/internals/EnrichedTextInputViewShadowNode.mm +45 -40
  114. package/ios/internals/EnrichedTextInputViewState.h +3 -1
  115. package/ios/styles/BlockQuoteStyle.mm +189 -118
  116. package/ios/styles/BoldStyle.mm +110 -63
  117. package/ios/styles/CodeBlockStyle.mm +204 -128
  118. package/ios/styles/H1Style.mm +10 -4
  119. package/ios/styles/H2Style.mm +10 -4
  120. package/ios/styles/H3Style.mm +10 -4
  121. package/ios/styles/H4Style.mm +17 -0
  122. package/ios/styles/H5Style.mm +17 -0
  123. package/ios/styles/H6Style.mm +17 -0
  124. package/ios/styles/HeadingStyleBase.mm +148 -86
  125. package/ios/styles/ImageStyle.mm +75 -73
  126. package/ios/styles/InlineCodeStyle.mm +162 -88
  127. package/ios/styles/ItalicStyle.mm +76 -52
  128. package/ios/styles/LinkStyle.mm +411 -232
  129. package/ios/styles/MentionStyle.mm +363 -246
  130. package/ios/styles/OrderedListStyle.mm +171 -106
  131. package/ios/styles/StrikethroughStyle.mm +52 -35
  132. package/ios/styles/UnderlineStyle.mm +68 -46
  133. package/ios/styles/UnorderedListStyle.mm +169 -106
  134. package/ios/utils/OccurenceUtils.h +42 -42
  135. package/ios/utils/OccurenceUtils.mm +142 -119
  136. package/ios/utils/ParagraphAttributesUtils.h +10 -2
  137. package/ios/utils/ParagraphAttributesUtils.mm +182 -71
  138. package/ios/utils/ParagraphsUtils.h +2 -1
  139. package/ios/utils/ParagraphsUtils.mm +41 -27
  140. package/ios/utils/TextInsertionUtils.h +13 -2
  141. package/ios/utils/TextInsertionUtils.mm +38 -20
  142. package/ios/utils/WordsUtils.h +2 -1
  143. package/ios/utils/WordsUtils.mm +32 -22
  144. package/ios/utils/ZeroWidthSpaceUtils.h +3 -1
  145. package/ios/utils/ZeroWidthSpaceUtils.mm +145 -79
  146. package/lib/module/EnrichedTextInput.js +61 -2
  147. package/lib/module/EnrichedTextInput.js.map +1 -1
  148. package/lib/module/EnrichedTextInputNativeComponent.ts +149 -12
  149. package/lib/module/{normalizeHtmlStyle.js → utils/normalizeHtmlStyle.js} +12 -0
  150. package/lib/module/utils/normalizeHtmlStyle.js.map +1 -0
  151. package/lib/module/utils/regexParser.js +46 -0
  152. package/lib/module/utils/regexParser.js.map +1 -0
  153. package/lib/typescript/src/EnrichedTextInput.d.ts +24 -14
  154. package/lib/typescript/src/EnrichedTextInput.d.ts.map +1 -1
  155. package/lib/typescript/src/EnrichedTextInputNativeComponent.d.ts +129 -12
  156. package/lib/typescript/src/EnrichedTextInputNativeComponent.d.ts.map +1 -1
  157. package/lib/typescript/src/index.d.ts +1 -1
  158. package/lib/typescript/src/index.d.ts.map +1 -1
  159. package/lib/typescript/src/utils/normalizeHtmlStyle.d.ts +4 -0
  160. package/lib/typescript/src/utils/normalizeHtmlStyle.d.ts.map +1 -0
  161. package/lib/typescript/src/utils/regexParser.d.ts +3 -0
  162. package/lib/typescript/src/utils/regexParser.d.ts.map +1 -0
  163. package/package.json +17 -6
  164. package/src/EnrichedTextInput.tsx +96 -13
  165. package/src/EnrichedTextInputNativeComponent.ts +149 -12
  166. package/src/index.tsx +2 -0
  167. package/src/{normalizeHtmlStyle.ts → utils/normalizeHtmlStyle.ts} +14 -2
  168. package/src/utils/regexParser.ts +56 -0
  169. package/ios/utils/FontExtension.mm +0 -91
  170. package/ios/utils/LayoutManagerExtension.mm +0 -286
  171. package/lib/module/normalizeHtmlStyle.js.map +0 -1
  172. package/lib/typescript/src/normalizeHtmlStyle.d.ts +0 -4
  173. package/lib/typescript/src/normalizeHtmlStyle.d.ts.map +0 -1
  174. package/ios/{utils → extensions}/ColorExtension.h +0 -0
  175. package/ios/{utils → extensions}/FontExtension.h +0 -0
  176. package/ios/{utils → extensions}/StringExtension.h +1 -1
  177. package/ios/{utils → interfaces}/ImageData.h +0 -0
  178. package/ios/{utils → interfaces}/ImageData.mm +0 -0
  179. package/ios/{utils → interfaces}/LinkData.h +0 -0
  180. package/ios/{utils → interfaces}/LinkData.mm +0 -0
  181. package/ios/{utils → interfaces}/MentionParams.mm +0 -0
  182. package/ios/{utils → interfaces}/MentionStyleProps.h +1 -1
  183. /package/ios/{utils → interfaces}/StylePair.h +0 -0
  184. /package/ios/{utils → interfaces}/StylePair.mm +0 -0
  185. /package/ios/{utils → interfaces}/TextDecorationLineEnum.h +0 -0
  186. /package/ios/{utils → interfaces}/TextDecorationLineEnum.mm +0 -0
@@ -1,24 +1,32 @@
1
1
  #import "EnrichedTextInputView.h"
2
+ #import "CoreText/CoreText.h"
3
+ #import "LayoutManagerExtension.h"
4
+ #import "ParagraphAttributesUtils.h"
2
5
  #import "RCTFabricComponentsPlugins.h"
6
+ #import "StringExtension.h"
7
+ #import "StyleHeaders.h"
8
+ #import "UIView+React.h"
9
+ #import "WordsUtils.h"
10
+ #import "ZeroWidthSpaceUtils.h"
11
+ #import <React/RCTConversions.h>
3
12
  #import <ReactNativeEnriched/EnrichedTextInputViewComponentDescriptor.h>
4
13
  #import <ReactNativeEnriched/EventEmitters.h>
5
14
  #import <ReactNativeEnriched/Props.h>
6
15
  #import <ReactNativeEnriched/RCTComponentViewHelpers.h>
7
- #import <react/utils/ManagedObjectWrapper.h>
8
16
  #import <folly/dynamic.h>
9
- #import "UIView+React.h"
10
- #import "StringExtension.h"
11
- #import "CoreText/CoreText.h"
12
- #import <React/RCTConversions.h>
13
- #import "StyleHeaders.h"
14
- #import "WordsUtils.h"
15
- #import "LayoutManagerExtension.h"
16
- #import "ZeroWidthSpaceUtils.h"
17
- #import "ParagraphAttributesUtils.h"
17
+ #import <react/utils/ManagedObjectWrapper.h>
18
+
19
+ #define GET_STYLE_STATE(TYPE_ENUM) \
20
+ { \
21
+ .isActive = [self isStyleActive:TYPE_ENUM], \
22
+ .isBlocking = [self isStyle:TYPE_ENUM activeInMap:blockingStyles], \
23
+ .isConflicting = [self isStyle:TYPE_ENUM activeInMap:conflictingStyles] \
24
+ }
18
25
 
19
26
  using namespace facebook::react;
20
27
 
21
- @interface EnrichedTextInputView () <RCTEnrichedTextInputViewViewProtocol, UITextViewDelegate, NSObject>
28
+ @interface EnrichedTextInputView () <RCTEnrichedTextInputViewViewProtocol,
29
+ UITextViewDelegate, NSObject>
22
30
 
23
31
  @end
24
32
 
@@ -26,9 +34,10 @@ using namespace facebook::react;
26
34
  EnrichedTextInputViewShadowNode::ConcreteState::Shared _state;
27
35
  int _componentViewHeightUpdateCounter;
28
36
  NSMutableSet<NSNumber *> *_activeStyles;
37
+ NSMutableSet<NSNumber *> *_blockedStyles;
29
38
  LinkData *_recentlyActiveLinkData;
30
39
  NSRange _recentlyActiveLinkRange;
31
- NSString *_recentlyEmittedString;
40
+ NSString *_recentInputString;
32
41
  MentionParams *_recentlyActiveMentionParams;
33
42
  NSRange _recentlyActiveMentionRange;
34
43
  NSString *_recentlyEmittedHtml;
@@ -36,12 +45,14 @@ using namespace facebook::react;
36
45
  UILabel *_placeholderLabel;
37
46
  UIColor *_placeholderColor;
38
47
  BOOL _emitFocusBlur;
48
+ BOOL _emitTextChange;
39
49
  }
40
50
 
41
51
  // MARK: - Component utils
42
52
 
43
53
  + (ComponentDescriptorProvider)componentDescriptorProvider {
44
- return concreteComponentDescriptorProvider<EnrichedTextInputViewComponentDescriptor>();
54
+ return concreteComponentDescriptorProvider<
55
+ EnrichedTextInputViewComponentDescriptor>();
45
56
  }
46
57
 
47
58
  Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
@@ -56,7 +67,8 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
56
67
 
57
68
  - (instancetype)initWithFrame:(CGRect)frame {
58
69
  if (self = [super initWithFrame:frame]) {
59
- static const auto defaultProps = std::make_shared<const EnrichedTextInputViewProps>();
70
+ static const auto defaultProps =
71
+ std::make_shared<const EnrichedTextInputViewProps>();
60
72
  _props = defaultProps;
61
73
  [self setDefaults];
62
74
  [self setupTextView];
@@ -69,72 +81,163 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
69
81
  - (void)setDefaults {
70
82
  _componentViewHeightUpdateCounter = 0;
71
83
  _activeStyles = [[NSMutableSet alloc] init];
84
+ _blockedStyles = [[NSMutableSet alloc] init];
72
85
  _recentlyActiveLinkRange = NSMakeRange(0, 0);
73
86
  _recentlyActiveMentionRange = NSMakeRange(0, 0);
74
87
  recentlyChangedRange = NSMakeRange(0, 0);
75
- _recentlyEmittedString = @"";
88
+ _recentInputString = @"";
76
89
  _recentlyEmittedHtml = @"<html>\n<p></p>\n</html>";
77
90
  _emitHtml = NO;
78
91
  blockEmitting = NO;
79
92
  _emitFocusBlur = YES;
80
-
81
- defaultTypingAttributes = [[NSMutableDictionary<NSAttributedStringKey, id> alloc] init];
82
-
93
+ _emitTextChange = NO;
94
+
95
+ defaultTypingAttributes =
96
+ [[NSMutableDictionary<NSAttributedStringKey, id> alloc] init];
97
+
83
98
  stylesDict = @{
84
99
  @([BoldStyle getStyleType]) : [[BoldStyle alloc] initWithInput:self],
85
- @([ItalicStyle getStyleType]): [[ItalicStyle alloc] initWithInput:self],
86
- @([UnderlineStyle getStyleType]): [[UnderlineStyle alloc] initWithInput:self],
87
- @([StrikethroughStyle getStyleType]): [[StrikethroughStyle alloc] initWithInput:self],
88
- @([InlineCodeStyle getStyleType]): [[InlineCodeStyle alloc] initWithInput:self],
89
- @([LinkStyle getStyleType]): [[LinkStyle alloc] initWithInput:self],
90
- @([MentionStyle getStyleType]): [[MentionStyle alloc] initWithInput:self],
91
- @([H1Style getStyleType]): [[H1Style alloc] initWithInput:self],
92
- @([H2Style getStyleType]): [[H2Style alloc] initWithInput:self],
93
- @([H3Style getStyleType]): [[H3Style alloc] initWithInput:self],
94
- @([UnorderedListStyle getStyleType]): [[UnorderedListStyle alloc] initWithInput:self],
95
- @([OrderedListStyle getStyleType]): [[OrderedListStyle alloc] initWithInput:self],
96
- @([BlockQuoteStyle getStyleType]): [[BlockQuoteStyle alloc] initWithInput:self],
97
- @([CodeBlockStyle getStyleType]): [[CodeBlockStyle alloc] initWithInput:self],
98
- @([ImageStyle getStyleType]): [[ImageStyle alloc] initWithInput:self]
100
+ @([ItalicStyle getStyleType]) : [[ItalicStyle alloc] initWithInput:self],
101
+ @([UnderlineStyle getStyleType]) :
102
+ [[UnderlineStyle alloc] initWithInput:self],
103
+ @([StrikethroughStyle getStyleType]) :
104
+ [[StrikethroughStyle alloc] initWithInput:self],
105
+ @([InlineCodeStyle getStyleType]) :
106
+ [[InlineCodeStyle alloc] initWithInput:self],
107
+ @([LinkStyle getStyleType]) : [[LinkStyle alloc] initWithInput:self],
108
+ @([MentionStyle getStyleType]) : [[MentionStyle alloc] initWithInput:self],
109
+ @([H1Style getStyleType]) : [[H1Style alloc] initWithInput:self],
110
+ @([H2Style getStyleType]) : [[H2Style alloc] initWithInput:self],
111
+ @([H3Style getStyleType]) : [[H3Style alloc] initWithInput:self],
112
+ @([H4Style getStyleType]) : [[H4Style alloc] initWithInput:self],
113
+ @([H5Style getStyleType]) : [[H5Style alloc] initWithInput:self],
114
+ @([H6Style getStyleType]) : [[H6Style alloc] initWithInput:self],
115
+ @([UnorderedListStyle getStyleType]) :
116
+ [[UnorderedListStyle alloc] initWithInput:self],
117
+ @([OrderedListStyle getStyleType]) :
118
+ [[OrderedListStyle alloc] initWithInput:self],
119
+ @([BlockQuoteStyle getStyleType]) :
120
+ [[BlockQuoteStyle alloc] initWithInput:self],
121
+ @([CodeBlockStyle getStyleType]) :
122
+ [[CodeBlockStyle alloc] initWithInput:self],
123
+ @([ImageStyle getStyleType]) : [[ImageStyle alloc] initWithInput:self]
99
124
  };
100
-
125
+
101
126
  conflictingStyles = @{
102
127
  @([BoldStyle getStyleType]) : @[],
103
128
  @([ItalicStyle getStyleType]) : @[],
104
129
  @([UnderlineStyle getStyleType]) : @[],
105
130
  @([StrikethroughStyle getStyleType]) : @[],
106
- @([InlineCodeStyle getStyleType]) : @[@([LinkStyle getStyleType]), @([MentionStyle getStyleType])],
107
- @([LinkStyle getStyleType]): @[@([InlineCodeStyle getStyleType]), @([LinkStyle getStyleType]), @([MentionStyle getStyleType])],
108
- @([MentionStyle getStyleType]): @[@([InlineCodeStyle getStyleType]), @([LinkStyle 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])]
131
+ @([InlineCodeStyle getStyleType]) :
132
+ @[ @([LinkStyle getStyleType]), @([MentionStyle getStyleType]) ],
133
+ @([LinkStyle getStyleType]) : @[
134
+ @([InlineCodeStyle getStyleType]), @([LinkStyle getStyleType]),
135
+ @([MentionStyle getStyleType])
136
+ ],
137
+ @([MentionStyle getStyleType]) :
138
+ @[ @([InlineCodeStyle getStyleType]), @([LinkStyle getStyleType]) ],
139
+ @([H1Style getStyleType]) : @[
140
+ @([H2Style getStyleType]), @([H3Style getStyleType]),
141
+ @([H4Style getStyleType]), @([H5Style getStyleType]),
142
+ @([H6Style getStyleType]), @([UnorderedListStyle getStyleType]),
143
+ @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]),
144
+ @([CodeBlockStyle getStyleType])
145
+ ],
146
+ @([H2Style getStyleType]) : @[
147
+ @([H1Style getStyleType]), @([H3Style getStyleType]),
148
+ @([H4Style getStyleType]), @([H5Style getStyleType]),
149
+ @([H6Style getStyleType]), @([UnorderedListStyle getStyleType]),
150
+ @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]),
151
+ @([CodeBlockStyle getStyleType])
152
+ ],
153
+ @([H3Style getStyleType]) : @[
154
+ @([H1Style getStyleType]), @([H2Style getStyleType]),
155
+ @([H4Style getStyleType]), @([H5Style getStyleType]),
156
+ @([H6Style getStyleType]), @([UnorderedListStyle getStyleType]),
157
+ @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]),
158
+ @([CodeBlockStyle getStyleType])
159
+ ],
160
+ @([H4Style getStyleType]) : @[
161
+ @([H1Style getStyleType]), @([H2Style getStyleType]),
162
+ @([H3Style getStyleType]), @([H5Style getStyleType]),
163
+ @([H6Style getStyleType]), @([UnorderedListStyle getStyleType]),
164
+ @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]),
165
+ @([CodeBlockStyle getStyleType])
166
+ ],
167
+ @([H5Style getStyleType]) : @[
168
+ @([H1Style getStyleType]), @([H2Style getStyleType]),
169
+ @([H3Style getStyleType]), @([H4Style getStyleType]),
170
+ @([H6Style getStyleType]), @([UnorderedListStyle getStyleType]),
171
+ @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]),
172
+ @([CodeBlockStyle getStyleType])
173
+ ],
174
+ @([H6Style getStyleType]) : @[
175
+ @([H1Style getStyleType]), @([H2Style getStyleType]),
176
+ @([H3Style getStyleType]), @([H4Style getStyleType]),
177
+ @([H5Style getStyleType]), @([UnorderedListStyle getStyleType]),
178
+ @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]),
179
+ @([CodeBlockStyle getStyleType])
180
+ ],
181
+ @([UnorderedListStyle getStyleType]) : @[
182
+ @([H1Style getStyleType]), @([H2Style getStyleType]),
183
+ @([H3Style getStyleType]), @([H4Style getStyleType]),
184
+ @([H5Style getStyleType]), @([H6Style getStyleType]),
185
+ @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]),
186
+ @([CodeBlockStyle getStyleType])
187
+ ],
188
+ @([OrderedListStyle getStyleType]) : @[
189
+ @([H1Style getStyleType]), @([H2Style getStyleType]),
190
+ @([H3Style getStyleType]), @([H4Style getStyleType]),
191
+ @([H5Style getStyleType]), @([H6Style getStyleType]),
192
+ @([UnorderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]),
193
+ @([CodeBlockStyle getStyleType])
194
+ ],
195
+ @([BlockQuoteStyle getStyleType]) : @[
196
+ @([H1Style getStyleType]), @([H2Style getStyleType]),
197
+ @([H3Style getStyleType]), @([H4Style getStyleType]),
198
+ @([H5Style getStyleType]), @([H6Style getStyleType]),
199
+ @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]),
200
+ @([CodeBlockStyle getStyleType])
201
+ ],
202
+ @([CodeBlockStyle getStyleType]) : @[
203
+ @([H1Style getStyleType]), @([H2Style getStyleType]),
204
+ @([H3Style getStyleType]), @([H4Style getStyleType]),
205
+ @([H5Style getStyleType]), @([H6Style getStyleType]),
206
+ @([BoldStyle getStyleType]), @([ItalicStyle getStyleType]),
207
+ @([UnderlineStyle getStyleType]), @([StrikethroughStyle getStyleType]),
208
+ @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]),
209
+ @([BlockQuoteStyle getStyleType]), @([InlineCodeStyle getStyleType]),
210
+ @([MentionStyle getStyleType]), @([LinkStyle getStyleType])
211
+ ],
212
+ @([ImageStyle getStyleType]) :
213
+ @[ @([LinkStyle getStyleType]), @([MentionStyle getStyleType]) ]
118
214
  };
119
-
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])],
128
- @([H1Style getStyleType]): @[],
129
- @([H2Style getStyleType]): @[],
130
- @([H3Style getStyleType]): @[],
131
- @([UnorderedListStyle getStyleType]): @[],
132
- @([OrderedListStyle getStyleType]): @[],
133
- @([BlockQuoteStyle getStyleType]): @[],
134
- @([CodeBlockStyle getStyleType]): @[],
135
- @([ImageStyle getStyleType]) : @[@([InlineCodeStyle getStyleType])]
136
- };
137
-
215
+
216
+ blockingStyles = [@{
217
+ @([BoldStyle getStyleType]) : @[ @([CodeBlockStyle getStyleType]) ],
218
+ @([ItalicStyle getStyleType]) : @[ @([CodeBlockStyle getStyleType]) ],
219
+ @([UnderlineStyle getStyleType]) : @[ @([CodeBlockStyle getStyleType]) ],
220
+ @([StrikethroughStyle getStyleType]) :
221
+ @[ @([CodeBlockStyle getStyleType]) ],
222
+ @([InlineCodeStyle getStyleType]) :
223
+ @[ @([CodeBlockStyle getStyleType]), @([ImageStyle getStyleType]) ],
224
+ @([LinkStyle getStyleType]) :
225
+ @[ @([CodeBlockStyle getStyleType]), @([ImageStyle getStyleType]) ],
226
+ @([MentionStyle getStyleType]) :
227
+ @[ @([CodeBlockStyle getStyleType]), @([ImageStyle getStyleType]) ],
228
+ @([H1Style getStyleType]) : @[],
229
+ @([H2Style getStyleType]) : @[],
230
+ @([H3Style getStyleType]) : @[],
231
+ @([H4Style getStyleType]) : @[],
232
+ @([H5Style getStyleType]) : @[],
233
+ @([H6Style getStyleType]) : @[],
234
+ @([UnorderedListStyle getStyleType]) : @[],
235
+ @([OrderedListStyle getStyleType]) : @[],
236
+ @([BlockQuoteStyle getStyleType]) : @[],
237
+ @([CodeBlockStyle getStyleType]) : @[],
238
+ @([ImageStyle getStyleType]) : @[ @([InlineCodeStyle getStyleType]) ]
239
+ } mutableCopy];
240
+
138
241
  parser = [[InputParser alloc] initWithInput:self];
139
242
  }
140
243
 
@@ -146,44 +249,52 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
146
249
  textView.delegate = self;
147
250
  textView.input = self;
148
251
  textView.layoutManager.input = self;
252
+ textView.adjustsFontForContentSizeCategory = YES;
149
253
  }
150
254
 
151
255
  - (void)setupPlaceholderLabel {
152
256
  _placeholderLabel = [[UILabel alloc] initWithFrame:CGRectZero];
153
257
  _placeholderLabel.translatesAutoresizingMaskIntoConstraints = NO;
154
258
  [textView addSubview:_placeholderLabel];
155
- [NSLayoutConstraint activateConstraints: @[
156
- [_placeholderLabel.leadingAnchor constraintEqualToAnchor:textView.leadingAnchor],
157
- [_placeholderLabel.widthAnchor constraintEqualToAnchor:textView.widthAnchor],
259
+ [NSLayoutConstraint activateConstraints:@[
260
+ [_placeholderLabel.leadingAnchor
261
+ constraintEqualToAnchor:textView.leadingAnchor],
262
+ [_placeholderLabel.widthAnchor
263
+ constraintEqualToAnchor:textView.widthAnchor],
158
264
  [_placeholderLabel.topAnchor constraintEqualToAnchor:textView.topAnchor],
159
- [_placeholderLabel.bottomAnchor constraintEqualToAnchor:textView.bottomAnchor]
265
+ [_placeholderLabel.bottomAnchor
266
+ constraintEqualToAnchor:textView.bottomAnchor]
160
267
  ]];
161
268
  _placeholderLabel.lineBreakMode = NSLineBreakByTruncatingTail;
162
269
  _placeholderLabel.text = @"";
163
270
  _placeholderLabel.hidden = YES;
271
+ _placeholderLabel.adjustsFontForContentSizeCategory = YES;
164
272
  }
165
273
 
166
274
  // MARK: - Props
167
275
 
168
- - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps {
169
- const auto &oldViewProps = *std::static_pointer_cast<EnrichedTextInputViewProps const>(_props);
170
- const auto &newViewProps = *std::static_pointer_cast<EnrichedTextInputViewProps const>(props);
276
+ - (void)updateProps:(Props::Shared const &)props
277
+ oldProps:(Props::Shared const &)oldProps {
278
+ const auto &oldViewProps =
279
+ *std::static_pointer_cast<EnrichedTextInputViewProps const>(_props);
280
+ const auto &newViewProps =
281
+ *std::static_pointer_cast<EnrichedTextInputViewProps const>(props);
171
282
  BOOL isFirstMount = NO;
172
283
  BOOL stylePropChanged = NO;
173
-
284
+
174
285
  // initial config
175
- if(config == nullptr) {
286
+ if (config == nullptr) {
176
287
  isFirstMount = YES;
177
288
  config = [[InputConfig alloc] init];
178
289
  }
179
-
290
+
180
291
  // any style prop changes:
181
292
  // firstly we create the new config for the changes
182
-
293
+
183
294
  InputConfig *newConfig = [config copy];
184
-
185
- if(newViewProps.color != oldViewProps.color) {
186
- if(isColorMeaningful(newViewProps.color)) {
295
+
296
+ if (newViewProps.color != oldViewProps.color) {
297
+ if (isColorMeaningful(newViewProps.color)) {
187
298
  UIColor *uiColor = RCTUIColorFromSharedColor(newViewProps.color);
188
299
  [newConfig setPrimaryColor:uiColor];
189
300
  } else {
@@ -191,362 +302,516 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
191
302
  }
192
303
  stylePropChanged = YES;
193
304
  }
194
-
195
- if(newViewProps.fontSize != oldViewProps.fontSize) {
196
- if(newViewProps.fontSize) {
197
- NSNumber* fontSize = @(newViewProps.fontSize);
305
+
306
+ if (newViewProps.fontSize != oldViewProps.fontSize) {
307
+ if (newViewProps.fontSize) {
308
+ NSNumber *fontSize = @(newViewProps.fontSize);
198
309
  [newConfig setPrimaryFontSize:fontSize];
199
310
  } else {
200
311
  [newConfig setPrimaryFontSize:nullptr];
201
312
  }
202
313
  stylePropChanged = YES;
203
314
  }
204
-
205
- if(newViewProps.fontWeight != oldViewProps.fontWeight) {
206
- if(!newViewProps.fontWeight.empty()) {
207
- [newConfig setPrimaryFontWeight:[NSString fromCppString:newViewProps.fontWeight]];
315
+
316
+ if (newViewProps.fontWeight != oldViewProps.fontWeight) {
317
+ if (!newViewProps.fontWeight.empty()) {
318
+ [newConfig
319
+ setPrimaryFontWeight:[NSString
320
+ fromCppString:newViewProps.fontWeight]];
208
321
  } else {
209
322
  [newConfig setPrimaryFontWeight:nullptr];
210
323
  }
211
324
  stylePropChanged = YES;
212
325
  }
213
-
214
- if(newViewProps.fontFamily != oldViewProps.fontFamily) {
215
- if(!newViewProps.fontFamily.empty()) {
216
- [newConfig setPrimaryFontFamily:[NSString fromCppString:newViewProps.fontFamily]];
326
+
327
+ if (newViewProps.fontFamily != oldViewProps.fontFamily) {
328
+ if (!newViewProps.fontFamily.empty()) {
329
+ [newConfig
330
+ setPrimaryFontFamily:[NSString
331
+ fromCppString:newViewProps.fontFamily]];
217
332
  } else {
218
333
  [newConfig setPrimaryFontFamily:nullptr];
219
334
  }
220
335
  stylePropChanged = YES;
221
336
  }
222
-
337
+
223
338
  // rich text style
224
-
225
- if(newViewProps.htmlStyle.h1.fontSize != oldViewProps.htmlStyle.h1.fontSize) {
339
+
340
+ if (newViewProps.htmlStyle.h1.fontSize !=
341
+ oldViewProps.htmlStyle.h1.fontSize) {
226
342
  [newConfig setH1FontSize:newViewProps.htmlStyle.h1.fontSize];
227
343
  stylePropChanged = YES;
228
344
  }
229
-
230
- if(newViewProps.htmlStyle.h1.bold != oldViewProps.htmlStyle.h1.bold) {
345
+
346
+ if (newViewProps.htmlStyle.h1.bold != oldViewProps.htmlStyle.h1.bold) {
231
347
  [newConfig setH1Bold:newViewProps.htmlStyle.h1.bold];
348
+
349
+ // Update style blocks for bold
350
+ newViewProps.htmlStyle.h1.bold ? [self addStyleBlock:H1 to:Bold]
351
+ : [self removeStyleBlock:H1 from:Bold];
352
+
232
353
  stylePropChanged = YES;
233
354
  }
234
-
235
- if(newViewProps.htmlStyle.h2.fontSize != oldViewProps.htmlStyle.h2.fontSize) {
355
+
356
+ if (newViewProps.htmlStyle.h2.fontSize !=
357
+ oldViewProps.htmlStyle.h2.fontSize) {
236
358
  [newConfig setH2FontSize:newViewProps.htmlStyle.h2.fontSize];
237
359
  stylePropChanged = YES;
238
360
  }
239
-
240
- if(newViewProps.htmlStyle.h2.bold != oldViewProps.htmlStyle.h2.bold) {
361
+
362
+ if (newViewProps.htmlStyle.h2.bold != oldViewProps.htmlStyle.h2.bold) {
241
363
  [newConfig setH2Bold:newViewProps.htmlStyle.h2.bold];
364
+
365
+ // Update style blocks for bold
366
+ newViewProps.htmlStyle.h2.bold ? [self addStyleBlock:H2 to:Bold]
367
+ : [self removeStyleBlock:H2 from:Bold];
368
+
242
369
  stylePropChanged = YES;
243
370
  }
244
-
245
- if(newViewProps.htmlStyle.h3.fontSize != oldViewProps.htmlStyle.h3.fontSize) {
371
+
372
+ if (newViewProps.htmlStyle.h3.fontSize !=
373
+ oldViewProps.htmlStyle.h3.fontSize) {
246
374
  [newConfig setH3FontSize:newViewProps.htmlStyle.h3.fontSize];
247
375
  stylePropChanged = YES;
248
376
  }
249
-
250
- if(newViewProps.htmlStyle.h3.bold != oldViewProps.htmlStyle.h3.bold) {
377
+
378
+ if (newViewProps.htmlStyle.h3.bold != oldViewProps.htmlStyle.h3.bold) {
251
379
  [newConfig setH3Bold:newViewProps.htmlStyle.h3.bold];
380
+
381
+ // Update style blocks for bold
382
+ newViewProps.htmlStyle.h3.bold ? [self addStyleBlock:H3 to:Bold]
383
+ : [self removeStyleBlock:H3 from:Bold];
384
+
252
385
  stylePropChanged = YES;
253
386
  }
254
-
255
- if(newViewProps.htmlStyle.blockquote.borderColor != oldViewProps.htmlStyle.blockquote.borderColor) {
256
- if(isColorMeaningful(newViewProps.htmlStyle.blockquote.borderColor)) {
257
- [newConfig setBlockquoteBorderColor:RCTUIColorFromSharedColor(newViewProps.htmlStyle.blockquote.borderColor)];
387
+
388
+ if (newViewProps.htmlStyle.h4.fontSize !=
389
+ oldViewProps.htmlStyle.h4.fontSize) {
390
+ [newConfig setH4FontSize:newViewProps.htmlStyle.h4.fontSize];
391
+ stylePropChanged = YES;
392
+ }
393
+
394
+ if (newViewProps.htmlStyle.h4.bold != oldViewProps.htmlStyle.h4.bold) {
395
+ [newConfig setH4Bold:newViewProps.htmlStyle.h4.bold];
396
+
397
+ // Update style blocks for bold
398
+ newViewProps.htmlStyle.h4.bold ? [self addStyleBlock:H4 to:Bold]
399
+ : [self removeStyleBlock:H4 from:Bold];
400
+
401
+ stylePropChanged = YES;
402
+ }
403
+
404
+ if (newViewProps.htmlStyle.h5.fontSize !=
405
+ oldViewProps.htmlStyle.h5.fontSize) {
406
+ [newConfig setH5FontSize:newViewProps.htmlStyle.h5.fontSize];
407
+ stylePropChanged = YES;
408
+ }
409
+
410
+ if (newViewProps.htmlStyle.h5.bold != oldViewProps.htmlStyle.h5.bold) {
411
+ [newConfig setH5Bold:newViewProps.htmlStyle.h5.bold];
412
+
413
+ // Update style blocks for bold
414
+ newViewProps.htmlStyle.h5.bold ? [self addStyleBlock:H5 to:Bold]
415
+ : [self removeStyleBlock:H5 from:Bold];
416
+
417
+ stylePropChanged = YES;
418
+ }
419
+
420
+ if (newViewProps.htmlStyle.h6.fontSize !=
421
+ oldViewProps.htmlStyle.h6.fontSize) {
422
+ [newConfig setH6FontSize:newViewProps.htmlStyle.h6.fontSize];
423
+ stylePropChanged = YES;
424
+ }
425
+
426
+ if (newViewProps.htmlStyle.h6.bold != oldViewProps.htmlStyle.h6.bold) {
427
+ [newConfig setH6Bold:newViewProps.htmlStyle.h6.bold];
428
+
429
+ // Update style blocks for bold
430
+ newViewProps.htmlStyle.h6.bold ? [self addStyleBlock:H6 to:Bold]
431
+ : [self removeStyleBlock:H6 from:Bold];
432
+
433
+ stylePropChanged = YES;
434
+ }
435
+
436
+ if (newViewProps.htmlStyle.blockquote.borderColor !=
437
+ oldViewProps.htmlStyle.blockquote.borderColor) {
438
+ if (isColorMeaningful(newViewProps.htmlStyle.blockquote.borderColor)) {
439
+ [newConfig setBlockquoteBorderColor:RCTUIColorFromSharedColor(
440
+ newViewProps.htmlStyle.blockquote
441
+ .borderColor)];
258
442
  stylePropChanged = YES;
259
443
  }
260
444
  }
261
-
262
- if(newViewProps.htmlStyle.blockquote.borderWidth != oldViewProps.htmlStyle.blockquote.borderWidth) {
263
- [newConfig setBlockquoteBorderWidth:newViewProps.htmlStyle.blockquote.borderWidth];
445
+
446
+ if (newViewProps.htmlStyle.blockquote.borderWidth !=
447
+ oldViewProps.htmlStyle.blockquote.borderWidth) {
448
+ [newConfig
449
+ setBlockquoteBorderWidth:newViewProps.htmlStyle.blockquote.borderWidth];
264
450
  stylePropChanged = YES;
265
451
  }
266
-
267
- if(newViewProps.htmlStyle.blockquote.gapWidth != oldViewProps.htmlStyle.blockquote.gapWidth) {
268
- [newConfig setBlockquoteGapWidth:newViewProps.htmlStyle.blockquote.gapWidth];
452
+
453
+ if (newViewProps.htmlStyle.blockquote.gapWidth !=
454
+ oldViewProps.htmlStyle.blockquote.gapWidth) {
455
+ [newConfig
456
+ setBlockquoteGapWidth:newViewProps.htmlStyle.blockquote.gapWidth];
269
457
  stylePropChanged = YES;
270
458
  }
271
-
272
- // since this prop defaults to undefined on JS side, we need to force set the value on first mount
273
- if(newViewProps.htmlStyle.blockquote.color != oldViewProps.htmlStyle.blockquote.color || isFirstMount) {
274
- if(isColorMeaningful(newViewProps.htmlStyle.blockquote.color)) {
275
- [newConfig setBlockquoteColor:RCTUIColorFromSharedColor(newViewProps.htmlStyle.blockquote.color)];
459
+
460
+ // since this prop defaults to undefined on JS side, we need to force set the
461
+ // value on first mount
462
+ if (newViewProps.htmlStyle.blockquote.color !=
463
+ oldViewProps.htmlStyle.blockquote.color ||
464
+ isFirstMount) {
465
+ if (isColorMeaningful(newViewProps.htmlStyle.blockquote.color)) {
466
+ [newConfig
467
+ setBlockquoteColor:RCTUIColorFromSharedColor(
468
+ newViewProps.htmlStyle.blockquote.color)];
276
469
  } else {
277
470
  [newConfig setBlockquoteColor:[newConfig primaryColor]];
278
471
  }
279
472
  stylePropChanged = YES;
280
473
  }
281
-
282
- if(newViewProps.htmlStyle.code.color != oldViewProps.htmlStyle.code.color) {
283
- if(isColorMeaningful(newViewProps.htmlStyle.code.color)) {
284
- [newConfig setInlineCodeFgColor:RCTUIColorFromSharedColor(newViewProps.htmlStyle.code.color)];
474
+
475
+ if (newViewProps.htmlStyle.code.color != oldViewProps.htmlStyle.code.color) {
476
+ if (isColorMeaningful(newViewProps.htmlStyle.code.color)) {
477
+ [newConfig setInlineCodeFgColor:RCTUIColorFromSharedColor(
478
+ newViewProps.htmlStyle.code.color)];
285
479
  stylePropChanged = YES;
286
480
  }
287
481
  }
288
-
289
- if(newViewProps.htmlStyle.code.backgroundColor != oldViewProps.htmlStyle.code.backgroundColor) {
290
- if(isColorMeaningful(newViewProps.htmlStyle.code.backgroundColor)) {
291
- [newConfig setInlineCodeBgColor:RCTUIColorFromSharedColor(newViewProps.htmlStyle.code.backgroundColor)];
482
+
483
+ if (newViewProps.htmlStyle.code.backgroundColor !=
484
+ oldViewProps.htmlStyle.code.backgroundColor) {
485
+ if (isColorMeaningful(newViewProps.htmlStyle.code.backgroundColor)) {
486
+ [newConfig setInlineCodeBgColor:RCTUIColorFromSharedColor(
487
+ newViewProps.htmlStyle.code
488
+ .backgroundColor)];
292
489
  stylePropChanged = YES;
293
490
  }
294
491
  }
295
-
296
- if(newViewProps.htmlStyle.ol.gapWidth != oldViewProps.htmlStyle.ol.gapWidth) {
492
+
493
+ if (newViewProps.htmlStyle.ol.gapWidth !=
494
+ oldViewProps.htmlStyle.ol.gapWidth) {
297
495
  [newConfig setOrderedListGapWidth:newViewProps.htmlStyle.ol.gapWidth];
298
496
  stylePropChanged = YES;
299
497
  }
300
-
301
- if(newViewProps.htmlStyle.ol.marginLeft != oldViewProps.htmlStyle.ol.marginLeft) {
498
+
499
+ if (newViewProps.htmlStyle.ol.marginLeft !=
500
+ oldViewProps.htmlStyle.ol.marginLeft) {
302
501
  [newConfig setOrderedListMarginLeft:newViewProps.htmlStyle.ol.marginLeft];
303
502
  stylePropChanged = YES;
304
503
  }
305
-
306
- // since this prop defaults to undefined on JS side, we need to force set the value on first mount
307
- if(newViewProps.htmlStyle.ol.markerFontWeight != oldViewProps.htmlStyle.ol.markerFontWeight || isFirstMount) {
308
- if(!newViewProps.htmlStyle.ol.markerFontWeight.empty()) {
309
- [newConfig setOrderedListMarkerFontWeight:[NSString fromCppString: newViewProps.htmlStyle.ol.markerFontWeight]];
504
+
505
+ // since this prop defaults to undefined on JS side, we need to force set the
506
+ // value on first mount
507
+ if (newViewProps.htmlStyle.ol.markerFontWeight !=
508
+ oldViewProps.htmlStyle.ol.markerFontWeight ||
509
+ isFirstMount) {
510
+ if (!newViewProps.htmlStyle.ol.markerFontWeight.empty()) {
511
+ [newConfig
512
+ setOrderedListMarkerFontWeight:
513
+ [NSString
514
+ fromCppString:newViewProps.htmlStyle.ol.markerFontWeight]];
310
515
  } else {
311
516
  [newConfig setOrderedListMarkerFontWeight:[newConfig primaryFontWeight]];
312
517
  }
313
518
  stylePropChanged = YES;
314
519
  }
315
-
316
- // since this prop defaults to undefined on JS side, we need to force set the value on first mount
317
- if(newViewProps.htmlStyle.ol.markerColor != oldViewProps.htmlStyle.ol.markerColor || isFirstMount) {
318
- if(isColorMeaningful(newViewProps.htmlStyle.ol.markerColor)) {
319
- [newConfig setOrderedListMarkerColor:RCTUIColorFromSharedColor(newViewProps.htmlStyle.ol.markerColor)];
520
+
521
+ // since this prop defaults to undefined on JS side, we need to force set the
522
+ // value on first mount
523
+ if (newViewProps.htmlStyle.ol.markerColor !=
524
+ oldViewProps.htmlStyle.ol.markerColor ||
525
+ isFirstMount) {
526
+ if (isColorMeaningful(newViewProps.htmlStyle.ol.markerColor)) {
527
+ [newConfig
528
+ setOrderedListMarkerColor:RCTUIColorFromSharedColor(
529
+ newViewProps.htmlStyle.ol.markerColor)];
320
530
  } else {
321
531
  [newConfig setOrderedListMarkerColor:[newConfig primaryColor]];
322
532
  }
323
533
  stylePropChanged = YES;
324
534
  }
325
-
326
- if(newViewProps.htmlStyle.ul.bulletColor != oldViewProps.htmlStyle.ul.bulletColor) {
327
- if(isColorMeaningful(newViewProps.htmlStyle.ul.bulletColor)) {
328
- [newConfig setUnorderedListBulletColor:RCTUIColorFromSharedColor(newViewProps.htmlStyle.ul.bulletColor)];
535
+
536
+ if (newViewProps.htmlStyle.ul.bulletColor !=
537
+ oldViewProps.htmlStyle.ul.bulletColor) {
538
+ if (isColorMeaningful(newViewProps.htmlStyle.ul.bulletColor)) {
539
+ [newConfig setUnorderedListBulletColor:RCTUIColorFromSharedColor(
540
+ newViewProps.htmlStyle.ul
541
+ .bulletColor)];
329
542
  stylePropChanged = YES;
330
543
  }
331
544
  }
332
-
333
- if(newViewProps.htmlStyle.ul.bulletSize != oldViewProps.htmlStyle.ul.bulletSize) {
545
+
546
+ if (newViewProps.htmlStyle.ul.bulletSize !=
547
+ oldViewProps.htmlStyle.ul.bulletSize) {
334
548
  [newConfig setUnorderedListBulletSize:newViewProps.htmlStyle.ul.bulletSize];
335
549
  stylePropChanged = YES;
336
550
  }
337
-
338
- if(newViewProps.htmlStyle.ul.gapWidth != oldViewProps.htmlStyle.ul.gapWidth) {
551
+
552
+ if (newViewProps.htmlStyle.ul.gapWidth !=
553
+ oldViewProps.htmlStyle.ul.gapWidth) {
339
554
  [newConfig setUnorderedListGapWidth:newViewProps.htmlStyle.ul.gapWidth];
340
555
  stylePropChanged = YES;
341
556
  }
342
-
343
- if(newViewProps.htmlStyle.ul.marginLeft != oldViewProps.htmlStyle.ul.marginLeft) {
557
+
558
+ if (newViewProps.htmlStyle.ul.marginLeft !=
559
+ oldViewProps.htmlStyle.ul.marginLeft) {
344
560
  [newConfig setUnorderedListMarginLeft:newViewProps.htmlStyle.ul.marginLeft];
345
561
  stylePropChanged = YES;
346
562
  }
347
-
348
- if(newViewProps.htmlStyle.a.color != oldViewProps.htmlStyle.a.color) {
349
- if(isColorMeaningful(newViewProps.htmlStyle.a.color)) {
350
- [newConfig setLinkColor:RCTUIColorFromSharedColor(newViewProps.htmlStyle.a.color)];
563
+
564
+ if (newViewProps.htmlStyle.a.color != oldViewProps.htmlStyle.a.color) {
565
+ if (isColorMeaningful(newViewProps.htmlStyle.a.color)) {
566
+ [newConfig setLinkColor:RCTUIColorFromSharedColor(
567
+ newViewProps.htmlStyle.a.color)];
351
568
  stylePropChanged = YES;
352
569
  }
353
570
  }
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)];
571
+
572
+ if (newViewProps.htmlStyle.codeblock.color !=
573
+ oldViewProps.htmlStyle.codeblock.color) {
574
+ if (isColorMeaningful(newViewProps.htmlStyle.codeblock.color)) {
575
+ [newConfig
576
+ setCodeBlockFgColor:RCTUIColorFromSharedColor(
577
+ newViewProps.htmlStyle.codeblock.color)];
358
578
  stylePropChanged = YES;
359
579
  }
360
580
  }
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)];
581
+
582
+ if (newViewProps.htmlStyle.codeblock.backgroundColor !=
583
+ oldViewProps.htmlStyle.codeblock.backgroundColor) {
584
+ if (isColorMeaningful(newViewProps.htmlStyle.codeblock.backgroundColor)) {
585
+ [newConfig setCodeBlockBgColor:RCTUIColorFromSharedColor(
586
+ newViewProps.htmlStyle.codeblock
587
+ .backgroundColor)];
365
588
  stylePropChanged = YES;
366
589
  }
367
590
  }
368
-
369
- if(newViewProps.htmlStyle.codeblock.borderRadius != oldViewProps.htmlStyle.codeblock.borderRadius) {
370
- [newConfig setCodeBlockBorderRadius:newViewProps.htmlStyle.codeblock.borderRadius];
591
+
592
+ if (newViewProps.htmlStyle.codeblock.borderRadius !=
593
+ oldViewProps.htmlStyle.codeblock.borderRadius) {
594
+ [newConfig
595
+ setCodeBlockBorderRadius:newViewProps.htmlStyle.codeblock.borderRadius];
371
596
  stylePropChanged = YES;
372
597
  }
373
-
374
- if(newViewProps.htmlStyle.a.textDecorationLine != oldViewProps.htmlStyle.a.textDecorationLine) {
375
- NSString *objcString = [NSString fromCppString:newViewProps.htmlStyle.a.textDecorationLine];
376
- if([objcString isEqualToString:DecorationUnderline]) {
598
+
599
+ if (newViewProps.htmlStyle.a.textDecorationLine !=
600
+ oldViewProps.htmlStyle.a.textDecorationLine) {
601
+ NSString *objcString =
602
+ [NSString fromCppString:newViewProps.htmlStyle.a.textDecorationLine];
603
+ if ([objcString isEqualToString:DecorationUnderline]) {
377
604
  [newConfig setLinkDecorationLine:DecorationUnderline];
378
605
  } else {
379
- // both DecorationNone and a different, wrong value gets a DecorationNone here
606
+ // both DecorationNone and a different, wrong value gets a DecorationNone
607
+ // here
380
608
  [newConfig setLinkDecorationLine:DecorationNone];
381
609
  }
382
610
  stylePropChanged = YES;
383
611
  }
384
-
385
- if(newViewProps.scrollEnabled != oldViewProps.scrollEnabled || textView.scrollEnabled != newViewProps.scrollEnabled) {
612
+
613
+ if (newViewProps.scrollEnabled != oldViewProps.scrollEnabled ||
614
+ textView.scrollEnabled != newViewProps.scrollEnabled) {
386
615
  [textView setScrollEnabled:newViewProps.scrollEnabled];
387
616
  }
388
-
617
+
389
618
  folly::dynamic oldMentionStyle = oldViewProps.htmlStyle.mention;
390
619
  folly::dynamic newMentionStyle = newViewProps.htmlStyle.mention;
391
- if(oldMentionStyle != newMentionStyle) {
620
+ if (oldMentionStyle != newMentionStyle) {
392
621
  bool newSingleProps = NO;
393
-
394
- for(const auto& obj : newMentionStyle.items()) {
395
- if(obj.second.isInt() || obj.second.isString()) {
622
+
623
+ for (const auto &obj : newMentionStyle.items()) {
624
+ if (obj.second.isInt() || obj.second.isString()) {
396
625
  // we are in just a single MentionStyleProps object
397
626
  newSingleProps = YES;
398
627
  break;
399
- } else if(obj.second.isObject()) {
628
+ } else if (obj.second.isObject()) {
400
629
  // we are in map of indicators to MentionStyleProps
401
630
  newSingleProps = NO;
402
631
  break;
403
632
  }
404
633
  }
405
-
406
- if(newSingleProps) {
407
- [newConfig setMentionStyleProps:[MentionStyleProps getSinglePropsFromFollyDynamic:newMentionStyle]];
634
+
635
+ if (newSingleProps) {
636
+ [newConfig setMentionStyleProps:
637
+ [MentionStyleProps
638
+ getSinglePropsFromFollyDynamic:newMentionStyle]];
408
639
  } else {
409
- [newConfig setMentionStyleProps:[MentionStyleProps getComplexPropsFromFollyDynamic:newMentionStyle]];
640
+ [newConfig setMentionStyleProps:
641
+ [MentionStyleProps
642
+ getComplexPropsFromFollyDynamic:newMentionStyle]];
410
643
  }
411
-
644
+
412
645
  stylePropChanged = YES;
413
646
  }
414
-
415
- if(stylePropChanged) {
647
+
648
+ if (stylePropChanged) {
416
649
  // all the text needs to be rebuilt
417
- // we get the current html using old config, then switch to new config and replace text using the html
418
- // this way, the newest config attributes are being used!
419
-
650
+ // we get the current html using old config, then switch to new config and
651
+ // replace text using the html this way, the newest config attributes are
652
+ // being used!
653
+
420
654
  // the html needs to be generated using the old config
421
- NSString *currentHtml = [parser parseToHtmlFromRange:NSMakeRange(0, textView.textStorage.string.length)];
422
-
655
+ NSString *currentHtml = [parser
656
+ parseToHtmlFromRange:NSMakeRange(0,
657
+ textView.textStorage.string.length)];
658
+ // we want to preserve the selection between props changes
659
+ NSRange prevSelectedRange = textView.selectedRange;
660
+
423
661
  // now set the new config
424
662
  config = newConfig;
425
-
663
+
426
664
  // no emitting during styles reload
427
665
  blockEmitting = YES;
428
-
666
+
429
667
  // make sure everything is sound in the html
430
- NSString *initiallyProcessedHtml = [parser initiallyProcessHtml:currentHtml];
431
- if(initiallyProcessedHtml != nullptr) {
668
+ NSString *initiallyProcessedHtml =
669
+ [parser initiallyProcessHtml:currentHtml];
670
+ if (initiallyProcessedHtml != nullptr) {
432
671
  [parser replaceWholeFromHtml:initiallyProcessedHtml];
433
672
  }
434
-
673
+
435
674
  blockEmitting = NO;
436
-
675
+
437
676
  // fill the typing attributes with style props
438
- defaultTypingAttributes[NSForegroundColorAttributeName] = [config primaryColor];
677
+ defaultTypingAttributes[NSForegroundColorAttributeName] =
678
+ [config primaryColor];
439
679
  defaultTypingAttributes[NSFontAttributeName] = [config primaryFont];
440
- defaultTypingAttributes[NSUnderlineColorAttributeName] = [config primaryColor];
441
- defaultTypingAttributes[NSStrikethroughColorAttributeName] = [config primaryColor];
442
- defaultTypingAttributes[NSParagraphStyleAttributeName] = [[NSParagraphStyle alloc] init];
680
+ defaultTypingAttributes[NSUnderlineColorAttributeName] =
681
+ [config primaryColor];
682
+ defaultTypingAttributes[NSStrikethroughColorAttributeName] =
683
+ [config primaryColor];
684
+ defaultTypingAttributes[NSParagraphStyleAttributeName] =
685
+ [[NSParagraphStyle alloc] init];
443
686
  textView.typingAttributes = defaultTypingAttributes;
444
-
687
+ textView.selectedRange = prevSelectedRange;
688
+
445
689
  // update the placeholder as well
446
690
  [self refreshPlaceholderLabelStyles];
447
691
  }
448
-
692
+
449
693
  // editable
450
- if(newViewProps.editable != textView.editable) {
694
+ if (newViewProps.editable != textView.editable) {
451
695
  textView.editable = newViewProps.editable;
452
696
  }
453
-
454
- // default value - must be set before placeholder to make sure it correctly shows on first mount
455
- if(newViewProps.defaultValue != oldViewProps.defaultValue) {
456
- NSString *newDefaultValue = [NSString fromCppString:newViewProps.defaultValue];
457
-
458
- NSString *initiallyProcessedHtml = [parser initiallyProcessHtml:newDefaultValue];
459
- if(initiallyProcessedHtml == nullptr) {
697
+
698
+ // default value - must be set before placeholder to make sure it correctly
699
+ // shows on first mount
700
+ if (newViewProps.defaultValue != oldViewProps.defaultValue) {
701
+ NSString *newDefaultValue =
702
+ [NSString fromCppString:newViewProps.defaultValue];
703
+
704
+ NSString *initiallyProcessedHtml =
705
+ [parser initiallyProcessHtml:newDefaultValue];
706
+ if (initiallyProcessedHtml == nullptr) {
460
707
  // just plain text
461
708
  textView.text = newDefaultValue;
462
709
  } else {
463
710
  // we've got some seemingly proper html
464
711
  [parser replaceWholeFromHtml:initiallyProcessedHtml];
465
712
  }
713
+ textView.selectedRange = NSRange(textView.textStorage.string.length, 0);
466
714
  }
467
-
715
+
468
716
  // placeholderTextColor
469
- if(newViewProps.placeholderTextColor != oldViewProps.placeholderTextColor) {
717
+ if (newViewProps.placeholderTextColor != oldViewProps.placeholderTextColor) {
470
718
  // some real color
471
- if(isColorMeaningful(newViewProps.placeholderTextColor)) {
472
- _placeholderColor = RCTUIColorFromSharedColor(newViewProps.placeholderTextColor);
719
+ if (isColorMeaningful(newViewProps.placeholderTextColor)) {
720
+ _placeholderColor =
721
+ RCTUIColorFromSharedColor(newViewProps.placeholderTextColor);
473
722
  } else {
474
723
  _placeholderColor = nullptr;
475
724
  }
476
725
  [self refreshPlaceholderLabelStyles];
477
726
  }
478
-
727
+
479
728
  // placeholder
480
- if(newViewProps.placeholder != oldViewProps.placeholder) {
729
+ if (newViewProps.placeholder != oldViewProps.placeholder) {
481
730
  _placeholderLabel.text = [NSString fromCppString:newViewProps.placeholder];
482
731
  [self refreshPlaceholderLabelStyles];
483
732
  // additionally show placeholder on first mount if it should be there
484
- if(isFirstMount && textView.text.length == 0) {
733
+ if (isFirstMount && textView.text.length == 0) {
485
734
  [self setPlaceholderLabelShown:YES];
486
735
  }
487
736
  }
488
-
737
+
489
738
  // mention indicators
490
- auto mismatchPair = std::mismatch(
491
- newViewProps.mentionIndicators.begin(), newViewProps.mentionIndicators.end(),
492
- oldViewProps.mentionIndicators.begin(), oldViewProps.mentionIndicators.end()
493
- );
494
- if(mismatchPair.first != newViewProps.mentionIndicators.end() || mismatchPair.second != oldViewProps.mentionIndicators.end()) {
739
+ auto mismatchPair = std::mismatch(newViewProps.mentionIndicators.begin(),
740
+ newViewProps.mentionIndicators.end(),
741
+ oldViewProps.mentionIndicators.begin(),
742
+ oldViewProps.mentionIndicators.end());
743
+ if (mismatchPair.first != newViewProps.mentionIndicators.end() ||
744
+ mismatchPair.second != oldViewProps.mentionIndicators.end()) {
495
745
  NSMutableSet<NSNumber *> *newIndicators = [[NSMutableSet alloc] init];
496
- for(const std::string &item : newViewProps.mentionIndicators) {
497
- if(item.length() == 1) {
746
+ for (const std::string &item : newViewProps.mentionIndicators) {
747
+ if (item.length() == 1) {
498
748
  [newIndicators addObject:@(item[0])];
499
749
  }
500
750
  }
501
751
  [config setMentionIndicators:newIndicators];
502
752
  }
503
-
753
+
754
+ // linkRegex
755
+ LinkRegexConfig *oldRegexConfig =
756
+ [[LinkRegexConfig alloc] initWithLinkRegexProp:oldViewProps.linkRegex];
757
+ LinkRegexConfig *newRegexConfig =
758
+ [[LinkRegexConfig alloc] initWithLinkRegexProp:newViewProps.linkRegex];
759
+ if (![newRegexConfig isEqualToConfig:oldRegexConfig]) {
760
+ [config setLinkRegexConfig:newRegexConfig];
761
+ }
762
+
504
763
  // selection color sets both selection and cursor on iOS (just as in RN)
505
- if(newViewProps.selectionColor != oldViewProps.selectionColor) {
506
- if(isColorMeaningful(newViewProps.selectionColor)) {
507
- textView.tintColor = RCTUIColorFromSharedColor(newViewProps.selectionColor);
764
+ if (newViewProps.selectionColor != oldViewProps.selectionColor) {
765
+ if (isColorMeaningful(newViewProps.selectionColor)) {
766
+ textView.tintColor =
767
+ RCTUIColorFromSharedColor(newViewProps.selectionColor);
508
768
  } else {
509
769
  textView.tintColor = nullptr;
510
770
  }
511
771
  }
512
-
772
+
513
773
  // autoCapitalize
514
- if(newViewProps.autoCapitalize != oldViewProps.autoCapitalize) {
774
+ if (newViewProps.autoCapitalize != oldViewProps.autoCapitalize) {
515
775
  NSString *str = [NSString fromCppString:newViewProps.autoCapitalize];
516
- if([str isEqualToString: @"none"]) {
776
+ if ([str isEqualToString:@"none"]) {
517
777
  textView.autocapitalizationType = UITextAutocapitalizationTypeNone;
518
- } else if([str isEqualToString: @"sentences"]) {
778
+ } else if ([str isEqualToString:@"sentences"]) {
519
779
  textView.autocapitalizationType = UITextAutocapitalizationTypeSentences;
520
- } else if([str isEqualToString: @"words"]) {
780
+ } else if ([str isEqualToString:@"words"]) {
521
781
  textView.autocapitalizationType = UITextAutocapitalizationTypeWords;
522
- } else if([str isEqualToString: @"characters"]) {
523
- textView.autocapitalizationType = UITextAutocapitalizationTypeAllCharacters;
782
+ } else if ([str isEqualToString:@"characters"]) {
783
+ textView.autocapitalizationType =
784
+ UITextAutocapitalizationTypeAllCharacters;
524
785
  }
525
-
526
- // textView needs to be refocused on autocapitalization type change and we don't want to emit these events
527
- if([textView isFirstResponder]) {
786
+
787
+ // textView needs to be refocused on autocapitalization type change and we
788
+ // don't want to emit these events
789
+ if ([textView isFirstResponder]) {
528
790
  _emitFocusBlur = NO;
529
791
  [textView reactBlur];
530
792
  [textView reactFocus];
531
793
  _emitFocusBlur = YES;
532
794
  }
533
795
  }
534
-
796
+
535
797
  // isOnChangeHtmlSet
536
798
  _emitHtml = newViewProps.isOnChangeHtmlSet;
537
-
799
+
800
+ // isOnChangeTextSet
801
+ _emitTextChange = newViewProps.isOnChangeTextSet;
802
+
538
803
  [super updateProps:props oldProps:oldProps];
539
804
  // run the changes callback
540
805
  [self anyTextMayHaveBeenModified];
541
-
806
+
542
807
  // autofocus - needs to be done at the very end
543
- if(isFirstMount && newViewProps.autoFocus) {
808
+ if (isFirstMount && newViewProps.autoFocus) {
544
809
  [textView reactFocus];
545
810
  }
546
811
  }
547
812
 
548
813
  - (void)setPlaceholderLabelShown:(BOOL)shown {
549
- if(shown) {
814
+ if (shown) {
550
815
  [self refreshPlaceholderLabelStyles];
551
816
  _placeholderLabel.hidden = NO;
552
817
  } else {
@@ -556,10 +821,12 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
556
821
 
557
822
  - (void)refreshPlaceholderLabelStyles {
558
823
  NSMutableDictionary *newAttrs = [defaultTypingAttributes mutableCopy];
559
- if(_placeholderColor != nullptr) {
824
+ if (_placeholderColor != nullptr) {
560
825
  newAttrs[NSForegroundColorAttributeName] = _placeholderColor;
561
826
  }
562
- NSAttributedString *newAttrStr = [[NSAttributedString alloc] initWithString:_placeholderLabel.text attributes: newAttrs];
827
+ NSAttributedString *newAttrStr =
828
+ [[NSAttributedString alloc] initWithString:_placeholderLabel.text
829
+ attributes:newAttrs];
563
830
  _placeholderLabel.attributedText = newAttrStr;
564
831
  }
565
832
 
@@ -567,247 +834,371 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
567
834
 
568
835
  - (CGSize)measureSize:(CGFloat)maxWidth {
569
836
  // copy the the whole attributed string
570
- NSMutableAttributedString *currentStr = [[NSMutableAttributedString alloc] initWithAttributedString:textView.textStorage];
571
-
572
- // edge case: empty input should still be of a height of a single line, so we add a mock "I" character
573
- if([currentStr length] == 0 ) {
574
- [currentStr appendAttributedString:
575
- [[NSAttributedString alloc] initWithString:@"I" attributes:textView.typingAttributes]
576
- ];
577
- }
578
-
579
- // edge case: input with only a zero width space should still be of a height of a single line, so we add a mock "I" character
580
- if([currentStr length] == 1 && [[currentStr.string substringWithRange:NSMakeRange(0, 1)] isEqualToString:@"\u200B"]) {
581
- [currentStr appendAttributedString:
582
- [[NSAttributedString alloc] initWithString:@"I" attributes:textView.typingAttributes]
583
- ];
584
- }
585
-
586
- // edge case: trailing newlines aren't counted towards height calculations, so we add a mock "I" character
587
- if(currentStr.length > 0) {
588
- unichar lastChar = [currentStr.string characterAtIndex:currentStr.length-1];
589
- if([[NSCharacterSet newlineCharacterSet] characterIsMember:lastChar]) {
590
- [currentStr appendAttributedString:
591
- [[NSAttributedString alloc] initWithString:@"I" attributes:defaultTypingAttributes]
592
- ];
837
+ NSMutableAttributedString *currentStr = [[NSMutableAttributedString alloc]
838
+ initWithAttributedString:textView.textStorage];
839
+
840
+ // edge case: empty input should still be of a height of a single line, so we
841
+ // add a mock "I" character
842
+ if ([currentStr length] == 0) {
843
+ [currentStr
844
+ appendAttributedString:[[NSAttributedString alloc]
845
+ initWithString:@"I"
846
+ attributes:textView.typingAttributes]];
847
+ }
848
+
849
+ // edge case: input with only a zero width space should still be of a height
850
+ // of a single line, so we add a mock "I" character
851
+ if ([currentStr length] == 1 &&
852
+ [[currentStr.string substringWithRange:NSMakeRange(0, 1)]
853
+ isEqualToString:@"\u200B"]) {
854
+ [currentStr
855
+ appendAttributedString:[[NSAttributedString alloc]
856
+ initWithString:@"I"
857
+ attributes:textView.typingAttributes]];
858
+ }
859
+
860
+ // edge case: trailing newlines aren't counted towards height calculations, so
861
+ // we add a mock "I" character
862
+ if (currentStr.length > 0) {
863
+ unichar lastChar =
864
+ [currentStr.string characterAtIndex:currentStr.length - 1];
865
+ if ([[NSCharacterSet newlineCharacterSet] characterIsMember:lastChar]) {
866
+ [currentStr
867
+ appendAttributedString:[[NSAttributedString alloc]
868
+ initWithString:@"I"
869
+ attributes:defaultTypingAttributes]];
593
870
  }
594
871
  }
595
-
596
- CGRect boundingBox = [currentStr boundingRectWithSize:
597
- CGSizeMake(maxWidth, CGFLOAT_MAX)
598
- options: NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
599
- context: nullptr
600
- ];
872
+
873
+ CGRect boundingBox =
874
+ [currentStr boundingRectWithSize:CGSizeMake(maxWidth, CGFLOAT_MAX)
875
+ options:NSStringDrawingUsesLineFragmentOrigin |
876
+ NSStringDrawingUsesFontLeading
877
+ context:nullptr];
601
878
 
602
879
  return CGSizeMake(maxWidth, ceil(boundingBox.size.height));
603
880
  }
604
881
 
605
882
  // make sure the newest state is kept in _state property
606
- - (void)updateState:(State::Shared const &)state oldState:(State::Shared const &)oldState {
607
- _state = std::static_pointer_cast<const EnrichedTextInputViewShadowNode::ConcreteState>(state);
608
-
609
- // first render with all the needed stuff already defined (state and componentView)
610
- // so we need to run a single height calculation for any initial values
611
- if(oldState == nullptr) {
883
+ - (void)updateState:(State::Shared const &)state
884
+ oldState:(State::Shared const &)oldState {
885
+ _state = std::static_pointer_cast<
886
+ const EnrichedTextInputViewShadowNode::ConcreteState>(state);
887
+
888
+ // first render with all the needed stuff already defined (state and
889
+ // componentView) so we need to run a single height calculation for any
890
+ // initial values
891
+ if (oldState == nullptr) {
612
892
  [self tryUpdatingHeight];
613
893
  }
614
894
  }
615
895
 
616
896
  - (void)tryUpdatingHeight {
617
- if(_state == nullptr) {
897
+ if (_state == nullptr) {
618
898
  return;
619
899
  }
620
900
  _componentViewHeightUpdateCounter++;
621
901
  auto selfRef = wrapManagedObjectWeakly(self);
622
- _state->updateState(EnrichedTextInputViewState(_componentViewHeightUpdateCounter, selfRef));
902
+ _state->updateState(
903
+ EnrichedTextInputViewState(_componentViewHeightUpdateCounter, selfRef));
623
904
  }
624
905
 
625
906
  // MARK: - Active styles
626
907
 
627
908
  - (void)tryUpdatingActiveStyles {
628
- // style updates are emitted only if something differs from the previously active styles
909
+ // style updates are emitted only if something differs from the previously
910
+ // active styles
629
911
  BOOL updateNeeded = NO;
630
-
631
- // active styles are kept in a separate set until we're sure they can be emitted
912
+
913
+ // active styles are kept in a separate set until we're sure they can be
914
+ // emitted
632
915
  NSMutableSet *newActiveStyles = [_activeStyles mutableCopy];
633
-
916
+
917
+ // currently blocked styles are subject to change (e.g. bold being blocked by
918
+ // headings might change in reaction to prop change) so they also are kept
919
+ // separately
920
+ NSMutableSet *newBlockedStyles = [_blockedStyles mutableCopy];
921
+
634
922
  // data for onLinkDetected event
635
923
  LinkData *detectedLinkData;
636
924
  NSRange detectedLinkRange = NSMakeRange(0, 0);
637
-
925
+
638
926
  // data for onMentionDetected event
639
927
  MentionParams *detectedMentionParams;
640
928
  NSRange detectedMentionRange = NSMakeRange(0, 0);
641
929
 
642
- for (NSNumber* type in stylesDict) {
930
+ for (NSNumber *type in stylesDict) {
643
931
  id<BaseStyleProtocol> style = stylesDict[type];
644
- BOOL wasActive = [newActiveStyles containsObject: type];
932
+
933
+ BOOL wasActive = [newActiveStyles containsObject:type];
645
934
  BOOL isActive = [style detectStyle:textView.selectedRange];
646
- if(wasActive != isActive) {
935
+
936
+ BOOL wasBlocked = [newBlockedStyles containsObject:type];
937
+ BOOL isBlocked = [self isStyle:(StyleType)[type integerValue]
938
+ activeInMap:blockingStyles];
939
+
940
+ if (wasActive != isActive) {
647
941
  updateNeeded = YES;
648
- if(isActive) {
942
+ if (isActive) {
649
943
  [newActiveStyles addObject:type];
650
944
  } else {
651
945
  [newActiveStyles removeObject:type];
652
946
  }
653
947
  }
654
-
948
+
949
+ // blocked state change for a style also needs an update
950
+ if (wasBlocked != isBlocked) {
951
+ updateNeeded = YES;
952
+ if (isBlocked) {
953
+ [newBlockedStyles addObject:type];
954
+ } else {
955
+ [newBlockedStyles removeObject:type];
956
+ }
957
+ }
958
+
655
959
  // onLinkDetected event
656
- if(isActive && [type intValue] == [LinkStyle getStyleType]) {
960
+ if (isActive && [type intValue] == [LinkStyle getStyleType]) {
657
961
  // get the link data
658
962
  LinkData *candidateLinkData;
659
963
  NSRange candidateLinkRange = NSMakeRange(0, 0);
660
- LinkStyle *linkStyleClass = (LinkStyle *)stylesDict[@([LinkStyle getStyleType])];
661
- if(linkStyleClass != nullptr) {
662
- candidateLinkData = [linkStyleClass getLinkDataAt:textView.selectedRange.location];
663
- candidateLinkRange = [linkStyleClass getFullLinkRangeAt:textView.selectedRange.location];
964
+ LinkStyle *linkStyleClass =
965
+ (LinkStyle *)stylesDict[@([LinkStyle getStyleType])];
966
+ if (linkStyleClass != nullptr) {
967
+ candidateLinkData =
968
+ [linkStyleClass getLinkDataAt:textView.selectedRange.location];
969
+ candidateLinkRange =
970
+ [linkStyleClass getFullLinkRangeAt:textView.selectedRange.location];
664
971
  }
665
-
666
- if(wasActive == NO) {
972
+
973
+ if (wasActive == NO) {
667
974
  // we changed selection from non-link to a link
668
975
  detectedLinkData = candidateLinkData;
669
976
  detectedLinkRange = candidateLinkRange;
670
- } else if(
671
- ![_recentlyActiveLinkData.url isEqualToString:candidateLinkData.url] ||
672
- ![_recentlyActiveLinkData.text isEqualToString:candidateLinkData.text] ||
673
- !NSEqualRanges(_recentlyActiveLinkRange, candidateLinkRange)
674
- ) {
675
- // we changed selection from one link to the other or modified current link's text
977
+ } else if (![_recentlyActiveLinkData.url
978
+ isEqualToString:candidateLinkData.url] ||
979
+ ![_recentlyActiveLinkData.text
980
+ isEqualToString:candidateLinkData.text] ||
981
+ !NSEqualRanges(_recentlyActiveLinkRange, candidateLinkRange)) {
982
+ // we changed selection from one link to the other or modified current
983
+ // link's text
676
984
  detectedLinkData = candidateLinkData;
677
985
  detectedLinkRange = candidateLinkRange;
678
986
  }
679
987
  }
680
-
988
+
681
989
  // onMentionDetected event
682
- if(isActive && [type intValue] == [MentionStyle getStyleType]) {
990
+ if (isActive && [type intValue] == [MentionStyle getStyleType]) {
683
991
  // get mention data
684
992
  MentionParams *candidateMentionParams;
685
993
  NSRange candidateMentionRange = NSMakeRange(0, 0);
686
- MentionStyle *mentionStyleClass = (MentionStyle *)stylesDict[@([MentionStyle getStyleType])];
687
- if(mentionStyleClass != nullptr) {
688
- candidateMentionParams = [mentionStyleClass getMentionParamsAt:textView.selectedRange.location];
689
- candidateMentionRange = [mentionStyleClass getFullMentionRangeAt:textView.selectedRange.location];
994
+ MentionStyle *mentionStyleClass =
995
+ (MentionStyle *)stylesDict[@([MentionStyle getStyleType])];
996
+ if (mentionStyleClass != nullptr) {
997
+ candidateMentionParams = [mentionStyleClass
998
+ getMentionParamsAt:textView.selectedRange.location];
999
+ candidateMentionRange = [mentionStyleClass
1000
+ getFullMentionRangeAt:textView.selectedRange.location];
690
1001
  }
691
-
692
- if(wasActive == NO) {
1002
+
1003
+ if (wasActive == NO) {
693
1004
  // selection was changed from a non-mention to a mention
694
1005
  detectedMentionParams = candidateMentionParams;
695
1006
  detectedMentionRange = candidateMentionRange;
696
- } else if(
697
- ![_recentlyActiveMentionParams.text isEqualToString:candidateMentionParams.text] ||
698
- ![_recentlyActiveMentionParams.attributes isEqualToString:candidateMentionParams.attributes] ||
699
- !NSEqualRanges(_recentlyActiveMentionRange, candidateMentionRange)
700
- ) {
1007
+ } else if (![_recentlyActiveMentionParams.text
1008
+ isEqualToString:candidateMentionParams.text] ||
1009
+ ![_recentlyActiveMentionParams.attributes
1010
+ isEqualToString:candidateMentionParams.attributes] ||
1011
+ !NSEqualRanges(_recentlyActiveMentionRange,
1012
+ candidateMentionRange)) {
701
1013
  // selection changed from one mention to another
702
1014
  detectedMentionParams = candidateMentionParams;
703
1015
  detectedMentionRange = candidateMentionRange;
704
1016
  }
705
1017
  }
706
1018
  }
707
-
708
- if(updateNeeded) {
1019
+
1020
+ if (updateNeeded) {
709
1021
  auto emitter = [self getEventEmitter];
710
- if(emitter != nullptr) {
711
- // update activeStyles only if emitter is available
1022
+ if (emitter != nullptr) {
1023
+ // update activeStyles and blockedStyles only if emitter is available
712
1024
  _activeStyles = newActiveStyles;
713
-
714
- emitter->onChangeState({
715
- .isBold = [_activeStyles containsObject: @([BoldStyle getStyleType])],
716
- .isItalic = [_activeStyles containsObject: @([ItalicStyle getStyleType])],
717
- .isUnderline = [_activeStyles containsObject: @([UnderlineStyle getStyleType])],
718
- .isStrikeThrough = [_activeStyles containsObject: @([StrikethroughStyle getStyleType])],
719
- .isInlineCode = [_activeStyles containsObject: @([InlineCodeStyle getStyleType])],
720
- .isLink = [_activeStyles containsObject: @([LinkStyle getStyleType])],
721
- .isMention = [_activeStyles containsObject: @([MentionStyle getStyleType])],
722
- .isH1 = [_activeStyles containsObject: @([H1Style getStyleType])],
723
- .isH2 = [_activeStyles containsObject: @([H2Style getStyleType])],
724
- .isH3 = [_activeStyles containsObject: @([H3Style getStyleType])],
725
- .isUnorderedList = [_activeStyles containsObject: @([UnorderedListStyle getStyleType])],
726
- .isOrderedList = [_activeStyles containsObject: @([OrderedListStyle getStyleType])],
727
- .isBlockQuote = [_activeStyles containsObject: @([BlockQuoteStyle getStyleType])],
728
- .isCodeBlock = [_activeStyles containsObject: @([CodeBlockStyle getStyleType])],
729
- .isImage = [_activeStyles containsObject: @([ImageStyle getStyleType])],
1025
+ _blockedStyles = newBlockedStyles;
1026
+
1027
+ emitter->onChangeStateDeprecated({
1028
+ .isBold = [self isStyleActive:[BoldStyle getStyleType]],
1029
+ .isItalic = [self isStyleActive:[ItalicStyle getStyleType]],
1030
+ .isUnderline = [self isStyleActive:[UnderlineStyle getStyleType]],
1031
+ .isStrikeThrough =
1032
+ [self isStyleActive:[StrikethroughStyle getStyleType]],
1033
+ .isInlineCode = [self isStyleActive:[InlineCodeStyle getStyleType]],
1034
+ .isLink = [self isStyleActive:[LinkStyle getStyleType]],
1035
+ .isMention = [self isStyleActive:[MentionStyle getStyleType]],
1036
+ .isH1 = [self isStyleActive:[H1Style getStyleType]],
1037
+ .isH2 = [self isStyleActive:[H2Style getStyleType]],
1038
+ .isH3 = [self isStyleActive:[H3Style getStyleType]],
1039
+ .isH4 = [self isStyleActive:[H4Style getStyleType]],
1040
+ .isH5 = [self isStyleActive:[H5Style getStyleType]],
1041
+ .isH6 = [self isStyleActive:[H6Style getStyleType]],
1042
+ .isUnorderedList =
1043
+ [self isStyleActive:[UnorderedListStyle getStyleType]],
1044
+ .isOrderedList = [self isStyleActive:[OrderedListStyle getStyleType]],
1045
+ .isBlockQuote = [self isStyleActive:[BlockQuoteStyle getStyleType]],
1046
+ .isCodeBlock = [self isStyleActive:[CodeBlockStyle getStyleType]],
1047
+ .isImage = [self isStyleActive:[ImageStyle getStyleType]],
730
1048
  });
1049
+ emitter->onChangeState(
1050
+ {.bold = GET_STYLE_STATE([BoldStyle getStyleType]),
1051
+ .italic = GET_STYLE_STATE([ItalicStyle getStyleType]),
1052
+ .underline = GET_STYLE_STATE([UnderlineStyle getStyleType]),
1053
+ .strikeThrough = GET_STYLE_STATE([StrikethroughStyle getStyleType]),
1054
+ .inlineCode = GET_STYLE_STATE([InlineCodeStyle getStyleType]),
1055
+ .link = GET_STYLE_STATE([LinkStyle getStyleType]),
1056
+ .mention = GET_STYLE_STATE([MentionStyle getStyleType]),
1057
+ .h1 = GET_STYLE_STATE([H1Style getStyleType]),
1058
+ .h2 = GET_STYLE_STATE([H2Style getStyleType]),
1059
+ .h3 = GET_STYLE_STATE([H3Style getStyleType]),
1060
+ .h4 = GET_STYLE_STATE([H4Style getStyleType]),
1061
+ .h5 = GET_STYLE_STATE([H5Style getStyleType]),
1062
+ .h6 = GET_STYLE_STATE([H6Style getStyleType]),
1063
+ .unorderedList = GET_STYLE_STATE([UnorderedListStyle getStyleType]),
1064
+ .orderedList = GET_STYLE_STATE([OrderedListStyle getStyleType]),
1065
+ .blockQuote = GET_STYLE_STATE([BlockQuoteStyle getStyleType]),
1066
+ .codeBlock = GET_STYLE_STATE([CodeBlockStyle getStyleType]),
1067
+ .image = GET_STYLE_STATE([ImageStyle getStyleType])});
731
1068
  }
732
1069
  }
733
-
734
- if(detectedLinkData != nullptr) {
1070
+
1071
+ if (detectedLinkData != nullptr) {
735
1072
  // emit onLinkeDetected event
736
- [self emitOnLinkDetectedEvent:detectedLinkData.text url:detectedLinkData.url range:detectedLinkRange];
1073
+ [self emitOnLinkDetectedEvent:detectedLinkData.text
1074
+ url:detectedLinkData.url
1075
+ range:detectedLinkRange];
737
1076
  }
738
-
739
- if(detectedMentionParams != nullptr) {
1077
+
1078
+ if (detectedMentionParams != nullptr) {
740
1079
  // emit onMentionDetected event
741
- [self emitOnMentionDetectedEvent:detectedMentionParams.text indicator:detectedMentionParams.indicator attributes:detectedMentionParams.attributes];
742
-
1080
+ [self emitOnMentionDetectedEvent:detectedMentionParams.text
1081
+ indicator:detectedMentionParams.indicator
1082
+ attributes:detectedMentionParams.attributes];
1083
+
743
1084
  _recentlyActiveMentionParams = detectedMentionParams;
744
1085
  _recentlyActiveMentionRange = detectedMentionRange;
745
1086
  }
746
-
1087
+
747
1088
  // emit onChangeHtml event if needed
748
1089
  [self tryEmittingOnChangeHtmlEvent];
749
1090
  }
750
1091
 
1092
+ - (bool)isStyleActive:(StyleType)type {
1093
+ return [_activeStyles containsObject:@(type)];
1094
+ }
1095
+
1096
+ - (bool)isStyle:(StyleType)type activeInMap:(NSDictionary *)styleMap {
1097
+ NSArray *relatedStyles = styleMap[@(type)];
1098
+
1099
+ if (!relatedStyles) {
1100
+ return false;
1101
+ }
1102
+
1103
+ for (NSNumber *style in relatedStyles) {
1104
+ if ([_activeStyles containsObject:style]) {
1105
+ return true;
1106
+ }
1107
+ }
1108
+
1109
+ return false;
1110
+ }
1111
+
1112
+ - (void)addStyleBlock:(StyleType)blocking to:(StyleType)blocked {
1113
+ NSMutableArray *blocksArr = [blockingStyles[@(blocked)] mutableCopy];
1114
+ if (![blocksArr containsObject:@(blocking)]) {
1115
+ [blocksArr addObject:@(blocking)];
1116
+ blockingStyles[@(blocked)] = blocksArr;
1117
+ }
1118
+ }
1119
+
1120
+ - (void)removeStyleBlock:(StyleType)blocking from:(StyleType)blocked {
1121
+ NSMutableArray *blocksArr = [blockingStyles[@(blocked)] mutableCopy];
1122
+ if ([blocksArr containsObject:@(blocking)]) {
1123
+ [blocksArr removeObject:@(blocking)];
1124
+ blockingStyles[@(blocked)] = blocksArr;
1125
+ }
1126
+ }
1127
+
751
1128
  // MARK: - Native commands and events
752
1129
 
753
1130
  - (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args {
754
- if([commandName isEqualToString:@"focus"]) {
1131
+ if ([commandName isEqualToString:@"focus"]) {
755
1132
  [self focus];
756
- } else if([commandName isEqualToString:@"blur"]) {
1133
+ } else if ([commandName isEqualToString:@"blur"]) {
757
1134
  [self blur];
758
- } else if([commandName isEqualToString:@"setValue"]) {
1135
+ } else if ([commandName isEqualToString:@"setValue"]) {
759
1136
  NSString *value = (NSString *)args[0];
760
1137
  [self setValue:value];
761
- } else if([commandName isEqualToString:@"toggleBold"]) {
762
- [self toggleRegularStyle: [BoldStyle getStyleType]];
763
- } else if([commandName isEqualToString:@"toggleItalic"]) {
764
- [self toggleRegularStyle: [ItalicStyle getStyleType]];
765
- } else if([commandName isEqualToString:@"toggleUnderline"]) {
766
- [self toggleRegularStyle: [UnderlineStyle getStyleType]];
767
- } else if([commandName isEqualToString:@"toggleStrikeThrough"]) {
768
- [self toggleRegularStyle: [StrikethroughStyle getStyleType]];
769
- } else if([commandName isEqualToString:@"toggleInlineCode"]) {
770
- [self toggleRegularStyle: [InlineCodeStyle getStyleType]];
771
- } else if([commandName isEqualToString:@"addLink"]) {
772
- NSInteger start = [((NSNumber*)args[0]) integerValue];
773
- NSInteger end = [((NSNumber*)args[1]) integerValue];
1138
+ } else if ([commandName isEqualToString:@"toggleBold"]) {
1139
+ [self toggleRegularStyle:[BoldStyle getStyleType]];
1140
+ } else if ([commandName isEqualToString:@"toggleItalic"]) {
1141
+ [self toggleRegularStyle:[ItalicStyle getStyleType]];
1142
+ } else if ([commandName isEqualToString:@"toggleUnderline"]) {
1143
+ [self toggleRegularStyle:[UnderlineStyle getStyleType]];
1144
+ } else if ([commandName isEqualToString:@"toggleStrikeThrough"]) {
1145
+ [self toggleRegularStyle:[StrikethroughStyle getStyleType]];
1146
+ } else if ([commandName isEqualToString:@"toggleInlineCode"]) {
1147
+ [self toggleRegularStyle:[InlineCodeStyle getStyleType]];
1148
+ } else if ([commandName isEqualToString:@"addLink"]) {
1149
+ NSInteger start = [((NSNumber *)args[0]) integerValue];
1150
+ NSInteger end = [((NSNumber *)args[1]) integerValue];
774
1151
  NSString *text = (NSString *)args[2];
775
1152
  NSString *url = (NSString *)args[3];
776
1153
  [self addLinkAt:start end:end text:text url:url];
777
- } else if([commandName isEqualToString:@"addMention"]) {
1154
+ } else if ([commandName isEqualToString:@"addMention"]) {
778
1155
  NSString *indicator = (NSString *)args[0];
779
1156
  NSString *text = (NSString *)args[1];
780
1157
  NSString *attributes = (NSString *)args[2];
781
1158
  [self addMention:indicator text:text attributes:attributes];
782
- } else if([commandName isEqualToString:@"startMention"]) {
1159
+ } else if ([commandName isEqualToString:@"startMention"]) {
783
1160
  NSString *indicator = (NSString *)args[0];
784
1161
  [self startMentionWithIndicator:indicator];
785
- } else if([commandName isEqualToString:@"toggleH1"]) {
1162
+ } else if ([commandName isEqualToString:@"toggleH1"]) {
786
1163
  [self toggleParagraphStyle:[H1Style getStyleType]];
787
- } else if([commandName isEqualToString:@"toggleH2"]) {
1164
+ } else if ([commandName isEqualToString:@"toggleH2"]) {
788
1165
  [self toggleParagraphStyle:[H2Style getStyleType]];
789
- } else if([commandName isEqualToString:@"toggleH3"]) {
1166
+ } else if ([commandName isEqualToString:@"toggleH3"]) {
790
1167
  [self toggleParagraphStyle:[H3Style getStyleType]];
791
- } else if([commandName isEqualToString:@"toggleUnorderedList"]) {
1168
+ } else if ([commandName isEqualToString:@"toggleH4"]) {
1169
+ [self toggleParagraphStyle:[H4Style getStyleType]];
1170
+ } else if ([commandName isEqualToString:@"toggleH5"]) {
1171
+ [self toggleParagraphStyle:[H5Style getStyleType]];
1172
+ } else if ([commandName isEqualToString:@"toggleH6"]) {
1173
+ [self toggleParagraphStyle:[H6Style getStyleType]];
1174
+ } else if ([commandName isEqualToString:@"toggleUnorderedList"]) {
792
1175
  [self toggleParagraphStyle:[UnorderedListStyle getStyleType]];
793
- } else if([commandName isEqualToString:@"toggleOrderedList"]) {
1176
+ } else if ([commandName isEqualToString:@"toggleOrderedList"]) {
794
1177
  [self toggleParagraphStyle:[OrderedListStyle getStyleType]];
795
- } else if([commandName isEqualToString:@"toggleBlockQuote"]) {
1178
+ } else if ([commandName isEqualToString:@"toggleBlockQuote"]) {
796
1179
  [self toggleParagraphStyle:[BlockQuoteStyle getStyleType]];
797
- } else if([commandName isEqualToString:@"toggleCodeBlock"]) {
1180
+ } else if ([commandName isEqualToString:@"toggleCodeBlock"]) {
798
1181
  [self toggleParagraphStyle:[CodeBlockStyle getStyleType]];
799
- } else if([commandName isEqualToString:@"addImage"]) {
1182
+ } else if ([commandName isEqualToString:@"addImage"]) {
800
1183
  NSString *uri = (NSString *)args[0];
801
- CGFloat imgWidth = [(NSNumber*)args[1] floatValue];
802
- CGFloat imgHeight = [(NSNumber*)args[2] floatValue];
803
-
1184
+ CGFloat imgWidth = [(NSNumber *)args[1] floatValue];
1185
+ CGFloat imgHeight = [(NSNumber *)args[2] floatValue];
1186
+
804
1187
  [self addImage:uri width:imgWidth height:imgHeight];
1188
+ } else if ([commandName isEqualToString:@"setSelection"]) {
1189
+ NSInteger start = [((NSNumber *)args[0]) integerValue];
1190
+ NSInteger end = [((NSNumber *)args[1]) integerValue];
1191
+ [self setCustomSelection:start end:end];
1192
+ } else if ([commandName isEqualToString:@"requestHTML"]) {
1193
+ NSInteger requestId = [((NSNumber *)args[0]) integerValue];
1194
+ [self requestHTML:requestId];
805
1195
  }
806
1196
  }
807
1197
 
808
1198
  - (std::shared_ptr<EnrichedTextInputViewEventEmitter>)getEventEmitter {
809
- if(_eventEmitter != nullptr && !blockEmitting) {
810
- auto emitter = static_cast<const EnrichedTextInputViewEventEmitter &>(*_eventEmitter);
1199
+ if (_eventEmitter != nullptr && !blockEmitting) {
1200
+ auto emitter =
1201
+ static_cast<const EnrichedTextInputViewEventEmitter &>(*_eventEmitter);
811
1202
  return std::make_shared<EnrichedTextInputViewEventEmitter>(emitter);
812
1203
  } else {
813
1204
  return nullptr;
@@ -824,91 +1215,143 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
824
1215
 
825
1216
  - (void)setValue:(NSString *)value {
826
1217
  NSString *initiallyProcessedHtml = [parser initiallyProcessHtml:value];
827
- if(initiallyProcessedHtml == nullptr) {
1218
+ if (initiallyProcessedHtml == nullptr) {
828
1219
  // just plain text
829
1220
  textView.text = value;
830
1221
  } else {
831
1222
  // we've got some seemingly proper html
832
1223
  [parser replaceWholeFromHtml:initiallyProcessedHtml];
833
1224
  }
834
-
1225
+
835
1226
  // set recentlyChangedRange and check for changes
836
1227
  recentlyChangedRange = NSMakeRange(0, textView.textStorage.string.length);
1228
+ textView.selectedRange = NSRange(textView.textStorage.string.length, 0);
837
1229
  [self anyTextMayHaveBeenModified];
838
1230
  }
839
1231
 
840
- - (void)emitOnLinkDetectedEvent:(NSString *)text url:(NSString *)url range:(NSRange)range {
1232
+ - (void)setCustomSelection:(NSInteger)visibleStart end:(NSInteger)visibleEnd {
1233
+ NSString *text = textView.textStorage.string;
1234
+
1235
+ NSUInteger actualStart = [self getActualIndex:visibleStart text:text];
1236
+ NSUInteger actualEnd = [self getActualIndex:visibleEnd text:text];
1237
+
1238
+ textView.selectedRange = NSMakeRange(actualStart, actualEnd - actualStart);
1239
+ }
1240
+
1241
+ // Helper: Walks through the string skipping ZWSPs to find the Nth visible
1242
+ // character
1243
+ - (NSUInteger)getActualIndex:(NSInteger)visibleIndex text:(NSString *)text {
1244
+ NSUInteger currentVisibleCount = 0;
1245
+ NSUInteger actualIndex = 0;
1246
+
1247
+ while (actualIndex < text.length) {
1248
+ if (currentVisibleCount == visibleIndex) {
1249
+ return actualIndex;
1250
+ }
1251
+
1252
+ // If the current char is not a hidden space, it counts towards our visible
1253
+ // index.
1254
+ if ([text characterAtIndex:actualIndex] != 0x200B) {
1255
+ currentVisibleCount++;
1256
+ }
1257
+
1258
+ actualIndex++;
1259
+ }
1260
+
1261
+ return actualIndex;
1262
+ }
1263
+
1264
+ - (void)emitOnLinkDetectedEvent:(NSString *)text
1265
+ url:(NSString *)url
1266
+ range:(NSRange)range {
841
1267
  auto emitter = [self getEventEmitter];
842
- if(emitter != nullptr) {
1268
+ if (emitter != nullptr) {
843
1269
  // update recently active link info
844
1270
  LinkData *newLinkData = [[LinkData alloc] init];
845
1271
  newLinkData.text = text;
846
1272
  newLinkData.url = url;
847
1273
  _recentlyActiveLinkData = newLinkData;
848
1274
  _recentlyActiveLinkRange = range;
849
-
1275
+
850
1276
  emitter->onLinkDetected({
851
- .text = [text toCppString],
852
- .url = [url toCppString],
853
- .start = static_cast<int>(range.location),
854
- .end = static_cast<int>(range.location + range.length),
1277
+ .text = [text toCppString],
1278
+ .url = [url toCppString],
1279
+ .start = static_cast<int>(range.location),
1280
+ .end = static_cast<int>(range.location + range.length),
855
1281
  });
856
1282
  }
857
1283
  }
858
1284
 
859
- - (void)emitOnMentionDetectedEvent:(NSString *)text indicator:(NSString *)indicator attributes:(NSString *)attributes {
1285
+ - (void)emitOnMentionDetectedEvent:(NSString *)text
1286
+ indicator:(NSString *)indicator
1287
+ attributes:(NSString *)attributes {
860
1288
  auto emitter = [self getEventEmitter];
861
- if(emitter != nullptr) {
862
- emitter->onMentionDetected({
863
- .text = [text toCppString],
864
- .indicator = [indicator toCppString],
865
- .payload = [attributes toCppString]
866
- });
1289
+ if (emitter != nullptr) {
1290
+ emitter->onMentionDetected({.text = [text toCppString],
1291
+ .indicator = [indicator toCppString],
1292
+ .payload = [attributes toCppString]});
867
1293
  }
868
1294
  }
869
1295
 
870
1296
  - (void)emitOnMentionEvent:(NSString *)indicator text:(NSString *)text {
871
1297
  auto emitter = [self getEventEmitter];
872
- if(emitter != nullptr) {
873
- if(text != nullptr) {
1298
+ if (emitter != nullptr) {
1299
+ if (text != nullptr) {
874
1300
  folly::dynamic fdStr = [text toCppString];
875
- emitter->onMention({
876
- .indicator = [indicator toCppString],
877
- .text = fdStr
878
- });
1301
+ emitter->onMention({.indicator = [indicator toCppString], .text = fdStr});
879
1302
  } else {
880
1303
  folly::dynamic nul = nullptr;
881
- emitter->onMention({
882
- .indicator = [indicator toCppString],
883
- .text = nul
884
- });
1304
+ emitter->onMention({.indicator = [indicator toCppString], .text = nul});
885
1305
  }
886
1306
  }
887
1307
  }
888
1308
 
889
1309
  - (void)tryEmittingOnChangeHtmlEvent {
890
- if(!_emitHtml || textView.markedTextRange != nullptr) {
1310
+ if (!_emitHtml || textView.markedTextRange != nullptr) {
891
1311
  return;
892
1312
  }
893
1313
  auto emitter = [self getEventEmitter];
894
- if(emitter != nullptr) {
895
- NSString *htmlOutput = [parser parseToHtmlFromRange:NSMakeRange(0, textView.textStorage.string.length)];
1314
+ if (emitter != nullptr) {
1315
+ NSString *htmlOutput = [parser
1316
+ parseToHtmlFromRange:NSMakeRange(0,
1317
+ textView.textStorage.string.length)];
896
1318
  // make sure html really changed
897
- if(![htmlOutput isEqualToString:_recentlyEmittedHtml]) {
1319
+ if (![htmlOutput isEqualToString:_recentlyEmittedHtml]) {
898
1320
  _recentlyEmittedHtml = htmlOutput;
899
- emitter->onChangeHtml({
900
- .value = [htmlOutput toCppString]
901
- });
1321
+ emitter->onChangeHtml({.value = [htmlOutput toCppString]});
902
1322
  }
903
1323
  }
904
1324
  }
905
1325
 
1326
+ - (void)requestHTML:(NSInteger)requestId {
1327
+ auto emitter = [self getEventEmitter];
1328
+ if (emitter != nullptr) {
1329
+ @try {
1330
+ NSString *htmlOutput = [parser
1331
+ parseToHtmlFromRange:NSMakeRange(0,
1332
+ textView.textStorage.string.length)];
1333
+ emitter->onRequestHtmlResult({.requestId = static_cast<int>(requestId),
1334
+ .html = [htmlOutput toCppString]});
1335
+ } @catch (NSException *exception) {
1336
+ emitter->onRequestHtmlResult({.requestId = static_cast<int>(requestId),
1337
+ .html = folly::dynamic(nullptr)});
1338
+ }
1339
+ }
1340
+ }
1341
+
1342
+ - (void)emitOnKeyPressEvent:(NSString *)key {
1343
+ auto emitter = [self getEventEmitter];
1344
+ if (emitter != nullptr) {
1345
+ emitter->onInputKeyPress({.key = [key toCppString]});
1346
+ }
1347
+ }
1348
+
906
1349
  // MARK: - Styles manipulation
907
1350
 
908
1351
  - (void)toggleRegularStyle:(StyleType)type {
909
1352
  id<BaseStyleProtocol> styleClass = stylesDict[@(type)];
910
-
911
- if([self handleStyleBlocksAndConflicts:type range:textView.selectedRange]) {
1353
+
1354
+ if ([self handleStyleBlocksAndConflicts:type range:textView.selectedRange]) {
912
1355
  [styleClass applyStyle:textView.selectedRange];
913
1356
  [self anyTextMayHaveBeenModified];
914
1357
  }
@@ -917,53 +1360,82 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
917
1360
  - (void)toggleParagraphStyle:(StyleType)type {
918
1361
  id<BaseStyleProtocol> styleClass = stylesDict[@(type)];
919
1362
  // we always pass whole paragraph/s range to these styles
920
- NSRange paragraphRange = [textView.textStorage.string paragraphRangeForRange:textView.selectedRange];
921
-
922
- if([self handleStyleBlocksAndConflicts:type range:paragraphRange]) {
1363
+ NSRange paragraphRange = [textView.textStorage.string
1364
+ paragraphRangeForRange:textView.selectedRange];
1365
+
1366
+ if ([self handleStyleBlocksAndConflicts:type range:paragraphRange]) {
923
1367
  [styleClass applyStyle:paragraphRange];
924
1368
  [self anyTextMayHaveBeenModified];
925
1369
  }
926
1370
  }
927
1371
 
928
- - (void)addLinkAt:(NSInteger)start end:(NSInteger)end text:(NSString *)text url:(NSString *)url {
929
- LinkStyle *linkStyleClass = (LinkStyle *)stylesDict[@([LinkStyle getStyleType])];
930
- if(linkStyleClass == nullptr) { return; }
931
-
1372
+ - (void)addLinkAt:(NSInteger)start
1373
+ end:(NSInteger)end
1374
+ text:(NSString *)text
1375
+ url:(NSString *)url {
1376
+ LinkStyle *linkStyleClass =
1377
+ (LinkStyle *)stylesDict[@([LinkStyle getStyleType])];
1378
+ if (linkStyleClass == nullptr) {
1379
+ return;
1380
+ }
1381
+
932
1382
  // translate the output start-end notation to range
933
1383
  NSRange linkRange = NSMakeRange(start, end - start);
934
- if([self handleStyleBlocksAndConflicts:[LinkStyle getStyleType] range:linkRange]) {
935
- [linkStyleClass addLink:text url:url range:linkRange manual:YES];
1384
+ if ([self handleStyleBlocksAndConflicts:[LinkStyle getStyleType]
1385
+ range:linkRange]) {
1386
+ [linkStyleClass addLink:text
1387
+ url:url
1388
+ range:linkRange
1389
+ manual:YES
1390
+ withSelection:YES];
936
1391
  [self anyTextMayHaveBeenModified];
937
1392
  }
938
1393
  }
939
1394
 
940
- - (void)addMention:(NSString *)indicator text:(NSString *)text attributes:(NSString *)attributes {
941
- MentionStyle *mentionStyleClass = (MentionStyle *)stylesDict[@([MentionStyle getStyleType])];
942
- if(mentionStyleClass == nullptr) { return; }
943
- if([mentionStyleClass getActiveMentionRange] == nullptr) { return; }
944
-
945
- if([self handleStyleBlocksAndConflicts:[MentionStyle getStyleType] range:[[mentionStyleClass getActiveMentionRange] rangeValue]]) {
1395
+ - (void)addMention:(NSString *)indicator
1396
+ text:(NSString *)text
1397
+ attributes:(NSString *)attributes {
1398
+ MentionStyle *mentionStyleClass =
1399
+ (MentionStyle *)stylesDict[@([MentionStyle getStyleType])];
1400
+ if (mentionStyleClass == nullptr) {
1401
+ return;
1402
+ }
1403
+ if ([mentionStyleClass getActiveMentionRange] == nullptr) {
1404
+ return;
1405
+ }
1406
+
1407
+ if ([self handleStyleBlocksAndConflicts:[MentionStyle getStyleType]
1408
+ range:[[mentionStyleClass
1409
+ getActiveMentionRange]
1410
+ rangeValue]]) {
946
1411
  [mentionStyleClass addMention:indicator text:text attributes:attributes];
947
1412
  [self anyTextMayHaveBeenModified];
948
1413
  }
949
1414
  }
950
1415
 
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]) {
1416
+ - (void)addImage:(NSString *)uri width:(float)width height:(float)height {
1417
+ ImageStyle *imageStyleClass =
1418
+ (ImageStyle *)stylesDict[@([ImageStyle getStyleType])];
1419
+ if (imageStyleClass == nullptr) {
1420
+ return;
1421
+ }
1422
+
1423
+ if ([self handleStyleBlocksAndConflicts:[ImageStyle getStyleType]
1424
+ range:textView.selectedRange]) {
957
1425
  [imageStyleClass addImage:uri width:width height:height];
958
1426
  [self anyTextMayHaveBeenModified];
959
1427
  }
960
1428
  }
961
1429
 
962
1430
  - (void)startMentionWithIndicator:(NSString *)indicator {
963
- MentionStyle *mentionStyleClass = (MentionStyle *)stylesDict[@([MentionStyle getStyleType])];
964
- if(mentionStyleClass == nullptr) { return; }
965
-
966
- if([self handleStyleBlocksAndConflicts:[MentionStyle getStyleType] range:textView.selectedRange]) {
1431
+ MentionStyle *mentionStyleClass =
1432
+ (MentionStyle *)stylesDict[@([MentionStyle getStyleType])];
1433
+ if (mentionStyleClass == nullptr) {
1434
+ return;
1435
+ }
1436
+
1437
+ if ([self handleStyleBlocksAndConflicts:[MentionStyle getStyleType]
1438
+ range:textView.selectedRange]) {
967
1439
  [mentionStyleClass startMentionWithIndicator:indicator];
968
1440
  [self anyTextMayHaveBeenModified];
969
1441
  }
@@ -972,26 +1444,30 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
972
1444
  // returns false when style shouldn't be applied and true when it can be
973
1445
  - (BOOL)handleStyleBlocksAndConflicts:(StyleType)type range:(NSRange)range {
974
1446
  // handle blocking styles: if any is present we do not apply the toggled style
975
- NSArray<NSNumber *> *blocking = [self getPresentStyleTypesFrom: blockingStyles[@(type)] range:range];
976
- if(blocking.count != 0) {
1447
+ NSArray<NSNumber *> *blocking =
1448
+ [self getPresentStyleTypesFrom:blockingStyles[@(type)] range:range];
1449
+ if (blocking.count != 0) {
977
1450
  return NO;
978
1451
  }
979
-
1452
+
980
1453
  // handle conflicting styles: all of their occurences have to be removed
981
- NSArray<NSNumber *> *conflicting = [self getPresentStyleTypesFrom: conflictingStyles[@(type)] range:range];
982
- if(conflicting.count != 0) {
983
- for(NSNumber *style in conflicting) {
1454
+ NSArray<NSNumber *> *conflicting =
1455
+ [self getPresentStyleTypesFrom:conflictingStyles[@(type)] range:range];
1456
+ if (conflicting.count != 0) {
1457
+ for (NSNumber *style in conflicting) {
984
1458
  id<BaseStyleProtocol> styleClass = stylesDict[style];
985
-
986
- if(range.length >= 1) {
1459
+
1460
+ if (range.length >= 1) {
987
1461
  // for ranges, we need to remove each occurence
988
- NSArray<StylePair *> *allOccurences = [styleClass findAllOccurences:range];
989
-
990
- for(StylePair* pair in allOccurences) {
991
- [styleClass removeAttributes: [pair.rangeValue rangeValue]];
1462
+ NSArray<StylePair *> *allOccurences =
1463
+ [styleClass findAllOccurences:range];
1464
+
1465
+ for (StylePair *pair in allOccurences) {
1466
+ [styleClass removeAttributes:[pair.rangeValue rangeValue]];
992
1467
  }
993
1468
  } else {
994
- // with in-place selection, we just remove the adequate typing attributes
1469
+ // with in-place selection, we just remove the adequate typing
1470
+ // attributes
995
1471
  [styleClass removeTypingAttributes];
996
1472
  }
997
1473
  }
@@ -999,17 +1475,19 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
999
1475
  return YES;
1000
1476
  }
1001
1477
 
1002
- - (NSArray<NSNumber *> *)getPresentStyleTypesFrom:(NSArray<NSNumber *> *)types range:(NSRange)range {
1003
- NSMutableArray<NSNumber *> *resultArray = [[NSMutableArray<NSNumber *> alloc] init];
1004
- for(NSNumber *type in types) {
1478
+ - (NSArray<NSNumber *> *)getPresentStyleTypesFrom:(NSArray<NSNumber *> *)types
1479
+ range:(NSRange)range {
1480
+ NSMutableArray<NSNumber *> *resultArray =
1481
+ [[NSMutableArray<NSNumber *> alloc] init];
1482
+ for (NSNumber *type in types) {
1005
1483
  id<BaseStyleProtocol> styleClass = stylesDict[type];
1006
-
1007
- if(range.length >= 1) {
1008
- if([styleClass anyOccurence:range]) {
1484
+
1485
+ if (range.length >= 1) {
1486
+ if ([styleClass anyOccurence:range]) {
1009
1487
  [resultArray addObject:type];
1010
1488
  }
1011
1489
  } else {
1012
- if([styleClass detectStyle:range]) {
1490
+ if ([styleClass detectStyle:range]) {
1013
1491
  [resultArray addObject:type];
1014
1492
  }
1015
1493
  }
@@ -1019,49 +1497,57 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
1019
1497
 
1020
1498
  - (void)manageSelectionBasedChanges {
1021
1499
  // link typing attributes fix
1022
- LinkStyle *linkStyleClass = (LinkStyle *)stylesDict[@([LinkStyle getStyleType])];
1023
- if(linkStyleClass != nullptr) {
1500
+ LinkStyle *linkStyleClass =
1501
+ (LinkStyle *)stylesDict[@([LinkStyle getStyleType])];
1502
+ if (linkStyleClass != nullptr) {
1024
1503
  [linkStyleClass manageLinkTypingAttributes];
1025
1504
  }
1026
-
1505
+ NSString *currentString = [textView.textStorage.string copy];
1506
+
1027
1507
  // mention typing attribtues fix and active editing
1028
- MentionStyle *mentionStyleClass = (MentionStyle *)stylesDict[@([MentionStyle getStyleType])];
1029
- if(mentionStyleClass != nullptr) {
1508
+ MentionStyle *mentionStyleClass =
1509
+ (MentionStyle *)stylesDict[@([MentionStyle getStyleType])];
1510
+ if (mentionStyleClass != nullptr) {
1030
1511
  [mentionStyleClass manageMentionTypingAttributes];
1031
-
1512
+
1032
1513
  // 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]]) {
1514
+ // otherwise we would double-emit with a second call in the
1515
+ // anyTextMayHaveBeenModified method
1516
+ if ([_recentInputString isEqualToString:currentString]) {
1035
1517
  [mentionStyleClass manageMentionEditing];
1036
1518
  }
1037
1519
  }
1038
-
1520
+
1039
1521
  // typing attributes for empty lines selection reset
1040
- NSString *currentString = [textView.textStorage.string copy];
1041
- if(textView.selectedRange.length == 0 && [_recentlyEmittedString isEqualToString:currentString]) {
1522
+ if (textView.selectedRange.length == 0 &&
1523
+ [_recentInputString isEqualToString:currentString]) {
1042
1524
  // no string change means only a selection changed with no character changes
1043
- NSRange paragraphRange = [textView.textStorage.string paragraphRangeForRange:textView.selectedRange];
1044
- if(
1045
- paragraphRange.length == 0 ||
1046
- (paragraphRange.length == 1 &&
1047
- [[NSCharacterSet newlineCharacterSet] characterIsMember:[textView.textStorage.string characterAtIndex:paragraphRange.location]])
1048
- ) {
1525
+ NSRange paragraphRange = [textView.textStorage.string
1526
+ paragraphRangeForRange:textView.selectedRange];
1527
+ if (paragraphRange.length == 0 ||
1528
+ (paragraphRange.length == 1 &&
1529
+ [[NSCharacterSet newlineCharacterSet]
1530
+ characterIsMember:[textView.textStorage.string
1531
+ characterAtIndex:paragraphRange
1532
+ .location]])) {
1049
1533
  // user changed selection to an empty line (or empty line with a newline)
1050
1534
  // typing attributes need to be reset
1051
1535
  textView.typingAttributes = defaultTypingAttributes;
1052
1536
  }
1053
1537
  }
1054
-
1538
+
1055
1539
  // update active styles as well
1056
1540
  [self tryUpdatingActiveStyles];
1057
1541
  }
1058
1542
 
1059
- - (void)handleWordModificationBasedChanges:(NSString*)word inRange:(NSRange)range {
1543
+ - (void)handleWordModificationBasedChanges:(NSString *)word
1544
+ inRange:(NSRange)range {
1060
1545
  // manual links refreshing and automatic links detection handling
1061
- LinkStyle* linkStyle = [stylesDict objectForKey:@([LinkStyle getStyleType])];
1062
-
1063
- if(linkStyle != nullptr) {
1064
- // manual links need to be handled first because they can block automatic links after being refreshed
1546
+ LinkStyle *linkStyle = [stylesDict objectForKey:@([LinkStyle getStyleType])];
1547
+
1548
+ if (linkStyle != nullptr) {
1549
+ // manual links need to be handled first because they can block automatic
1550
+ // links after being refreshed
1065
1551
  [linkStyle handleManualLinks:word inRange:range];
1066
1552
  [linkStyle handleAutomaticLinks:word inRange:range];
1067
1553
  }
@@ -1069,92 +1555,109 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
1069
1555
 
1070
1556
  - (void)anyTextMayHaveBeenModified {
1071
1557
  // we don't do no text changes when working with iOS marked text
1072
- if(textView.markedTextRange != nullptr) {
1558
+ if (textView.markedTextRange != nullptr) {
1073
1559
  return;
1074
1560
  }
1075
-
1561
+
1076
1562
  // zero width space adding or removal
1077
1563
  [ZeroWidthSpaceUtils handleZeroWidthSpacesInInput:self];
1078
-
1564
+
1079
1565
  // emptying input typing attributes management
1080
- if(textView.textStorage.string.length == 0 && _recentlyEmittedString.length > 0) {
1566
+ if (textView.textStorage.string.length == 0 &&
1567
+ _recentInputString.length > 0) {
1081
1568
  // reset typing attribtues
1082
1569
  textView.typingAttributes = defaultTypingAttributes;
1083
1570
  }
1084
-
1571
+
1085
1572
  // inline code on newlines fix
1086
1573
  InlineCodeStyle *codeStyle = stylesDict[@([InlineCodeStyle getStyleType])];
1087
- if(codeStyle != nullptr) {
1574
+ if (codeStyle != nullptr) {
1088
1575
  [codeStyle handleNewlines];
1089
1576
  }
1090
-
1577
+
1091
1578
  // blockquote colors management
1092
1579
  BlockQuoteStyle *bqStyle = stylesDict[@([BlockQuoteStyle getStyleType])];
1093
- if(bqStyle != nullptr) {
1580
+ if (bqStyle != nullptr) {
1094
1581
  [bqStyle manageBlockquoteColor];
1095
1582
  }
1096
-
1583
+
1097
1584
  // codeblock font and color management
1098
1585
  CodeBlockStyle *codeBlockStyle = stylesDict[@([CodeBlockStyle getStyleType])];
1099
- if(codeBlockStyle != nullptr) {
1586
+ if (codeBlockStyle != nullptr) {
1100
1587
  [codeBlockStyle manageCodeBlockFontAndColor];
1101
1588
  }
1102
-
1589
+
1103
1590
  // improper headings fix
1104
1591
  H1Style *h1Style = stylesDict[@([H1Style getStyleType])];
1105
1592
  H2Style *h2Style = stylesDict[@([H2Style getStyleType])];
1106
1593
  H3Style *h3Style = stylesDict[@([H3Style getStyleType])];
1107
- if(h1Style != nullptr && h2Style != nullptr && h3Style != nullptr) {
1594
+ H4Style *h4Style = stylesDict[@([H4Style getStyleType])];
1595
+ H5Style *h5Style = stylesDict[@([H5Style getStyleType])];
1596
+ H6Style *h6Style = stylesDict[@([H6Style getStyleType])];
1597
+
1598
+ bool headingStylesDefined = h1Style != nullptr && h2Style != nullptr &&
1599
+ h3Style != nullptr && h4Style != nullptr &&
1600
+ h5Style != nullptr && h6Style != nullptr;
1601
+
1602
+ if (headingStylesDefined) {
1108
1603
  [h1Style handleImproperHeadings];
1109
1604
  [h2Style handleImproperHeadings];
1110
1605
  [h3Style handleImproperHeadings];
1606
+ [h4Style handleImproperHeadings];
1607
+ [h5Style handleImproperHeadings];
1608
+ [h6Style handleImproperHeadings];
1111
1609
  }
1112
-
1610
+
1113
1611
  // mentions management: removal and editing
1114
- MentionStyle *mentionStyleClass = (MentionStyle *)stylesDict[@([MentionStyle getStyleType])];
1115
- if(mentionStyleClass != nullptr) {
1612
+ MentionStyle *mentionStyleClass =
1613
+ (MentionStyle *)stylesDict[@([MentionStyle getStyleType])];
1614
+ if (mentionStyleClass != nullptr) {
1116
1615
  [mentionStyleClass handleExistingMentions];
1117
1616
  [mentionStyleClass manageMentionEditing];
1118
1617
  }
1119
-
1618
+
1120
1619
  // placholder management
1121
- if(!_placeholderLabel.hidden && textView.textStorage.string.length > 0) {
1620
+ if (!_placeholderLabel.hidden && textView.textStorage.string.length > 0) {
1122
1621
  [self setPlaceholderLabelShown:NO];
1123
- } else if(textView.textStorage.string.length == 0 && _placeholderLabel.hidden) {
1622
+ } else if (textView.textStorage.string.length == 0 &&
1623
+ _placeholderLabel.hidden) {
1124
1624
  [self setPlaceholderLabelShown:YES];
1125
1625
  }
1126
-
1127
- if(![textView.textStorage.string isEqualToString:_recentlyEmittedString]) {
1626
+
1627
+ if (![textView.textStorage.string isEqualToString:_recentInputString]) {
1128
1628
  // modified words handling
1129
- NSArray *modifiedWords = [WordsUtils getAffectedWordsFromText:textView.textStorage.string modificationRange:recentlyChangedRange];
1130
- if(modifiedWords != nullptr) {
1131
- for(NSDictionary *wordDict in modifiedWords) {
1629
+ NSArray *modifiedWords =
1630
+ [WordsUtils getAffectedWordsFromText:textView.textStorage.string
1631
+ modificationRange:recentlyChangedRange];
1632
+ if (modifiedWords != nullptr) {
1633
+ for (NSDictionary *wordDict in modifiedWords) {
1132
1634
  NSString *wordText = (NSString *)[wordDict objectForKey:@"word"];
1133
1635
  NSValue *wordRange = (NSValue *)[wordDict objectForKey:@"range"];
1134
-
1135
- if(wordText == nullptr || wordRange == nullptr) {
1636
+
1637
+ if (wordText == nullptr || wordRange == nullptr) {
1136
1638
  continue;
1137
1639
  }
1138
-
1139
- [self handleWordModificationBasedChanges:wordText inRange:[wordRange rangeValue]];
1640
+
1641
+ [self handleWordModificationBasedChanges:wordText
1642
+ inRange:[wordRange rangeValue]];
1140
1643
  }
1141
1644
  }
1142
-
1143
- // emit string without zero width spaces
1144
- NSString *stringToBeEmitted = [[textView.textStorage.string stringByReplacingOccurrencesOfString:@"\u200B" withString:@""] copy];
1145
-
1645
+
1146
1646
  // emit onChangeText event
1147
1647
  auto emitter = [self getEventEmitter];
1148
- if(emitter != nullptr) {
1149
- // set the recently emitted string only if the emitter is defined
1150
- _recentlyEmittedString = stringToBeEmitted;
1151
-
1152
- emitter->onChangeText({
1153
- .value = [stringToBeEmitted toCppString]
1154
- });
1648
+ if (emitter != nullptr && _emitTextChange) {
1649
+ // set the recent input string only if the emitter is defined
1650
+ _recentInputString = [textView.textStorage.string copy];
1651
+
1652
+ // emit string without zero width spaces
1653
+ NSString *stringToBeEmitted = [[textView.textStorage.string
1654
+ stringByReplacingOccurrencesOfString:@"\u200B"
1655
+ withString:@""] copy];
1656
+
1657
+ emitter->onChangeText({.value = [stringToBeEmitted toCppString]});
1155
1658
  }
1156
1659
  }
1157
-
1660
+
1158
1661
  // update height on each character change
1159
1662
  [self tryUpdatingHeight];
1160
1663
  // update active styles as well
@@ -1163,29 +1666,39 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
1163
1666
  [self scheduleRelayoutIfNeeded];
1164
1667
  }
1165
1668
 
1166
- // Debounced relayout helper - coalesces multiple requests into one per runloop tick
1167
- - (void)scheduleRelayoutIfNeeded
1168
- {
1669
+ // Debounced relayout helper - coalesces multiple requests into one per runloop
1670
+ // tick
1671
+ - (void)scheduleRelayoutIfNeeded {
1169
1672
  // Cancel any previously scheduled invocation to debounce
1170
- [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_performRelayout) object:nil];
1673
+ [NSObject cancelPreviousPerformRequestsWithTarget:self
1674
+ selector:@selector(_performRelayout)
1675
+ object:nil];
1171
1676
  // Schedule on next runloop cycle
1172
- [self performSelector:@selector(_performRelayout) withObject:nil afterDelay:0];
1677
+ [self performSelector:@selector(_performRelayout)
1678
+ withObject:nil
1679
+ afterDelay:0];
1173
1680
  }
1174
1681
 
1175
- - (void)_performRelayout
1176
- {
1177
- if (!textView) { return; }
1682
+ - (void)_performRelayout {
1683
+ if (!textView) {
1684
+ return;
1685
+ }
1178
1686
 
1179
1687
  dispatch_async(dispatch_get_main_queue(), ^{
1180
- NSRange wholeRange = NSMakeRange(0, self->textView.textStorage.string.length);
1688
+ NSRange wholeRange =
1689
+ NSMakeRange(0, self->textView.textStorage.string.length);
1181
1690
  NSRange actualRange = NSMakeRange(0, 0);
1182
- [self->textView.layoutManager invalidateLayoutForCharacterRange:wholeRange actualCharacterRange:&actualRange];
1691
+ [self->textView.layoutManager
1692
+ invalidateLayoutForCharacterRange:wholeRange
1693
+ actualCharacterRange:&actualRange];
1183
1694
  [self->textView.layoutManager ensureLayoutForCharacterRange:actualRange];
1184
- [self->textView.layoutManager invalidateDisplayForCharacterRange:wholeRange];
1185
-
1695
+ [self->textView.layoutManager
1696
+ invalidateDisplayForCharacterRange:wholeRange];
1697
+
1186
1698
  // We have to explicitly set contentSize
1187
1699
  // 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
1700
+ // We recall measureSize here because value returned from previous
1701
+ // measureSize may not be up-to date at that point
1189
1702
  CGSize measuredSize = [self measureSize:self->textView.frame.size.width];
1190
1703
  self->textView.contentSize = measuredSize;
1191
1704
  });
@@ -1201,34 +1714,58 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
1201
1714
 
1202
1715
  - (void)textViewDidBeginEditing:(UITextView *)textView {
1203
1716
  auto emitter = [self getEventEmitter];
1204
- if(emitter != nullptr) {
1205
- //send onFocus event if allowed
1206
- if(_emitFocusBlur) {
1717
+ if (emitter != nullptr) {
1718
+ // send onFocus event if allowed
1719
+ if (_emitFocusBlur) {
1207
1720
  emitter->onInputFocus({});
1208
1721
  }
1209
-
1210
- NSString *textAtSelection = [[[NSMutableString alloc] initWithString:textView.textStorage.string] substringWithRange: textView.selectedRange];
1211
- emitter->onChangeSelection({
1212
- .start = static_cast<int>(textView.selectedRange.location),
1213
- .end = static_cast<int>(textView.selectedRange.location + textView.selectedRange.length),
1214
- .text = [textAtSelection toCppString]
1215
- });
1216
- }
1217
- // manage selection changes since textViewDidChangeSelection sometimes doesn't run on focus
1722
+
1723
+ NSString *textAtSelection =
1724
+ [[[NSMutableString alloc] initWithString:textView.textStorage.string]
1725
+ substringWithRange:textView.selectedRange];
1726
+ emitter->onChangeSelection(
1727
+ {.start = static_cast<int>(textView.selectedRange.location),
1728
+ .end = static_cast<int>(textView.selectedRange.location +
1729
+ textView.selectedRange.length),
1730
+ .text = [textAtSelection toCppString]});
1731
+ }
1732
+ // manage selection changes since textViewDidChangeSelection sometimes doesn't
1733
+ // run on focus
1218
1734
  [self manageSelectionBasedChanges];
1219
1735
  }
1220
1736
 
1221
1737
  - (void)textViewDidEndEditing:(UITextView *)textView {
1222
1738
  auto emitter = [self getEventEmitter];
1223
- if(emitter != nullptr && _emitFocusBlur) {
1224
- //send onBlur event
1739
+ if (emitter != nullptr && _emitFocusBlur) {
1740
+ // send onBlur event
1225
1741
  emitter->onInputBlur({});
1226
1742
  }
1227
1743
  }
1228
1744
 
1229
- - (bool)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
1745
+ - (void)handleKeyPressInRange:(NSString *)text range:(NSRange)range {
1746
+ NSString *key = nil;
1747
+
1748
+ if (text.length == 0 && range.length > 0) {
1749
+ key = @"Backspace";
1750
+ } else if ([text isEqualToString:@"\n"]) {
1751
+ key = @"Enter";
1752
+ } else if ([text isEqualToString:@"\t"]) {
1753
+ key = @"Tab";
1754
+ } else if (text.length == 1) {
1755
+ key = text;
1756
+ }
1757
+
1758
+ if (key != nil) {
1759
+ [self emitOnKeyPressEvent:key];
1760
+ }
1761
+ }
1762
+
1763
+ - (bool)textView:(UITextView *)textView
1764
+ shouldChangeTextInRange:(NSRange)range
1765
+ replacementText:(NSString *)text {
1230
1766
  recentlyChangedRange = NSMakeRange(range.location, text.length);
1231
-
1767
+ [self handleKeyPressInRange:text range:range];
1768
+
1232
1769
  UnorderedListStyle *uStyle = stylesDict[@([UnorderedListStyle getStyleType])];
1233
1770
  OrderedListStyle *oStyle = stylesDict[@([OrderedListStyle getStyleType])];
1234
1771
  BlockQuoteStyle *bqStyle = stylesDict[@([BlockQuoteStyle getStyleType])];
@@ -1238,30 +1775,46 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
1238
1775
  H1Style *h1Style = stylesDict[@([H1Style getStyleType])];
1239
1776
  H2Style *h2Style = stylesDict[@([H2Style getStyleType])];
1240
1777
  H3Style *h3Style = stylesDict[@([H3Style getStyleType])];
1241
-
1242
- // some of the changes these checks do could interfere with later checks and cause a crash
1243
- // so here I rely on short circuiting evaluation of the logical expression
1244
- // either way it's not possible to have two of them come off at the same time
1245
- if(
1246
- [uStyle handleBackspaceInRange:range replacementText:text] ||
1247
- [uStyle tryHandlingListShorcutInRange:range replacementText:text] ||
1248
- [oStyle handleBackspaceInRange:range replacementText:text] ||
1249
- [oStyle tryHandlingListShorcutInRange:range replacementText:text] ||
1250
- [bqStyle handleBackspaceInRange:range replacementText:text] ||
1251
- [cbStyle handleBackspaceInRange:range replacementText:text] ||
1252
- [linkStyle handleLeadingLinkReplacement:range replacementText:text] ||
1253
- [mentionStyle handleLeadingMentionReplacement:range replacementText:text] ||
1254
- [h1Style handleNewlinesInRange:range replacementText:text] ||
1255
- [h2Style handleNewlinesInRange:range replacementText:text] ||
1256
- [h3Style handleNewlinesInRange:range replacementText:text] ||
1257
- [ZeroWidthSpaceUtils 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]
1264
- ) {
1778
+ H4Style *h4Style = stylesDict[@([H4Style getStyleType])];
1779
+ H5Style *h5Style = stylesDict[@([H5Style getStyleType])];
1780
+ H6Style *h6Style = stylesDict[@([H6Style getStyleType])];
1781
+
1782
+ // some of the changes these checks do could interfere with later checks and
1783
+ // cause a crash so here I rely on short circuiting evaluation of the logical
1784
+ // expression either way it's not possible to have two of them come off at the
1785
+ // same time
1786
+ if ([uStyle handleBackspaceInRange:range replacementText:text] ||
1787
+ [uStyle tryHandlingListShorcutInRange:range replacementText:text] ||
1788
+ [oStyle handleBackspaceInRange:range replacementText:text] ||
1789
+ [oStyle tryHandlingListShorcutInRange:range replacementText:text] ||
1790
+ [bqStyle handleBackspaceInRange:range replacementText:text] ||
1791
+ [cbStyle handleBackspaceInRange:range replacementText:text] ||
1792
+ [linkStyle handleLeadingLinkReplacement:range replacementText:text] ||
1793
+ [mentionStyle handleLeadingMentionReplacement:range
1794
+ replacementText:text] ||
1795
+ [h1Style handleNewlinesInRange:range replacementText:text] ||
1796
+ [h2Style handleNewlinesInRange:range replacementText:text] ||
1797
+ [h3Style handleNewlinesInRange:range replacementText:text] ||
1798
+ [h4Style handleNewlinesInRange:range replacementText:text] ||
1799
+ [h5Style handleNewlinesInRange:range replacementText:text] ||
1800
+ [h6Style handleNewlinesInRange:range replacementText:text] ||
1801
+ [ZeroWidthSpaceUtils handleBackspaceInRange:range
1802
+ replacementText:text
1803
+ input:self] ||
1804
+ [ParagraphAttributesUtils handleBackspaceInRange:range
1805
+ replacementText:text
1806
+ input:self] ||
1807
+ [ParagraphAttributesUtils handleResetTypingAttributesOnBackspace:range
1808
+ replacementText:text
1809
+ input:self] ||
1810
+ // CRITICAL: This callback HAS TO be always evaluated last.
1811
+ //
1812
+ // This function is the "Generic Fallback": if no specific style claims
1813
+ // the backspace action to change its state, only then do we proceed to
1814
+ // physically delete the newline and merge paragraphs.
1815
+ [ParagraphAttributesUtils handleParagraphStylesMergeOnBackspace:range
1816
+ replacementText:text
1817
+ input:self]) {
1265
1818
  [self anyTextMayHaveBeenModified];
1266
1819
  return NO;
1267
1820
  }
@@ -1271,27 +1824,97 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
1271
1824
 
1272
1825
  - (void)textViewDidChangeSelection:(UITextView *)textView {
1273
1826
  // emit the event
1274
- NSString *textAtSelection = [[[NSMutableString alloc] initWithString:textView.textStorage.string] substringWithRange: textView.selectedRange];
1275
-
1827
+ NSString *textAtSelection =
1828
+ [[[NSMutableString alloc] initWithString:textView.textStorage.string]
1829
+ substringWithRange:textView.selectedRange];
1830
+
1276
1831
  auto emitter = [self getEventEmitter];
1277
- if(emitter != nullptr) {
1832
+ if (emitter != nullptr) {
1278
1833
  // iOS range works differently because it specifies location and length
1279
- // here, start is the location, but end is the first index BEHIND the end. So a 0 length range will have equal start and end
1280
- emitter->onChangeSelection({
1281
- .start = static_cast<int>(textView.selectedRange.location),
1282
- .end = static_cast<int>(textView.selectedRange.location + textView.selectedRange.length),
1283
- .text = [textAtSelection toCppString]
1284
- });
1834
+ // here, start is the location, but end is the first index BEHIND the end.
1835
+ // So a 0 length range will have equal start and end
1836
+ emitter->onChangeSelection(
1837
+ {.start = static_cast<int>(textView.selectedRange.location),
1838
+ .end = static_cast<int>(textView.selectedRange.location +
1839
+ textView.selectedRange.length),
1840
+ .text = [textAtSelection toCppString]});
1285
1841
  }
1286
-
1842
+
1287
1843
  // manage selection changes
1288
1844
  [self manageSelectionBasedChanges];
1289
1845
  }
1290
1846
 
1291
- // this function isn't called always when some text changes (for example setting link or starting mention with indicator doesn't fire it)
1292
- // so all the logic is in anyTextMayHaveBeenModified
1847
+ // this function isn't called always when some text changes (for example setting
1848
+ // link or starting mention with indicator doesn't fire it) so all the logic is
1849
+ // in anyTextMayHaveBeenModified
1293
1850
  - (void)textViewDidChange:(UITextView *)textView {
1294
1851
  [self anyTextMayHaveBeenModified];
1295
1852
  }
1296
1853
 
1854
+ /**
1855
+ * Handles iOS Dynamic Type changes (User changing font size in System
1856
+ * Settings).
1857
+ *
1858
+ * Unlike Android, iOS Views do not automatically rescale existing
1859
+ * NSAttributedStrings when the system font size changes. The text attributes
1860
+ * are static once drawn.
1861
+ *
1862
+ * This method detects the change and performs a "Hard Refresh" of the content.
1863
+ */
1864
+ - (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
1865
+ [super traitCollectionDidChange:previousTraitCollection];
1866
+
1867
+ if (previousTraitCollection.preferredContentSizeCategory !=
1868
+ self.traitCollection.preferredContentSizeCategory) {
1869
+ [config invalidateFonts];
1870
+
1871
+ NSMutableDictionary *newTypingAttrs = [defaultTypingAttributes mutableCopy];
1872
+ newTypingAttrs[NSFontAttributeName] = [config primaryFont];
1873
+
1874
+ defaultTypingAttributes = newTypingAttrs;
1875
+ textView.typingAttributes = defaultTypingAttributes;
1876
+
1877
+ [self refreshPlaceholderLabelStyles];
1878
+
1879
+ NSRange prevSelectedRange = textView.selectedRange;
1880
+
1881
+ NSString *currentHtml = [parser
1882
+ parseToHtmlFromRange:NSMakeRange(0,
1883
+ textView.textStorage.string.length)];
1884
+ NSString *initiallyProcessedHtml =
1885
+ [parser initiallyProcessHtml:currentHtml];
1886
+ [parser replaceWholeFromHtml:initiallyProcessedHtml];
1887
+
1888
+ textView.selectedRange = prevSelectedRange;
1889
+ [self anyTextMayHaveBeenModified];
1890
+ }
1891
+ }
1892
+
1893
+ // MARK: - Media attachments delegate
1894
+
1895
+ - (void)mediaAttachmentDidUpdate:(NSTextAttachment *)attachment {
1896
+ NSTextStorage *storage = textView.textStorage;
1897
+ NSRange fullRange = NSMakeRange(0, storage.length);
1898
+
1899
+ __block NSRange foundRange = NSMakeRange(NSNotFound, 0);
1900
+
1901
+ [storage enumerateAttribute:NSAttachmentAttributeName
1902
+ inRange:fullRange
1903
+ options:0
1904
+ usingBlock:^(id value, NSRange range, BOOL *stop) {
1905
+ if (value == attachment) {
1906
+ foundRange = range;
1907
+ *stop = YES;
1908
+ }
1909
+ }];
1910
+
1911
+ if (foundRange.location == NSNotFound) {
1912
+ return;
1913
+ }
1914
+
1915
+ [storage edited:NSTextStorageEditedAttributes
1916
+ range:foundRange
1917
+ changeInLength:0];
1918
+ }
1919
+
1297
1920
  @end