react-native-enriched-html 0.0.0 → 1.0.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 (666) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +402 -0
  3. package/ReactNativeEnrichedHtml.podspec +31 -0
  4. package/android/build.gradle +106 -0
  5. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerDelegate.java +203 -0
  6. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerInterface.java +74 -0
  7. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextViewManagerDelegate.java +70 -0
  8. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextViewManagerInterface.java +31 -0
  9. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/ComponentDescriptors.cpp +22 -0
  10. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/ComponentDescriptors.h +24 -0
  11. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/EventEmitters.cpp +456 -0
  12. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/EventEmitters.h +410 -0
  13. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/Props.cpp +272 -0
  14. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/Props.h +1595 -0
  15. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/ShadowNodes.cpp +17 -0
  16. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/ShadowNodes.h +23 -0
  17. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/States.cpp +16 -0
  18. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/States.h +20 -0
  19. package/android/gradle.properties +5 -0
  20. package/android/lint.gradle +70 -0
  21. package/android/src/main/AndroidManifest.xml +3 -0
  22. package/android/src/main/AndroidManifestNew.xml +2 -0
  23. package/android/src/main/java/com/swmansion/enriched/ReactNativeEnrichedPackage.kt +22 -0
  24. package/android/src/main/java/com/swmansion/enriched/common/AllowFontScaling.kt +36 -0
  25. package/android/src/main/java/com/swmansion/enriched/common/AsyncDrawable.kt +126 -0
  26. package/android/src/main/java/com/swmansion/enriched/common/CheckboxDrawable.kt +81 -0
  27. package/android/src/main/java/com/swmansion/enriched/common/EnrichedConstants.kt +16 -0
  28. package/android/src/main/java/com/swmansion/enriched/common/EnrichedStyle.kt +57 -0
  29. package/android/src/main/java/com/swmansion/enriched/common/ForceRedrawSpan.kt +14 -0
  30. package/android/src/main/java/com/swmansion/enriched/common/GumboNormalizer.kt +5 -0
  31. package/android/src/main/java/com/swmansion/enriched/common/MentionStyle.kt +9 -0
  32. package/android/src/main/java/com/swmansion/enriched/common/ResourceManager.kt +26 -0
  33. package/android/src/main/java/com/swmansion/enriched/common/parser/EnrichedParser.java +1034 -0
  34. package/android/src/main/java/com/swmansion/enriched/common/parser/EnrichedSpanFactory.kt +82 -0
  35. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedAlignmentSpan.kt +19 -0
  36. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedBlockQuoteSpan.kt +53 -0
  37. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedBoldSpan.kt +12 -0
  38. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedCheckboxListSpan.kt +92 -0
  39. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedCodeBlockSpan.kt +81 -0
  40. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH1Span.kt +20 -0
  41. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH2Span.kt +20 -0
  42. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH3Span.kt +20 -0
  43. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH4Span.kt +21 -0
  44. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH5Span.kt +20 -0
  45. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH6Span.kt +20 -0
  46. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedImageSpan.kt +185 -0
  47. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedInlineCodeSpan.kt +24 -0
  48. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedItalicSpan.kt +12 -0
  49. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedLinkSpan.kt +29 -0
  50. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedMentionSpan.kt +35 -0
  51. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedOrderedListSpan.kt +79 -0
  52. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedStrikeThroughSpan.kt +11 -0
  53. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedUnderlineSpan.kt +11 -0
  54. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedUnorderedListSpan.kt +62 -0
  55. package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedBlockSpan.kt +5 -0
  56. package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedHeadingSpan.kt +3 -0
  57. package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedInlineSpan.kt +3 -0
  58. package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedParagraphSpan.kt +5 -0
  59. package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedSpan.kt +3 -0
  60. package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedZeroWidthSpaceSpan.kt +4 -0
  61. package/android/src/main/java/com/swmansion/enriched/text/EnrichedTextMovementMethod.kt +75 -0
  62. package/android/src/main/java/com/swmansion/enriched/text/EnrichedTextSpanFactory.kt +83 -0
  63. package/android/src/main/java/com/swmansion/enriched/text/EnrichedTextStyle.kt +202 -0
  64. package/android/src/main/java/com/swmansion/enriched/text/EnrichedTextView.kt +361 -0
  65. package/android/src/main/java/com/swmansion/enriched/text/EnrichedTextViewManager.kt +157 -0
  66. package/android/src/main/java/com/swmansion/enriched/text/MeasurementStore.kt +170 -0
  67. package/android/src/main/java/com/swmansion/enriched/text/events/OnLinkPressEvent.kt +23 -0
  68. package/android/src/main/java/com/swmansion/enriched/text/events/OnMentionPressEvent.kt +32 -0
  69. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextAlignmentSpan.kt +14 -0
  70. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextBlockQuoteSpan.kt +14 -0
  71. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextBoldSpan.kt +14 -0
  72. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextCheckboxListSpan.kt +15 -0
  73. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextCodeBlockSpan.kt +14 -0
  74. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextH1Span.kt +14 -0
  75. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextH2Span.kt +14 -0
  76. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextH3Span.kt +15 -0
  77. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextH4Span.kt +15 -0
  78. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextH5Span.kt +14 -0
  79. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextH6Span.kt +14 -0
  80. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextImageSpan.kt +57 -0
  81. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextInlineCodeSpan.kt +14 -0
  82. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextItalicSpan.kt +14 -0
  83. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextLinkSpan.kt +41 -0
  84. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextMentionSpan.kt +63 -0
  85. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextOrderedListSpan.kt +16 -0
  86. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextStrikeThroughSpan.kt +15 -0
  87. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextUnderlineSpan.kt +15 -0
  88. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextUnorderedListSpan.kt +15 -0
  89. package/android/src/main/java/com/swmansion/enriched/text/spans/interfaces/EnrichedTextClickableSpan.kt +9 -0
  90. package/android/src/main/java/com/swmansion/enriched/text/spans/interfaces/EnrichedTextSpan.kt +10 -0
  91. package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputConnectionWrapper.kt +140 -0
  92. package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputSpannableFactory.kt +85 -0
  93. package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputView.kt +1141 -0
  94. package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputViewLayoutManager.kt +27 -0
  95. package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputViewManager.kt +500 -0
  96. package/android/src/main/java/com/swmansion/enriched/textinput/MeasurementStore.kt +230 -0
  97. package/android/src/main/java/com/swmansion/enriched/textinput/events/MentionHandler.kt +55 -0
  98. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnChangeHtmlEvent.kt +27 -0
  99. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnChangeSelectionEvent.kt +30 -0
  100. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnChangeStateEvent.kt +21 -0
  101. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnChangeTextEvent.kt +30 -0
  102. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnContextMenuItemPressEvent.kt +35 -0
  103. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnInputBlurEvent.kt +25 -0
  104. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnInputFocusEvent.kt +25 -0
  105. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnInputKeyPressEvent.kt +27 -0
  106. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnLinkDetectedEvent.kt +32 -0
  107. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnMentionDetectedEvent.kt +30 -0
  108. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnMentionEvent.kt +34 -0
  109. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnPasteImagesEvent.kt +47 -0
  110. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnRequestHtmlResultEvent.kt +32 -0
  111. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnSubmitEditingEvent.kt +29 -0
  112. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputAlignmentSpan.kt +14 -0
  113. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputBlockQuoteSpan.kt +14 -0
  114. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputBoldSpan.kt +14 -0
  115. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputCheckboxListSpan.kt +15 -0
  116. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputCodeBlockSpan.kt +14 -0
  117. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH1Span.kt +14 -0
  118. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH2Span.kt +14 -0
  119. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH3Span.kt +14 -0
  120. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH4Span.kt +14 -0
  121. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH5Span.kt +14 -0
  122. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH6Span.kt +14 -0
  123. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputImageSpan.kt +36 -0
  124. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputInlineCodeSpan.kt +14 -0
  125. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputItalicSpan.kt +14 -0
  126. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputLinkSpan.kt +16 -0
  127. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputMentionSpan.kt +18 -0
  128. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputOrderedListSpan.kt +21 -0
  129. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputStrikeThroughSpan.kt +14 -0
  130. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputUnderlineSpan.kt +14 -0
  131. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputUnorderedListSpan.kt +14 -0
  132. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedLineHeightSpan.kt +45 -0
  133. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedSpans.kt +240 -0
  134. package/android/src/main/java/com/swmansion/enriched/textinput/spans/interfaces/EnrichedInputSpan.kt +10 -0
  135. package/android/src/main/java/com/swmansion/enriched/textinput/styles/AlignmentStyles.kt +367 -0
  136. package/android/src/main/java/com/swmansion/enriched/textinput/styles/HtmlStyle.kt +371 -0
  137. package/android/src/main/java/com/swmansion/enriched/textinput/styles/InlineStyles.kt +232 -0
  138. package/android/src/main/java/com/swmansion/enriched/textinput/styles/ListStyles.kt +260 -0
  139. package/android/src/main/java/com/swmansion/enriched/textinput/styles/ParagraphStyles.kt +439 -0
  140. package/android/src/main/java/com/swmansion/enriched/textinput/styles/ParametrizedStyles.kt +408 -0
  141. package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedEditableFactory.kt +17 -0
  142. package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSelection.kt +340 -0
  143. package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSpanState.kt +318 -0
  144. package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSpannable.kt +156 -0
  145. package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSpannableStringBuilder.kt +59 -0
  146. package/android/src/main/java/com/swmansion/enriched/textinput/utils/RichContentReceiver.kt +127 -0
  147. package/android/src/main/java/com/swmansion/enriched/textinput/utils/ShortcutsHandler.kt +150 -0
  148. package/android/src/main/java/com/swmansion/enriched/textinput/utils/StyleUtils.kt +51 -0
  149. package/android/src/main/java/com/swmansion/enriched/textinput/utils/Utils.kt +106 -0
  150. package/android/src/main/java/com/swmansion/enriched/textinput/watchers/EnrichedSpanWatcher.kt +109 -0
  151. package/android/src/main/java/com/swmansion/enriched/textinput/watchers/EnrichedTextWatcher.kt +106 -0
  152. package/android/src/main/new_arch/CMakeLists.txt +62 -0
  153. package/android/src/main/new_arch/GumboNormalizerJni.cpp +14 -0
  154. package/android/src/main/new_arch/ReactNativeEnrichedSpec.cpp +11 -0
  155. package/android/src/main/new_arch/ReactNativeEnrichedSpec.h +16 -0
  156. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextComponentDescriptor.h +32 -0
  157. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputComponentDescriptor.h +35 -0
  158. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputMeasurementManager.cpp +53 -0
  159. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputMeasurementManager.h +25 -0
  160. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputShadowNode.cpp +35 -0
  161. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputShadowNode.h +53 -0
  162. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputState.cpp +9 -0
  163. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputState.h +24 -0
  164. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextMeasurementManager.cpp +45 -0
  165. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextMeasurementManager.h +25 -0
  166. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextShadowNode.cpp +21 -0
  167. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextShadowNode.h +39 -0
  168. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/conversions.h +44 -0
  169. package/android/src/main/res/drawable/broken_image.xml +10 -0
  170. package/cpp/CMakeLists.txt +50 -0
  171. package/cpp/GumboParser/GumboParser.h +34043 -0
  172. package/cpp/README.md +59 -0
  173. package/cpp/parser/GumboNormalizer.c +915 -0
  174. package/cpp/parser/GumboParser.cpp +16 -0
  175. package/cpp/parser/GumboParser.hpp +23 -0
  176. package/cpp/tests/GumboParserTest.cpp +457 -0
  177. package/ios/EnrichedTextInputView.h +54 -0
  178. package/ios/EnrichedTextInputView.mm +2170 -0
  179. package/ios/EnrichedTextInputViewManager.mm +13 -0
  180. package/ios/EnrichedTextView.h +35 -0
  181. package/ios/EnrichedTextView.mm +806 -0
  182. package/ios/EnrichedTextViewManager.mm +13 -0
  183. package/ios/config/EnrichedConfig.h +114 -0
  184. package/ios/config/EnrichedConfig.mm +727 -0
  185. package/ios/enrichedInputTextView/EnrichedInputTextView.h +6 -0
  186. package/ios/enrichedInputTextView/EnrichedInputTextView.mm +334 -0
  187. package/ios/enrichedTextTextView/EnrichedTextTextView.h +16 -0
  188. package/ios/enrichedTextTextView/EnrichedTextTextView.mm +71 -0
  189. package/ios/extensions/ColorExtension.h +8 -0
  190. package/ios/extensions/ColorExtension.mm +48 -0
  191. package/ios/extensions/FontExtension.h +11 -0
  192. package/ios/extensions/FontExtension.mm +72 -0
  193. package/ios/extensions/ImageExtension.h +33 -0
  194. package/ios/extensions/ImageExtension.mm +176 -0
  195. package/ios/extensions/LayoutManagerExtension.h +6 -0
  196. package/ios/extensions/LayoutManagerExtension.mm +454 -0
  197. package/ios/extensions/StringExtension.h +15 -0
  198. package/ios/extensions/StringExtension.mm +69 -0
  199. package/ios/generated/ReactNativeEnrichedSpec/ComponentDescriptors.cpp +22 -0
  200. package/ios/generated/ReactNativeEnrichedSpec/ComponentDescriptors.h +24 -0
  201. package/ios/generated/ReactNativeEnrichedSpec/EventEmitters.cpp +456 -0
  202. package/ios/generated/ReactNativeEnrichedSpec/EventEmitters.h +410 -0
  203. package/ios/generated/ReactNativeEnrichedSpec/Props.cpp +272 -0
  204. package/ios/generated/ReactNativeEnrichedSpec/Props.h +1595 -0
  205. package/ios/generated/ReactNativeEnrichedSpec/RCTComponentViewHelpers.h +570 -0
  206. package/ios/generated/ReactNativeEnrichedSpec/ShadowNodes.cpp +17 -0
  207. package/ios/generated/ReactNativeEnrichedSpec/ShadowNodes.h +23 -0
  208. package/ios/generated/ReactNativeEnrichedSpec/States.cpp +16 -0
  209. package/ios/generated/ReactNativeEnrichedSpec/States.h +20 -0
  210. package/ios/htmlParser/HtmlParser.h +11 -0
  211. package/ios/htmlParser/HtmlParser.mm +1468 -0
  212. package/ios/inputAttributesManager/InputAttributesManager.h +18 -0
  213. package/ios/inputAttributesManager/InputAttributesManager.mm +222 -0
  214. package/ios/inputHtmlParser/InputHtmlParser.h +10 -0
  215. package/ios/inputHtmlParser/InputHtmlParser.mm +225 -0
  216. package/ios/interfaces/AlignmentEntry.h +9 -0
  217. package/ios/interfaces/AlignmentEntry.mm +4 -0
  218. package/ios/interfaces/AttributeEntry.h +9 -0
  219. package/ios/interfaces/AttributeEntry.mm +4 -0
  220. package/ios/interfaces/BaseStyleProtocol.h +17 -0
  221. package/ios/interfaces/EnrichedTextStyleHeaders.h +62 -0
  222. package/ios/interfaces/EnrichedViewHost.h +26 -0
  223. package/ios/interfaces/ImageAttachment.h +11 -0
  224. package/ios/interfaces/ImageAttachment.mm +107 -0
  225. package/ios/interfaces/ImageData.h +10 -0
  226. package/ios/interfaces/ImageData.mm +4 -0
  227. package/ios/interfaces/LinkData.h +11 -0
  228. package/ios/interfaces/LinkData.mm +43 -0
  229. package/ios/interfaces/LinkRegexConfig.h +19 -0
  230. package/ios/interfaces/LinkRegexConfig.mm +37 -0
  231. package/ios/interfaces/MediaAttachment.h +23 -0
  232. package/ios/interfaces/MediaAttachment.mm +31 -0
  233. package/ios/interfaces/MentionParams.h +8 -0
  234. package/ios/interfaces/MentionParams.mm +4 -0
  235. package/ios/interfaces/MentionStyleProps.h +17 -0
  236. package/ios/interfaces/MentionStyleProps.mm +80 -0
  237. package/ios/interfaces/StyleBase.h +38 -0
  238. package/ios/interfaces/StyleBase.mm +279 -0
  239. package/ios/interfaces/StyleHeaders.h +110 -0
  240. package/ios/interfaces/StylePair.h +9 -0
  241. package/ios/interfaces/StylePair.mm +4 -0
  242. package/ios/interfaces/StyleTypeEnum.h +27 -0
  243. package/ios/interfaces/TextDecorationLineEnum.h +6 -0
  244. package/ios/interfaces/TextDecorationLineEnum.mm +4 -0
  245. package/ios/internals/EnrichedTextComponentDescriptor.h +19 -0
  246. package/ios/internals/EnrichedTextInputViewComponentDescriptor.h +19 -0
  247. package/ios/internals/EnrichedTextInputViewShadowNode.h +44 -0
  248. package/ios/internals/EnrichedTextInputViewShadowNode.mm +103 -0
  249. package/ios/internals/EnrichedTextInputViewState.cpp +10 -0
  250. package/ios/internals/EnrichedTextInputViewState.h +22 -0
  251. package/ios/internals/EnrichedTextViewShadowNode.h +34 -0
  252. package/ios/internals/EnrichedTextViewShadowNode.mm +72 -0
  253. package/ios/internals/EnrichedTextViewState.cpp +7 -0
  254. package/ios/internals/EnrichedTextViewState.h +18 -0
  255. package/ios/styles/AlignmentStyle.mm +203 -0
  256. package/ios/styles/BlockQuoteStyle.mm +54 -0
  257. package/ios/styles/BoldStyle.mm +37 -0
  258. package/ios/styles/CheckboxListStyle.mm +154 -0
  259. package/ios/styles/CodeBlockStyle.mm +49 -0
  260. package/ios/styles/EnrichedTextStyles.mm +61 -0
  261. package/ios/styles/H1Style.mm +20 -0
  262. package/ios/styles/H2Style.mm +20 -0
  263. package/ios/styles/H3Style.mm +20 -0
  264. package/ios/styles/H4Style.mm +20 -0
  265. package/ios/styles/H5Style.mm +20 -0
  266. package/ios/styles/H6Style.mm +20 -0
  267. package/ios/styles/HeadingStyleBase.mm +96 -0
  268. package/ios/styles/ImageStyle.mm +150 -0
  269. package/ios/styles/InlineCodeStyle.mm +65 -0
  270. package/ios/styles/ItalicStyle.mm +37 -0
  271. package/ios/styles/LinkStyle.mm +532 -0
  272. package/ios/styles/MentionStyle.mm +541 -0
  273. package/ios/styles/OrderedListStyle.mm +48 -0
  274. package/ios/styles/StrikethroughStyle.mm +25 -0
  275. package/ios/styles/UnderlineStyle.mm +24 -0
  276. package/ios/styles/UnorderedListStyle.mm +48 -0
  277. package/ios/textHtmlParser/TextHtmlParser.h +10 -0
  278. package/ios/textHtmlParser/TextHtmlParser.mm +181 -0
  279. package/ios/utils/AlignmentUtils.h +20 -0
  280. package/ios/utils/AlignmentUtils.mm +111 -0
  281. package/ios/utils/AttachmentLayoutUtils.h +24 -0
  282. package/ios/utils/AttachmentLayoutUtils.mm +144 -0
  283. package/ios/utils/CheckboxHitTestUtils.h +10 -0
  284. package/ios/utils/CheckboxHitTestUtils.mm +122 -0
  285. package/ios/utils/DotReplacementUtils.h +10 -0
  286. package/ios/utils/DotReplacementUtils.mm +68 -0
  287. package/ios/utils/EnrichedTextTouchHandler.h +14 -0
  288. package/ios/utils/EnrichedTextTouchHandler.mm +152 -0
  289. package/ios/utils/KeyboardUtils.h +7 -0
  290. package/ios/utils/KeyboardUtils.mm +30 -0
  291. package/ios/utils/OccurenceUtils.h +44 -0
  292. package/ios/utils/OccurenceUtils.mm +185 -0
  293. package/ios/utils/ParagraphAttributesUtils.h +18 -0
  294. package/ios/utils/ParagraphAttributesUtils.mm +289 -0
  295. package/ios/utils/RangeUtils.h +12 -0
  296. package/ios/utils/RangeUtils.mm +183 -0
  297. package/ios/utils/ShortcutsUtils.h +21 -0
  298. package/ios/utils/ShortcutsUtils.mm +486 -0
  299. package/ios/utils/StyleUtils.h +33 -0
  300. package/ios/utils/StyleUtils.mm +290 -0
  301. package/ios/utils/TextBlockTapGestureRecognizer.h +17 -0
  302. package/ios/utils/TextBlockTapGestureRecognizer.mm +56 -0
  303. package/ios/utils/TextInsertionUtils.h +17 -0
  304. package/ios/utils/TextInsertionUtils.mm +60 -0
  305. package/ios/utils/TextListsUtils.h +40 -0
  306. package/ios/utils/TextListsUtils.mm +93 -0
  307. package/ios/utils/WeakBox.h +3 -0
  308. package/ios/utils/WeakBox.m +4 -0
  309. package/ios/utils/WordsUtils.h +7 -0
  310. package/ios/utils/WordsUtils.mm +98 -0
  311. package/ios/utils/ZeroWidthSpaceUtils.h +14 -0
  312. package/ios/utils/ZeroWidthSpaceUtils.mm +310 -0
  313. package/lib/module/index.js +4 -0
  314. package/lib/module/index.js.map +1 -0
  315. package/lib/module/index.native.js +7 -0
  316. package/lib/module/index.native.js.map +1 -0
  317. package/lib/module/native/EnrichedText.js +76 -0
  318. package/lib/module/native/EnrichedText.js.map +1 -0
  319. package/lib/module/native/EnrichedTextInput.js +306 -0
  320. package/lib/module/native/EnrichedTextInput.js.map +1 -0
  321. package/lib/module/package.json +1 -0
  322. package/lib/module/spec/EnrichedTextInputNativeComponent.ts +524 -0
  323. package/lib/module/spec/EnrichedTextNativeComponent.ts +110 -0
  324. package/lib/module/types.js +4 -0
  325. package/lib/module/types.js.map +1 -0
  326. package/lib/module/utils/EnrichedTextInputDefaultProps.js +20 -0
  327. package/lib/module/utils/EnrichedTextInputDefaultProps.js.map +1 -0
  328. package/lib/module/utils/defaultHtmlStyle.js +83 -0
  329. package/lib/module/utils/defaultHtmlStyle.js.map +1 -0
  330. package/lib/module/utils/expandMentionStylesForIndicators.js +15 -0
  331. package/lib/module/utils/expandMentionStylesForIndicators.js.map +1 -0
  332. package/lib/module/utils/isMentionStyleRecord.js +10 -0
  333. package/lib/module/utils/isMentionStyleRecord.js.map +1 -0
  334. package/lib/module/utils/normalizeHtmlStyle.js +126 -0
  335. package/lib/module/utils/normalizeHtmlStyle.js.map +1 -0
  336. package/lib/module/utils/nullthrows.js +9 -0
  337. package/lib/module/utils/nullthrows.js.map +1 -0
  338. package/lib/module/utils/regexParser.js +46 -0
  339. package/lib/module/utils/regexParser.js.map +1 -0
  340. package/lib/module/web/EnrichedTextInput.css +201 -0
  341. package/lib/module/web/EnrichedTextInput.js +314 -0
  342. package/lib/module/web/EnrichedTextInput.js.map +1 -0
  343. package/lib/module/web/adaptWebToNativeEvent.js +34 -0
  344. package/lib/module/web/adaptWebToNativeEvent.js.map +1 -0
  345. package/lib/module/web/checkboxHtmlNormalizer.js +54 -0
  346. package/lib/module/web/checkboxHtmlNormalizer.js.map +1 -0
  347. package/lib/module/web/formats/EnrichedBlockquote.js +32 -0
  348. package/lib/module/web/formats/EnrichedBlockquote.js.map +1 -0
  349. package/lib/module/web/formats/EnrichedBold.js +22 -0
  350. package/lib/module/web/formats/EnrichedBold.js.map +1 -0
  351. package/lib/module/web/formats/EnrichedCheckboxItem.js +32 -0
  352. package/lib/module/web/formats/EnrichedCheckboxItem.js.map +1 -0
  353. package/lib/module/web/formats/EnrichedCheckboxList.js +35 -0
  354. package/lib/module/web/formats/EnrichedCheckboxList.js.map +1 -0
  355. package/lib/module/web/formats/EnrichedCode.js +15 -0
  356. package/lib/module/web/formats/EnrichedCode.js.map +1 -0
  357. package/lib/module/web/formats/EnrichedCodeBlock.js +43 -0
  358. package/lib/module/web/formats/EnrichedCodeBlock.js.map +1 -0
  359. package/lib/module/web/formats/EnrichedHeading.js +27 -0
  360. package/lib/module/web/formats/EnrichedHeading.js.map +1 -0
  361. package/lib/module/web/formats/EnrichedImage.js +57 -0
  362. package/lib/module/web/formats/EnrichedImage.js.map +1 -0
  363. package/lib/module/web/formats/EnrichedImageNodeView.js +90 -0
  364. package/lib/module/web/formats/EnrichedImageNodeView.js.map +1 -0
  365. package/lib/module/web/formats/EnrichedItalic.js +22 -0
  366. package/lib/module/web/formats/EnrichedItalic.js.map +1 -0
  367. package/lib/module/web/formats/EnrichedLink.js +140 -0
  368. package/lib/module/web/formats/EnrichedLink.js.map +1 -0
  369. package/lib/module/web/formats/EnrichedListItem.js +19 -0
  370. package/lib/module/web/formats/EnrichedListItem.js.map +1 -0
  371. package/lib/module/web/formats/EnrichedMention.js +50 -0
  372. package/lib/module/web/formats/EnrichedMention.js.map +1 -0
  373. package/lib/module/web/formats/EnrichedOrderedList.js +32 -0
  374. package/lib/module/web/formats/EnrichedOrderedList.js.map +1 -0
  375. package/lib/module/web/formats/EnrichedStrike.js +22 -0
  376. package/lib/module/web/formats/EnrichedStrike.js.map +1 -0
  377. package/lib/module/web/formats/EnrichedUnderline.js +22 -0
  378. package/lib/module/web/formats/EnrichedUnderline.js.map +1 -0
  379. package/lib/module/web/formats/EnrichedUnorderedList.js +33 -0
  380. package/lib/module/web/formats/EnrichedUnorderedList.js.map +1 -0
  381. package/lib/module/web/formats/applyWrappingListToSelection.js +63 -0
  382. package/lib/module/web/formats/applyWrappingListToSelection.js.map +1 -0
  383. package/lib/module/web/formats/formatRules.js +48 -0
  384. package/lib/module/web/formats/formatRules.js.map +1 -0
  385. package/lib/module/web/formats/listKeyboard.js +50 -0
  386. package/lib/module/web/formats/listKeyboard.js.map +1 -0
  387. package/lib/module/web/formats/wrappedBlockKeyboard.js +58 -0
  388. package/lib/module/web/formats/wrappedBlockKeyboard.js.map +1 -0
  389. package/lib/module/web/pasteImages.js +78 -0
  390. package/lib/module/web/pasteImages.js.map +1 -0
  391. package/lib/module/web/pastedImageDimensions.js +49 -0
  392. package/lib/module/web/pastedImageDimensions.js.map +1 -0
  393. package/lib/module/web/pmPlugins/MentionPlugin/index.js +36 -0
  394. package/lib/module/web/pmPlugins/MentionPlugin/index.js.map +1 -0
  395. package/lib/module/web/pmPlugins/MentionPlugin/isCaretInBlockedContext.js +11 -0
  396. package/lib/module/web/pmPlugins/MentionPlugin/isCaretInBlockedContext.js.map +1 -0
  397. package/lib/module/web/pmPlugins/MentionPlugin/makeMentionPluginState.js +67 -0
  398. package/lib/module/web/pmPlugins/MentionPlugin/makeMentionPluginState.js.map +1 -0
  399. package/lib/module/web/pmPlugins/MentionPlugin/mentionPluginKey.js +5 -0
  400. package/lib/module/web/pmPlugins/MentionPlugin/mentionPluginKey.js.map +1 -0
  401. package/lib/module/web/pmPlugins/MentionPlugin/removeMentionMarksIfSpansResized.js +56 -0
  402. package/lib/module/web/pmPlugins/MentionPlugin/removeMentionMarksIfSpansResized.js.map +1 -0
  403. package/lib/module/web/pmPlugins/MentionPlugin/setMention.js +58 -0
  404. package/lib/module/web/pmPlugins/MentionPlugin/setMention.js.map +1 -0
  405. package/lib/module/web/pmPlugins/MentionPlugin/startMention.js +9 -0
  406. package/lib/module/web/pmPlugins/MentionPlugin/startMention.js.map +1 -0
  407. package/lib/module/web/pmPlugins/MentionPlugin/stripPartialMentionMarks.js +9 -0
  408. package/lib/module/web/pmPlugins/MentionPlugin/stripPartialMentionMarks.js.map +1 -0
  409. package/lib/module/web/pmPlugins/MentionPlugin/subscribeMentionEvents.js +100 -0
  410. package/lib/module/web/pmPlugins/MentionPlugin/subscribeMentionEvents.js.map +1 -0
  411. package/lib/module/web/pmPlugins/MentionPlugin/types.js +4 -0
  412. package/lib/module/web/pmPlugins/MentionPlugin/types.js.map +1 -0
  413. package/lib/module/web/pmPlugins/MergeAdjacentSameKindBlocksPlugin.js +42 -0
  414. package/lib/module/web/pmPlugins/MergeAdjacentSameKindBlocksPlugin.js.map +1 -0
  415. package/lib/module/web/pmPlugins/ShortcutPlugin.js +123 -0
  416. package/lib/module/web/pmPlugins/ShortcutPlugin.js.map +1 -0
  417. package/lib/module/web/pmPlugins/StrictMarksPlugin.js +98 -0
  418. package/lib/module/web/pmPlugins/StrictMarksPlugin.js.map +1 -0
  419. package/lib/module/web/pmPlugins/StripBoldInStyledHeadingsPlugin.js +56 -0
  420. package/lib/module/web/pmPlugins/StripBoldInStyledHeadingsPlugin.js.map +1 -0
  421. package/lib/module/web/pmPlugins/StripMarksInCodeBlockPlugin.js +28 -0
  422. package/lib/module/web/pmPlugins/StripMarksInCodeBlockPlugin.js.map +1 -0
  423. package/lib/module/web/pmPlugins/StripMarksOnImagePlugin.js +29 -0
  424. package/lib/module/web/pmPlugins/StripMarksOnImagePlugin.js.map +1 -0
  425. package/lib/module/web/positionMapping.js +76 -0
  426. package/lib/module/web/positionMapping.js.map +1 -0
  427. package/lib/module/web/returnKeyTypeToEnterKeyHint.js +20 -0
  428. package/lib/module/web/returnKeyTypeToEnterKeyHint.js.map +1 -0
  429. package/lib/module/web/styleConversion/buildMentionRulesCSS.js +33 -0
  430. package/lib/module/web/styleConversion/buildMentionRulesCSS.js.map +1 -0
  431. package/lib/module/web/styleConversion/enrichedInputStyleToCSSProperties.js +159 -0
  432. package/lib/module/web/styleConversion/enrichedInputStyleToCSSProperties.js.map +1 -0
  433. package/lib/module/web/styleConversion/enrichedInputThemingToCSSProperties.js +18 -0
  434. package/lib/module/web/styleConversion/enrichedInputThemingToCSSProperties.js.map +1 -0
  435. package/lib/module/web/styleConversion/htmlStyleToCSSVariables.js +145 -0
  436. package/lib/module/web/styleConversion/htmlStyleToCSSVariables.js.map +1 -0
  437. package/lib/module/web/styleConversion/mentionIndicatorCssKey.js +15 -0
  438. package/lib/module/web/styleConversion/mentionIndicatorCssKey.js.map +1 -0
  439. package/lib/module/web/styleConversion/toColor.js +18 -0
  440. package/lib/module/web/styleConversion/toColor.js.map +1 -0
  441. package/lib/module/web/tiptapHtmlNormalizer.js +30 -0
  442. package/lib/module/web/tiptapHtmlNormalizer.js.map +1 -0
  443. package/lib/module/web/useOnChangeHtml.js +8 -0
  444. package/lib/module/web/useOnChangeHtml.js.map +1 -0
  445. package/lib/module/web/useOnChangeState.js +88 -0
  446. package/lib/module/web/useOnChangeState.js.map +1 -0
  447. package/lib/module/web/useOnChangeText.js +11 -0
  448. package/lib/module/web/useOnChangeText.js.map +1 -0
  449. package/lib/module/web/useOnEditorChange.js +25 -0
  450. package/lib/module/web/useOnEditorChange.js.map +1 -0
  451. package/lib/module/web/useOnLinkDetected.js +68 -0
  452. package/lib/module/web/useOnLinkDetected.js.map +1 -0
  453. package/lib/typescript/package.json +1 -0
  454. package/lib/typescript/src/index.d.ts +3 -0
  455. package/lib/typescript/src/index.d.ts.map +1 -0
  456. package/lib/typescript/src/index.native.d.ts +5 -0
  457. package/lib/typescript/src/index.native.d.ts.map +1 -0
  458. package/lib/typescript/src/native/EnrichedText.d.ts +3 -0
  459. package/lib/typescript/src/native/EnrichedText.d.ts.map +1 -0
  460. package/lib/typescript/src/native/EnrichedTextInput.d.ts +3 -0
  461. package/lib/typescript/src/native/EnrichedTextInput.d.ts.map +1 -0
  462. package/lib/typescript/src/spec/EnrichedTextInputNativeComponent.d.ts +405 -0
  463. package/lib/typescript/src/spec/EnrichedTextInputNativeComponent.d.ts.map +1 -0
  464. package/lib/typescript/src/spec/EnrichedTextNativeComponent.d.ts +88 -0
  465. package/lib/typescript/src/spec/EnrichedTextNativeComponent.d.ts.map +1 -0
  466. package/lib/typescript/src/types.d.ts +721 -0
  467. package/lib/typescript/src/types.d.ts.map +1 -0
  468. package/lib/typescript/src/utils/EnrichedTextInputDefaultProps.d.ts +13 -0
  469. package/lib/typescript/src/utils/EnrichedTextInputDefaultProps.d.ts.map +1 -0
  470. package/lib/typescript/src/utils/defaultHtmlStyle.d.ts +4 -0
  471. package/lib/typescript/src/utils/defaultHtmlStyle.d.ts.map +1 -0
  472. package/lib/typescript/src/utils/expandMentionStylesForIndicators.d.ts +3 -0
  473. package/lib/typescript/src/utils/expandMentionStylesForIndicators.d.ts.map +1 -0
  474. package/lib/typescript/src/utils/isMentionStyleRecord.d.ts +3 -0
  475. package/lib/typescript/src/utils/isMentionStyleRecord.d.ts.map +1 -0
  476. package/lib/typescript/src/utils/normalizeHtmlStyle.d.ts +6 -0
  477. package/lib/typescript/src/utils/normalizeHtmlStyle.d.ts.map +1 -0
  478. package/lib/typescript/src/utils/nullthrows.d.ts +2 -0
  479. package/lib/typescript/src/utils/nullthrows.d.ts.map +1 -0
  480. package/lib/typescript/src/utils/regexParser.d.ts +3 -0
  481. package/lib/typescript/src/utils/regexParser.d.ts.map +1 -0
  482. package/lib/typescript/src/web/EnrichedTextInput.d.ts +4 -0
  483. package/lib/typescript/src/web/EnrichedTextInput.d.ts.map +1 -0
  484. package/lib/typescript/src/web/adaptWebToNativeEvent.d.ts +3 -0
  485. package/lib/typescript/src/web/adaptWebToNativeEvent.d.ts.map +1 -0
  486. package/lib/typescript/src/web/checkboxHtmlNormalizer.d.ts +3 -0
  487. package/lib/typescript/src/web/checkboxHtmlNormalizer.d.ts.map +1 -0
  488. package/lib/typescript/src/web/formats/EnrichedBlockquote.d.ts +2 -0
  489. package/lib/typescript/src/web/formats/EnrichedBlockquote.d.ts.map +1 -0
  490. package/lib/typescript/src/web/formats/EnrichedBold.d.ts +2 -0
  491. package/lib/typescript/src/web/formats/EnrichedBold.d.ts.map +1 -0
  492. package/lib/typescript/src/web/formats/EnrichedCheckboxItem.d.ts +2 -0
  493. package/lib/typescript/src/web/formats/EnrichedCheckboxItem.d.ts.map +1 -0
  494. package/lib/typescript/src/web/formats/EnrichedCheckboxList.d.ts +9 -0
  495. package/lib/typescript/src/web/formats/EnrichedCheckboxList.d.ts.map +1 -0
  496. package/lib/typescript/src/web/formats/EnrichedCode.d.ts +2 -0
  497. package/lib/typescript/src/web/formats/EnrichedCode.d.ts.map +1 -0
  498. package/lib/typescript/src/web/formats/EnrichedCodeBlock.d.ts +9 -0
  499. package/lib/typescript/src/web/formats/EnrichedCodeBlock.d.ts.map +1 -0
  500. package/lib/typescript/src/web/formats/EnrichedHeading.d.ts +5 -0
  501. package/lib/typescript/src/web/formats/EnrichedHeading.d.ts.map +1 -0
  502. package/lib/typescript/src/web/formats/EnrichedImage.d.ts +2 -0
  503. package/lib/typescript/src/web/formats/EnrichedImage.d.ts.map +1 -0
  504. package/lib/typescript/src/web/formats/EnrichedImageNodeView.d.ts +3 -0
  505. package/lib/typescript/src/web/formats/EnrichedImageNodeView.d.ts.map +1 -0
  506. package/lib/typescript/src/web/formats/EnrichedItalic.d.ts +2 -0
  507. package/lib/typescript/src/web/formats/EnrichedItalic.d.ts.map +1 -0
  508. package/lib/typescript/src/web/formats/EnrichedLink.d.ts +5 -0
  509. package/lib/typescript/src/web/formats/EnrichedLink.d.ts.map +1 -0
  510. package/lib/typescript/src/web/formats/EnrichedListItem.d.ts +2 -0
  511. package/lib/typescript/src/web/formats/EnrichedListItem.d.ts.map +1 -0
  512. package/lib/typescript/src/web/formats/EnrichedMention.d.ts +4 -0
  513. package/lib/typescript/src/web/formats/EnrichedMention.d.ts.map +1 -0
  514. package/lib/typescript/src/web/formats/EnrichedOrderedList.d.ts +2 -0
  515. package/lib/typescript/src/web/formats/EnrichedOrderedList.d.ts.map +1 -0
  516. package/lib/typescript/src/web/formats/EnrichedStrike.d.ts +2 -0
  517. package/lib/typescript/src/web/formats/EnrichedStrike.d.ts.map +1 -0
  518. package/lib/typescript/src/web/formats/EnrichedUnderline.d.ts +2 -0
  519. package/lib/typescript/src/web/formats/EnrichedUnderline.d.ts.map +1 -0
  520. package/lib/typescript/src/web/formats/EnrichedUnorderedList.d.ts +9 -0
  521. package/lib/typescript/src/web/formats/EnrichedUnorderedList.d.ts.map +1 -0
  522. package/lib/typescript/src/web/formats/applyWrappingListToSelection.d.ts +18 -0
  523. package/lib/typescript/src/web/formats/applyWrappingListToSelection.d.ts.map +1 -0
  524. package/lib/typescript/src/web/formats/formatRules.d.ts +10 -0
  525. package/lib/typescript/src/web/formats/formatRules.d.ts.map +1 -0
  526. package/lib/typescript/src/web/formats/listKeyboard.d.ts +4 -0
  527. package/lib/typescript/src/web/formats/listKeyboard.d.ts.map +1 -0
  528. package/lib/typescript/src/web/formats/wrappedBlockKeyboard.d.ts +12 -0
  529. package/lib/typescript/src/web/formats/wrappedBlockKeyboard.d.ts.map +1 -0
  530. package/lib/typescript/src/web/pasteImages.d.ts +10 -0
  531. package/lib/typescript/src/web/pasteImages.d.ts.map +1 -0
  532. package/lib/typescript/src/web/pastedImageDimensions.d.ts +9 -0
  533. package/lib/typescript/src/web/pastedImageDimensions.d.ts.map +1 -0
  534. package/lib/typescript/src/web/pmPlugins/MentionPlugin/index.d.ts +9 -0
  535. package/lib/typescript/src/web/pmPlugins/MentionPlugin/index.d.ts.map +1 -0
  536. package/lib/typescript/src/web/pmPlugins/MentionPlugin/isCaretInBlockedContext.d.ts +3 -0
  537. package/lib/typescript/src/web/pmPlugins/MentionPlugin/isCaretInBlockedContext.d.ts.map +1 -0
  538. package/lib/typescript/src/web/pmPlugins/MentionPlugin/makeMentionPluginState.d.ts +4 -0
  539. package/lib/typescript/src/web/pmPlugins/MentionPlugin/makeMentionPluginState.d.ts.map +1 -0
  540. package/lib/typescript/src/web/pmPlugins/MentionPlugin/mentionPluginKey.d.ts +4 -0
  541. package/lib/typescript/src/web/pmPlugins/MentionPlugin/mentionPluginKey.d.ts.map +1 -0
  542. package/lib/typescript/src/web/pmPlugins/MentionPlugin/removeMentionMarksIfSpansResized.d.ts +9 -0
  543. package/lib/typescript/src/web/pmPlugins/MentionPlugin/removeMentionMarksIfSpansResized.d.ts.map +1 -0
  544. package/lib/typescript/src/web/pmPlugins/MentionPlugin/setMention.d.ts +3 -0
  545. package/lib/typescript/src/web/pmPlugins/MentionPlugin/setMention.d.ts.map +1 -0
  546. package/lib/typescript/src/web/pmPlugins/MentionPlugin/startMention.d.ts +3 -0
  547. package/lib/typescript/src/web/pmPlugins/MentionPlugin/startMention.d.ts.map +1 -0
  548. package/lib/typescript/src/web/pmPlugins/MentionPlugin/stripPartialMentionMarks.d.ts +3 -0
  549. package/lib/typescript/src/web/pmPlugins/MentionPlugin/stripPartialMentionMarks.d.ts.map +1 -0
  550. package/lib/typescript/src/web/pmPlugins/MentionPlugin/subscribeMentionEvents.d.ts +4 -0
  551. package/lib/typescript/src/web/pmPlugins/MentionPlugin/subscribeMentionEvents.d.ts.map +1 -0
  552. package/lib/typescript/src/web/pmPlugins/MentionPlugin/types.d.ts +20 -0
  553. package/lib/typescript/src/web/pmPlugins/MentionPlugin/types.d.ts.map +1 -0
  554. package/lib/typescript/src/web/pmPlugins/MergeAdjacentSameKindBlocksPlugin.d.ts +3 -0
  555. package/lib/typescript/src/web/pmPlugins/MergeAdjacentSameKindBlocksPlugin.d.ts.map +1 -0
  556. package/lib/typescript/src/web/pmPlugins/ShortcutPlugin.d.ts +7 -0
  557. package/lib/typescript/src/web/pmPlugins/ShortcutPlugin.d.ts.map +1 -0
  558. package/lib/typescript/src/web/pmPlugins/StrictMarksPlugin.d.ts +3 -0
  559. package/lib/typescript/src/web/pmPlugins/StrictMarksPlugin.d.ts.map +1 -0
  560. package/lib/typescript/src/web/pmPlugins/StripBoldInStyledHeadingsPlugin.d.ts +10 -0
  561. package/lib/typescript/src/web/pmPlugins/StripBoldInStyledHeadingsPlugin.d.ts.map +1 -0
  562. package/lib/typescript/src/web/pmPlugins/StripMarksInCodeBlockPlugin.d.ts +3 -0
  563. package/lib/typescript/src/web/pmPlugins/StripMarksInCodeBlockPlugin.d.ts.map +1 -0
  564. package/lib/typescript/src/web/pmPlugins/StripMarksOnImagePlugin.d.ts +3 -0
  565. package/lib/typescript/src/web/pmPlugins/StripMarksOnImagePlugin.d.ts.map +1 -0
  566. package/lib/typescript/src/web/positionMapping.d.ts +39 -0
  567. package/lib/typescript/src/web/positionMapping.d.ts.map +1 -0
  568. package/lib/typescript/src/web/returnKeyTypeToEnterKeyHint.d.ts +4 -0
  569. package/lib/typescript/src/web/returnKeyTypeToEnterKeyHint.d.ts.map +1 -0
  570. package/lib/typescript/src/web/styleConversion/buildMentionRulesCSS.d.ts +3 -0
  571. package/lib/typescript/src/web/styleConversion/buildMentionRulesCSS.d.ts.map +1 -0
  572. package/lib/typescript/src/web/styleConversion/enrichedInputStyleToCSSProperties.d.ts +8 -0
  573. package/lib/typescript/src/web/styleConversion/enrichedInputStyleToCSSProperties.d.ts.map +1 -0
  574. package/lib/typescript/src/web/styleConversion/enrichedInputThemingToCSSProperties.d.ts +9 -0
  575. package/lib/typescript/src/web/styleConversion/enrichedInputThemingToCSSProperties.d.ts.map +1 -0
  576. package/lib/typescript/src/web/styleConversion/htmlStyleToCSSVariables.d.ts +10 -0
  577. package/lib/typescript/src/web/styleConversion/htmlStyleToCSSVariables.d.ts.map +1 -0
  578. package/lib/typescript/src/web/styleConversion/mentionIndicatorCssKey.d.ts +3 -0
  579. package/lib/typescript/src/web/styleConversion/mentionIndicatorCssKey.d.ts.map +1 -0
  580. package/lib/typescript/src/web/styleConversion/toColor.d.ts +3 -0
  581. package/lib/typescript/src/web/styleConversion/toColor.d.ts.map +1 -0
  582. package/lib/typescript/src/web/tiptapHtmlNormalizer.d.ts +3 -0
  583. package/lib/typescript/src/web/tiptapHtmlNormalizer.d.ts.map +1 -0
  584. package/lib/typescript/src/web/useOnChangeHtml.d.ts +5 -0
  585. package/lib/typescript/src/web/useOnChangeHtml.d.ts.map +1 -0
  586. package/lib/typescript/src/web/useOnChangeState.d.ts +6 -0
  587. package/lib/typescript/src/web/useOnChangeState.d.ts.map +1 -0
  588. package/lib/typescript/src/web/useOnChangeText.d.ts +5 -0
  589. package/lib/typescript/src/web/useOnChangeText.d.ts.map +1 -0
  590. package/lib/typescript/src/web/useOnEditorChange.d.ts +6 -0
  591. package/lib/typescript/src/web/useOnEditorChange.d.ts.map +1 -0
  592. package/lib/typescript/src/web/useOnLinkDetected.d.ts +4 -0
  593. package/lib/typescript/src/web/useOnLinkDetected.d.ts.map +1 -0
  594. package/package.json +232 -1
  595. package/react-native.config.js +16 -0
  596. package/src/index.native.tsx +34 -0
  597. package/src/index.tsx +24 -0
  598. package/src/native/EnrichedText.tsx +115 -0
  599. package/src/native/EnrichedTextInput.tsx +374 -0
  600. package/src/spec/EnrichedTextInputNativeComponent.ts +524 -0
  601. package/src/spec/EnrichedTextNativeComponent.ts +110 -0
  602. package/src/types.ts +864 -0
  603. package/src/utils/EnrichedTextInputDefaultProps.ts +16 -0
  604. package/src/utils/defaultHtmlStyle.ts +83 -0
  605. package/src/utils/expandMentionStylesForIndicators.ts +19 -0
  606. package/src/utils/isMentionStyleRecord.ts +22 -0
  607. package/src/utils/normalizeHtmlStyle.ts +181 -0
  608. package/src/utils/nullthrows.ts +7 -0
  609. package/src/utils/regexParser.ts +56 -0
  610. package/src/web/EnrichedTextInput.css +201 -0
  611. package/src/web/EnrichedTextInput.tsx +403 -0
  612. package/src/web/adaptWebToNativeEvent.ts +37 -0
  613. package/src/web/checkboxHtmlNormalizer.ts +62 -0
  614. package/src/web/formats/EnrichedBlockquote.ts +36 -0
  615. package/src/web/formats/EnrichedBold.ts +16 -0
  616. package/src/web/formats/EnrichedCheckboxItem.ts +35 -0
  617. package/src/web/formats/EnrichedCheckboxList.ts +47 -0
  618. package/src/web/formats/EnrichedCode.ts +13 -0
  619. package/src/web/formats/EnrichedCodeBlock.ts +53 -0
  620. package/src/web/formats/EnrichedHeading.ts +36 -0
  621. package/src/web/formats/EnrichedImage.ts +59 -0
  622. package/src/web/formats/EnrichedImageNodeView.tsx +89 -0
  623. package/src/web/formats/EnrichedItalic.ts +16 -0
  624. package/src/web/formats/EnrichedLink.ts +149 -0
  625. package/src/web/formats/EnrichedListItem.ts +17 -0
  626. package/src/web/formats/EnrichedMention.ts +55 -0
  627. package/src/web/formats/EnrichedOrderedList.ts +40 -0
  628. package/src/web/formats/EnrichedStrike.ts +16 -0
  629. package/src/web/formats/EnrichedUnderline.ts +16 -0
  630. package/src/web/formats/EnrichedUnorderedList.ts +50 -0
  631. package/src/web/formats/applyWrappingListToSelection.ts +76 -0
  632. package/src/web/formats/formatRules.ts +84 -0
  633. package/src/web/formats/listKeyboard.ts +58 -0
  634. package/src/web/formats/wrappedBlockKeyboard.ts +76 -0
  635. package/src/web/pasteImages.ts +96 -0
  636. package/src/web/pastedImageDimensions.ts +40 -0
  637. package/src/web/pmPlugins/MentionPlugin/index.ts +45 -0
  638. package/src/web/pmPlugins/MentionPlugin/isCaretInBlockedContext.ts +17 -0
  639. package/src/web/pmPlugins/MentionPlugin/makeMentionPluginState.ts +75 -0
  640. package/src/web/pmPlugins/MentionPlugin/mentionPluginKey.ts +4 -0
  641. package/src/web/pmPlugins/MentionPlugin/removeMentionMarksIfSpansResized.ts +68 -0
  642. package/src/web/pmPlugins/MentionPlugin/setMention.ts +83 -0
  643. package/src/web/pmPlugins/MentionPlugin/startMention.ts +14 -0
  644. package/src/web/pmPlugins/MentionPlugin/stripPartialMentionMarks.ts +20 -0
  645. package/src/web/pmPlugins/MentionPlugin/subscribeMentionEvents.ts +105 -0
  646. package/src/web/pmPlugins/MentionPlugin/types.ts +22 -0
  647. package/src/web/pmPlugins/MergeAdjacentSameKindBlocksPlugin.ts +57 -0
  648. package/src/web/pmPlugins/ShortcutPlugin.ts +98 -0
  649. package/src/web/pmPlugins/StrictMarksPlugin.ts +131 -0
  650. package/src/web/pmPlugins/StripBoldInStyledHeadingsPlugin.ts +79 -0
  651. package/src/web/pmPlugins/StripMarksInCodeBlockPlugin.ts +34 -0
  652. package/src/web/pmPlugins/StripMarksOnImagePlugin.ts +33 -0
  653. package/src/web/positionMapping.ts +81 -0
  654. package/src/web/returnKeyTypeToEnterKeyHint.ts +29 -0
  655. package/src/web/styleConversion/buildMentionRulesCSS.ts +42 -0
  656. package/src/web/styleConversion/enrichedInputStyleToCSSProperties.ts +224 -0
  657. package/src/web/styleConversion/enrichedInputThemingToCSSProperties.ts +34 -0
  658. package/src/web/styleConversion/htmlStyleToCSSVariables.ts +226 -0
  659. package/src/web/styleConversion/mentionIndicatorCssKey.ts +14 -0
  660. package/src/web/styleConversion/toColor.ts +17 -0
  661. package/src/web/tiptapHtmlNormalizer.ts +33 -0
  662. package/src/web/useOnChangeHtml.ts +14 -0
  663. package/src/web/useOnChangeState.ts +125 -0
  664. package/src/web/useOnChangeText.ts +15 -0
  665. package/src/web/useOnEditorChange.ts +33 -0
  666. package/src/web/useOnLinkDetected.ts +82 -0
@@ -0,0 +1,2170 @@
1
+ #import "EnrichedTextInputView.h"
2
+ #import "AlignmentUtils.h"
3
+ #import "AttachmentLayoutUtils.h"
4
+ #import "CoreText/CoreText.h"
5
+ #import "DotReplacementUtils.h"
6
+ #import "HtmlParser.h"
7
+ #import "ImageAttachment.h"
8
+ #import "KeyboardUtils.h"
9
+ #import "LayoutManagerExtension.h"
10
+ #import "ParagraphAttributesUtils.h"
11
+ #import "RCTFabricComponentsPlugins.h"
12
+ #import "ShortcutsUtils.h"
13
+ #import "StringExtension.h"
14
+ #import "StyleHeaders.h"
15
+ #import "StyleUtils.h"
16
+ #import "TextBlockTapGestureRecognizer.h"
17
+ #import "TextInsertionUtils.h"
18
+ #import "UIView+React.h"
19
+ #import "WordsUtils.h"
20
+ #import "ZeroWidthSpaceUtils.h"
21
+ #import <React/RCTConversions.h>
22
+ #import <ReactNativeEnrichedHtml/EnrichedTextInputViewComponentDescriptor.h>
23
+ #import <ReactNativeEnrichedHtml/EventEmitters.h>
24
+ #import <ReactNativeEnrichedHtml/Props.h>
25
+ #import <ReactNativeEnrichedHtml/RCTComponentViewHelpers.h>
26
+ #import <folly/dynamic.h>
27
+ #import <react/utils/ManagedObjectWrapper.h>
28
+
29
+ #define GET_STYLE_STATE(TYPE_ENUM) \
30
+ { \
31
+ .isActive = [self isStyleActive:TYPE_ENUM], \
32
+ .isBlocking = [self isStyle:TYPE_ENUM activeInMap:blockingStyles], \
33
+ .isConflicting = [self isStyle:TYPE_ENUM activeInMap:conflictingStyles] \
34
+ }
35
+
36
+ using namespace facebook::react;
37
+
38
+ @interface EnrichedTextInputView () <
39
+ RCTEnrichedTextInputViewViewProtocol, UITextViewDelegate,
40
+ UIGestureRecognizerDelegate, NSTextStorageDelegate, NSObject>
41
+
42
+ @end
43
+
44
+ @implementation EnrichedTextInputView {
45
+ EnrichedTextInputViewShadowNode::ConcreteState::Shared _state;
46
+ int _componentViewHeightUpdateCounter;
47
+ NSMutableSet<NSNumber *> *_activeStyles;
48
+ NSMutableSet<NSNumber *> *_blockedStyles;
49
+ LinkData *_recentlyActiveLinkData;
50
+ NSRange _recentlyActiveLinkRange;
51
+ NSString *_recentInputString;
52
+ MentionParams *_recentlyActiveMentionParams;
53
+ NSRange _recentlyActiveMentionRange;
54
+ NSString *_recentlyEmittedHtml;
55
+ BOOL _emitHtml;
56
+ UILabel *_placeholderLabel;
57
+ UIColor *_placeholderColor;
58
+ BOOL _emitFocusBlur;
59
+ BOOL _emitTextChange;
60
+ NSMutableDictionary<NSValue *, UIImageView *> *_attachmentViews;
61
+ NSArray<NSDictionary *> *_contextMenuItems;
62
+ NSString *_submitBehavior;
63
+ NSDictionary<NSAttributedStringKey, id> *_capturedAttributesBeforeChange;
64
+ NSString *_recentlyEmittedAlignment;
65
+ }
66
+
67
+ @synthesize blockEmitting = blockEmitting;
68
+
69
+ // MARK: - Component utils
70
+
71
+ + (ComponentDescriptorProvider)componentDescriptorProvider {
72
+ return concreteComponentDescriptorProvider<
73
+ EnrichedTextInputViewComponentDescriptor>();
74
+ }
75
+
76
+ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
77
+ return EnrichedTextInputView.class;
78
+ }
79
+
80
+ + (BOOL)shouldBeRecycled {
81
+ return NO;
82
+ }
83
+
84
+ // MARK: - EnrichedViewHost protocol
85
+
86
+ - (UITextView *)textView {
87
+ return textView;
88
+ }
89
+
90
+ - (EnrichedConfig *)config {
91
+ return config;
92
+ }
93
+
94
+ - (NSDictionary<NSNumber *, id> *)stylesDict {
95
+ return stylesDict;
96
+ }
97
+
98
+ - (InputAttributesManager *)attributesManager {
99
+ return attributesManager;
100
+ }
101
+
102
+ - (NSMutableDictionary<NSNumber *, NSArray<NSNumber *> *> *)conflictingStyles {
103
+ return conflictingStyles;
104
+ }
105
+
106
+ - (NSMutableDictionary<NSNumber *, NSArray<NSNumber *> *> *)blockingStyles {
107
+ return blockingStyles;
108
+ }
109
+
110
+ - (NSMutableDictionary<NSAttributedStringKey, id> *)defaultTypingAttributes {
111
+ return defaultTypingAttributes;
112
+ }
113
+
114
+ // MARK: - Init
115
+
116
+ - (instancetype)initWithFrame:(CGRect)frame {
117
+ if (self = [super initWithFrame:frame]) {
118
+ static const auto defaultProps =
119
+ std::make_shared<const EnrichedTextInputViewProps>();
120
+ _props = defaultProps;
121
+ [self setDefaults];
122
+ [self setupTextView];
123
+ [self setupPlaceholderLabel];
124
+ self.contentView = textView;
125
+ }
126
+ return self;
127
+ }
128
+
129
+ - (void)setDefaults {
130
+ _componentViewHeightUpdateCounter = 0;
131
+ _activeStyles = [[NSMutableSet alloc] init];
132
+ _blockedStyles = [[NSMutableSet alloc] init];
133
+ _recentlyActiveLinkRange = NSMakeRange(0, 0);
134
+ _recentlyActiveMentionRange = NSMakeRange(0, 0);
135
+ _recentlyEmittedAlignment = @"left";
136
+ _recentInputString = @"";
137
+ _recentlyEmittedHtml = @"<html>\n<p></p>\n</html>";
138
+ _emitHtml = NO;
139
+ blockEmitting = NO;
140
+ _emitFocusBlur = YES;
141
+ _emitTextChange = NO;
142
+ dotReplacementRange = nullptr;
143
+
144
+ defaultTypingAttributes =
145
+ [[NSMutableDictionary<NSAttributedStringKey, id> alloc] init];
146
+
147
+ stylesDict = [StyleUtils stylesDictForHost:self isInput:YES];
148
+ conflictingStyles = [[StyleUtils conflictMap] mutableCopy];
149
+ blockingStyles = [[StyleUtils blockingMap] mutableCopy];
150
+
151
+ parser = [[InputHtmlParser alloc] initWithInput:self];
152
+ _attachmentViews = [[NSMutableDictionary alloc] init];
153
+ attributesManager = [[InputAttributesManager alloc] initWithInput:self];
154
+ }
155
+
156
+ - (void)setupTextView {
157
+ textView = [[EnrichedInputTextView alloc] init];
158
+ textView.backgroundColor = UIColor.clearColor;
159
+ textView.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0);
160
+ textView.textContainer.lineFragmentPadding = 0;
161
+ textView.delegate = self;
162
+ textView.input = self;
163
+ textView.layoutManager.input = self;
164
+ textView.textStorage.delegate = self;
165
+
166
+ textView.adjustsFontForContentSizeCategory = YES;
167
+ [textView addGestureRecognizer:[[TextBlockTapGestureRecognizer alloc]
168
+ initWithInput:self
169
+ action:@selector(onTextBlockTap:)]];
170
+ }
171
+
172
+ - (void)setupPlaceholderLabel {
173
+ _placeholderLabel = [[UILabel alloc] initWithFrame:CGRectZero];
174
+ _placeholderLabel.translatesAutoresizingMaskIntoConstraints = NO;
175
+ [textView addSubview:_placeholderLabel];
176
+ [NSLayoutConstraint activateConstraints:@[
177
+ [_placeholderLabel.leadingAnchor
178
+ constraintEqualToAnchor:textView.leadingAnchor],
179
+ [_placeholderLabel.widthAnchor
180
+ constraintEqualToAnchor:textView.widthAnchor],
181
+ [_placeholderLabel.topAnchor constraintEqualToAnchor:textView.topAnchor],
182
+ [_placeholderLabel.bottomAnchor
183
+ constraintEqualToAnchor:textView.bottomAnchor]
184
+ ]];
185
+ _placeholderLabel.lineBreakMode = NSLineBreakByTruncatingTail;
186
+ _placeholderLabel.text = @"";
187
+ _placeholderLabel.hidden = YES;
188
+ _placeholderLabel.adjustsFontForContentSizeCategory = YES;
189
+ }
190
+
191
+ // MARK: - Props
192
+
193
+ - (void)updateProps:(Props::Shared const &)props
194
+ oldProps:(Props::Shared const &)oldProps {
195
+ const auto &oldViewProps =
196
+ *std::static_pointer_cast<EnrichedTextInputViewProps const>(_props);
197
+ const auto &newViewProps =
198
+ *std::static_pointer_cast<EnrichedTextInputViewProps const>(props);
199
+ BOOL isFirstMount = NO;
200
+ BOOL stylePropChanged = NO;
201
+
202
+ // initial config
203
+ if (config == nullptr) {
204
+ isFirstMount = YES;
205
+ config = [[EnrichedConfig alloc] init];
206
+ }
207
+
208
+ // any style prop changes:
209
+ // firstly we create the new config for the changes
210
+
211
+ EnrichedConfig *newConfig = [config copy];
212
+
213
+ if (newViewProps.color != oldViewProps.color) {
214
+ if (isColorMeaningful(newViewProps.color)) {
215
+ UIColor *uiColor = RCTUIColorFromSharedColor(newViewProps.color);
216
+ [newConfig setPrimaryColor:uiColor];
217
+ } else {
218
+ [newConfig setPrimaryColor:nullptr];
219
+ }
220
+ stylePropChanged = YES;
221
+ }
222
+
223
+ if (newViewProps.fontSize != oldViewProps.fontSize) {
224
+ if (newViewProps.fontSize) {
225
+ NSNumber *fontSize = @(newViewProps.fontSize);
226
+ [newConfig setPrimaryFontSize:fontSize];
227
+ } else {
228
+ [newConfig setPrimaryFontSize:nullptr];
229
+ }
230
+ stylePropChanged = YES;
231
+ }
232
+
233
+ if (newViewProps.lineHeight != oldViewProps.lineHeight) {
234
+ [newConfig setPrimaryLineHeight:newViewProps.lineHeight];
235
+ stylePropChanged = YES;
236
+ }
237
+
238
+ if (newViewProps.fontWeight != oldViewProps.fontWeight) {
239
+ if (!newViewProps.fontWeight.empty()) {
240
+ [newConfig
241
+ setPrimaryFontWeight:[NSString
242
+ fromCppString:newViewProps.fontWeight]];
243
+ } else {
244
+ [newConfig setPrimaryFontWeight:nullptr];
245
+ }
246
+ stylePropChanged = YES;
247
+ }
248
+
249
+ if (newViewProps.fontFamily != oldViewProps.fontFamily) {
250
+ if (!newViewProps.fontFamily.empty()) {
251
+ [newConfig
252
+ setPrimaryFontFamily:[NSString
253
+ fromCppString:newViewProps.fontFamily]];
254
+ } else {
255
+ [newConfig setPrimaryFontFamily:nullptr];
256
+ }
257
+ stylePropChanged = YES;
258
+ }
259
+
260
+ // rich text style
261
+
262
+ if (newViewProps.htmlStyle.h1.fontSize !=
263
+ oldViewProps.htmlStyle.h1.fontSize) {
264
+ [newConfig setH1FontSize:newViewProps.htmlStyle.h1.fontSize];
265
+ stylePropChanged = YES;
266
+ }
267
+
268
+ if (newViewProps.htmlStyle.h1.bold != oldViewProps.htmlStyle.h1.bold) {
269
+ [newConfig setH1Bold:newViewProps.htmlStyle.h1.bold];
270
+
271
+ // Update style blocks and conflicts for bold
272
+ if (newViewProps.htmlStyle.h1.bold) {
273
+ [StyleUtils addStyleBlock:H1 to:Bold forHost:self];
274
+ [StyleUtils addStyleConflict:Bold to:H1 forHost:self];
275
+ } else {
276
+ [StyleUtils removeStyleBlock:H1 from:Bold forHost:self];
277
+ [StyleUtils removeStyleConflict:Bold from:H1 forHost:self];
278
+ }
279
+
280
+ stylePropChanged = YES;
281
+ }
282
+
283
+ if (newViewProps.htmlStyle.h2.fontSize !=
284
+ oldViewProps.htmlStyle.h2.fontSize) {
285
+ [newConfig setH2FontSize:newViewProps.htmlStyle.h2.fontSize];
286
+ stylePropChanged = YES;
287
+ }
288
+
289
+ if (newViewProps.htmlStyle.h2.bold != oldViewProps.htmlStyle.h2.bold) {
290
+ [newConfig setH2Bold:newViewProps.htmlStyle.h2.bold];
291
+
292
+ // Update style blocks and conflicts for bold
293
+ if (newViewProps.htmlStyle.h2.bold) {
294
+ [StyleUtils addStyleBlock:H2 to:Bold forHost:self];
295
+ [StyleUtils addStyleConflict:Bold to:H2 forHost:self];
296
+ } else {
297
+ [StyleUtils removeStyleBlock:H2 from:Bold forHost:self];
298
+ [StyleUtils removeStyleConflict:Bold from:H2 forHost:self];
299
+ }
300
+
301
+ stylePropChanged = YES;
302
+ }
303
+
304
+ if (newViewProps.htmlStyle.h3.fontSize !=
305
+ oldViewProps.htmlStyle.h3.fontSize) {
306
+ [newConfig setH3FontSize:newViewProps.htmlStyle.h3.fontSize];
307
+ stylePropChanged = YES;
308
+ }
309
+
310
+ if (newViewProps.htmlStyle.h3.bold != oldViewProps.htmlStyle.h3.bold) {
311
+ [newConfig setH3Bold:newViewProps.htmlStyle.h3.bold];
312
+
313
+ // Update style blocks and conflicts for bold
314
+ if (newViewProps.htmlStyle.h3.bold) {
315
+ [StyleUtils addStyleBlock:H3 to:Bold forHost:self];
316
+ [StyleUtils addStyleConflict:Bold to:H3 forHost:self];
317
+ } else {
318
+ [StyleUtils removeStyleBlock:H3 from:Bold forHost:self];
319
+ [StyleUtils removeStyleConflict:Bold from:H3 forHost:self];
320
+ }
321
+
322
+ stylePropChanged = YES;
323
+ }
324
+
325
+ if (newViewProps.htmlStyle.h4.fontSize !=
326
+ oldViewProps.htmlStyle.h4.fontSize) {
327
+ [newConfig setH4FontSize:newViewProps.htmlStyle.h4.fontSize];
328
+ stylePropChanged = YES;
329
+ }
330
+
331
+ if (newViewProps.htmlStyle.h4.bold != oldViewProps.htmlStyle.h4.bold) {
332
+ [newConfig setH4Bold:newViewProps.htmlStyle.h4.bold];
333
+
334
+ // Update style blocks and conflicts for bold
335
+ if (newViewProps.htmlStyle.h4.bold) {
336
+ [StyleUtils addStyleBlock:H4 to:Bold forHost:self];
337
+ [StyleUtils addStyleConflict:Bold to:H4 forHost:self];
338
+ } else {
339
+ [StyleUtils removeStyleBlock:H4 from:Bold forHost:self];
340
+ [StyleUtils removeStyleConflict:Bold from:H4 forHost:self];
341
+ }
342
+
343
+ stylePropChanged = YES;
344
+ }
345
+
346
+ if (newViewProps.htmlStyle.h5.fontSize !=
347
+ oldViewProps.htmlStyle.h5.fontSize) {
348
+ [newConfig setH5FontSize:newViewProps.htmlStyle.h5.fontSize];
349
+ stylePropChanged = YES;
350
+ }
351
+
352
+ if (newViewProps.htmlStyle.h5.bold != oldViewProps.htmlStyle.h5.bold) {
353
+ [newConfig setH5Bold:newViewProps.htmlStyle.h5.bold];
354
+
355
+ // Update style blocks and conflicts for bold
356
+ if (newViewProps.htmlStyle.h5.bold) {
357
+ [StyleUtils addStyleBlock:H5 to:Bold forHost:self];
358
+ [StyleUtils addStyleConflict:Bold to:H5 forHost:self];
359
+ } else {
360
+ [StyleUtils removeStyleBlock:H5 from:Bold forHost:self];
361
+ [StyleUtils removeStyleConflict:Bold from:H5 forHost:self];
362
+ }
363
+
364
+ stylePropChanged = YES;
365
+ }
366
+
367
+ if (newViewProps.htmlStyle.h6.fontSize !=
368
+ oldViewProps.htmlStyle.h6.fontSize) {
369
+ [newConfig setH6FontSize:newViewProps.htmlStyle.h6.fontSize];
370
+ stylePropChanged = YES;
371
+ }
372
+
373
+ if (newViewProps.htmlStyle.h6.bold != oldViewProps.htmlStyle.h6.bold) {
374
+ [newConfig setH6Bold:newViewProps.htmlStyle.h6.bold];
375
+
376
+ // Update style blocks and conflicts for bold
377
+ if (newViewProps.htmlStyle.h6.bold) {
378
+ [StyleUtils addStyleBlock:H6 to:Bold forHost:self];
379
+ [StyleUtils addStyleConflict:Bold to:H6 forHost:self];
380
+ } else {
381
+ [StyleUtils removeStyleBlock:H6 from:Bold forHost:self];
382
+ [StyleUtils removeStyleConflict:Bold from:H6 forHost:self];
383
+ }
384
+
385
+ stylePropChanged = YES;
386
+ }
387
+
388
+ if (newViewProps.htmlStyle.blockquote.borderColor !=
389
+ oldViewProps.htmlStyle.blockquote.borderColor) {
390
+ if (isColorMeaningful(newViewProps.htmlStyle.blockquote.borderColor)) {
391
+ [newConfig setBlockquoteBorderColor:RCTUIColorFromSharedColor(
392
+ newViewProps.htmlStyle.blockquote
393
+ .borderColor)];
394
+ stylePropChanged = YES;
395
+ }
396
+ }
397
+
398
+ if (newViewProps.htmlStyle.blockquote.borderWidth !=
399
+ oldViewProps.htmlStyle.blockquote.borderWidth) {
400
+ [newConfig
401
+ setBlockquoteBorderWidth:newViewProps.htmlStyle.blockquote.borderWidth];
402
+ stylePropChanged = YES;
403
+ }
404
+
405
+ if (newViewProps.htmlStyle.blockquote.gapWidth !=
406
+ oldViewProps.htmlStyle.blockquote.gapWidth) {
407
+ [newConfig
408
+ setBlockquoteGapWidth:newViewProps.htmlStyle.blockquote.gapWidth];
409
+ stylePropChanged = YES;
410
+ }
411
+
412
+ // since this prop defaults to undefined on JS side, we need to force set the
413
+ // value on first mount
414
+ if (newViewProps.htmlStyle.blockquote.color !=
415
+ oldViewProps.htmlStyle.blockquote.color ||
416
+ isFirstMount) {
417
+ if (isColorMeaningful(newViewProps.htmlStyle.blockquote.color)) {
418
+ [newConfig
419
+ setBlockquoteColor:RCTUIColorFromSharedColor(
420
+ newViewProps.htmlStyle.blockquote.color)];
421
+ } else {
422
+ [newConfig setBlockquoteColor:[newConfig primaryColor]];
423
+ }
424
+ stylePropChanged = YES;
425
+ }
426
+
427
+ if (newViewProps.htmlStyle.code.color != oldViewProps.htmlStyle.code.color) {
428
+ if (isColorMeaningful(newViewProps.htmlStyle.code.color)) {
429
+ [newConfig setInlineCodeFgColor:RCTUIColorFromSharedColor(
430
+ newViewProps.htmlStyle.code.color)];
431
+ stylePropChanged = YES;
432
+ }
433
+ }
434
+
435
+ if (newViewProps.htmlStyle.code.backgroundColor !=
436
+ oldViewProps.htmlStyle.code.backgroundColor) {
437
+ if (isColorMeaningful(newViewProps.htmlStyle.code.backgroundColor)) {
438
+ [newConfig setInlineCodeBgColor:RCTUIColorFromSharedColor(
439
+ newViewProps.htmlStyle.code
440
+ .backgroundColor)];
441
+ stylePropChanged = YES;
442
+ }
443
+ }
444
+
445
+ if (newViewProps.htmlStyle.ol.gapWidth !=
446
+ oldViewProps.htmlStyle.ol.gapWidth) {
447
+ [newConfig setOrderedListGapWidth:newViewProps.htmlStyle.ol.gapWidth];
448
+ stylePropChanged = YES;
449
+ }
450
+
451
+ if (newViewProps.htmlStyle.ol.marginLeft !=
452
+ oldViewProps.htmlStyle.ol.marginLeft) {
453
+ [newConfig setOrderedListMarginLeft:newViewProps.htmlStyle.ol.marginLeft];
454
+ stylePropChanged = YES;
455
+ }
456
+
457
+ // since this prop defaults to undefined on JS side, we need to force set the
458
+ // value on first mount
459
+ if (newViewProps.htmlStyle.ol.markerFontWeight !=
460
+ oldViewProps.htmlStyle.ol.markerFontWeight ||
461
+ isFirstMount) {
462
+ if (!newViewProps.htmlStyle.ol.markerFontWeight.empty()) {
463
+ [newConfig
464
+ setOrderedListMarkerFontWeight:
465
+ [NSString
466
+ fromCppString:newViewProps.htmlStyle.ol.markerFontWeight]];
467
+ } else {
468
+ [newConfig setOrderedListMarkerFontWeight:[newConfig primaryFontWeight]];
469
+ }
470
+ stylePropChanged = YES;
471
+ }
472
+
473
+ // since this prop defaults to undefined on JS side, we need to force set the
474
+ // value on first mount
475
+ if (newViewProps.htmlStyle.ol.markerColor !=
476
+ oldViewProps.htmlStyle.ol.markerColor ||
477
+ isFirstMount) {
478
+ if (isColorMeaningful(newViewProps.htmlStyle.ol.markerColor)) {
479
+ [newConfig
480
+ setOrderedListMarkerColor:RCTUIColorFromSharedColor(
481
+ newViewProps.htmlStyle.ol.markerColor)];
482
+ } else {
483
+ [newConfig setOrderedListMarkerColor:[newConfig primaryColor]];
484
+ }
485
+ stylePropChanged = YES;
486
+ }
487
+
488
+ if (newViewProps.htmlStyle.ul.bulletColor !=
489
+ oldViewProps.htmlStyle.ul.bulletColor) {
490
+ if (isColorMeaningful(newViewProps.htmlStyle.ul.bulletColor)) {
491
+ [newConfig setUnorderedListBulletColor:RCTUIColorFromSharedColor(
492
+ newViewProps.htmlStyle.ul
493
+ .bulletColor)];
494
+ stylePropChanged = YES;
495
+ }
496
+ }
497
+
498
+ if (newViewProps.htmlStyle.ul.bulletSize !=
499
+ oldViewProps.htmlStyle.ul.bulletSize) {
500
+ [newConfig setUnorderedListBulletSize:newViewProps.htmlStyle.ul.bulletSize];
501
+ stylePropChanged = YES;
502
+ }
503
+
504
+ if (newViewProps.htmlStyle.ul.gapWidth !=
505
+ oldViewProps.htmlStyle.ul.gapWidth) {
506
+ [newConfig setUnorderedListGapWidth:newViewProps.htmlStyle.ul.gapWidth];
507
+ stylePropChanged = YES;
508
+ }
509
+
510
+ if (newViewProps.htmlStyle.ul.marginLeft !=
511
+ oldViewProps.htmlStyle.ul.marginLeft) {
512
+ [newConfig setUnorderedListMarginLeft:newViewProps.htmlStyle.ul.marginLeft];
513
+ stylePropChanged = YES;
514
+ }
515
+
516
+ if (newViewProps.htmlStyle.a.color != oldViewProps.htmlStyle.a.color) {
517
+ if (isColorMeaningful(newViewProps.htmlStyle.a.color)) {
518
+ [newConfig setLinkColor:RCTUIColorFromSharedColor(
519
+ newViewProps.htmlStyle.a.color)];
520
+ stylePropChanged = YES;
521
+ }
522
+ }
523
+
524
+ if (newViewProps.htmlStyle.codeblock.color !=
525
+ oldViewProps.htmlStyle.codeblock.color) {
526
+ if (isColorMeaningful(newViewProps.htmlStyle.codeblock.color)) {
527
+ [newConfig
528
+ setCodeBlockFgColor:RCTUIColorFromSharedColor(
529
+ newViewProps.htmlStyle.codeblock.color)];
530
+ stylePropChanged = YES;
531
+ }
532
+ }
533
+
534
+ if (newViewProps.htmlStyle.codeblock.backgroundColor !=
535
+ oldViewProps.htmlStyle.codeblock.backgroundColor) {
536
+ if (isColorMeaningful(newViewProps.htmlStyle.codeblock.backgroundColor)) {
537
+ [newConfig setCodeBlockBgColor:RCTUIColorFromSharedColor(
538
+ newViewProps.htmlStyle.codeblock
539
+ .backgroundColor)];
540
+ stylePropChanged = YES;
541
+ }
542
+ }
543
+
544
+ if (newViewProps.htmlStyle.codeblock.borderRadius !=
545
+ oldViewProps.htmlStyle.codeblock.borderRadius) {
546
+ [newConfig
547
+ setCodeBlockBorderRadius:newViewProps.htmlStyle.codeblock.borderRadius];
548
+ stylePropChanged = YES;
549
+ }
550
+
551
+ if (newViewProps.htmlStyle.ulCheckbox.boxSize !=
552
+ oldViewProps.htmlStyle.ulCheckbox.boxSize) {
553
+ [newConfig
554
+ setCheckboxListBoxSize:newViewProps.htmlStyle.ulCheckbox.boxSize];
555
+ stylePropChanged = YES;
556
+ }
557
+
558
+ if (newViewProps.htmlStyle.ulCheckbox.gapWidth !=
559
+ oldViewProps.htmlStyle.ulCheckbox.gapWidth) {
560
+ [newConfig
561
+ setCheckboxListGapWidth:newViewProps.htmlStyle.ulCheckbox.gapWidth];
562
+ stylePropChanged = YES;
563
+ }
564
+
565
+ if (newViewProps.htmlStyle.ulCheckbox.marginLeft !=
566
+ oldViewProps.htmlStyle.ulCheckbox.marginLeft) {
567
+ [newConfig
568
+ setCheckboxListMarginLeft:newViewProps.htmlStyle.ulCheckbox.marginLeft];
569
+ stylePropChanged = YES;
570
+ }
571
+
572
+ if (newViewProps.htmlStyle.ulCheckbox.boxColor !=
573
+ oldViewProps.htmlStyle.ulCheckbox.boxColor) {
574
+ if (isColorMeaningful(newViewProps.htmlStyle.ulCheckbox.boxColor)) {
575
+ [newConfig setCheckboxListBoxColor:RCTUIColorFromSharedColor(
576
+ newViewProps.htmlStyle.ulCheckbox
577
+ .boxColor)];
578
+ stylePropChanged = YES;
579
+ }
580
+ }
581
+
582
+ if (newViewProps.htmlStyle.a.textDecorationLine !=
583
+ oldViewProps.htmlStyle.a.textDecorationLine) {
584
+ NSString *objcString =
585
+ [NSString fromCppString:newViewProps.htmlStyle.a.textDecorationLine];
586
+ if ([objcString isEqualToString:DecorationUnderline]) {
587
+ [newConfig setLinkDecorationLine:DecorationUnderline];
588
+ } else {
589
+ // both DecorationNone and a different, wrong value gets a DecorationNone
590
+ // here
591
+ [newConfig setLinkDecorationLine:DecorationNone];
592
+ }
593
+ stylePropChanged = YES;
594
+ }
595
+
596
+ if (newViewProps.scrollEnabled != oldViewProps.scrollEnabled ||
597
+ textView.scrollEnabled != newViewProps.scrollEnabled) {
598
+ [textView setScrollEnabled:newViewProps.scrollEnabled];
599
+ }
600
+
601
+ if (newViewProps.allowFontScaling != oldViewProps.allowFontScaling) {
602
+ [newConfig setAllowFontScaling:newViewProps.allowFontScaling];
603
+ stylePropChanged = YES;
604
+ }
605
+
606
+ folly::dynamic oldMentionStyle = oldViewProps.htmlStyle.mention;
607
+ folly::dynamic newMentionStyle = newViewProps.htmlStyle.mention;
608
+ if (oldMentionStyle != newMentionStyle) {
609
+ bool newSingleProps = NO;
610
+
611
+ for (const auto &obj : newMentionStyle.items()) {
612
+ if (obj.second.isInt() || obj.second.isString()) {
613
+ // we are in just a single MentionStyleProps object
614
+ newSingleProps = YES;
615
+ break;
616
+ } else if (obj.second.isObject()) {
617
+ // we are in map of indicators to MentionStyleProps
618
+ newSingleProps = NO;
619
+ break;
620
+ }
621
+ }
622
+
623
+ if (newSingleProps) {
624
+ [newConfig setMentionStyleProps:
625
+ [MentionStyleProps
626
+ getSinglePropsFromFollyDynamic:newMentionStyle]];
627
+ } else {
628
+ [newConfig setMentionStyleProps:
629
+ [MentionStyleProps
630
+ getComplexPropsFromFollyDynamic:newMentionStyle]];
631
+ }
632
+
633
+ stylePropChanged = YES;
634
+ }
635
+
636
+ if (stylePropChanged) {
637
+ // all the text needs to be rebuilt
638
+ // we get the current html using old config, then switch to new config and
639
+ // replace text using the html this way, the newest config attributes are
640
+ // being used!
641
+
642
+ // the html needs to be generated using the old config
643
+ NSString *currentHtml = [HtmlParser
644
+ parseToHtmlFromRange:NSMakeRange(0, textView.textStorage.string.length)
645
+ host:self];
646
+ // we want to preserve the selection between props changes
647
+ NSRange prevSelectedRange = textView.selectedRange;
648
+
649
+ // now set the new config
650
+ config = newConfig;
651
+
652
+ // fill the typing attributes with style props
653
+ defaultTypingAttributes[NSForegroundColorAttributeName] =
654
+ [config primaryColor];
655
+ defaultTypingAttributes[NSFontAttributeName] = [config primaryFont];
656
+ defaultTypingAttributes[NSUnderlineColorAttributeName] =
657
+ [config primaryColor];
658
+ defaultTypingAttributes[NSStrikethroughColorAttributeName] =
659
+ [config primaryColor];
660
+ NSMutableParagraphStyle *defaultPStyle =
661
+ [[NSMutableParagraphStyle alloc] init];
662
+ defaultPStyle.minimumLineHeight = [config scaledPrimaryLineHeight];
663
+ defaultTypingAttributes[NSParagraphStyleAttributeName] = defaultPStyle;
664
+
665
+ // no emitting during styles reload
666
+ blockEmitting = YES;
667
+
668
+ // make sure everything is sound in the html
669
+ NSString *initiallyProcessedHtml =
670
+ [parser initiallyProcessHtml:currentHtml];
671
+ if (initiallyProcessedHtml != nullptr) {
672
+ [parser replaceWholeFromHtml:initiallyProcessedHtml];
673
+ }
674
+
675
+ blockEmitting = NO;
676
+
677
+ textView.typingAttributes = defaultTypingAttributes;
678
+ textView.selectedRange = prevSelectedRange;
679
+
680
+ // make sure the newest lineHeight is applied
681
+ [self refreshLineHeight];
682
+ // update the placeholder as well
683
+ [self refreshPlaceholderLabelStyles];
684
+ }
685
+
686
+ // editable
687
+ if (newViewProps.editable != textView.editable) {
688
+ textView.editable = newViewProps.editable;
689
+ }
690
+
691
+ // useHtmlNormalizer
692
+ if (newViewProps.useHtmlNormalizer != oldViewProps.useHtmlNormalizer) {
693
+ useHtmlNormalizer = newViewProps.useHtmlNormalizer;
694
+ }
695
+
696
+ // textShortcuts
697
+ bool textShortcutsChanged =
698
+ newViewProps.textShortcuts.size() != oldViewProps.textShortcuts.size();
699
+ if (!textShortcutsChanged) {
700
+ for (size_t i = 0; i < newViewProps.textShortcuts.size(); i++) {
701
+ const auto &newItem = newViewProps.textShortcuts[i];
702
+ const auto &oldItem = oldViewProps.textShortcuts[i];
703
+ if (newItem.trigger != oldItem.trigger ||
704
+ newItem.style != oldItem.style) {
705
+ textShortcutsChanged = true;
706
+ break;
707
+ }
708
+ }
709
+ }
710
+
711
+ if (textShortcutsChanged) {
712
+ NSMutableArray *shortcuts = [NSMutableArray new];
713
+ for (const auto &item : newViewProps.textShortcuts) {
714
+ [shortcuts addObject:@{
715
+ @"trigger" : [NSString fromCppString:item.trigger],
716
+ @"style" : [NSString fromCppString:item.style],
717
+ }];
718
+ }
719
+ textShortcuts = shortcuts;
720
+ }
721
+
722
+ // default value - must be set before placeholder to make sure it correctly
723
+ // shows on first mount
724
+ if (newViewProps.defaultValue != oldViewProps.defaultValue) {
725
+ NSString *newDefaultValue =
726
+ [NSString fromCppString:newViewProps.defaultValue];
727
+
728
+ NSString *initiallyProcessedHtml =
729
+ [parser initiallyProcessHtml:newDefaultValue];
730
+ if (initiallyProcessedHtml == nullptr) {
731
+ // just plain text
732
+ textView.text = newDefaultValue;
733
+ } else {
734
+ // we've got some seemingly proper html
735
+ [parser replaceWholeFromHtml:initiallyProcessedHtml];
736
+ }
737
+ textView.selectedRange = NSRange(textView.textStorage.string.length, 0);
738
+ }
739
+
740
+ // placeholderTextColor
741
+ if (newViewProps.placeholderTextColor != oldViewProps.placeholderTextColor) {
742
+ // some real color
743
+ if (isColorMeaningful(newViewProps.placeholderTextColor)) {
744
+ _placeholderColor =
745
+ RCTUIColorFromSharedColor(newViewProps.placeholderTextColor);
746
+ } else {
747
+ _placeholderColor = nullptr;
748
+ }
749
+ [self refreshPlaceholderLabelStyles];
750
+ }
751
+
752
+ // placeholder
753
+ if (newViewProps.placeholder != oldViewProps.placeholder) {
754
+ _placeholderLabel.text = [NSString fromCppString:newViewProps.placeholder];
755
+ [self refreshPlaceholderLabelStyles];
756
+ // additionally show placeholder on first mount if it should be there
757
+ if (isFirstMount && textView.text.length == 0) {
758
+ [self setPlaceholderLabelShown:YES];
759
+ }
760
+ }
761
+
762
+ // mention indicators
763
+ auto mismatchPair = std::mismatch(newViewProps.mentionIndicators.begin(),
764
+ newViewProps.mentionIndicators.end(),
765
+ oldViewProps.mentionIndicators.begin(),
766
+ oldViewProps.mentionIndicators.end());
767
+ if (mismatchPair.first != newViewProps.mentionIndicators.end() ||
768
+ mismatchPair.second != oldViewProps.mentionIndicators.end()) {
769
+ NSMutableSet<NSNumber *> *newIndicators = [[NSMutableSet alloc] init];
770
+ for (const std::string &item : newViewProps.mentionIndicators) {
771
+ if (item.length() == 1) {
772
+ [newIndicators addObject:@(item[0])];
773
+ }
774
+ }
775
+ [config setMentionIndicators:newIndicators];
776
+ }
777
+
778
+ // linkRegex
779
+ LinkRegexConfig *oldRegexConfig =
780
+ [[LinkRegexConfig alloc] initWithLinkRegexProp:oldViewProps.linkRegex];
781
+ LinkRegexConfig *newRegexConfig =
782
+ [[LinkRegexConfig alloc] initWithLinkRegexProp:newViewProps.linkRegex];
783
+ if (![newRegexConfig isEqualToConfig:oldRegexConfig]) {
784
+ [config setLinkRegexConfig:newRegexConfig];
785
+ }
786
+
787
+ // selection color sets both selection and cursor on iOS (just as in RN)
788
+ if (newViewProps.selectionColor != oldViewProps.selectionColor) {
789
+ if (isColorMeaningful(newViewProps.selectionColor)) {
790
+ textView.tintColor =
791
+ RCTUIColorFromSharedColor(newViewProps.selectionColor);
792
+ } else {
793
+ textView.tintColor = nullptr;
794
+ }
795
+ }
796
+
797
+ if (newViewProps.returnKeyType != oldViewProps.returnKeyType) {
798
+ NSString *str = [NSString fromCppString:newViewProps.returnKeyType];
799
+
800
+ textView.returnKeyType =
801
+ [KeyboardUtils getUIReturnKeyTypeFromReturnKeyType:str];
802
+ }
803
+
804
+ if (newViewProps.submitBehavior != oldViewProps.submitBehavior) {
805
+ _submitBehavior = [NSString fromCppString:newViewProps.submitBehavior];
806
+ }
807
+
808
+ // autoCapitalize
809
+ if (newViewProps.autoCapitalize != oldViewProps.autoCapitalize) {
810
+ NSString *str = [NSString fromCppString:newViewProps.autoCapitalize];
811
+ if ([str isEqualToString:@"none"]) {
812
+ textView.autocapitalizationType = UITextAutocapitalizationTypeNone;
813
+ } else if ([str isEqualToString:@"sentences"]) {
814
+ textView.autocapitalizationType = UITextAutocapitalizationTypeSentences;
815
+ } else if ([str isEqualToString:@"words"]) {
816
+ textView.autocapitalizationType = UITextAutocapitalizationTypeWords;
817
+ } else if ([str isEqualToString:@"characters"]) {
818
+ textView.autocapitalizationType =
819
+ UITextAutocapitalizationTypeAllCharacters;
820
+ }
821
+
822
+ // textView needs to be refocused on autocapitalization type change and we
823
+ // don't want to emit these events
824
+ if ([textView isFirstResponder]) {
825
+ _emitFocusBlur = NO;
826
+ [textView reactBlur];
827
+ [textView reactFocus];
828
+ _emitFocusBlur = YES;
829
+ }
830
+ }
831
+
832
+ // isOnChangeHtmlSet
833
+ _emitHtml = newViewProps.isOnChangeHtmlSet;
834
+
835
+ // isOnChangeTextSet
836
+ _emitTextChange = newViewProps.isOnChangeTextSet;
837
+
838
+ // contextMenuItems
839
+ bool contextMenuChanged = newViewProps.contextMenuItems.size() !=
840
+ oldViewProps.contextMenuItems.size();
841
+ if (!contextMenuChanged) {
842
+ for (size_t i = 0; i < newViewProps.contextMenuItems.size(); i++) {
843
+ if (newViewProps.contextMenuItems[i].text !=
844
+ oldViewProps.contextMenuItems[i].text) {
845
+ contextMenuChanged = true;
846
+ break;
847
+ }
848
+ }
849
+ }
850
+ if (contextMenuChanged) {
851
+ NSMutableArray<NSString *> *items = [NSMutableArray new];
852
+ for (const auto &item : newViewProps.contextMenuItems) {
853
+ [items addObject:[NSString fromCppString:item.text]];
854
+ }
855
+ _contextMenuItems = [items copy];
856
+ }
857
+
858
+ [super updateProps:props oldProps:oldProps];
859
+ // run the changes callback
860
+ [self anyTextMayHaveBeenModified];
861
+
862
+ // autofocus - needs to be done at the very end
863
+ if (isFirstMount && newViewProps.autoFocus) {
864
+ [textView reactFocus];
865
+ }
866
+ }
867
+
868
+ - (void)setPlaceholderLabelShown:(BOOL)shown {
869
+ if (shown) {
870
+ [self refreshPlaceholderLabelStyles];
871
+ _placeholderLabel.hidden = NO;
872
+ } else {
873
+ _placeholderLabel.hidden = YES;
874
+ }
875
+ }
876
+
877
+ - (void)refreshPlaceholderLabelStyles {
878
+ NSMutableDictionary *newAttrs = [defaultTypingAttributes mutableCopy];
879
+ if (_placeholderColor != nullptr) {
880
+ newAttrs[NSForegroundColorAttributeName] = _placeholderColor;
881
+ }
882
+
883
+ // Get the current active alignment in input
884
+ NSParagraphStyle *currentTypingPara =
885
+ textView.typingAttributes[NSParagraphStyleAttributeName];
886
+ NSTextAlignment activeAlignment =
887
+ currentTypingPara ? currentTypingPara.alignment : NSTextAlignmentNatural;
888
+ NSMutableParagraphStyle *placeholderPStyle =
889
+ [newAttrs[NSParagraphStyleAttributeName] mutableCopy];
890
+ if (!placeholderPStyle) {
891
+ placeholderPStyle = [[NSMutableParagraphStyle alloc] init];
892
+ }
893
+ placeholderPStyle.alignment = activeAlignment;
894
+ newAttrs[NSParagraphStyleAttributeName] = placeholderPStyle;
895
+
896
+ NSAttributedString *newAttrStr =
897
+ [[NSAttributedString alloc] initWithString:_placeholderLabel.text
898
+ attributes:newAttrs];
899
+ _placeholderLabel.attributedText = newAttrStr;
900
+ }
901
+
902
+ - (void)refreshLineHeight {
903
+ [textView.textStorage
904
+ enumerateAttribute:NSParagraphStyleAttributeName
905
+ inRange:NSMakeRange(0, textView.textStorage.string.length)
906
+ options:0
907
+ usingBlock:^(id _Nullable value, NSRange range,
908
+ BOOL *_Nonnull stop) {
909
+ NSMutableParagraphStyle *pStyle =
910
+ [(NSParagraphStyle *)value mutableCopy];
911
+ if (pStyle == nil)
912
+ return;
913
+ pStyle.minimumLineHeight = [config scaledPrimaryLineHeight];
914
+ [textView.textStorage addAttribute:NSParagraphStyleAttributeName
915
+ value:pStyle
916
+ range:range];
917
+ }];
918
+ }
919
+
920
+ // MARK: - Measuring and states
921
+
922
+ - (CGSize)measureSize:(CGFloat)maxWidth {
923
+ // copy the the whole attributed string
924
+ NSMutableAttributedString *currentStr = [[NSMutableAttributedString alloc]
925
+ initWithAttributedString:textView.textStorage];
926
+
927
+ // edge case: empty input should still be of a height of a single line, so we
928
+ // add a mock "I" character
929
+ if ([currentStr length] == 0) {
930
+ [currentStr
931
+ appendAttributedString:[[NSAttributedString alloc]
932
+ initWithString:@"I"
933
+ attributes:textView.typingAttributes]];
934
+ }
935
+
936
+ // edge case: input with only a zero width space should still be of a height
937
+ // of a single line, so we add a mock "I" character
938
+ if ([currentStr length] == 1 &&
939
+ [[currentStr.string substringWithRange:NSMakeRange(0, 1)]
940
+ isEqualToString:@"\u200B"]) {
941
+ [currentStr
942
+ appendAttributedString:[[NSAttributedString alloc]
943
+ initWithString:@"I"
944
+ attributes:textView.typingAttributes]];
945
+ }
946
+
947
+ // edge case: trailing newlines aren't counted towards height calculations, so
948
+ // we add a mock "I" character
949
+ if (currentStr.length > 0) {
950
+ unichar lastChar =
951
+ [currentStr.string characterAtIndex:currentStr.length - 1];
952
+ if ([[NSCharacterSet newlineCharacterSet] characterIsMember:lastChar]) {
953
+ [currentStr
954
+ appendAttributedString:[[NSAttributedString alloc]
955
+ initWithString:@"I"
956
+ attributes:defaultTypingAttributes]];
957
+ }
958
+ }
959
+
960
+ CGRect boundingBox =
961
+ [currentStr boundingRectWithSize:CGSizeMake(maxWidth, CGFLOAT_MAX)
962
+ options:NSStringDrawingUsesLineFragmentOrigin |
963
+ NSStringDrawingUsesFontLeading
964
+ context:nullptr];
965
+
966
+ return CGSizeMake(maxWidth, ceil(boundingBox.size.height));
967
+ }
968
+
969
+ // make sure the newest state is kept in _state property
970
+ - (void)updateState:(State::Shared const &)state
971
+ oldState:(State::Shared const &)oldState {
972
+ _state = std::static_pointer_cast<
973
+ const EnrichedTextInputViewShadowNode::ConcreteState>(state);
974
+
975
+ // first render with all the needed stuff already defined (state and
976
+ // componentView) so we need to run a single height calculation for any
977
+ // initial values
978
+ if (oldState == nullptr) {
979
+ [self tryUpdatingHeight];
980
+ }
981
+ }
982
+
983
+ - (void)tryUpdatingHeight {
984
+ if (_state == nullptr) {
985
+ return;
986
+ }
987
+ _componentViewHeightUpdateCounter++;
988
+ auto selfRef = wrapManagedObjectWeakly(self);
989
+ _state->updateState(
990
+ EnrichedTextInputViewState(_componentViewHeightUpdateCounter, selfRef));
991
+ }
992
+
993
+ // MARK: - Active styles
994
+
995
+ - (void)tryUpdatingActiveStyles {
996
+ // style updates are emitted only if something differs from the previously
997
+ // active styles
998
+ BOOL updateNeeded = NO;
999
+
1000
+ // active styles are kept in a separate set until we're sure they can be
1001
+ // emitted
1002
+ NSMutableSet *newActiveStyles = [_activeStyles mutableCopy];
1003
+
1004
+ // currently blocked styles are subject to change (e.g. bold being blocked by
1005
+ // headings might change in reaction to prop change) so they also are kept
1006
+ // separately
1007
+ NSMutableSet *newBlockedStyles = [_blockedStyles mutableCopy];
1008
+
1009
+ // data for onLinkDetected event
1010
+ LinkData *detectedLinkData;
1011
+ NSRange detectedLinkRange = NSMakeRange(0, 0);
1012
+ BOOL shouldClearLink = NO;
1013
+
1014
+ // data for onMentionDetected event
1015
+ MentionParams *detectedMentionParams = nullptr;
1016
+ NSRange detectedMentionRange = NSMakeRange(0, 0);
1017
+ BOOL shouldClearMention = NO;
1018
+
1019
+ for (NSNumber *type in stylesDict) {
1020
+ StyleBase *style = stylesDict[type];
1021
+
1022
+ BOOL wasActive = [newActiveStyles containsObject:type];
1023
+ BOOL isActive = [style detect:textView.selectedRange];
1024
+
1025
+ BOOL wasBlocked = [newBlockedStyles containsObject:type];
1026
+ BOOL isBlocked = [self isStyle:(StyleType)[type integerValue]
1027
+ activeInMap:blockingStyles];
1028
+
1029
+ if (wasActive != isActive) {
1030
+ updateNeeded = YES;
1031
+ if (isActive) {
1032
+ [newActiveStyles addObject:type];
1033
+ } else {
1034
+ [newActiveStyles removeObject:type];
1035
+ }
1036
+ }
1037
+
1038
+ // blocked state change for a style also needs an update
1039
+ if (wasBlocked != isBlocked) {
1040
+ updateNeeded = YES;
1041
+ if (isBlocked) {
1042
+ [newBlockedStyles addObject:type];
1043
+ } else {
1044
+ [newBlockedStyles removeObject:type];
1045
+ }
1046
+ }
1047
+
1048
+ // onLinkDetected event
1049
+ if ([type intValue] == [LinkStyle getType]) {
1050
+ if (isActive) {
1051
+ // get the link data
1052
+ LinkData *candidateLinkData;
1053
+ NSRange candidateLinkRange = NSMakeRange(0, 0);
1054
+ LinkStyle *linkStyleClass =
1055
+ (LinkStyle *)stylesDict[@([LinkStyle getType])];
1056
+ if (linkStyleClass != nullptr) {
1057
+ candidateLinkData =
1058
+ [linkStyleClass getLinkDataAt:textView.selectedRange.location];
1059
+ candidateLinkRange = [linkStyleClass
1060
+ getFullLinkRangeAt:textView.selectedRange.location];
1061
+ }
1062
+
1063
+ if (wasActive == NO) {
1064
+ // we changed selection from non-link to a link
1065
+ detectedLinkData = candidateLinkData;
1066
+ detectedLinkRange = candidateLinkRange;
1067
+ } else if (![_recentlyActiveLinkData
1068
+ isEqualToLinkData:candidateLinkData] ||
1069
+ !NSEqualRanges(_recentlyActiveLinkRange,
1070
+ candidateLinkRange)) {
1071
+ // we changed selection from one link to the other or modified
1072
+ // current link's text
1073
+ detectedLinkData = candidateLinkData;
1074
+ detectedLinkRange = candidateLinkRange;
1075
+ }
1076
+ } else if (wasActive) {
1077
+ shouldClearLink = YES;
1078
+ }
1079
+ }
1080
+
1081
+ // onMentionDetected event
1082
+ if ([type intValue] == [MentionStyle getType]) {
1083
+ if (isActive) {
1084
+ // get mention data
1085
+ MentionParams *candidateMentionParams;
1086
+ NSRange candidateMentionRange = NSMakeRange(0, 0);
1087
+ MentionStyle *mentionStyleClass =
1088
+ (MentionStyle *)stylesDict[@([MentionStyle getType])];
1089
+ if (mentionStyleClass != nullptr) {
1090
+ candidateMentionParams = [mentionStyleClass
1091
+ getMentionParamsAt:textView.selectedRange.location];
1092
+ candidateMentionRange = [mentionStyleClass
1093
+ getFullMentionRangeAt:textView.selectedRange.location];
1094
+ }
1095
+
1096
+ if (wasActive == NO) {
1097
+ // selection was changed from a non-mention to a mention
1098
+ detectedMentionParams = candidateMentionParams;
1099
+ detectedMentionRange = candidateMentionRange;
1100
+ } else if (![_recentlyActiveMentionParams.text
1101
+ isEqualToString:candidateMentionParams.text] ||
1102
+ ![_recentlyActiveMentionParams.attributes
1103
+ isEqualToString:candidateMentionParams.attributes] ||
1104
+ !NSEqualRanges(_recentlyActiveMentionRange,
1105
+ candidateMentionRange)) {
1106
+ // selection changed from one mention to another
1107
+ detectedMentionParams = candidateMentionParams;
1108
+ detectedMentionRange = candidateMentionRange;
1109
+ }
1110
+ } else if (wasActive) {
1111
+ shouldClearMention = YES;
1112
+ }
1113
+ }
1114
+ }
1115
+
1116
+ // detect alignment change
1117
+ AlignmentStyle *alignmentStyle = stylesDict[@([AlignmentStyle getType])];
1118
+ NSString *currentAlignment = [alignmentStyle getStyleState];
1119
+ if (![currentAlignment isEqualToString:_recentlyEmittedAlignment]) {
1120
+ updateNeeded = YES;
1121
+ }
1122
+
1123
+ if (updateNeeded) {
1124
+ auto emitter = [self getEventEmitter];
1125
+ if (emitter != nullptr) {
1126
+ // update activeStyles and blockedStyles only if emitter is available
1127
+ _activeStyles = newActiveStyles;
1128
+ _blockedStyles = newBlockedStyles;
1129
+ _recentlyEmittedAlignment = currentAlignment;
1130
+
1131
+ emitter->onChangeState(
1132
+ {.bold = GET_STYLE_STATE([BoldStyle getType]),
1133
+ .italic = GET_STYLE_STATE([ItalicStyle getType]),
1134
+ .underline = GET_STYLE_STATE([UnderlineStyle getType]),
1135
+ .strikeThrough = GET_STYLE_STATE([StrikethroughStyle getType]),
1136
+ .inlineCode = GET_STYLE_STATE([InlineCodeStyle getType]),
1137
+ .link = GET_STYLE_STATE([LinkStyle getType]),
1138
+ .mention = GET_STYLE_STATE([MentionStyle getType]),
1139
+ .h1 = GET_STYLE_STATE([H1Style getType]),
1140
+ .h2 = GET_STYLE_STATE([H2Style getType]),
1141
+ .h3 = GET_STYLE_STATE([H3Style getType]),
1142
+ .h4 = GET_STYLE_STATE([H4Style getType]),
1143
+ .h5 = GET_STYLE_STATE([H5Style getType]),
1144
+ .h6 = GET_STYLE_STATE([H6Style getType]),
1145
+ .unorderedList = GET_STYLE_STATE([UnorderedListStyle getType]),
1146
+ .orderedList = GET_STYLE_STATE([OrderedListStyle getType]),
1147
+ .blockQuote = GET_STYLE_STATE([BlockQuoteStyle getType]),
1148
+ .codeBlock = GET_STYLE_STATE([CodeBlockStyle getType]),
1149
+ .image = GET_STYLE_STATE([ImageStyle getType]),
1150
+ .checkboxList = GET_STYLE_STATE([CheckboxListStyle getType]),
1151
+ .alignment = [currentAlignment UTF8String]});
1152
+ }
1153
+ }
1154
+
1155
+ if (detectedLinkData != nullptr) {
1156
+ // emit onLinkeDetected event
1157
+ [self emitOnLinkDetectedEvent:detectedLinkData range:detectedLinkRange];
1158
+ } else if (shouldClearLink) {
1159
+ LinkData *emptyLinkData = [[LinkData alloc] init];
1160
+ emptyLinkData.text = @"";
1161
+ emptyLinkData.url = @"";
1162
+ [self emitOnLinkDetectedEvent:emptyLinkData range:NSMakeRange(0, 0)];
1163
+ }
1164
+
1165
+ if (detectedMentionParams != nullptr) {
1166
+ // emit onMentionDetected event
1167
+ [self emitOnMentionDetectedEvent:detectedMentionParams.text
1168
+ indicator:detectedMentionParams.indicator
1169
+ attributes:detectedMentionParams.attributes];
1170
+
1171
+ _recentlyActiveMentionParams = detectedMentionParams;
1172
+ _recentlyActiveMentionRange = detectedMentionRange;
1173
+ } else if (shouldClearMention) {
1174
+ [self emitOnMentionDetectedEvent:@"" indicator:@"" attributes:@"{}"];
1175
+ _recentlyActiveMentionParams = nullptr;
1176
+ _recentlyActiveMentionRange = NSMakeRange(0, 0);
1177
+ }
1178
+ // emit onChangeHtml event if needed
1179
+ [self tryEmittingOnChangeHtmlEvent];
1180
+ }
1181
+
1182
+ - (bool)isStyleActive:(StyleType)type {
1183
+ return [_activeStyles containsObject:@(type)];
1184
+ }
1185
+
1186
+ - (bool)isStyle:(StyleType)type activeInMap:(NSDictionary *)styleMap {
1187
+ NSArray *relatedStyles = styleMap[@(type)];
1188
+
1189
+ if (!relatedStyles) {
1190
+ return false;
1191
+ }
1192
+
1193
+ for (NSNumber *style in relatedStyles) {
1194
+ if ([_activeStyles containsObject:style]) {
1195
+ return true;
1196
+ }
1197
+ }
1198
+
1199
+ return false;
1200
+ }
1201
+
1202
+ - (bool)textInputShouldReturn {
1203
+ return [_submitBehavior isEqualToString:@"blurAndSubmit"];
1204
+ }
1205
+
1206
+ - (bool)textInputShouldSubmitOnReturn {
1207
+ return [_submitBehavior isEqualToString:@"blurAndSubmit"] ||
1208
+ [_submitBehavior isEqualToString:@"submit"];
1209
+ }
1210
+
1211
+ // MARK: - Native commands and events
1212
+
1213
+ - (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args {
1214
+ if ([commandName isEqualToString:@"focus"]) {
1215
+ [self focus];
1216
+ } else if ([commandName isEqualToString:@"blur"]) {
1217
+ [self blur];
1218
+ } else if ([commandName isEqualToString:@"setValue"]) {
1219
+ NSString *value = (NSString *)args[0];
1220
+ [self setValue:value];
1221
+ } else if ([commandName isEqualToString:@"toggleBold"]) {
1222
+ [self toggleRegularStyle:[BoldStyle getType]];
1223
+ } else if ([commandName isEqualToString:@"toggleItalic"]) {
1224
+ [self toggleRegularStyle:[ItalicStyle getType]];
1225
+ } else if ([commandName isEqualToString:@"toggleUnderline"]) {
1226
+ [self toggleRegularStyle:[UnderlineStyle getType]];
1227
+ } else if ([commandName isEqualToString:@"toggleStrikeThrough"]) {
1228
+ [self toggleRegularStyle:[StrikethroughStyle getType]];
1229
+ } else if ([commandName isEqualToString:@"toggleInlineCode"]) {
1230
+ [self toggleRegularStyle:[InlineCodeStyle getType]];
1231
+ } else if ([commandName isEqualToString:@"addLink"]) {
1232
+ NSInteger start = [((NSNumber *)args[0]) integerValue];
1233
+ NSInteger end = [((NSNumber *)args[1]) integerValue];
1234
+ NSString *text = (NSString *)args[2];
1235
+ NSString *url = (NSString *)args[3];
1236
+ [self addLinkAt:start end:end text:text url:url];
1237
+ } else if ([commandName isEqualToString:@"removeLink"]) {
1238
+ NSInteger start = [((NSNumber *)args[0]) integerValue];
1239
+ NSInteger end = [((NSNumber *)args[1]) integerValue];
1240
+ [self removeLinkAt:start end:end];
1241
+ } else if ([commandName isEqualToString:@"addMention"]) {
1242
+ NSString *indicator = (NSString *)args[0];
1243
+ NSString *text = (NSString *)args[1];
1244
+ NSString *attributes = (NSString *)args[2];
1245
+ [self addMention:indicator text:text attributes:attributes];
1246
+ } else if ([commandName isEqualToString:@"startMention"]) {
1247
+ NSString *indicator = (NSString *)args[0];
1248
+ [self startMentionWithIndicator:indicator];
1249
+ } else if ([commandName isEqualToString:@"toggleH1"]) {
1250
+ [self toggleRegularStyle:[H1Style getType]];
1251
+ } else if ([commandName isEqualToString:@"toggleH2"]) {
1252
+ [self toggleRegularStyle:[H2Style getType]];
1253
+ } else if ([commandName isEqualToString:@"toggleH3"]) {
1254
+ [self toggleRegularStyle:[H3Style getType]];
1255
+ } else if ([commandName isEqualToString:@"toggleH4"]) {
1256
+ [self toggleRegularStyle:[H4Style getType]];
1257
+ } else if ([commandName isEqualToString:@"toggleH5"]) {
1258
+ [self toggleRegularStyle:[H5Style getType]];
1259
+ } else if ([commandName isEqualToString:@"toggleH6"]) {
1260
+ [self toggleRegularStyle:[H6Style getType]];
1261
+ } else if ([commandName isEqualToString:@"toggleUnorderedList"]) {
1262
+ [self toggleRegularStyle:[UnorderedListStyle getType]];
1263
+ } else if ([commandName isEqualToString:@"toggleOrderedList"]) {
1264
+ [self toggleRegularStyle:[OrderedListStyle getType]];
1265
+ } else if ([commandName isEqualToString:@"toggleCheckboxList"]) {
1266
+ BOOL checked = [args[0] boolValue];
1267
+ [self toggleCheckboxList:checked];
1268
+ } else if ([commandName isEqualToString:@"toggleBlockQuote"]) {
1269
+ [self toggleRegularStyle:[BlockQuoteStyle getType]];
1270
+ } else if ([commandName isEqualToString:@"toggleCodeBlock"]) {
1271
+ [self toggleRegularStyle:[CodeBlockStyle getType]];
1272
+ } else if ([commandName isEqualToString:@"addImage"]) {
1273
+ NSString *uri = (NSString *)args[0];
1274
+ CGFloat imgWidth = [(NSNumber *)args[1] floatValue];
1275
+ CGFloat imgHeight = [(NSNumber *)args[2] floatValue];
1276
+
1277
+ [self addImage:uri width:imgWidth height:imgHeight];
1278
+ } else if ([commandName isEqualToString:@"setSelection"]) {
1279
+ NSInteger start = [((NSNumber *)args[0]) integerValue];
1280
+ NSInteger end = [((NSNumber *)args[1]) integerValue];
1281
+ [self setCustomSelection:start end:end];
1282
+ } else if ([commandName isEqualToString:@"requestHTML"]) {
1283
+ NSInteger requestId = [((NSNumber *)args[0]) integerValue];
1284
+ [self requestHTML:requestId];
1285
+ } else if ([commandName isEqualToString:@"setTextAlignment"]) {
1286
+ NSString *alignmentString = (NSString *)args[0];
1287
+
1288
+ AlignmentStyle *alignmentStyle = stylesDict[@([AlignmentStyle getType])];
1289
+ [alignmentStyle
1290
+ addAlignment:[AlignmentUtils stringToAlignment:alignmentString]
1291
+ range:textView.selectedRange
1292
+ withTyping:YES
1293
+ withDirtyRange:YES];
1294
+
1295
+ [self anyTextMayHaveBeenModified];
1296
+ if (!_placeholderLabel.isHidden) {
1297
+ [self refreshPlaceholderLabelStyles];
1298
+ }
1299
+ }
1300
+ }
1301
+
1302
+ - (std::shared_ptr<EnrichedTextInputViewEventEmitter>)getEventEmitter {
1303
+ if (_eventEmitter != nullptr && !blockEmitting) {
1304
+ auto emitter =
1305
+ static_cast<const EnrichedTextInputViewEventEmitter &>(*_eventEmitter);
1306
+ return std::make_shared<EnrichedTextInputViewEventEmitter>(emitter);
1307
+ } else {
1308
+ return nullptr;
1309
+ }
1310
+ }
1311
+
1312
+ - (void)blur {
1313
+ [textView reactBlur];
1314
+ }
1315
+
1316
+ - (void)focus {
1317
+ [textView reactFocus];
1318
+ }
1319
+
1320
+ - (void)setValue:(NSString *)value {
1321
+ NSString *initiallyProcessedHtml = [parser initiallyProcessHtml:value];
1322
+ if (initiallyProcessedHtml == nullptr) {
1323
+ // reset the text first and reset typing attributes
1324
+ textView.text = @"";
1325
+ textView.typingAttributes = defaultTypingAttributes;
1326
+ // set new text
1327
+ textView.text = value;
1328
+ } else {
1329
+ // we've got some seemingly proper html
1330
+ [parser replaceWholeFromHtml:initiallyProcessedHtml];
1331
+ }
1332
+
1333
+ // set selectedRange and check for changes
1334
+ textView.selectedRange = NSRange(textView.textStorage.string.length, 0);
1335
+ [self anyTextMayHaveBeenModified];
1336
+ }
1337
+
1338
+ - (void)setCustomSelection:(NSInteger)visibleStart end:(NSInteger)visibleEnd {
1339
+ NSString *text = textView.textStorage.string;
1340
+
1341
+ NSUInteger actualStart = [self getActualIndex:visibleStart text:text];
1342
+ NSUInteger actualEnd = [self getActualIndex:visibleEnd text:text];
1343
+
1344
+ textView.selectedRange = NSMakeRange(actualStart, actualEnd - actualStart);
1345
+ }
1346
+
1347
+ // Helper: Walks through the string skipping ZWSPs to find the Nth visible
1348
+ // character
1349
+ - (NSUInteger)getActualIndex:(NSInteger)visibleIndex text:(NSString *)text {
1350
+ NSUInteger currentVisibleCount = 0;
1351
+ NSUInteger actualIndex = 0;
1352
+
1353
+ while (actualIndex < text.length) {
1354
+ if (currentVisibleCount == visibleIndex) {
1355
+ return actualIndex;
1356
+ }
1357
+
1358
+ // If the current char is not a hidden space, it counts towards our visible
1359
+ // index.
1360
+ if ([text characterAtIndex:actualIndex] != 0x200B) {
1361
+ currentVisibleCount++;
1362
+ }
1363
+
1364
+ actualIndex++;
1365
+ }
1366
+
1367
+ return actualIndex;
1368
+ }
1369
+
1370
+ - (void)emitOnSubmitEdittingEvent {
1371
+ auto emitter = [self getEventEmitter];
1372
+ if (emitter != nullptr) {
1373
+ NSString *stringToBeEmitted = [[textView.textStorage.string
1374
+ stringByReplacingOccurrencesOfString:@"\u200B"
1375
+ withString:@""] copy];
1376
+
1377
+ emitter->onSubmitEditing({
1378
+ .text = [stringToBeEmitted toCppString],
1379
+ });
1380
+ }
1381
+ }
1382
+
1383
+ - (void)emitOnLinkDetectedEvent:(LinkData *)linkData range:(NSRange)range {
1384
+ auto emitter = [self getEventEmitter];
1385
+ if (emitter != nullptr) {
1386
+ // update recently active link info
1387
+ _recentlyActiveLinkData = linkData;
1388
+ _recentlyActiveLinkRange = range;
1389
+
1390
+ emitter->onLinkDetected({
1391
+ .text = [linkData.text toCppString],
1392
+ .url = [linkData.url toCppString],
1393
+ .start = static_cast<int>(range.location),
1394
+ .end = static_cast<int>(range.location + range.length),
1395
+ });
1396
+ }
1397
+ }
1398
+
1399
+ - (void)emitOnPasteImagesEvent:(NSArray<NSDictionary *> *)images {
1400
+ auto emitter = [self getEventEmitter];
1401
+ if (emitter != nullptr) {
1402
+ std::vector<EnrichedTextInputViewEventEmitter::OnPasteImagesImages>
1403
+ imagesVector;
1404
+ imagesVector.reserve(images.count);
1405
+
1406
+ for (NSDictionary *img in images) {
1407
+ NSString *uri = img[@"uri"];
1408
+ NSString *type = img[@"type"];
1409
+ double width = [img[@"width"] doubleValue];
1410
+ double height = [img[@"height"] doubleValue];
1411
+
1412
+ EnrichedTextInputViewEventEmitter::OnPasteImagesImages imageStruct = {
1413
+ .uri = [uri toCppString],
1414
+ .type = [type toCppString],
1415
+ .width = width,
1416
+ .height = height};
1417
+
1418
+ imagesVector.push_back(imageStruct);
1419
+ }
1420
+
1421
+ emitter->onPasteImages({.images = imagesVector});
1422
+ }
1423
+ }
1424
+
1425
+ - (void)emitOnMentionDetectedEvent:(NSString *)text
1426
+ indicator:(NSString *)indicator
1427
+ attributes:(NSString *)attributes {
1428
+ auto emitter = [self getEventEmitter];
1429
+ if (emitter != nullptr) {
1430
+ emitter->onMentionDetected({.text = [text toCppString],
1431
+ .indicator = [indicator toCppString],
1432
+ .payload = [attributes toCppString]});
1433
+ }
1434
+ }
1435
+
1436
+ - (void)emitOnMentionEvent:(NSString *)indicator text:(NSString *)text {
1437
+ auto emitter = [self getEventEmitter];
1438
+ if (emitter != nullptr) {
1439
+ if (text != nullptr) {
1440
+ folly::dynamic fdStr = [text toCppString];
1441
+ emitter->onMention({.indicator = [indicator toCppString], .text = fdStr});
1442
+ } else {
1443
+ folly::dynamic nul = nullptr;
1444
+ emitter->onMention({.indicator = [indicator toCppString], .text = nul});
1445
+ }
1446
+ }
1447
+ }
1448
+
1449
+ - (void)tryEmittingOnChangeHtmlEvent {
1450
+ if (!_emitHtml || textView.markedTextRange != nullptr) {
1451
+ return;
1452
+ }
1453
+ auto emitter = [self getEventEmitter];
1454
+ if (emitter != nullptr) {
1455
+ NSString *htmlOutput = [HtmlParser
1456
+ parseToHtmlFromRange:NSMakeRange(0, textView.textStorage.string.length)
1457
+ host:self];
1458
+ // make sure html really changed
1459
+ if (![htmlOutput isEqualToString:_recentlyEmittedHtml]) {
1460
+ _recentlyEmittedHtml = htmlOutput;
1461
+ emitter->onChangeHtml({.value = [htmlOutput toCppString]});
1462
+ }
1463
+ }
1464
+ }
1465
+
1466
+ - (void)requestHTML:(NSInteger)requestId {
1467
+ auto emitter = [self getEventEmitter];
1468
+ if (emitter != nullptr) {
1469
+ @try {
1470
+ NSString *htmlOutput = [HtmlParser
1471
+ parseToHtmlFromRange:NSMakeRange(0,
1472
+ textView.textStorage.string.length)
1473
+ host:self];
1474
+ emitter->onRequestHtmlResult({.requestId = static_cast<int>(requestId),
1475
+ .html = [htmlOutput toCppString]});
1476
+ } @catch (NSException *exception) {
1477
+ emitter->onRequestHtmlResult({.requestId = static_cast<int>(requestId),
1478
+ .html = folly::dynamic(nullptr)});
1479
+ }
1480
+ }
1481
+ }
1482
+
1483
+ - (void)emitOnKeyPressEvent:(NSString *)key {
1484
+ auto emitter = [self getEventEmitter];
1485
+ if (emitter != nullptr) {
1486
+ emitter->onInputKeyPress({.key = [key toCppString]});
1487
+ }
1488
+ }
1489
+
1490
+ // MARK: - Styles manipulation
1491
+
1492
+ - (void)toggleRegularStyle:(StyleType)type {
1493
+ StyleBase *style = stylesDict[@(type)];
1494
+ NSRange range = textView.selectedRange;
1495
+ if ([style isParagraph]) {
1496
+ range = [textView.textStorage.string paragraphRangeForRange:range];
1497
+ }
1498
+ if ([StyleUtils handleStyleBlocksAndConflicts:type
1499
+ range:range
1500
+ forHost:self]) {
1501
+ [style toggle:range];
1502
+ [self anyTextMayHaveBeenModified];
1503
+ }
1504
+ }
1505
+
1506
+ - (void)toggleCheckboxList:(BOOL)checked {
1507
+ CheckboxListStyle *style =
1508
+ (CheckboxListStyle *)stylesDict[@([CheckboxListStyle getType])];
1509
+ if (style == nullptr) {
1510
+ return;
1511
+ }
1512
+ NSRange range = [textView.textStorage.string
1513
+ paragraphRangeForRange:textView.selectedRange];
1514
+ if ([StyleUtils handleStyleBlocksAndConflicts:[CheckboxListStyle getType]
1515
+ range:range
1516
+ forHost:self]) {
1517
+ [style toggleWithChecked:checked range:range];
1518
+ [self anyTextMayHaveBeenModified];
1519
+ }
1520
+ }
1521
+
1522
+ - (void)addLinkAt:(NSInteger)start
1523
+ end:(NSInteger)end
1524
+ text:(NSString *)text
1525
+ url:(NSString *)url {
1526
+ LinkStyle *linkStyleClass = (LinkStyle *)stylesDict[@([LinkStyle getType])];
1527
+ if (linkStyleClass == nullptr) {
1528
+ return;
1529
+ }
1530
+
1531
+ // translate the output start-end notation to range
1532
+ NSRange linkRange = NSMakeRange(start, end - start);
1533
+ if ([StyleUtils handleStyleBlocksAndConflicts:[LinkStyle getType]
1534
+ range:linkRange
1535
+ forHost:self]) {
1536
+ LinkData *linkData = [[LinkData alloc] init];
1537
+ linkData.text = text;
1538
+ linkData.url = url;
1539
+ linkData.isManual = YES;
1540
+ [linkStyleClass addLink:linkData range:linkRange withSelection:YES];
1541
+ [self anyTextMayHaveBeenModified];
1542
+ }
1543
+ }
1544
+
1545
+ - (void)removeLinkAt:(NSInteger)start end:(NSInteger)end {
1546
+ LinkStyle *linkStyleClass = (LinkStyle *)stylesDict[@([LinkStyle getType])];
1547
+ if (linkStyleClass == nullptr) {
1548
+ return;
1549
+ }
1550
+
1551
+ NSInteger textLength = (NSInteger)textView.textStorage.length;
1552
+ if (start < 0) {
1553
+ start = 0;
1554
+ }
1555
+ if (end > textLength) {
1556
+ end = textLength;
1557
+ }
1558
+
1559
+ NSInteger rangeStart = MIN(start, end);
1560
+ NSInteger rangeLength = MAX(start, end) - rangeStart;
1561
+ NSRange linkRange = NSMakeRange(rangeStart, rangeLength);
1562
+
1563
+ [linkStyleClass remove:linkRange withDirtyRange:YES];
1564
+ [self anyTextMayHaveBeenModified];
1565
+ }
1566
+
1567
+ - (void)addMention:(NSString *)indicator
1568
+ text:(NSString *)text
1569
+ attributes:(NSString *)attributes {
1570
+ MentionStyle *mentionStyleClass =
1571
+ (MentionStyle *)stylesDict[@([MentionStyle getType])];
1572
+ if (mentionStyleClass == nullptr) {
1573
+ return;
1574
+ }
1575
+
1576
+ NSValue *activeMentionRange = [mentionStyleClass getActiveMentionRange];
1577
+ NSRange rangeToUse = activeMentionRange != nullptr
1578
+ ? [activeMentionRange rangeValue]
1579
+ : self.textView.selectedRange;
1580
+
1581
+ if ([StyleUtils handleStyleBlocksAndConflicts:[MentionStyle getType]
1582
+ range:rangeToUse
1583
+ forHost:self]) {
1584
+ [mentionStyleClass addMention:indicator text:text attributes:attributes];
1585
+ [self anyTextMayHaveBeenModified];
1586
+ }
1587
+ }
1588
+
1589
+ - (void)addImage:(NSString *)uri width:(float)width height:(float)height {
1590
+ ImageStyle *imageStyleClass =
1591
+ (ImageStyle *)stylesDict[@([ImageStyle getType])];
1592
+ if (imageStyleClass == nullptr) {
1593
+ return;
1594
+ }
1595
+
1596
+ if ([StyleUtils handleStyleBlocksAndConflicts:[ImageStyle getType]
1597
+ range:textView.selectedRange
1598
+ forHost:self]) {
1599
+ [imageStyleClass addImage:uri width:width height:height];
1600
+ [self anyTextMayHaveBeenModified];
1601
+ }
1602
+ }
1603
+
1604
+ - (void)startMentionWithIndicator:(NSString *)indicator {
1605
+ MentionStyle *mentionStyleClass =
1606
+ (MentionStyle *)stylesDict[@([MentionStyle getType])];
1607
+ if (mentionStyleClass == nullptr) {
1608
+ return;
1609
+ }
1610
+
1611
+ if ([StyleUtils handleStyleBlocksAndConflicts:[MentionStyle getType]
1612
+ range:textView.selectedRange
1613
+ forHost:self]) {
1614
+ [mentionStyleClass startMentionWithIndicator:indicator];
1615
+ [self anyTextMayHaveBeenModified];
1616
+ }
1617
+ }
1618
+
1619
+ - (void)manageSelectionBasedChanges {
1620
+ NSString *currentString = [textView.textStorage.string copy];
1621
+
1622
+ MentionStyle *mentionStyleClass =
1623
+ (MentionStyle *)stylesDict[@([MentionStyle getType])];
1624
+ if (mentionStyleClass != nullptr) {
1625
+ // mention editing runs if only a selection was done (no text change)
1626
+ // otherwise we would double-emit with a second call in the
1627
+ // anyTextMayHaveBeenModified method
1628
+ if ([_recentInputString isEqualToString:currentString]) {
1629
+ [mentionStyleClass manageMentionEditing];
1630
+ }
1631
+ }
1632
+
1633
+ // attributes manager handles proper typingAttributes at all times to properly
1634
+ // extend meta-attributes
1635
+ BOOL onlySelectionChanged =
1636
+ textView.selectedRange.length == 0 &&
1637
+ [_recentInputString isEqualToString:currentString];
1638
+ // removedTypingAttributes aren't normally removed during the regular flow
1639
+ // and we do remove them only here - so when we are sure that selection
1640
+ // changed. We want to remember which attributes were removed as long as we
1641
+ // stay at the same position. This prevents a removed attribute from being
1642
+ // re-applied from the preceding character right after we toggled it off.
1643
+ [attributesManager clearRemovedTypingAttributes];
1644
+ [attributesManager
1645
+ manageTypingAttributesWithOnlySelection:onlySelectionChanged];
1646
+
1647
+ // always update active styles
1648
+ [self tryUpdatingActiveStyles];
1649
+ }
1650
+
1651
+ - (void)handleWordModificationBasedChanges:(NSString *)word
1652
+ inRange:(NSRange)range {
1653
+ // manual links refreshing and automatic links detection handling
1654
+ LinkStyle *linkStyle = [stylesDict objectForKey:@([LinkStyle getType])];
1655
+
1656
+ if (linkStyle != nullptr) {
1657
+ // manual links need to be handled first because they can block automatic
1658
+ // links after being refreshed
1659
+ [linkStyle handleManualLinks:word inRange:range];
1660
+ [linkStyle handleAutomaticLinks:word inRange:range];
1661
+ }
1662
+ }
1663
+
1664
+ - (void)anyTextMayHaveBeenModified {
1665
+ // we don't do no text changes when working with iOS marked text
1666
+ if (textView.markedTextRange != nullptr) {
1667
+ return;
1668
+ }
1669
+
1670
+ // zero width space adding or removal
1671
+ [ZeroWidthSpaceUtils handleZeroWidthSpacesInHost:self];
1672
+
1673
+ // emptying input typing attributes management
1674
+ if (textView.textStorage.string.length == 0 &&
1675
+ _recentInputString.length > 0) {
1676
+ // reset typing attribtues
1677
+ textView.typingAttributes = defaultTypingAttributes;
1678
+ }
1679
+
1680
+ // mentions management: removal and editing
1681
+ MentionStyle *mentionStyleClass =
1682
+ (MentionStyle *)stylesDict[@([MentionStyle getType])];
1683
+ if (mentionStyleClass != nullptr) {
1684
+ [mentionStyleClass handleExistingMentions];
1685
+ [mentionStyleClass manageMentionEditing];
1686
+ }
1687
+
1688
+ // placholder management
1689
+ if (!_placeholderLabel.hidden && textView.textStorage.string.length > 0) {
1690
+ [self setPlaceholderLabelShown:NO];
1691
+ } else if (textView.textStorage.string.length == 0 &&
1692
+ _placeholderLabel.hidden) {
1693
+ [self setPlaceholderLabelShown:YES];
1694
+ }
1695
+
1696
+ // modified words handling
1697
+ NSArray *currentDirtyRanges = [attributesManager getDirtyRanges];
1698
+ if (currentDirtyRanges.count > 0) {
1699
+ NSMutableArray *modifiedWords = [[NSMutableArray alloc] init];
1700
+
1701
+ for (NSValue *dirtyRangeValue in currentDirtyRanges) {
1702
+ NSRange dirtyRange = [dirtyRangeValue rangeValue];
1703
+ NSArray *words =
1704
+ [WordsUtils getAffectedWordsFromText:textView.textStorage.string
1705
+ modificationRange:dirtyRange];
1706
+ if (words != nullptr) {
1707
+ [modifiedWords addObjectsFromArray:words];
1708
+ }
1709
+ }
1710
+
1711
+ for (NSDictionary *wordDict in modifiedWords) {
1712
+ NSString *wordText = (NSString *)[wordDict objectForKey:@"word"];
1713
+ NSValue *wordRange = (NSValue *)[wordDict objectForKey:@"range"];
1714
+
1715
+ if (wordText == nullptr || wordRange == nullptr) {
1716
+ continue;
1717
+ }
1718
+
1719
+ [self handleWordModificationBasedChanges:wordText
1720
+ inRange:[wordRange rangeValue]];
1721
+ }
1722
+ }
1723
+
1724
+ if (![textView.textStorage.string isEqualToString:_recentInputString]) {
1725
+ // emit onChangeText event
1726
+ auto emitter = [self getEventEmitter];
1727
+ if (emitter != nullptr && _emitTextChange) {
1728
+ // set the recent input string only if the emitter is defined
1729
+ _recentInputString = [textView.textStorage.string copy];
1730
+
1731
+ // emit string without zero width spaces
1732
+ NSString *stringToBeEmitted = [[textView.textStorage.string
1733
+ stringByReplacingOccurrencesOfString:@"\u200B"
1734
+ withString:@""] copy];
1735
+
1736
+ emitter->onChangeText({.value = [stringToBeEmitted toCppString]});
1737
+ }
1738
+ }
1739
+ // all the visible (not meta) attributes handling in the ranges that could
1740
+ // have changed
1741
+ [attributesManager handleDirtyRangesStyling];
1742
+ // update height on each character change
1743
+ [self tryUpdatingHeight];
1744
+ // update active styles as well
1745
+ [self tryUpdatingActiveStyles];
1746
+ [self layoutAttachments];
1747
+ // update drawing - schedule debounced relayout
1748
+ [self scheduleRelayoutIfNeeded];
1749
+ }
1750
+
1751
+ // Debounced relayout helper - coalesces multiple requests into one per runloop
1752
+ // tick
1753
+ - (void)scheduleRelayoutIfNeeded {
1754
+ // Cancel any previously scheduled invocation to debounce
1755
+ [NSObject cancelPreviousPerformRequestsWithTarget:self
1756
+ selector:@selector(_performRelayout)
1757
+ object:nil];
1758
+ // Schedule on next runloop cycle
1759
+ [self performSelector:@selector(_performRelayout)
1760
+ withObject:nil
1761
+ afterDelay:0];
1762
+ }
1763
+
1764
+ - (void)_performRelayout {
1765
+ if (!textView) {
1766
+ return;
1767
+ }
1768
+
1769
+ dispatch_async(dispatch_get_main_queue(), ^{
1770
+ NSRange wholeRange =
1771
+ NSMakeRange(0, self->textView.textStorage.string.length);
1772
+ NSRange actualRange = NSMakeRange(0, 0);
1773
+ [self->textView.layoutManager
1774
+ invalidateLayoutForCharacterRange:wholeRange
1775
+ actualCharacterRange:&actualRange];
1776
+ [self->textView.layoutManager ensureLayoutForCharacterRange:actualRange];
1777
+ [self->textView.layoutManager
1778
+ invalidateDisplayForCharacterRange:wholeRange];
1779
+
1780
+ // We have to explicitly set contentSize
1781
+ // That way textView knows if content overflows and if should be scrollable
1782
+ // We recall measureSize here because value returned from previous
1783
+ // measureSize may not be up-to date at that point
1784
+ CGSize measuredSize = [self measureSize:self->textView.frame.size.width];
1785
+ self->textView.contentSize = measuredSize;
1786
+ });
1787
+ }
1788
+
1789
+ - (void)didMoveToWindow {
1790
+ [super didMoveToWindow];
1791
+ // used to run all lifecycle callbacks
1792
+ [self anyTextMayHaveBeenModified];
1793
+ }
1794
+
1795
+ // MARK: - Delegate methods
1796
+
1797
+ - (void)textViewDidBeginEditing:(UITextView *)textView {
1798
+ auto emitter = [self getEventEmitter];
1799
+ if (emitter != nullptr) {
1800
+ // send onFocus event if allowed
1801
+ if (_emitFocusBlur) {
1802
+ emitter->onInputFocus({});
1803
+ }
1804
+ }
1805
+ // manage selection changes since textViewDidChangeSelection sometimes doesn't
1806
+ // run on focus
1807
+ [self manageSelectionBasedChanges];
1808
+ }
1809
+
1810
+ - (void)textViewDidEndEditing:(UITextView *)textView {
1811
+ auto emitter = [self getEventEmitter];
1812
+ if (emitter != nullptr && _emitFocusBlur) {
1813
+ // send onBlur event
1814
+ emitter->onInputBlur({});
1815
+ }
1816
+ }
1817
+
1818
+ - (UIMenu *)textView:(UITextView *)tv
1819
+ editMenuForTextInRange:(NSRange)range
1820
+ suggestedActions:(NSArray<UIMenuElement *> *)suggestedActions
1821
+ API_AVAILABLE(ios(16.0)) {
1822
+ if (_contextMenuItems == nil || _contextMenuItems.count == 0) {
1823
+ return [UIMenu menuWithChildren:suggestedActions];
1824
+ }
1825
+
1826
+ NSMutableArray<UIMenuElement *> *customActions = [NSMutableArray new];
1827
+
1828
+ for (NSString *title in _contextMenuItems) {
1829
+ __weak EnrichedTextInputView *weakSelf = self;
1830
+
1831
+ UIAction *action =
1832
+ [UIAction actionWithTitle:title
1833
+ image:nil
1834
+ identifier:nil
1835
+ handler:^(__kindof UIAction *_Nonnull action) {
1836
+ [weakSelf emitOnContextMenuItemPressEvent:title];
1837
+ }];
1838
+ [customActions addObject:action];
1839
+ }
1840
+
1841
+ [customActions addObjectsFromArray:suggestedActions];
1842
+ return [UIMenu menuWithChildren:customActions];
1843
+ }
1844
+
1845
+ - (void)emitOnContextMenuItemPressEvent:(NSString *)itemText {
1846
+ auto emitter = [self getEventEmitter];
1847
+ if (emitter != nullptr) {
1848
+ NSRange selectedRange = textView.selectedRange;
1849
+ NSString *selectedText = @"";
1850
+ if (selectedRange.length > 0) {
1851
+ selectedText =
1852
+ [textView.textStorage.string substringWithRange:selectedRange];
1853
+ }
1854
+
1855
+ AlignmentStyle *alignmentStyle = stylesDict[@([AlignmentStyle getType])];
1856
+ NSString *currentAlignment = [alignmentStyle getStyleState];
1857
+
1858
+ emitter->onContextMenuItemPress(
1859
+ {.itemText = [itemText toCppString],
1860
+ .selectedText = [selectedText toCppString],
1861
+ .selectionStart = static_cast<int>(selectedRange.location),
1862
+ .selectionEnd =
1863
+ static_cast<int>(selectedRange.location + selectedRange.length),
1864
+ .styleState = {
1865
+ .bold = GET_STYLE_STATE([BoldStyle getType]),
1866
+ .italic = GET_STYLE_STATE([ItalicStyle getType]),
1867
+ .underline = GET_STYLE_STATE([UnderlineStyle getType]),
1868
+ .strikeThrough = GET_STYLE_STATE([StrikethroughStyle getType]),
1869
+ .inlineCode = GET_STYLE_STATE([InlineCodeStyle getType]),
1870
+ .h1 = GET_STYLE_STATE([H1Style getType]),
1871
+ .h2 = GET_STYLE_STATE([H2Style getType]),
1872
+ .h3 = GET_STYLE_STATE([H3Style getType]),
1873
+ .h4 = GET_STYLE_STATE([H4Style getType]),
1874
+ .h5 = GET_STYLE_STATE([H5Style getType]),
1875
+ .h6 = GET_STYLE_STATE([H6Style getType]),
1876
+ .codeBlock = GET_STYLE_STATE([CodeBlockStyle getType]),
1877
+ .blockQuote = GET_STYLE_STATE([BlockQuoteStyle getType]),
1878
+ .orderedList = GET_STYLE_STATE([OrderedListStyle getType]),
1879
+ .unorderedList = GET_STYLE_STATE([UnorderedListStyle getType]),
1880
+ .link = GET_STYLE_STATE([LinkStyle getType]),
1881
+ .image = GET_STYLE_STATE([ImageStyle getType]),
1882
+ .mention = GET_STYLE_STATE([MentionStyle getType]),
1883
+ .checkboxList = GET_STYLE_STATE([CheckboxListStyle getType]),
1884
+ .alignment = [currentAlignment UTF8String]}});
1885
+ }
1886
+ }
1887
+
1888
+ - (void)handleKeyPressInRange:(NSString *)text range:(NSRange)range {
1889
+ NSString *key = nil;
1890
+
1891
+ if (text.length == 0) {
1892
+ key = @"Backspace";
1893
+ } else if ([text isEqualToString:@"\n"]) {
1894
+ key = @"Enter";
1895
+ } else if ([text isEqualToString:@"\t"]) {
1896
+ key = @"Tab";
1897
+ } else if (text.length == 1) {
1898
+ key = text;
1899
+ }
1900
+
1901
+ if (key != nil) {
1902
+ [self emitOnKeyPressEvent:key];
1903
+ }
1904
+ }
1905
+
1906
+ - (bool)textView:(UITextView *)textView
1907
+ shouldChangeTextInRange:(NSRange)range
1908
+ replacementText:(NSString *)text {
1909
+ // Capture the attributes at range.location that are being replaced
1910
+ // (autocorrect / predictive) so didProcessEditing: can re-stamp them onto the
1911
+ // replacement.
1912
+ if (range.length > 0) {
1913
+ _capturedAttributesBeforeChange =
1914
+ [textView.textStorage attributesAtIndex:range.location
1915
+ effectiveRange:NULL];
1916
+ }
1917
+
1918
+ // Check if the user pressed "Enter"
1919
+ if ([text isEqualToString:@"\n"]) {
1920
+ const bool shouldSubmit = [self textInputShouldSubmitOnReturn];
1921
+ const bool shouldReturn = [self textInputShouldReturn];
1922
+
1923
+ if (shouldSubmit) {
1924
+ [self emitOnSubmitEdittingEvent];
1925
+ }
1926
+
1927
+ if (shouldReturn) {
1928
+ [textView endEditing:NO];
1929
+ }
1930
+
1931
+ if (shouldSubmit || shouldReturn) {
1932
+ return NO;
1933
+ }
1934
+ }
1935
+
1936
+ [self handleKeyPressInRange:text range:range];
1937
+
1938
+ CheckboxListStyle *cbLStyle =
1939
+ (CheckboxListStyle *)stylesDict[@([CheckboxListStyle getType])];
1940
+ H1Style *h1Style = stylesDict[@([H1Style getType])];
1941
+ H2Style *h2Style = stylesDict[@([H2Style getType])];
1942
+ H3Style *h3Style = stylesDict[@([H3Style getType])];
1943
+ H4Style *h4Style = stylesDict[@([H4Style getType])];
1944
+ H5Style *h5Style = stylesDict[@([H5Style getType])];
1945
+ H6Style *h6Style = stylesDict[@([H6Style getType])];
1946
+
1947
+ // some of the changes these checks do could interfere with later checks and
1948
+ // cause a crash so here we rely on short circuiting evaluation of the logical
1949
+ // expression. Either way it's not possible to have two of them come off at
1950
+ // the same time
1951
+ if (
1952
+ // ZWS backspace handling for paragraph styles
1953
+ [ZeroWidthSpaceUtils handleBackspaceInRange:range
1954
+ replacementText:text
1955
+ host:self] ||
1956
+ [cbLStyle handleNewlinesInRange:range replacementText:text] ||
1957
+ [h1Style handleNewlinesInRange:range replacementText:text] ||
1958
+ [h2Style handleNewlinesInRange:range replacementText:text] ||
1959
+ [h3Style handleNewlinesInRange:range replacementText:text] ||
1960
+ [h4Style handleNewlinesInRange:range replacementText:text] ||
1961
+ [h5Style handleNewlinesInRange:range replacementText:text] ||
1962
+ [h6Style handleNewlinesInRange:range replacementText:text] ||
1963
+ [ParagraphAttributesUtils handleBackspaceInRange:range
1964
+ replacementText:text
1965
+ input:self] ||
1966
+ [ParagraphAttributesUtils handleResetTypingAttributesOnBackspace:range
1967
+ replacementText:text
1968
+ input:self]
1969
+ // Check configurable text shortcuts (block: "# " → h1, inline: `code` →
1970
+ // inline_code)
1971
+ || [ShortcutsUtils tryHandlingParagraphShortcutsInRange:range
1972
+ replacementText:text
1973
+ input:self] ||
1974
+ [ShortcutsUtils tryHandlingInlineShortcutsInRange:range
1975
+ replacementText:text
1976
+ input:self]
1977
+ // CRITICAL: This callback HAS TO be always evaluated last.
1978
+ //
1979
+ // This function is the "Generic Fallback": if no specific style
1980
+ // claims the backspace action to change its state, only then do we
1981
+ // proceed to physically delete the newline and merge paragraphs.
1982
+ ||
1983
+ [ParagraphAttributesUtils handleParagraphStylesMergeOnBackspace:range
1984
+ replacementText:text
1985
+ input:self]) {
1986
+ [self anyTextMayHaveBeenModified];
1987
+ return NO;
1988
+ }
1989
+
1990
+ return YES;
1991
+ }
1992
+
1993
+ - (void)textViewDidChangeSelection:(UITextView *)textView {
1994
+ // emit the event
1995
+ NSString *textAtSelection =
1996
+ [[[NSMutableString alloc] initWithString:textView.textStorage.string]
1997
+ substringWithRange:textView.selectedRange];
1998
+
1999
+ auto emitter = [self getEventEmitter];
2000
+ if (emitter != nullptr) {
2001
+ // iOS range works differently because it specifies location and length
2002
+ // here, start is the location, but end is the first index BEHIND the end.
2003
+ // So a 0 length range will have equal start and end
2004
+ emitter->onChangeSelection(
2005
+ {.start = static_cast<int>(textView.selectedRange.location),
2006
+ .end = static_cast<int>(textView.selectedRange.location +
2007
+ textView.selectedRange.length),
2008
+ .text = [textAtSelection toCppString]});
2009
+ }
2010
+
2011
+ // manage selection changes
2012
+ [self manageSelectionBasedChanges];
2013
+ }
2014
+
2015
+ // this function isn't called always when some text changes (for example setting
2016
+ // link or starting mention with indicator doesn't fire it) so all the logic is
2017
+ // in anyTextMayHaveBeenModified
2018
+ - (void)textViewDidChange:(UITextView *)textView {
2019
+ [self anyTextMayHaveBeenModified];
2020
+ }
2021
+
2022
+ /**
2023
+ * Handles iOS Dynamic Type changes (User changing font size in System
2024
+ * Settings).
2025
+ *
2026
+ * Unlike Android, iOS Views do not automatically rescale existing
2027
+ * NSAttributedStrings when the system font size changes. The text attributes
2028
+ * are static once drawn.
2029
+ *
2030
+ * This method detects the change and performs a "Hard Refresh" of the content.
2031
+ */
2032
+ - (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
2033
+ [super traitCollectionDidChange:previousTraitCollection];
2034
+
2035
+ if (!config.allowFontScaling) {
2036
+ return;
2037
+ }
2038
+
2039
+ if (previousTraitCollection.preferredContentSizeCategory ==
2040
+ self.traitCollection.preferredContentSizeCategory) {
2041
+ return;
2042
+ }
2043
+
2044
+ [config invalidateFonts];
2045
+
2046
+ NSMutableDictionary *newTypingAttrs = [defaultTypingAttributes mutableCopy];
2047
+ newTypingAttrs[NSFontAttributeName] = [config primaryFont];
2048
+
2049
+ defaultTypingAttributes = newTypingAttrs;
2050
+ textView.typingAttributes = defaultTypingAttributes;
2051
+
2052
+ [self refreshPlaceholderLabelStyles];
2053
+
2054
+ NSRange prevSelectedRange = textView.selectedRange;
2055
+
2056
+ NSString *currentHtml = [HtmlParser
2057
+ parseToHtmlFromRange:NSMakeRange(0, textView.textStorage.string.length)
2058
+ host:self];
2059
+ NSString *initiallyProcessedHtml = [parser initiallyProcessHtml:currentHtml];
2060
+ [parser replaceWholeFromHtml:initiallyProcessedHtml];
2061
+
2062
+ textView.selectedRange = prevSelectedRange;
2063
+ [self anyTextMayHaveBeenModified];
2064
+ }
2065
+
2066
+ - (void)onTextBlockTap:(TextBlockTapGestureRecognizer *)gr {
2067
+ if (gr.state != UIGestureRecognizerStateEnded)
2068
+ return;
2069
+ if (![self->textView isFirstResponder]) {
2070
+ [self->textView becomeFirstResponder];
2071
+ }
2072
+
2073
+ switch (gr.tapKind) {
2074
+
2075
+ case TextBlockTapKindCheckbox: {
2076
+ CheckboxListStyle *checkboxStyle =
2077
+ (CheckboxListStyle *)stylesDict[@([CheckboxListStyle getType])];
2078
+
2079
+ if (checkboxStyle) {
2080
+ NSUInteger charIndex = (NSUInteger)gr.characterIndex;
2081
+ [checkboxStyle toggleCheckedAt:charIndex withDirtyRange:YES];
2082
+ [self anyTextMayHaveBeenModified];
2083
+
2084
+ NSString *fullText = textView.textStorage.string;
2085
+ NSRange paragraphRange =
2086
+ [fullText paragraphRangeForRange:NSMakeRange(charIndex, 0)];
2087
+ NSUInteger endOfLineIndex = NSMaxRange(paragraphRange);
2088
+
2089
+ // If the paragraph ends with a newline, step back by 1 so the cursor
2090
+ // stays on the current line instead of jumping to the next one.
2091
+ if (endOfLineIndex > 0 && endOfLineIndex <= fullText.length) {
2092
+ unichar lastChar = [fullText characterAtIndex:endOfLineIndex - 1];
2093
+ if ([[NSCharacterSet newlineCharacterSet] characterIsMember:lastChar]) {
2094
+ endOfLineIndex--;
2095
+ }
2096
+ }
2097
+
2098
+ // Move the cursor to the end of the currently tapped checkbox line.
2099
+ // Without this, the cursor may remain at its previous position,
2100
+ // potentially inside a different checkbox line.
2101
+ textView.selectedRange = NSMakeRange(endOfLineIndex, 0);
2102
+ }
2103
+ break;
2104
+ }
2105
+
2106
+ default:
2107
+ break;
2108
+ }
2109
+ }
2110
+
2111
+ - (void)textStorage:(NSTextStorage *)textStorage
2112
+ didProcessEditing:(NSTextStorageEditActions)editedMask
2113
+ range:(NSRange)editedRange
2114
+ changeInLength:(NSInteger)delta {
2115
+ // iOS replacing quick double space with ". " attributes fix.
2116
+ [DotReplacementUtils handleDotReplacement:self
2117
+ textStorage:textStorage
2118
+ editedMask:editedMask
2119
+ editedRange:editedRange
2120
+ delta:delta];
2121
+
2122
+ // Needed dirty ranges adjustments happen on every character edition.
2123
+ if ((editedMask & NSTextStorageEditedCharacters) != 0) {
2124
+ // Re-stamp custom meta-attributes captured in shouldChangeTextInRange: onto
2125
+ // the new range so autocorrect/predictive replacements keep their styling.
2126
+ if (_capturedAttributesBeforeChange != nil) {
2127
+ // Skip while an IME composition is in progress; restamp on commit.
2128
+ if (textView.markedTextRange == nil) {
2129
+ NSSet *customKeys = [attributesManager customAttributesKeys];
2130
+ for (NSString *key in _capturedAttributesBeforeChange) {
2131
+ if ([customKeys containsObject:key]) {
2132
+ [textStorage addAttribute:key
2133
+ value:_capturedAttributesBeforeChange[key]
2134
+ range:editedRange];
2135
+ }
2136
+ }
2137
+ }
2138
+
2139
+ // Clear after consuming
2140
+ _capturedAttributesBeforeChange = nil;
2141
+ }
2142
+ // Always try shifting dirty ranges (happens only with delta != 0).
2143
+ [attributesManager shiftDirtyRangesWithEditedRange:editedRange
2144
+ changeInLength:delta];
2145
+
2146
+ // Add dirty ranges. We also add zero-length ranges because they are useful
2147
+ // for word modification-based changes.
2148
+ [attributesManager addDirtyRange:editedRange];
2149
+ }
2150
+ }
2151
+
2152
+ // MARK: - Media attachments delegate
2153
+
2154
+ - (void)mediaAttachmentDidUpdate:(MediaAttachment *)attachment {
2155
+ [AttachmentLayoutUtils handleAttachmentUpdate:attachment
2156
+ textView:textView
2157
+ onLayoutBlock:^{
2158
+ [self layoutAttachments];
2159
+ }];
2160
+ }
2161
+
2162
+ // MARK: - Image/GIF Overlay Management
2163
+
2164
+ - (void)layoutAttachments {
2165
+ _attachmentViews =
2166
+ [AttachmentLayoutUtils layoutAttachmentsInTextView:textView
2167
+ config:config
2168
+ existingViews:_attachmentViews];
2169
+ }
2170
+ @end