react-native-enriched 0.1.6 → 0.2.1

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