react-native-enriched 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/README.md +16 -17
  2. package/android/build.gradle +77 -72
  3. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerDelegate.java +21 -0
  4. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerInterface.java +7 -0
  5. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/EventEmitters.cpp +156 -0
  6. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/EventEmitters.h +147 -0
  7. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/Props.cpp +10 -0
  8. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/Props.h +194 -0
  9. package/android/lint.gradle +70 -0
  10. package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputConnectionWrapper.kt +140 -0
  11. package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt +304 -83
  12. package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewLayoutManager.kt +3 -1
  13. package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewManager.kt +166 -51
  14. package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewPackage.kt +1 -3
  15. package/android/src/main/java/com/swmansion/enriched/MeasurementStore.kt +70 -21
  16. package/android/src/main/java/com/swmansion/enriched/events/MentionHandler.kt +21 -11
  17. package/android/src/main/java/com/swmansion/enriched/events/OnChangeHtmlEvent.kt +8 -9
  18. package/android/src/main/java/com/swmansion/enriched/events/OnChangeSelectionEvent.kt +10 -9
  19. package/android/src/main/java/com/swmansion/enriched/events/OnChangeStateDeprecatedEvent.kt +21 -0
  20. package/android/src/main/java/com/swmansion/enriched/events/OnChangeStateEvent.kt +9 -12
  21. package/android/src/main/java/com/swmansion/enriched/events/OnChangeTextEvent.kt +10 -10
  22. package/android/src/main/java/com/swmansion/enriched/events/OnInputBlurEvent.kt +7 -9
  23. package/android/src/main/java/com/swmansion/enriched/events/OnInputFocusEvent.kt +7 -9
  24. package/android/src/main/java/com/swmansion/enriched/events/OnInputKeyPressEvent.kt +27 -0
  25. package/android/src/main/java/com/swmansion/enriched/events/OnLinkDetectedEvent.kt +13 -11
  26. package/android/src/main/java/com/swmansion/enriched/events/OnMentionDetectedEvent.kt +10 -9
  27. package/android/src/main/java/com/swmansion/enriched/events/OnMentionEvent.kt +9 -8
  28. package/android/src/main/java/com/swmansion/enriched/events/OnRequestHtmlResultEvent.kt +32 -0
  29. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedBlockQuoteSpan.kt +24 -5
  30. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedBoldSpan.kt +8 -1
  31. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedCodeBlockSpan.kt +10 -2
  32. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH1Span.kt +8 -1
  33. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH2Span.kt +8 -1
  34. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH3Span.kt +8 -1
  35. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH4Span.kt +24 -0
  36. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH5Span.kt +24 -0
  37. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH6Span.kt +24 -0
  38. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedImageSpan.kt +34 -17
  39. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedInlineCodeSpan.kt +8 -1
  40. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedItalicSpan.kt +7 -1
  41. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedLinkSpan.kt +10 -4
  42. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedMentionSpan.kt +14 -11
  43. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedOrderedListSpan.kt +18 -11
  44. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedSpans.kt +174 -72
  45. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedStrikeThroughSpan.kt +7 -1
  46. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedUnderlineSpan.kt +7 -1
  47. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedUnorderedListSpan.kt +11 -5
  48. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedBlockSpan.kt +3 -2
  49. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedHeadingSpan.kt +1 -2
  50. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedInlineSpan.kt +1 -2
  51. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedParagraphSpan.kt +3 -2
  52. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedSpan.kt +5 -0
  53. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedZeroWidthSpaceSpan.kt +1 -2
  54. package/android/src/main/java/com/swmansion/enriched/spans/utils/ForceRedrawSpan.kt +2 -1
  55. package/android/src/main/java/com/swmansion/enriched/styles/HtmlStyle.kt +155 -20
  56. package/android/src/main/java/com/swmansion/enriched/styles/InlineStyles.kt +25 -8
  57. package/android/src/main/java/com/swmansion/enriched/styles/ListStyles.kt +60 -20
  58. package/android/src/main/java/com/swmansion/enriched/styles/ParagraphStyles.kt +161 -25
  59. package/android/src/main/java/com/swmansion/enriched/styles/ParametrizedStyles.kt +128 -52
  60. package/android/src/main/java/com/swmansion/enriched/utils/AsyncDrawable.kt +10 -7
  61. package/android/src/main/java/com/swmansion/enriched/utils/EnrichedConstants.kt +11 -0
  62. package/android/src/main/java/com/swmansion/enriched/utils/EnrichedEditableFactory.kt +17 -0
  63. package/android/src/main/java/com/swmansion/enriched/utils/EnrichedParser.java +136 -87
  64. package/android/src/main/java/com/swmansion/enriched/utils/EnrichedSelection.kt +71 -42
  65. package/android/src/main/java/com/swmansion/enriched/utils/EnrichedSpanState.kt +183 -48
  66. package/android/src/main/java/com/swmansion/enriched/utils/EnrichedSpannable.kt +82 -0
  67. package/android/src/main/java/com/swmansion/enriched/utils/EnrichedSpannableStringBuilder.kt +15 -0
  68. package/android/src/main/java/com/swmansion/enriched/utils/Utils.kt +0 -70
  69. package/android/src/main/java/com/swmansion/enriched/watchers/EnrichedSpanWatcher.kt +46 -14
  70. package/android/src/main/java/com/swmansion/enriched/watchers/EnrichedTextWatcher.kt +34 -11
  71. package/android/src/main/new_arch/CMakeLists.txt +6 -0
  72. package/android/src/main/new_arch/RNEnrichedTextInputViewSpec.cpp +6 -6
  73. package/android/src/main/new_arch/RNEnrichedTextInputViewSpec.h +6 -6
  74. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputComponentDescriptor.h +19 -19
  75. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputMeasurementManager.cpp +40 -51
  76. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputMeasurementManager.h +13 -15
  77. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputShadowNode.cpp +23 -21
  78. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputShadowNode.h +35 -36
  79. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputState.cpp +4 -4
  80. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputState.h +13 -14
  81. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/conversions.h +33 -14
  82. package/ios/EnrichedTextInputView.h +26 -14
  83. package/ios/EnrichedTextInputView.mm +1209 -586
  84. package/ios/config/InputConfig.h +24 -6
  85. package/ios/config/InputConfig.mm +154 -38
  86. package/ios/{utils → extensions}/ColorExtension.mm +7 -5
  87. package/ios/extensions/FontExtension.mm +106 -0
  88. package/ios/{utils → extensions}/LayoutManagerExtension.h +1 -1
  89. package/ios/extensions/LayoutManagerExtension.mm +396 -0
  90. package/ios/{utils → extensions}/StringExtension.mm +19 -16
  91. package/ios/generated/RNEnrichedTextInputViewSpec/EventEmitters.cpp +156 -0
  92. package/ios/generated/RNEnrichedTextInputViewSpec/EventEmitters.h +147 -0
  93. package/ios/generated/RNEnrichedTextInputViewSpec/Props.cpp +10 -0
  94. package/ios/generated/RNEnrichedTextInputViewSpec/Props.h +194 -0
  95. package/ios/generated/RNEnrichedTextInputViewSpec/RCTComponentViewHelpers.h +95 -0
  96. package/ios/inputParser/InputParser.h +5 -5
  97. package/ios/inputParser/InputParser.mm +864 -380
  98. package/ios/inputTextView/InputTextView.h +1 -1
  99. package/ios/inputTextView/InputTextView.mm +100 -59
  100. package/ios/{utils → interfaces}/BaseStyleProtocol.h +2 -2
  101. package/ios/interfaces/ImageAttachment.h +10 -0
  102. package/ios/interfaces/ImageAttachment.mm +36 -0
  103. package/ios/interfaces/LinkRegexConfig.h +19 -0
  104. package/ios/interfaces/LinkRegexConfig.mm +37 -0
  105. package/ios/interfaces/MediaAttachment.h +23 -0
  106. package/ios/interfaces/MediaAttachment.mm +31 -0
  107. package/ios/{utils → interfaces}/MentionParams.h +0 -1
  108. package/ios/{utils → interfaces}/MentionStyleProps.mm +27 -20
  109. package/ios/{utils → interfaces}/StyleHeaders.h +37 -15
  110. package/ios/{utils → interfaces}/StyleTypeEnum.h +3 -0
  111. package/ios/internals/EnrichedTextInputViewComponentDescriptor.h +11 -9
  112. package/ios/internals/EnrichedTextInputViewShadowNode.h +28 -25
  113. package/ios/internals/EnrichedTextInputViewShadowNode.mm +45 -40
  114. package/ios/internals/EnrichedTextInputViewState.h +3 -1
  115. package/ios/styles/BlockQuoteStyle.mm +189 -118
  116. package/ios/styles/BoldStyle.mm +110 -63
  117. package/ios/styles/CodeBlockStyle.mm +204 -128
  118. package/ios/styles/H1Style.mm +10 -4
  119. package/ios/styles/H2Style.mm +10 -4
  120. package/ios/styles/H3Style.mm +10 -4
  121. package/ios/styles/H4Style.mm +17 -0
  122. package/ios/styles/H5Style.mm +17 -0
  123. package/ios/styles/H6Style.mm +17 -0
  124. package/ios/styles/HeadingStyleBase.mm +148 -86
  125. package/ios/styles/ImageStyle.mm +75 -73
  126. package/ios/styles/InlineCodeStyle.mm +162 -88
  127. package/ios/styles/ItalicStyle.mm +76 -52
  128. package/ios/styles/LinkStyle.mm +411 -232
  129. package/ios/styles/MentionStyle.mm +363 -246
  130. package/ios/styles/OrderedListStyle.mm +171 -106
  131. package/ios/styles/StrikethroughStyle.mm +52 -35
  132. package/ios/styles/UnderlineStyle.mm +68 -46
  133. package/ios/styles/UnorderedListStyle.mm +169 -106
  134. package/ios/utils/OccurenceUtils.h +42 -42
  135. package/ios/utils/OccurenceUtils.mm +142 -119
  136. package/ios/utils/ParagraphAttributesUtils.h +10 -2
  137. package/ios/utils/ParagraphAttributesUtils.mm +182 -71
  138. package/ios/utils/ParagraphsUtils.h +2 -1
  139. package/ios/utils/ParagraphsUtils.mm +41 -27
  140. package/ios/utils/TextInsertionUtils.h +13 -2
  141. package/ios/utils/TextInsertionUtils.mm +38 -20
  142. package/ios/utils/WordsUtils.h +2 -1
  143. package/ios/utils/WordsUtils.mm +32 -22
  144. package/ios/utils/ZeroWidthSpaceUtils.h +3 -1
  145. package/ios/utils/ZeroWidthSpaceUtils.mm +145 -79
  146. package/lib/module/EnrichedTextInput.js +61 -2
  147. package/lib/module/EnrichedTextInput.js.map +1 -1
  148. package/lib/module/EnrichedTextInputNativeComponent.ts +149 -12
  149. package/lib/module/{normalizeHtmlStyle.js → utils/normalizeHtmlStyle.js} +12 -0
  150. package/lib/module/utils/normalizeHtmlStyle.js.map +1 -0
  151. package/lib/module/utils/regexParser.js +46 -0
  152. package/lib/module/utils/regexParser.js.map +1 -0
  153. package/lib/typescript/src/EnrichedTextInput.d.ts +24 -14
  154. package/lib/typescript/src/EnrichedTextInput.d.ts.map +1 -1
  155. package/lib/typescript/src/EnrichedTextInputNativeComponent.d.ts +129 -12
  156. package/lib/typescript/src/EnrichedTextInputNativeComponent.d.ts.map +1 -1
  157. package/lib/typescript/src/index.d.ts +1 -1
  158. package/lib/typescript/src/index.d.ts.map +1 -1
  159. package/lib/typescript/src/utils/normalizeHtmlStyle.d.ts +4 -0
  160. package/lib/typescript/src/utils/normalizeHtmlStyle.d.ts.map +1 -0
  161. package/lib/typescript/src/utils/regexParser.d.ts +3 -0
  162. package/lib/typescript/src/utils/regexParser.d.ts.map +1 -0
  163. package/package.json +17 -6
  164. package/src/EnrichedTextInput.tsx +96 -13
  165. package/src/EnrichedTextInputNativeComponent.ts +149 -12
  166. package/src/index.tsx +2 -0
  167. package/src/{normalizeHtmlStyle.ts → utils/normalizeHtmlStyle.ts} +14 -2
  168. package/src/utils/regexParser.ts +56 -0
  169. package/ios/utils/FontExtension.mm +0 -91
  170. package/ios/utils/LayoutManagerExtension.mm +0 -286
  171. package/lib/module/normalizeHtmlStyle.js.map +0 -1
  172. package/lib/typescript/src/normalizeHtmlStyle.d.ts +0 -4
  173. package/lib/typescript/src/normalizeHtmlStyle.d.ts.map +0 -1
  174. package/ios/{utils → extensions}/ColorExtension.h +0 -0
  175. package/ios/{utils → extensions}/FontExtension.h +0 -0
  176. package/ios/{utils → extensions}/StringExtension.h +1 -1
  177. package/ios/{utils → interfaces}/ImageData.h +0 -0
  178. package/ios/{utils → interfaces}/ImageData.mm +0 -0
  179. package/ios/{utils → interfaces}/LinkData.h +0 -0
  180. package/ios/{utils → interfaces}/LinkData.mm +0 -0
  181. package/ios/{utils → interfaces}/MentionParams.mm +0 -0
  182. package/ios/{utils → interfaces}/MentionStyleProps.h +1 -1
  183. /package/ios/{utils → interfaces}/StylePair.h +0 -0
  184. /package/ios/{utils → interfaces}/StylePair.mm +0 -0
  185. /package/ios/{utils → interfaces}/TextDecorationLineEnum.h +0 -0
  186. /package/ios/{utils → interfaces}/TextDecorationLineEnum.mm +0 -0
@@ -1,59 +1,84 @@
1
1
  #import "ParagraphAttributesUtils.h"
2
2
  #import "EnrichedTextInputView.h"
3
- #import "StyleHeaders.h"
4
3
  #import "ParagraphsUtils.h"
4
+ #import "StyleHeaders.h"
5
5
  #import "TextInsertionUtils.h"
6
6
 
7
7
  @implementation ParagraphAttributesUtils
8
8
 
9
- // if the user backspaces the last character in a line, the iOS applies typing attributes from the previous line
10
- // in the case of some paragraph styles it works especially bad when a list point just appears
11
- // this method handles that case differently with or without present paragraph styles
12
- + (BOOL)handleBackspaceInRange:(NSRange)range replacementText:(NSString *)text input:(id)input {
9
+ // if the user backspaces the last character in a line, the iOS applies typing
10
+ // attributes from the previous line in the case of some paragraph styles it
11
+ // works especially bad when a list point just appears this method handles that
12
+ // case differently with or without present paragraph styles
13
+ + (BOOL)handleBackspaceInRange:(NSRange)range
14
+ replacementText:(NSString *)text
15
+ input:(id)input {
13
16
  EnrichedTextInputView *typedInput = (EnrichedTextInputView *)input;
14
- UnorderedListStyle *ulStyle = typedInput->stylesDict[@([UnorderedListStyle getStyleType])];
15
- OrderedListStyle *olStyle = typedInput->stylesDict[@([OrderedListStyle getStyleType])];
16
- BlockQuoteStyle *bqStyle = typedInput->stylesDict[@([BlockQuoteStyle getStyleType])];
17
- CodeBlockStyle *cbStyle = typedInput->stylesDict[@([CodeBlockStyle getStyleType])];
18
-
19
- if(typedInput == nullptr) {
17
+ UnorderedListStyle *ulStyle =
18
+ typedInput->stylesDict[@([UnorderedListStyle getStyleType])];
19
+ OrderedListStyle *olStyle =
20
+ typedInput->stylesDict[@([OrderedListStyle getStyleType])];
21
+ BlockQuoteStyle *bqStyle =
22
+ typedInput->stylesDict[@([BlockQuoteStyle getStyleType])];
23
+ CodeBlockStyle *cbStyle =
24
+ typedInput->stylesDict[@([CodeBlockStyle getStyleType])];
25
+
26
+ if (typedInput == nullptr) {
20
27
  return NO;
21
28
  }
22
-
23
- // we make sure it was a backspace (text with 0 length) and it deleted something (range longer than 0)
24
- if(text.length > 0 || range.length == 0) {
29
+
30
+ // we make sure it was a backspace (text with 0 length) and it deleted
31
+ // something (range longer than 0)
32
+ if (text.length > 0 || range.length == 0) {
25
33
  return NO;
26
34
  }
27
-
35
+
28
36
  // find a non-newline range of the paragraph
29
- NSRange paragraphRange = [typedInput->textView.textStorage.string paragraphRangeForRange:range];
30
-
31
- NSArray *paragraphs = [ParagraphsUtils getNonNewlineRangesIn:typedInput->textView range:paragraphRange];
32
- if(paragraphs.count == 0) {
37
+ NSRange paragraphRange =
38
+ [typedInput->textView.textStorage.string paragraphRangeForRange:range];
39
+
40
+ NSArray *paragraphs =
41
+ [ParagraphsUtils getNonNewlineRangesIn:typedInput->textView
42
+ range:paragraphRange];
43
+ if (paragraphs.count == 0) {
33
44
  return NO;
34
45
  }
35
-
46
+
36
47
  NSRange nonNewlineRange = [(NSValue *)paragraphs.firstObject rangeValue];
37
-
38
- // the backspace removes the whole content of a paragraph (possibly more but has to start where the paragraph starts)
39
- if(range.location == nonNewlineRange.location && range.length >= nonNewlineRange.length) {
40
-
48
+
49
+ // the backspace removes the whole content of a paragraph (possibly more but
50
+ // has to start where the paragraph starts)
51
+ if (range.location == nonNewlineRange.location &&
52
+ range.length >= nonNewlineRange.length) {
53
+
41
54
  // for lists, quotes and codeblocks present we do the following:
42
55
  // - manually do the removing
43
- // - reset typing attribtues so that the previous line styles don't get applied
44
- // - reapply the paragraph style that was present so that a zero width space appears here
45
- NSArray *handledStyles = @[ulStyle, olStyle, bqStyle, cbStyle];
46
- for(id<BaseStyleProtocol> style in handledStyles) {
47
- if([style detectStyle:nonNewlineRange]) {
48
- [TextInsertionUtils replaceText:text at:range additionalAttributes:nullptr input:typedInput withSelection:YES];
49
- typedInput->textView.typingAttributes = typedInput->defaultTypingAttributes;
50
- [style addAttributes:NSMakeRange(range.location, 0)];
56
+ // - reset typing attribtues so that the previous line styles don't get
57
+ // applied
58
+ // - reapply the paragraph style that was present so that a zero width space
59
+ // appears here
60
+ NSArray *handledStyles = @[ ulStyle, olStyle, bqStyle, cbStyle ];
61
+ for (id<BaseStyleProtocol> style in handledStyles) {
62
+ if ([style detectStyle:nonNewlineRange]) {
63
+ [TextInsertionUtils replaceText:text
64
+ at:range
65
+ additionalAttributes:nullptr
66
+ input:typedInput
67
+ withSelection:YES];
68
+ typedInput->textView.typingAttributes =
69
+ typedInput->defaultTypingAttributes;
70
+ [style addAttributes:NSMakeRange(range.location, 0) withTypingAttr:YES];
51
71
  return YES;
52
72
  }
53
73
  }
54
-
55
- // otherwise (no paragraph styles present), we just do the replacement manually and reset typing attribtues
56
- [TextInsertionUtils replaceText:text at:range additionalAttributes:nullptr input:typedInput withSelection:YES];
74
+
75
+ // otherwise (no paragraph styles present), we just do the replacement
76
+ // manually and reset typing attribtues
77
+ [TextInsertionUtils replaceText:text
78
+ at:range
79
+ additionalAttributes:nullptr
80
+ input:typedInput
81
+ withSelection:YES];
57
82
  typedInput->textView.typingAttributes = typedInput->defaultTypingAttributes;
58
83
  return YES;
59
84
  }
@@ -62,73 +87,159 @@
62
87
  }
63
88
 
64
89
  /**
65
- * Handles the specific case of backspacing a newline character, which results in merging two paragraphs.
90
+ * Handles the specific case of backspacing a newline character, which results
91
+ * in merging two paragraphs.
66
92
  *
67
93
  * THE PROBLEM:
68
- * When merging a bottom paragraph (Source) into a top paragraph (Destination), the bottom paragraph
69
- * normally brings all its attributes with it. If the top paragraph is a restrictive style (like a CodeBlock),
70
- * and the bottom paragraph contains a conflicting style (like an H1 Header), a standard merge would
94
+ * When merging a bottom paragraph (Source) into a top paragraph (Destination),
95
+ * the bottom paragraph normally brings all its attributes with it. If the top
96
+ * paragraph is a restrictive style (like a CodeBlock), and the bottom paragraph
97
+ * contains a conflicting style (like an H1 Header), a standard merge would
71
98
  * create an invalid state (e.g., a CodeBlock that is also a Header).
72
99
  *
73
100
  * THE SOLUTION:
74
- * 1. Identifies the dominant style of the paragraph ABOVE the deleted newline (`leftParagraphStyle`).
75
- * 2. Checks the paragraph BELOW the newline (`rightRange`) for any styles that conflict with or are blocked by the top style.
76
- * 3. Explicitly removes those forbidden styles from the bottom paragraph *before* the merge occurs.
101
+ * 1. Identifies the dominant style of the paragraph ABOVE the deleted newline
102
+ * (`leftParagraphStyle`).
103
+ * 2. Checks the paragraph BELOW the newline (`rightRange`) for any styles that
104
+ * conflict with or are blocked by the top style.
105
+ * 3. Explicitly removes those forbidden styles from the bottom paragraph
106
+ * *before* the merge occurs.
77
107
  * 4. Performs the merge (deletes the newline).
78
108
  *
79
109
  * @return YES if the newline backspace was handled and sanitized; NO otherwise.
80
110
  */
81
- + (BOOL)handleNewlineBackspaceInRange:(NSRange)range replacementText:(NSString *)text input:(id)input {
111
+ + (BOOL)handleParagraphStylesMergeOnBackspace:(NSRange)range
112
+ replacementText:(NSString *)text
113
+ input:(id)input {
82
114
  EnrichedTextInputView *typedInput = (EnrichedTextInputView *)input;
83
- if(typedInput == nullptr) {
115
+ if (typedInput == nullptr) {
84
116
  return NO;
85
117
  }
86
-
87
- if(text.length == 0 && range.length == 1 &&
88
- [[NSCharacterSet newlineCharacterSet] characterIsMember:[typedInput->textView.textStorage.string characterAtIndex:range.location]]) {
89
- NSRange leftRange = [typedInput->textView.textStorage.string paragraphRangeForRange:range];
90
-
118
+
119
+ if (text.length == 0) {
120
+ NSRange leftRange = [typedInput->textView.textStorage.string
121
+ paragraphRangeForRange:NSMakeRange(range.location, 0)];
122
+
91
123
  id<BaseStyleProtocol> leftParagraphStyle = nullptr;
92
124
  for (NSNumber *key in typedInput->stylesDict) {
93
125
  id<BaseStyleProtocol> style = typedInput->stylesDict[key];
94
- if([[style class] isParagraphStyle] && [style detectStyle:leftRange]) {
126
+ if ([[style class] isParagraphStyle] && [style detectStyle:leftRange]) {
95
127
  leftParagraphStyle = style;
96
128
  }
97
129
  }
98
-
99
- if(leftParagraphStyle == nullptr) {
130
+
131
+ if (leftParagraphStyle == nullptr) {
100
132
  return NO;
101
133
  }
102
-
134
+
103
135
  // index out of bounds
104
- if(range.location + 1 >= typedInput->textView.textStorage.string.length) {
136
+ NSUInteger rightRangeStart = range.location + range.length;
137
+ if (rightRangeStart >= typedInput->textView.textStorage.string.length) {
105
138
  return NO;
106
139
  }
107
-
108
- NSRange rightRange = [typedInput->textView.textStorage.string paragraphRangeForRange:NSMakeRange(range.location + 1, 1)];
109
-
140
+
141
+ NSRange rightRange = [typedInput->textView.textStorage.string
142
+ paragraphRangeForRange:NSMakeRange(rightRangeStart, 1)];
143
+
110
144
  StyleType type = [[leftParagraphStyle class] getStyleType];
111
-
112
- NSArray *conflictingStyles = [typedInput getPresentStyleTypesFrom:typedInput->conflictingStyles[@(type)] range:rightRange];
113
- NSArray *blockingStyles = [typedInput getPresentStyleTypesFrom:typedInput->blockingStyles[@(type)] range:rightRange];
114
- NSArray *allToBeRemoved = [conflictingStyles arrayByAddingObjectsFromArray:blockingStyles];
115
-
116
- for(NSNumber *style in allToBeRemoved) {
145
+
146
+ NSArray *conflictingStyles = [typedInput
147
+ getPresentStyleTypesFrom:typedInput->conflictingStyles[@(type)]
148
+ range:rightRange];
149
+ NSArray *blockingStyles =
150
+ [typedInput getPresentStyleTypesFrom:typedInput->blockingStyles[@(type)]
151
+ range:rightRange];
152
+ NSArray *allToBeRemoved =
153
+ [conflictingStyles arrayByAddingObjectsFromArray:blockingStyles];
154
+
155
+ for (NSNumber *style in allToBeRemoved) {
117
156
  id<BaseStyleProtocol> styleClass = typedInput->stylesDict[style];
118
-
157
+
119
158
  // for ranges, we need to remove each occurence
120
- NSArray<StylePair *> *allOccurences = [styleClass findAllOccurences:rightRange];
121
-
122
- for(StylePair* pair in allOccurences) {
123
- [styleClass removeAttributes: [pair.rangeValue rangeValue]];
159
+ NSArray<StylePair *> *allOccurences =
160
+ [styleClass findAllOccurences:rightRange];
161
+
162
+ for (StylePair *pair in allOccurences) {
163
+ [styleClass removeAttributes:[pair.rangeValue rangeValue]];
124
164
  }
125
165
  }
126
-
127
- [TextInsertionUtils replaceText:text at:range additionalAttributes:nullptr input:typedInput withSelection:YES];
166
+
167
+ [TextInsertionUtils replaceText:text
168
+ at:range
169
+ additionalAttributes:nullptr
170
+ input:typedInput
171
+ withSelection:YES];
172
+ return YES;
173
+ }
174
+
175
+ return NO;
176
+ }
177
+
178
+ /**
179
+ * Resets typing attributes to defaults when the cursor lands on an empty line
180
+ * after a deletion.
181
+ *
182
+ * This override is necessary because `UITextView` automatically inherits
183
+ * attributes from the preceding newline. This prevents scenarios where a
184
+ * restrictive style (like CodeBlock) "leaks" into the next empty paragraph.
185
+ */
186
+ + (BOOL)handleResetTypingAttributesOnBackspace:(NSRange)range
187
+ replacementText:(NSString *)text
188
+ input:(id)input {
189
+ EnrichedTextInputView *typedInput = (EnrichedTextInputView *)input;
190
+ if (typedInput == nullptr) {
191
+ return NO;
192
+ }
193
+
194
+ NSString *storageString = typedInput->textView.textStorage.string;
195
+
196
+ if (text.length > 0 || range.location >= storageString.length) {
197
+ return NO;
198
+ }
199
+
200
+ unichar firstCharToDelete = [storageString characterAtIndex:range.location];
201
+ if (![[NSCharacterSet newlineCharacterSet]
202
+ characterIsMember:firstCharToDelete]) {
203
+ return NO;
204
+ }
205
+
206
+ NSRange leftParagraphRange =
207
+ [storageString paragraphRangeForRange:NSMakeRange(range.location, 0)];
208
+ BOOL isLeftLineEmpty = [self isParagraphEmpty:leftParagraphRange
209
+ inString:storageString];
210
+
211
+ BOOL isRightLineEmpty = YES;
212
+ NSUInteger rightRangeStart = range.location + range.length;
213
+
214
+ if (rightRangeStart < storageString.length) {
215
+ NSRange rightParagraphRange =
216
+ [storageString paragraphRangeForRange:NSMakeRange(rightRangeStart, 0)];
217
+ isRightLineEmpty = [self isParagraphEmpty:rightParagraphRange
218
+ inString:storageString];
219
+ }
220
+
221
+ if (isLeftLineEmpty && isRightLineEmpty) {
222
+ [TextInsertionUtils replaceText:text
223
+ at:range
224
+ additionalAttributes:nullptr
225
+ input:typedInput
226
+ withSelection:YES];
227
+
228
+ typedInput->textView.typingAttributes = typedInput->defaultTypingAttributes;
128
229
  return YES;
129
230
  }
130
-
231
+
131
232
  return NO;
132
233
  }
133
234
 
235
+ + (BOOL)isParagraphEmpty:(NSRange)range inString:(NSString *)string {
236
+ if (range.length == 0)
237
+ return YES;
238
+
239
+ NSString *paragraphText = [string substringWithRange:range];
240
+ NSString *trimmed = [paragraphText
241
+ stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
242
+ return trimmed.length == 0;
243
+ }
244
+
134
245
  @end
@@ -2,6 +2,7 @@
2
2
  #import <UIKit/UIKit.h>
3
3
 
4
4
  @interface ParagraphsUtils : NSObject
5
- + (NSArray *)getSeparateParagraphsRangesIn:(UITextView *)textView range:(NSRange)range;
5
+ + (NSArray *)getSeparateParagraphsRangesIn:(UITextView *)textView
6
+ range:(NSRange)range;
6
7
  + (NSArray *)getNonNewlineRangesIn:(UITextView *)textView range:(NSRange)range;
7
8
  @end
@@ -2,52 +2,66 @@
2
2
 
3
3
  @implementation ParagraphsUtils
4
4
 
5
- + (NSArray *)getSeparateParagraphsRangesIn:(UITextView *)textView range:(NSRange)range {
5
+ + (NSArray *)getSeparateParagraphsRangesIn:(UITextView *)textView
6
+ range:(NSRange)range {
6
7
  // just in case, get full paragraphs range
7
- NSRange fullRange = [textView.textStorage.string paragraphRangeForRange:range];
8
-
8
+ NSRange fullRange =
9
+ [textView.textStorage.string paragraphRangeForRange:range];
10
+
9
11
  // we are in an empty paragraph
10
- if(fullRange.length == 0) {
11
- return @[[NSValue valueWithRange:fullRange]];
12
+ if (fullRange.length == 0) {
13
+ return @[ [NSValue valueWithRange:fullRange] ];
12
14
  }
13
-
15
+
14
16
  NSMutableArray *results = [[NSMutableArray alloc] init];
15
-
17
+
16
18
  NSInteger lastStart = fullRange.location;
17
- for(int i = fullRange.location; i < fullRange.location + fullRange.length; i++) {
19
+ for (int i = int(fullRange.location);
20
+ i < fullRange.location + fullRange.length; i++) {
18
21
  unichar currentChar = [textView.textStorage.string characterAtIndex:i];
19
- if([[NSCharacterSet newlineCharacterSet] characterIsMember:currentChar]) {
20
- NSRange paragraphRange = [textView.textStorage.string paragraphRangeForRange:NSMakeRange(lastStart, i - lastStart)];
21
- [results addObject: [NSValue valueWithRange:paragraphRange]];
22
- lastStart = i+1;
22
+ if ([[NSCharacterSet newlineCharacterSet] characterIsMember:currentChar]) {
23
+ NSRange paragraphRange = [textView.textStorage.string
24
+ paragraphRangeForRange:NSMakeRange(lastStart, i - lastStart)];
25
+ [results addObject:[NSValue valueWithRange:paragraphRange]];
26
+ lastStart = i + 1;
23
27
  }
24
28
  }
25
-
26
- if(lastStart < fullRange.location + fullRange.length) {
27
- NSRange paragraphRange = [textView.textStorage.string paragraphRangeForRange:NSMakeRange(lastStart, fullRange.location + fullRange.length - lastStart)];
28
- [results addObject: [NSValue valueWithRange:paragraphRange]];
29
+
30
+ if (lastStart < fullRange.location + fullRange.length) {
31
+ NSRange paragraphRange = [textView.textStorage.string
32
+ paragraphRangeForRange:NSMakeRange(lastStart, fullRange.location +
33
+ fullRange.length -
34
+ lastStart)];
35
+ [results addObject:[NSValue valueWithRange:paragraphRange]];
29
36
  }
30
-
37
+
31
38
  return results;
32
39
  }
33
40
 
34
41
  + (NSArray *)getNonNewlineRangesIn:(UITextView *)textView range:(NSRange)range {
35
42
  NSMutableArray *nonNewlineRanges = [[NSMutableArray alloc] init];
36
- int lastRangeLocation = range.location;
37
-
38
- for(int i = range.location; i < range.location + range.length; i++) {
43
+ int lastRangeLocation = int(range.location);
44
+
45
+ for (int i = int(range.location); i < range.location + range.length; i++) {
39
46
  unichar currentChar = [textView.textStorage.string characterAtIndex:i];
40
- if([[NSCharacterSet newlineCharacterSet] characterIsMember:currentChar]) {
41
- if(i - lastRangeLocation > 0) {
42
- [nonNewlineRanges addObject:[NSValue valueWithRange:NSMakeRange(lastRangeLocation, i - lastRangeLocation)]];
47
+ if ([[NSCharacterSet newlineCharacterSet] characterIsMember:currentChar]) {
48
+ if (i - lastRangeLocation > 0) {
49
+ [nonNewlineRanges
50
+ addObject:[NSValue
51
+ valueWithRange:NSMakeRange(lastRangeLocation,
52
+ i - lastRangeLocation)]];
43
53
  }
44
- lastRangeLocation = i+1;
54
+ lastRangeLocation = i + 1;
45
55
  }
46
56
  }
47
- if(lastRangeLocation < range.location + range.length) {
48
- [nonNewlineRanges addObject:[NSValue valueWithRange:NSMakeRange(lastRangeLocation, range.location + range.length - lastRangeLocation)]];
57
+ if (lastRangeLocation < range.location + range.length) {
58
+ [nonNewlineRanges
59
+ addObject:[NSValue
60
+ valueWithRange:NSMakeRange(lastRangeLocation,
61
+ range.location + range.length -
62
+ lastRangeLocation)]];
49
63
  }
50
-
64
+
51
65
  return nonNewlineRanges;
52
66
  }
53
67
 
@@ -1,6 +1,17 @@
1
1
  #import <UIKit/UIKit.h>
2
2
 
3
3
  @interface TextInsertionUtils : NSObject
4
- + (void)insertText:(NSString*)text at:(NSInteger)index additionalAttributes:(NSDictionary<NSAttributedStringKey, id>*)additionalAttrs input:(id)input withSelection:(BOOL)withSelection;
5
- + (void)replaceText:(NSString*)text at:(NSRange)range additionalAttributes:(NSDictionary<NSAttributedStringKey, id>*)additionalAttrs input:(id)input withSelection:(BOOL)withSelection;;
4
+ + (void)insertText:(NSString *)text
5
+ at:(NSInteger)index
6
+ additionalAttributes:
7
+ (NSDictionary<NSAttributedStringKey, id> *)additionalAttrs
8
+ input:(id)input
9
+ withSelection:(BOOL)withSelection;
10
+ + (void)replaceText:(NSString *)text
11
+ at:(NSRange)range
12
+ additionalAttributes:
13
+ (NSDictionary<NSAttributedStringKey, id> *)additionalAttrs
14
+ input:(id)input
15
+ withSelection:(BOOL)withSelection;
16
+ ;
6
17
  @end
@@ -1,24 +1,33 @@
1
1
  #import "TextInsertionUtils.h"
2
- #import "UIView+React.h"
3
2
  #import "EnrichedTextInputView.h"
3
+ #import "UIView+React.h"
4
4
 
5
5
  @implementation TextInsertionUtils
6
- + (void)insertText:(NSString*)text at:(NSInteger)index additionalAttributes:(NSDictionary<NSAttributedStringKey, id>*)additionalAttrs input:(id)input withSelection:(BOOL)withSelection {
6
+ + (void)insertText:(NSString *)text
7
+ at:(NSInteger)index
8
+ additionalAttributes:
9
+ (NSDictionary<NSAttributedStringKey, id> *)additionalAttrs
10
+ input:(id)input
11
+ withSelection:(BOOL)withSelection {
7
12
  EnrichedTextInputView *typedInput = (EnrichedTextInputView *)input;
8
- if(typedInput == nullptr) { return; }
9
-
13
+ if (typedInput == nullptr) {
14
+ return;
15
+ }
16
+
10
17
  UITextView *textView = typedInput->textView;
11
18
 
12
- NSMutableDictionary<NSAttributedStringKey, id> *copiedAttrs = [textView.typingAttributes mutableCopy];
13
- if(additionalAttrs != nullptr) {
14
- [copiedAttrs addEntriesFromDictionary: additionalAttrs];
19
+ NSMutableDictionary<NSAttributedStringKey, id> *copiedAttrs =
20
+ [textView.typingAttributes mutableCopy];
21
+ if (additionalAttrs != nullptr) {
22
+ [copiedAttrs addEntriesFromDictionary:additionalAttrs];
15
23
  }
16
-
17
- NSAttributedString *newAttrStr = [[NSAttributedString alloc] initWithString:text attributes:copiedAttrs];
24
+
25
+ NSAttributedString *newAttrStr =
26
+ [[NSAttributedString alloc] initWithString:text attributes:copiedAttrs];
18
27
  [textView.textStorage insertAttributedString:newAttrStr atIndex:index];
19
-
20
- if(withSelection) {
21
- if(![textView isFirstResponder]) {
28
+
29
+ if (withSelection) {
30
+ if (![textView isFirstResponder]) {
22
31
  [textView reactFocus];
23
32
  }
24
33
  textView.selectedRange = NSMakeRange(index + text.length, 0);
@@ -26,19 +35,28 @@
26
35
  typedInput->recentlyChangedRange = NSMakeRange(index, text.length);
27
36
  }
28
37
 
29
- + (void)replaceText:(NSString*)text at:(NSRange)range additionalAttributes:(NSDictionary<NSAttributedStringKey, id>*)additionalAttrs input:(id)input withSelection:(BOOL)withSelection {
38
+ + (void)replaceText:(NSString *)text
39
+ at:(NSRange)range
40
+ additionalAttributes:
41
+ (NSDictionary<NSAttributedStringKey, id> *)additionalAttrs
42
+ input:(id)input
43
+ withSelection:(BOOL)withSelection {
30
44
  EnrichedTextInputView *typedInput = (EnrichedTextInputView *)input;
31
- if(typedInput == nullptr) { return; }
32
-
45
+ if (typedInput == nullptr) {
46
+ return;
47
+ }
48
+
33
49
  UITextView *textView = typedInput->textView;
34
50
 
35
51
  [textView.textStorage replaceCharactersInRange:range withString:text];
36
- if(additionalAttrs != nullptr) {
37
- [textView.textStorage addAttributes:additionalAttrs range:NSMakeRange(range.location, [text length])];
52
+ if (additionalAttrs != nullptr) {
53
+ [textView.textStorage
54
+ addAttributes:additionalAttrs
55
+ range:NSMakeRange(range.location, [text length])];
38
56
  }
39
-
40
- if(withSelection) {
41
- if(![textView isFirstResponder]) {
57
+
58
+ if (withSelection) {
59
+ if (![textView isFirstResponder]) {
42
60
  [textView reactFocus];
43
61
  }
44
62
  textView.selectedRange = NSMakeRange(range.location + text.length, 0);
@@ -1,6 +1,7 @@
1
1
  #import <UIKit/UIKit.h>
2
2
 
3
3
  @interface WordsUtils : NSObject
4
- + (NSArray<NSDictionary *> *)getAffectedWordsFromText:(NSString *)text modificationRange:(NSRange)range;
4
+ + (NSArray<NSDictionary *> *)getAffectedWordsFromText:(NSString *)text
5
+ modificationRange:(NSRange)range;
5
6
  + (NSDictionary *)getCurrentWord:(NSString *)text range:(NSRange)range;
6
7
  @end
@@ -2,8 +2,9 @@
2
2
 
3
3
  @implementation WordsUtils
4
4
 
5
- + (NSArray<NSDictionary *> *)getAffectedWordsFromText:(NSString *)text modificationRange:(NSRange)range {
6
- if(text.length == 0) {
5
+ + (NSArray<NSDictionary *> *)getAffectedWordsFromText:(NSString *)text
6
+ modificationRange:(NSRange)range {
7
+ if (text.length == 0) {
7
8
  return [[NSArray alloc] init];
8
9
  }
9
10
 
@@ -12,7 +13,8 @@
12
13
  if (leftIt > 0) {
13
14
  while (leftIt >= 0) {
14
15
  unichar charAtIndex = [text characterAtIndex:leftIt];
15
- if ([[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:charAtIndex]) {
16
+ if ([[NSCharacterSet whitespaceAndNewlineCharacterSet]
17
+ characterIsMember:charAtIndex]) {
16
18
  leftIt += 1;
17
19
  break;
18
20
  }
@@ -21,12 +23,13 @@
21
23
  }
22
24
  leftIt = MAX(0, leftIt);
23
25
  leftIt = MIN(NSInteger(text.length - 1), leftIt);
24
-
26
+
25
27
  NSInteger rightIt = range.location + range.length;
26
28
  if (rightIt < text.length - 1) {
27
29
  while (rightIt <= text.length - 1) {
28
30
  unichar charAtIndex = [text characterAtIndex:rightIt];
29
- if ([[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:charAtIndex]) {
31
+ if ([[NSCharacterSet whitespaceAndNewlineCharacterSet]
32
+ characterIsMember:charAtIndex]) {
30
33
  rightIt -= 1;
31
34
  break;
32
35
  }
@@ -34,23 +37,26 @@
34
37
  }
35
38
  }
36
39
  rightIt = MIN(NSInteger(text.length - 1), rightIt);
37
-
38
- if(leftIt > rightIt) {
40
+
41
+ if (leftIt > rightIt) {
39
42
  return [[NSArray alloc] init];
40
43
  }
41
-
42
- NSMutableArray<NSDictionary *> *separatedWords = [[NSMutableArray<NSDictionary *> alloc] init];
44
+
45
+ NSMutableArray<NSDictionary *> *separatedWords =
46
+ [[NSMutableArray<NSDictionary *> alloc] init];
43
47
  NSMutableString *currentWord = [[NSMutableString alloc] init];
44
48
  NSInteger currentRangeStart = leftIt;
45
49
  NSInteger currentIdx = leftIt;
46
-
50
+
47
51
  while (currentIdx <= rightIt) {
48
52
  unichar charAtIndex = [text characterAtIndex:currentIdx];
49
- if ([[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:charAtIndex]) {
53
+ if ([[NSCharacterSet whitespaceAndNewlineCharacterSet]
54
+ characterIsMember:charAtIndex]) {
50
55
  if (currentWord.length > 0) {
51
56
  [separatedWords addObject:@{
52
- @"word": [currentWord copy],
53
- @"range": [NSValue valueWithRange:NSMakeRange(currentRangeStart, currentWord.length)]
57
+ @"word" : [currentWord copy],
58
+ @"range" : [NSValue
59
+ valueWithRange:NSMakeRange(currentRangeStart, currentWord.length)]
54
60
  }];
55
61
  [currentWord setString:@""];
56
62
  }
@@ -60,28 +66,32 @@
60
66
  }
61
67
  currentIdx += 1;
62
68
  }
63
-
69
+
64
70
  if (currentWord.length > 0) {
65
71
  [separatedWords addObject:@{
66
- @"word": [currentWord copy],
67
- @"range": [NSValue valueWithRange:NSMakeRange(currentRangeStart, rightIt - currentRangeStart + 1)]
72
+ @"word" : [currentWord copy],
73
+ @"range" :
74
+ [NSValue valueWithRange:NSMakeRange(currentRangeStart,
75
+ rightIt - currentRangeStart + 1)]
68
76
  }];
69
77
  }
70
-
78
+
71
79
  return separatedWords;
72
80
  }
73
81
 
74
82
  + (NSDictionary *)getCurrentWord:(NSString *)text range:(NSRange)range {
75
83
  // we just get current word at the cursor
76
- if(range.length > 0) {
84
+ if (range.length > 0) {
77
85
  return nullptr;
78
86
  }
79
-
80
- NSArray<NSDictionary *> *words = [WordsUtils getAffectedWordsFromText:text modificationRange:range];
81
- if(words != nullptr && [words count] == 1 && [words firstObject] != nullptr) {
87
+
88
+ NSArray<NSDictionary *> *words = [WordsUtils getAffectedWordsFromText:text
89
+ modificationRange:range];
90
+ if (words != nullptr && [words count] == 1 &&
91
+ [words firstObject] != nullptr) {
82
92
  return [words firstObject];
83
93
  }
84
-
94
+
85
95
  return nullptr;
86
96
  }
87
97
 
@@ -3,5 +3,7 @@
3
3
 
4
4
  @interface ZeroWidthSpaceUtils : NSObject
5
5
  + (void)handleZeroWidthSpacesInInput:(id)input;
6
- + (BOOL)handleBackspaceInRange:(NSRange)range replacementText:(NSString *)text input:(id)input;
6
+ + (BOOL)handleBackspaceInRange:(NSRange)range
7
+ replacementText:(NSString *)text
8
+ input:(id)input;
7
9
  @end