react-native-enriched 0.2.1 → 0.4.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 (263) hide show
  1. package/README.md +21 -16
  2. package/android/build.gradle +77 -72
  3. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerDelegate.java +21 -0
  4. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerInterface.java +7 -0
  5. package/android/generated/jni/react/renderer/components/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/ComponentDescriptors.cpp +1 -1
  6. package/android/generated/jni/react/renderer/components/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/ComponentDescriptors.h +1 -1
  7. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/EventEmitters.cpp +276 -0
  8. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/EventEmitters.h +239 -0
  9. package/android/generated/jni/react/renderer/components/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/Props.cpp +10 -0
  10. package/android/generated/jni/react/renderer/components/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/Props.h +251 -0
  11. package/android/gradle.properties +5 -5
  12. package/android/lint.gradle +70 -0
  13. package/android/src/main/java/com/swmansion/enriched/{EnrichedTextInputViewPackage.kt → ReactNativeEnrichedPackage.kt} +4 -5
  14. package/android/src/main/java/com/swmansion/enriched/{utils → common}/AsyncDrawable.kt +50 -15
  15. package/android/src/main/java/com/swmansion/enriched/common/CheckboxDrawable.kt +81 -0
  16. package/android/src/main/java/com/swmansion/enriched/common/EnrichedConstants.kt +11 -0
  17. package/android/src/main/java/com/swmansion/enriched/common/EnrichedStyle.kt +57 -0
  18. package/android/src/main/java/com/swmansion/enriched/{spans/utils → common}/ForceRedrawSpan.kt +3 -2
  19. package/android/src/main/java/com/swmansion/enriched/common/MentionStyle.kt +7 -0
  20. package/android/src/main/java/com/swmansion/enriched/{utils → common}/ResourceManager.kt +1 -1
  21. package/android/src/main/java/com/swmansion/enriched/{utils → common/parser}/EnrichedParser.java +228 -160
  22. package/android/src/main/java/com/swmansion/enriched/common/parser/EnrichedSpanFactory.kt +79 -0
  23. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedBlockQuoteSpan.kt +53 -0
  24. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedBoldSpan.kt +12 -0
  25. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedCheckboxListSpan.kt +91 -0
  26. package/android/src/main/java/com/swmansion/enriched/{spans → common/spans}/EnrichedCodeBlockSpan.kt +12 -14
  27. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH1Span.kt +20 -0
  28. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH2Span.kt +20 -0
  29. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH3Span.kt +20 -0
  30. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH4Span.kt +21 -0
  31. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH5Span.kt +20 -0
  32. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH6Span.kt +20 -0
  33. package/android/src/main/java/com/swmansion/enriched/{spans → common/spans}/EnrichedImageSpan.kt +68 -51
  34. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedInlineCodeSpan.kt +24 -0
  35. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedItalicSpan.kt +12 -0
  36. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedLinkSpan.kt +26 -0
  37. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedMentionSpan.kt +35 -0
  38. package/android/src/main/java/com/swmansion/enriched/{spans → common/spans}/EnrichedOrderedListSpan.kt +21 -29
  39. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedStrikeThroughSpan.kt +11 -0
  40. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedUnderlineSpan.kt +11 -0
  41. package/android/src/main/java/com/swmansion/enriched/{spans → common/spans}/EnrichedUnorderedListSpan.kt +13 -17
  42. package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedBlockSpan.kt +5 -0
  43. package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedHeadingSpan.kt +3 -0
  44. package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedInlineSpan.kt +3 -0
  45. package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedParagraphSpan.kt +5 -0
  46. package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedSpan.kt +3 -0
  47. package/android/src/main/java/com/swmansion/enriched/{spans → common/spans}/interfaces/EnrichedZeroWidthSpaceSpan.kt +2 -3
  48. package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputConnectionWrapper.kt +140 -0
  49. package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputSpannableFactory.kt +83 -0
  50. package/android/src/main/java/com/swmansion/enriched/{EnrichedTextInputView.kt → textinput/EnrichedTextInputView.kt} +322 -157
  51. package/android/src/main/java/com/swmansion/enriched/{EnrichedTextInputViewLayoutManager.kt → textinput/EnrichedTextInputViewLayoutManager.kt} +4 -2
  52. package/android/src/main/java/com/swmansion/enriched/{EnrichedTextInputViewManager.kt → textinput/EnrichedTextInputViewManager.kt} +182 -66
  53. package/android/src/main/java/com/swmansion/enriched/{MeasurementStore.kt → textinput/MeasurementStore.kt} +75 -25
  54. package/android/src/main/java/com/swmansion/enriched/{events → textinput/events}/MentionHandler.kt +22 -12
  55. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnChangeHtmlEvent.kt +27 -0
  56. package/android/src/main/java/com/swmansion/enriched/{events → textinput/events}/OnChangeSelectionEvent.kt +11 -10
  57. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnChangeStateEvent.kt +21 -0
  58. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnChangeTextEvent.kt +30 -0
  59. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnInputBlurEvent.kt +25 -0
  60. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnInputFocusEvent.kt +25 -0
  61. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnInputKeyPressEvent.kt +27 -0
  62. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnLinkDetectedEvent.kt +32 -0
  63. package/android/src/main/java/com/swmansion/enriched/{events → textinput/events}/OnMentionDetectedEvent.kt +11 -10
  64. package/android/src/main/java/com/swmansion/enriched/{events → textinput/events}/OnMentionEvent.kt +10 -9
  65. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnPasteImagesEvent.kt +47 -0
  66. package/android/src/main/java/com/swmansion/enriched/{events → textinput/events}/OnRequestHtmlResultEvent.kt +2 -3
  67. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputBlockQuoteSpan.kt +14 -0
  68. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputBoldSpan.kt +14 -0
  69. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputCheckboxListSpan.kt +15 -0
  70. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputCodeBlockSpan.kt +14 -0
  71. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH1Span.kt +14 -0
  72. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH2Span.kt +14 -0
  73. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH3Span.kt +14 -0
  74. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH4Span.kt +14 -0
  75. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH5Span.kt +14 -0
  76. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH6Span.kt +14 -0
  77. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputImageSpan.kt +36 -0
  78. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputInlineCodeSpan.kt +14 -0
  79. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputItalicSpan.kt +14 -0
  80. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputLinkSpan.kt +15 -0
  81. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputMentionSpan.kt +18 -0
  82. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputOrderedListSpan.kt +21 -0
  83. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputStrikeThroughSpan.kt +14 -0
  84. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputUnderlineSpan.kt +14 -0
  85. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputUnorderedListSpan.kt +14 -0
  86. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedSpans.kt +241 -0
  87. package/android/src/main/java/com/swmansion/enriched/textinput/spans/interfaces/EnrichedInputSpan.kt +10 -0
  88. package/android/src/main/java/com/swmansion/enriched/{styles → textinput/styles}/HtmlStyle.kt +129 -57
  89. package/android/src/main/java/com/swmansion/enriched/{styles → textinput/styles}/InlineStyles.kt +30 -13
  90. package/android/src/main/java/com/swmansion/enriched/textinput/styles/ListStyles.kt +263 -0
  91. package/android/src/main/java/com/swmansion/enriched/{styles → textinput/styles}/ParagraphStyles.kt +94 -34
  92. package/android/src/main/java/com/swmansion/enriched/{styles → textinput/styles}/ParametrizedStyles.kt +143 -67
  93. package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedEditableFactory.kt +17 -0
  94. package/android/src/main/java/com/swmansion/enriched/{utils → textinput/utils}/EnrichedSelection.kt +84 -54
  95. package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSpanState.kt +304 -0
  96. package/android/src/main/java/com/swmansion/enriched/{utils/Utils.kt → textinput/utils/EnrichedSpannable.kt} +22 -31
  97. package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSpannableStringBuilder.kt +16 -0
  98. package/android/src/main/java/com/swmansion/enriched/textinput/utils/RichContentReceiver.kt +127 -0
  99. package/android/src/main/java/com/swmansion/enriched/textinput/utils/Utils.kt +106 -0
  100. package/android/src/main/java/com/swmansion/enriched/{watchers → textinput/watchers}/EnrichedSpanWatcher.kt +56 -24
  101. package/android/src/main/java/com/swmansion/enriched/{watchers → textinput/watchers}/EnrichedTextWatcher.kt +37 -14
  102. package/android/src/main/new_arch/CMakeLists.txt +7 -1
  103. package/android/src/main/new_arch/ReactNativeEnrichedSpec.cpp +11 -0
  104. package/android/src/main/new_arch/ReactNativeEnrichedSpec.h +15 -0
  105. package/android/src/main/new_arch/react/renderer/components/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/EnrichedTextInputMeasurementManager.h +1 -1
  106. package/android/src/main/new_arch/react/renderer/components/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/EnrichedTextInputShadowNode.h +2 -2
  107. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/conversions.h +46 -0
  108. package/ios/EnrichedTextInputView.h +2 -1
  109. package/ios/EnrichedTextInputView.mm +603 -60
  110. package/ios/config/InputConfig.h +28 -0
  111. package/ios/config/InputConfig.mm +237 -8
  112. package/ios/extensions/ImageExtension.h +35 -0
  113. package/ios/extensions/ImageExtension.mm +156 -0
  114. package/ios/{utils → extensions}/LayoutManagerExtension.mm +115 -95
  115. package/ios/generated/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/ComponentDescriptors.cpp +1 -1
  116. package/ios/generated/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/ComponentDescriptors.h +1 -1
  117. package/ios/generated/ReactNativeEnrichedSpec/EventEmitters.cpp +276 -0
  118. package/ios/generated/ReactNativeEnrichedSpec/EventEmitters.h +239 -0
  119. package/ios/generated/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/Props.cpp +10 -0
  120. package/ios/generated/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/Props.h +251 -0
  121. package/ios/generated/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/RCTComponentViewHelpers.h +95 -0
  122. package/ios/inputParser/InputParser.mm +218 -18
  123. package/ios/inputTextView/InputTextView.mm +118 -0
  124. package/ios/{attachments → interfaces}/ImageAttachment.h +1 -0
  125. package/ios/interfaces/ImageAttachment.mm +46 -0
  126. package/ios/interfaces/LinkRegexConfig.h +19 -0
  127. package/ios/interfaces/LinkRegexConfig.mm +37 -0
  128. package/ios/{utils → interfaces}/MentionStyleProps.mm +2 -2
  129. package/ios/{utils → interfaces}/StyleHeaders.h +22 -1
  130. package/ios/{utils → interfaces}/StyleTypeEnum.h +4 -0
  131. package/ios/internals/EnrichedTextInputViewState.cpp +6 -6
  132. package/ios/styles/BlockQuoteStyle.mm +5 -5
  133. package/ios/styles/BoldStyle.mm +21 -6
  134. package/ios/styles/CheckboxListStyle.mm +321 -0
  135. package/ios/styles/CodeBlockStyle.mm +5 -5
  136. package/ios/styles/H1Style.mm +3 -0
  137. package/ios/styles/H2Style.mm +3 -0
  138. package/ios/styles/H3Style.mm +3 -0
  139. package/ios/styles/H4Style.mm +20 -0
  140. package/ios/styles/H5Style.mm +20 -0
  141. package/ios/styles/H6Style.mm +20 -0
  142. package/ios/styles/HeadingStyleBase.mm +161 -72
  143. package/ios/styles/ImageStyle.mm +5 -5
  144. package/ios/styles/InlineCodeStyle.mm +30 -19
  145. package/ios/styles/ItalicStyle.mm +5 -5
  146. package/ios/styles/LinkStyle.mm +98 -40
  147. package/ios/styles/MentionStyle.mm +4 -4
  148. package/ios/styles/OrderedListStyle.mm +5 -5
  149. package/ios/styles/StrikethroughStyle.mm +5 -5
  150. package/ios/styles/UnderlineStyle.mm +5 -5
  151. package/ios/styles/UnorderedListStyle.mm +5 -5
  152. package/ios/utils/CheckboxHitTestUtils.h +10 -0
  153. package/ios/utils/CheckboxHitTestUtils.mm +123 -0
  154. package/ios/utils/ParagraphAttributesUtils.h +4 -0
  155. package/ios/utils/ParagraphAttributesUtils.mm +142 -45
  156. package/ios/utils/ParagraphsUtils.mm +4 -4
  157. package/ios/utils/TextBlockTapGestureRecognizer.h +17 -0
  158. package/ios/utils/TextBlockTapGestureRecognizer.mm +56 -0
  159. package/ios/utils/ZeroWidthSpaceUtils.mm +14 -3
  160. package/lib/module/EnrichedTextInput.js +57 -11
  161. package/lib/module/EnrichedTextInput.js.map +1 -1
  162. package/lib/module/{EnrichedTextInputNativeComponent.ts → spec/EnrichedTextInputNativeComponent.ts} +175 -18
  163. package/lib/module/types.js +4 -0
  164. package/lib/module/types.js.map +1 -0
  165. package/lib/module/{normalizeHtmlStyle.js → utils/normalizeHtmlStyle.js} +18 -0
  166. package/lib/module/utils/normalizeHtmlStyle.js.map +1 -0
  167. package/lib/module/utils/nullthrows.js +9 -0
  168. package/lib/module/utils/nullthrows.js.map +1 -0
  169. package/lib/module/utils/regexParser.js +46 -0
  170. package/lib/module/utils/regexParser.js.map +1 -0
  171. package/lib/typescript/src/EnrichedTextInput.d.ts +20 -51
  172. package/lib/typescript/src/EnrichedTextInput.d.ts.map +1 -1
  173. package/lib/typescript/src/index.d.ts +2 -1
  174. package/lib/typescript/src/index.d.ts.map +1 -1
  175. package/lib/typescript/src/{EnrichedTextInputNativeComponent.d.ts → spec/EnrichedTextInputNativeComponent.d.ts} +154 -18
  176. package/lib/typescript/src/spec/EnrichedTextInputNativeComponent.d.ts.map +1 -0
  177. package/lib/typescript/src/types.d.ts +58 -0
  178. package/lib/typescript/src/types.d.ts.map +1 -0
  179. package/lib/typescript/src/utils/normalizeHtmlStyle.d.ts +4 -0
  180. package/lib/typescript/src/utils/normalizeHtmlStyle.d.ts.map +1 -0
  181. package/lib/typescript/src/utils/nullthrows.d.ts +2 -0
  182. package/lib/typescript/src/utils/nullthrows.d.ts.map +1 -0
  183. package/lib/typescript/src/utils/regexParser.d.ts +3 -0
  184. package/lib/typescript/src/utils/regexParser.d.ts.map +1 -0
  185. package/package.json +13 -9
  186. package/src/EnrichedTextInput.tsx +88 -63
  187. package/src/index.tsx +5 -1
  188. package/src/{EnrichedTextInputNativeComponent.ts → spec/EnrichedTextInputNativeComponent.ts} +175 -18
  189. package/src/types.ts +59 -0
  190. package/src/{normalizeHtmlStyle.ts → utils/normalizeHtmlStyle.ts} +20 -5
  191. package/src/utils/nullthrows.ts +7 -0
  192. package/src/utils/regexParser.ts +56 -0
  193. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/EventEmitters.cpp +0 -128
  194. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/EventEmitters.h +0 -102
  195. package/android/src/main/java/com/swmansion/enriched/events/OnChangeHtmlEvent.kt +0 -28
  196. package/android/src/main/java/com/swmansion/enriched/events/OnChangeStateEvent.kt +0 -24
  197. package/android/src/main/java/com/swmansion/enriched/events/OnChangeTextEvent.kt +0 -30
  198. package/android/src/main/java/com/swmansion/enriched/events/OnInputBlurEvent.kt +0 -27
  199. package/android/src/main/java/com/swmansion/enriched/events/OnInputFocusEvent.kt +0 -27
  200. package/android/src/main/java/com/swmansion/enriched/events/OnLinkDetectedEvent.kt +0 -30
  201. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedBlockQuoteSpan.kt +0 -44
  202. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedBoldSpan.kt +0 -16
  203. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH1Span.kt +0 -23
  204. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH2Span.kt +0 -23
  205. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH3Span.kt +0 -23
  206. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedInlineCodeSpan.kt +0 -27
  207. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedItalicSpan.kt +0 -15
  208. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedLinkSpan.kt +0 -30
  209. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedMentionSpan.kt +0 -42
  210. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedSpans.kt +0 -136
  211. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedStrikeThroughSpan.kt +0 -14
  212. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedUnderlineSpan.kt +0 -14
  213. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedBlockSpan.kt +0 -4
  214. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedHeadingSpan.kt +0 -4
  215. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedInlineSpan.kt +0 -4
  216. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedParagraphSpan.kt +0 -4
  217. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedSpan.kt +0 -8
  218. package/android/src/main/java/com/swmansion/enriched/styles/ListStyles.kt +0 -172
  219. package/android/src/main/java/com/swmansion/enriched/utils/EnrichedSpanState.kt +0 -204
  220. package/android/src/main/new_arch/RNEnrichedTextInputViewSpec.cpp +0 -22
  221. package/android/src/main/new_arch/RNEnrichedTextInputViewSpec.h +0 -26
  222. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/conversions.h +0 -26
  223. package/ios/attachments/ImageAttachment.mm +0 -34
  224. package/ios/generated/RNEnrichedTextInputViewSpec/EventEmitters.cpp +0 -128
  225. package/ios/generated/RNEnrichedTextInputViewSpec/EventEmitters.h +0 -102
  226. package/lib/module/normalizeHtmlStyle.js.map +0 -1
  227. package/lib/typescript/src/EnrichedTextInputNativeComponent.d.ts.map +0 -1
  228. package/lib/typescript/src/normalizeHtmlStyle.d.ts +0 -4
  229. package/lib/typescript/src/normalizeHtmlStyle.d.ts.map +0 -1
  230. /package/android/generated/jni/react/renderer/components/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/ShadowNodes.cpp +0 -0
  231. /package/android/generated/jni/react/renderer/components/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/ShadowNodes.h +0 -0
  232. /package/android/generated/jni/react/renderer/components/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/States.cpp +0 -0
  233. /package/android/generated/jni/react/renderer/components/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/States.h +0 -0
  234. /package/android/src/main/new_arch/react/renderer/components/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/EnrichedTextInputComponentDescriptor.h +0 -0
  235. /package/android/src/main/new_arch/react/renderer/components/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/EnrichedTextInputMeasurementManager.cpp +0 -0
  236. /package/android/src/main/new_arch/react/renderer/components/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/EnrichedTextInputShadowNode.cpp +0 -0
  237. /package/android/src/main/new_arch/react/renderer/components/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/EnrichedTextInputState.cpp +0 -0
  238. /package/android/src/main/new_arch/react/renderer/components/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/EnrichedTextInputState.h +0 -0
  239. /package/ios/{utils → extensions}/ColorExtension.h +0 -0
  240. /package/ios/{utils → extensions}/ColorExtension.mm +0 -0
  241. /package/ios/{utils → extensions}/FontExtension.h +0 -0
  242. /package/ios/{utils → extensions}/FontExtension.mm +0 -0
  243. /package/ios/{utils → extensions}/LayoutManagerExtension.h +0 -0
  244. /package/ios/{utils → extensions}/StringExtension.h +0 -0
  245. /package/ios/{utils → extensions}/StringExtension.mm +0 -0
  246. /package/ios/generated/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/ShadowNodes.cpp +0 -0
  247. /package/ios/generated/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/ShadowNodes.h +0 -0
  248. /package/ios/generated/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/States.cpp +0 -0
  249. /package/ios/generated/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/States.h +0 -0
  250. /package/ios/{utils → interfaces}/BaseStyleProtocol.h +0 -0
  251. /package/ios/{utils → interfaces}/ImageData.h +0 -0
  252. /package/ios/{utils → interfaces}/ImageData.mm +0 -0
  253. /package/ios/{utils → interfaces}/LinkData.h +0 -0
  254. /package/ios/{utils → interfaces}/LinkData.mm +0 -0
  255. /package/ios/{attachments → interfaces}/MediaAttachment.h +0 -0
  256. /package/ios/{attachments → interfaces}/MediaAttachment.mm +0 -0
  257. /package/ios/{utils → interfaces}/MentionParams.h +0 -0
  258. /package/ios/{utils → interfaces}/MentionParams.mm +0 -0
  259. /package/ios/{utils → interfaces}/MentionStyleProps.h +0 -0
  260. /package/ios/{utils → interfaces}/StylePair.h +0 -0
  261. /package/ios/{utils → interfaces}/StylePair.mm +0 -0
  262. /package/ios/{utils → interfaces}/TextDecorationLineEnum.h +0 -0
  263. /package/ios/{utils → interfaces}/TextDecorationLineEnum.mm +0 -0
@@ -1,4 +1,4 @@
1
- package com.swmansion.enriched
1
+ package com.swmansion.enriched.textinput
2
2
 
3
3
  import android.content.ClipData
4
4
  import android.content.ClipboardManager
@@ -13,12 +13,19 @@ import android.text.InputType
13
13
  import android.text.Spannable
14
14
  import android.util.AttributeSet
15
15
  import android.util.Log
16
+ import android.util.Patterns
16
17
  import android.util.TypedValue
17
18
  import android.view.Gravity
18
19
  import android.view.MotionEvent
20
+ import android.view.inputmethod.EditorInfo
21
+ import android.view.inputmethod.InputConnection
19
22
  import android.view.inputmethod.InputMethodManager
20
23
  import androidx.appcompat.widget.AppCompatEditText
24
+ import androidx.core.view.ViewCompat
25
+ import androidx.core.view.inputmethod.EditorInfoCompat
26
+ import androidx.core.view.inputmethod.InputConnectionCompat
21
27
  import com.facebook.react.bridge.ReactContext
28
+ import com.facebook.react.bridge.ReadableMap
22
29
  import com.facebook.react.common.ReactConstants
23
30
  import com.facebook.react.uimanager.PixelUtil
24
31
  import com.facebook.react.uimanager.StateWrapper
@@ -26,30 +33,38 @@ import com.facebook.react.uimanager.UIManagerHelper
26
33
  import com.facebook.react.views.text.ReactTypefaceUtils.applyStyles
27
34
  import com.facebook.react.views.text.ReactTypefaceUtils.parseFontStyle
28
35
  import com.facebook.react.views.text.ReactTypefaceUtils.parseFontWeight
29
- import com.swmansion.enriched.events.MentionHandler
30
- import com.swmansion.enriched.events.OnInputBlurEvent
31
- import com.swmansion.enriched.events.OnInputFocusEvent
32
- import com.swmansion.enriched.events.OnRequestHtmlResultEvent
33
- import com.swmansion.enriched.spans.EnrichedH1Span
34
- import com.swmansion.enriched.spans.EnrichedH2Span
35
- import com.swmansion.enriched.spans.EnrichedH3Span
36
- import com.swmansion.enriched.spans.EnrichedImageSpan
37
- import com.swmansion.enriched.spans.EnrichedSpans
38
- import com.swmansion.enriched.spans.interfaces.EnrichedSpan
39
- import com.swmansion.enriched.styles.InlineStyles
40
- import com.swmansion.enriched.styles.ListStyles
41
- import com.swmansion.enriched.styles.ParagraphStyles
42
- import com.swmansion.enriched.styles.ParametrizedStyles
43
- import com.swmansion.enriched.styles.HtmlStyle
44
- import com.swmansion.enriched.utils.EnrichedParser
45
- import com.swmansion.enriched.utils.EnrichedSelection
46
- import com.swmansion.enriched.utils.EnrichedSpanState
47
- import com.swmansion.enriched.utils.mergeSpannables
48
- import com.swmansion.enriched.watchers.EnrichedSpanWatcher
49
- import com.swmansion.enriched.watchers.EnrichedTextWatcher
36
+ import com.swmansion.enriched.common.EnrichedConstants
37
+ import com.swmansion.enriched.common.parser.EnrichedParser
38
+ import com.swmansion.enriched.textinput.events.MentionHandler
39
+ import com.swmansion.enriched.textinput.events.OnInputBlurEvent
40
+ import com.swmansion.enriched.textinput.events.OnInputFocusEvent
41
+ import com.swmansion.enriched.textinput.events.OnRequestHtmlResultEvent
42
+ import com.swmansion.enriched.textinput.spans.EnrichedInputH1Span
43
+ import com.swmansion.enriched.textinput.spans.EnrichedInputH2Span
44
+ import com.swmansion.enriched.textinput.spans.EnrichedInputH3Span
45
+ import com.swmansion.enriched.textinput.spans.EnrichedInputH4Span
46
+ import com.swmansion.enriched.textinput.spans.EnrichedInputH5Span
47
+ import com.swmansion.enriched.textinput.spans.EnrichedInputH6Span
48
+ import com.swmansion.enriched.textinput.spans.EnrichedInputImageSpan
49
+ import com.swmansion.enriched.textinput.spans.EnrichedSpans
50
+ import com.swmansion.enriched.textinput.spans.interfaces.EnrichedInputSpan
51
+ import com.swmansion.enriched.textinput.styles.HtmlStyle
52
+ import com.swmansion.enriched.textinput.styles.InlineStyles
53
+ import com.swmansion.enriched.textinput.styles.ListStyles
54
+ import com.swmansion.enriched.textinput.styles.ParagraphStyles
55
+ import com.swmansion.enriched.textinput.styles.ParametrizedStyles
56
+ import com.swmansion.enriched.textinput.utils.EnrichedEditableFactory
57
+ import com.swmansion.enriched.textinput.utils.EnrichedSelection
58
+ import com.swmansion.enriched.textinput.utils.EnrichedSpanState
59
+ import com.swmansion.enriched.textinput.utils.RichContentReceiver
60
+ import com.swmansion.enriched.textinput.utils.mergeSpannables
61
+ import com.swmansion.enriched.textinput.utils.setCheckboxClickListener
62
+ import com.swmansion.enriched.textinput.watchers.EnrichedSpanWatcher
63
+ import com.swmansion.enriched.textinput.watchers.EnrichedTextWatcher
64
+ import java.util.regex.Pattern
65
+ import java.util.regex.PatternSyntaxException
50
66
  import kotlin.math.ceil
51
67
 
52
-
53
68
  class EnrichedTextInputView : AppCompatEditText {
54
69
  var stateWrapper: StateWrapper? = null
55
70
  val selection: EnrichedSelection? = EnrichedSelection(this)
@@ -65,16 +80,19 @@ class EnrichedTextInputView : AppCompatEditText {
65
80
  val mentionHandler: MentionHandler? = MentionHandler(this)
66
81
  var htmlStyle: HtmlStyle = HtmlStyle(this, null)
67
82
  set(value) {
68
- if (field != value) {
69
- val prev = field
70
- field = value
71
- reApplyHtmlStyleForSpans(prev, value)
72
- }
83
+ if (field != value) {
84
+ val prev = field
85
+ field = value
86
+ reApplyHtmlStyleForSpans(prev, value)
87
+ }
73
88
  }
89
+
90
+ var linkRegex: Pattern? = Patterns.WEB_URL
74
91
  var spanWatcher: EnrichedSpanWatcher? = null
75
92
  var layoutManager: EnrichedTextInputViewLayoutManager = EnrichedTextInputViewLayoutManager(this)
76
93
 
77
- var shouldEmitHtml: Boolean = true
94
+ var shouldEmitHtml: Boolean = false
95
+ var shouldEmitOnChangeText: Boolean = false
78
96
  var experimentalSynchronousEvents: Boolean = false
79
97
 
80
98
  var fontSize: Float? = null
@@ -89,6 +107,7 @@ class EnrichedTextInputView : AppCompatEditText {
89
107
  private var defaultValueDirty: Boolean = false
90
108
 
91
109
  private var inputMethodManager: InputMethodManager? = null
110
+ private val spannableFactory = EnrichedTextInputSpannableFactory()
92
111
 
93
112
  constructor(context: Context) : super(context) {
94
113
  prepareComponent()
@@ -101,13 +120,33 @@ class EnrichedTextInputView : AppCompatEditText {
101
120
  constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(
102
121
  context,
103
122
  attrs,
104
- defStyleAttr
123
+ defStyleAttr,
105
124
  ) {
106
125
  prepareComponent()
107
126
  }
108
127
 
128
+ override fun onCreateInputConnection(outAttrs: EditorInfo): InputConnection? {
129
+ var inputConnection = super.onCreateInputConnection(outAttrs)
130
+ if (inputConnection != null) {
131
+ inputConnection =
132
+ EnrichedTextInputConnectionWrapper(
133
+ inputConnection,
134
+ context as ReactContext,
135
+ this,
136
+ experimentalSynchronousEvents,
137
+ )
138
+ }
139
+
140
+ return inputConnection
141
+ }
142
+
109
143
  init {
110
144
  inputMethodManager = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
145
+ ViewCompat.setOnReceiveContentListener(
146
+ this,
147
+ RichContentReceiver.MIME_TYPES,
148
+ RichContentReceiver(this, context as ReactContext),
149
+ )
111
150
  }
112
151
 
113
152
  private fun prepareComponent() {
@@ -124,8 +163,15 @@ class EnrichedTextInputView : AppCompatEditText {
124
163
  setPadding(0, 0, 0, 0)
125
164
  setBackgroundColor(Color.TRANSPARENT)
126
165
 
127
- addSpanWatcher(EnrichedSpanWatcher(this))
166
+ // Ensure that every time new editable is created, it has EnrichedSpanWatcher attached
167
+ val spanWatcher = EnrichedSpanWatcher(this)
168
+ this.spanWatcher = spanWatcher
169
+
170
+ setEditableFactory(EnrichedEditableFactory(spanWatcher))
128
171
  addTextChangedListener(EnrichedTextWatcher(this))
172
+
173
+ // Handle checkbox list item clicks
174
+ this.setCheckboxClickListener()
129
175
  }
130
176
 
131
177
  // https://github.com/facebook/react-native/blob/36df97f500aa0aa8031098caf7526db358b6ddc1/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.kt#L295C1-L296C1
@@ -138,31 +184,32 @@ class EnrichedTextInputView : AppCompatEditText {
138
184
  this.parent.requestDisallowInterceptTouchEvent(true)
139
185
  }
140
186
 
141
- MotionEvent.ACTION_MOVE ->
187
+ MotionEvent.ACTION_MOVE -> {
142
188
  if (detectScrollMovement) {
143
189
  if (!canScrollVertically(-1) &&
144
190
  !canScrollVertically(1) &&
145
191
  !canScrollHorizontally(-1) &&
146
- !canScrollHorizontally(1)) {
192
+ !canScrollHorizontally(1)
193
+ ) {
147
194
  // We cannot scroll, let parent views take care of these touches.
148
195
  this.parent.requestDisallowInterceptTouchEvent(false)
149
196
  }
150
197
  detectScrollMovement = false
151
198
  }
199
+ }
152
200
  }
153
201
 
154
202
  return super.onTouchEvent(ev)
155
203
  }
156
204
 
157
- override fun canScrollVertically(direction: Int): Boolean {
158
- return scrollEnabled
159
- }
205
+ override fun canScrollVertically(direction: Int): Boolean = scrollEnabled
160
206
 
161
- override fun canScrollHorizontally(direction: Int): Boolean {
162
- return scrollEnabled
163
- }
207
+ override fun canScrollHorizontally(direction: Int): Boolean = scrollEnabled
164
208
 
165
- override fun onSelectionChanged(selStart: Int, selEnd: Int) {
209
+ override fun onSelectionChanged(
210
+ selStart: Int,
211
+ selEnd: Int,
212
+ ) {
166
213
  super.onSelectionChanged(selStart, selEnd)
167
214
  selection?.onSelection(selStart, selEnd)
168
215
  }
@@ -172,7 +219,11 @@ class EnrichedTextInputView : AppCompatEditText {
172
219
  inputMethodManager?.hideSoftInputFromWindow(windowToken, 0)
173
220
  }
174
221
 
175
- override fun onFocusChanged(focused: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
222
+ override fun onFocusChanged(
223
+ focused: Boolean,
224
+ direction: Int,
225
+ previouslyFocusedRect: Rect?,
226
+ ) {
176
227
  super.onFocusChanged(focused, direction, previouslyFocusedRect)
177
228
  val context = context as ReactContext
178
229
  val surfaceId = UIManagerHelper.getSurfaceId(context)
@@ -191,10 +242,6 @@ class EnrichedTextInputView : AppCompatEditText {
191
242
  handleCustomCopy()
192
243
  return true
193
244
  }
194
- android.R.id.paste -> {
195
- handleCustomPaste()
196
- return true
197
- }
198
245
  }
199
246
  return super.onTextContextMenuItem(id)
200
247
  }
@@ -214,16 +261,11 @@ class EnrichedTextInputView : AppCompatEditText {
214
261
  }
215
262
  }
216
263
 
217
- private fun handleCustomPaste() {
218
- val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
219
- if (!clipboard.hasPrimaryClip()) return
220
-
221
- val clip = clipboard.primaryClip
222
- val item = clip?.getItemAt(0)
223
- val htmlText = item?.htmlText
264
+ fun handleTextPaste(item: ClipData.Item) {
265
+ val htmlText = item.htmlText
224
266
  val currentText = text as Spannable
225
- val start = selection?.start ?: 0
226
- val end = selection?.end ?: 0
267
+ val start = selectionStart.coerceAtLeast(0)
268
+ val end = selectionEnd.coerceAtLeast(0)
227
269
 
228
270
  if (htmlText != null) {
229
271
  val parsedText = parseText(htmlText)
@@ -234,11 +276,14 @@ class EnrichedTextInputView : AppCompatEditText {
234
276
  }
235
277
  }
236
278
 
237
- // Currently, we do not support pasting images
238
- if (item?.text == null) return
279
+ if (item.text == null) return
280
+ val lengthBefore = currentText.length
239
281
  val finalText = currentText.mergeSpannables(start, end, item.text.toString())
240
282
  setValue(finalText)
241
- parametrizedStyles?.detectAllLinks()
283
+
284
+ // Detect links in the newly pasted range
285
+ val finalEndIndex = start + finalText.length - lengthBefore
286
+ parametrizedStyles?.detectLinksInRange(finalText, start, finalEndIndex)
242
287
  }
243
288
 
244
289
  fun requestFocusProgrammatically() {
@@ -252,7 +297,7 @@ class EnrichedTextInputView : AppCompatEditText {
252
297
  if (!isHtml) return text
253
298
 
254
299
  try {
255
- val parsed = EnrichedParser.fromHtml(text.toString(), htmlStyle, null)
300
+ val parsed = EnrichedParser.fromHtml(text.toString(), htmlStyle, spannableFactory)
256
301
  val withoutLastNewLine = parsed.trimEnd('\n')
257
302
  return withoutLastNewLine
258
303
  } catch (e: Exception) {
@@ -269,14 +314,43 @@ class EnrichedTextInputView : AppCompatEditText {
269
314
  setText(newText)
270
315
 
271
316
  observeAsyncImages()
272
- // Assign SpanWatcher one more time as our previous spannable has been replaced
273
- addSpanWatcher(EnrichedSpanWatcher(this))
274
317
 
275
318
  // Scroll to the last line of text
276
319
  setSelection(text?.length ?: 0)
277
320
  }
278
321
  }
279
322
 
323
+ fun setCustomSelection(
324
+ visibleStart: Int,
325
+ visibleEnd: Int,
326
+ ) {
327
+ val actualStart = getActualIndex(visibleStart)
328
+ val actualEnd = getActualIndex(visibleEnd)
329
+
330
+ setSelection(actualStart, actualEnd)
331
+ }
332
+
333
+ // Helper: Walks through the string skipping ZWSPs to find the Nth visible character
334
+ private fun getActualIndex(visibleIndex: Int): Int {
335
+ val currentText = text as Spannable
336
+ var currentVisibleCount = 0
337
+ var actualIndex = 0
338
+
339
+ while (actualIndex < currentText.length) {
340
+ if (currentVisibleCount == visibleIndex) {
341
+ return actualIndex
342
+ }
343
+
344
+ // If the current char is not a hidden space, it counts towards our visible index
345
+ if (currentText[actualIndex] != EnrichedConstants.ZWS) {
346
+ currentVisibleCount++
347
+ }
348
+ actualIndex++
349
+ }
350
+
351
+ return actualIndex
352
+ }
353
+
280
354
  /**
281
355
  * Finds all async images in the current text and sets up listeners
282
356
  * to redraw the text layout when they finish downloading.
@@ -284,7 +358,7 @@ class EnrichedTextInputView : AppCompatEditText {
284
358
  private fun observeAsyncImages() {
285
359
  val liveText = text ?: return
286
360
 
287
- val spans = liveText.getSpans(0, liveText.length, EnrichedImageSpan::class.java)
361
+ val spans = liveText.getSpans(0, liveText.length, EnrichedInputImageSpan::class.java)
288
362
 
289
363
  for (span in spans) {
290
364
  span.observeAsyncDrawableLoaded(liveText)
@@ -376,19 +450,50 @@ class EnrichedTextInputView : AppCompatEditText {
376
450
  }
377
451
 
378
452
  fun setAutoCapitalize(flagName: String?) {
379
- val flag = when (flagName) {
380
- "none" -> InputType.TYPE_NULL
381
- "sentences" -> InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
382
- "words" -> InputType.TYPE_TEXT_FLAG_CAP_WORDS
383
- "characters" -> InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS
384
- else -> InputType.TYPE_NULL
453
+ val flag =
454
+ when (flagName) {
455
+ "none" -> InputType.TYPE_NULL
456
+ "sentences" -> InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
457
+ "words" -> InputType.TYPE_TEXT_FLAG_CAP_WORDS
458
+ "characters" -> InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS
459
+ else -> InputType.TYPE_NULL
460
+ }
461
+
462
+ inputType = (
463
+ inputType and
464
+ InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS.inv() and
465
+ InputType.TYPE_TEXT_FLAG_CAP_WORDS.inv() and
466
+ InputType.TYPE_TEXT_FLAG_CAP_SENTENCES.inv()
467
+ ) or if (flag == InputType.TYPE_NULL) 0 else flag
468
+ }
469
+
470
+ fun setLinkRegex(config: ReadableMap?) {
471
+ val patternStr = config?.getString("pattern")
472
+ if (patternStr == null) {
473
+ linkRegex = Patterns.WEB_URL
474
+ return
385
475
  }
386
476
 
387
- inputType = (inputType and
388
- InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS.inv() and
389
- InputType.TYPE_TEXT_FLAG_CAP_WORDS.inv() and
390
- InputType.TYPE_TEXT_FLAG_CAP_SENTENCES.inv()
391
- ) or if (flag == InputType.TYPE_NULL) 0 else flag
477
+ if (config.getBoolean("isDefault")) {
478
+ linkRegex = Patterns.WEB_URL
479
+ return
480
+ }
481
+
482
+ if (config.getBoolean("isDisabled")) {
483
+ linkRegex = null
484
+ return
485
+ }
486
+
487
+ var flags = 0
488
+ if (config.getBoolean("caseInsensitive")) flags = flags or Pattern.CASE_INSENSITIVE
489
+ if (config.getBoolean("dotAll")) flags = flags or Pattern.DOTALL
490
+
491
+ try {
492
+ linkRegex = Pattern.compile("(?s).*?($patternStr).*", flags)
493
+ } catch (e: PatternSyntaxException) {
494
+ Log.w("EnrichedTextInputView", "Invalid link regex pattern: $patternStr")
495
+ linkRegex = Patterns.WEB_URL
496
+ }
392
497
  }
393
498
 
394
499
  // https://github.com/facebook/react-native/blob/36df97f500aa0aa8031098caf7526db358b6ddc1/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.kt#L283C2-L284C1
@@ -397,9 +502,7 @@ class EnrichedTextInputView : AppCompatEditText {
397
502
  // next layout() to be called. However, we do not perform a layout() after a requestLayout(), so
398
503
  // we need to override isLayoutRequested to force EditText to scroll to the end of the new text
399
504
  // immediately.
400
- override fun isLayoutRequested(): Boolean {
401
- return false
402
- }
505
+ override fun isLayoutRequested(): Boolean = false
403
506
 
404
507
  fun afterUpdateTransaction() {
405
508
  updateTypeface()
@@ -439,58 +542,76 @@ class EnrichedTextInputView : AppCompatEditText {
439
542
  EnrichedSpans.H1 -> paragraphStyles?.toggleStyle(EnrichedSpans.H1)
440
543
  EnrichedSpans.H2 -> paragraphStyles?.toggleStyle(EnrichedSpans.H2)
441
544
  EnrichedSpans.H3 -> paragraphStyles?.toggleStyle(EnrichedSpans.H3)
545
+ EnrichedSpans.H4 -> paragraphStyles?.toggleStyle(EnrichedSpans.H4)
546
+ EnrichedSpans.H5 -> paragraphStyles?.toggleStyle(EnrichedSpans.H5)
547
+ EnrichedSpans.H6 -> paragraphStyles?.toggleStyle(EnrichedSpans.H6)
442
548
  EnrichedSpans.CODE_BLOCK -> paragraphStyles?.toggleStyle(EnrichedSpans.CODE_BLOCK)
443
549
  EnrichedSpans.BLOCK_QUOTE -> paragraphStyles?.toggleStyle(EnrichedSpans.BLOCK_QUOTE)
444
550
  EnrichedSpans.ORDERED_LIST -> listStyles?.toggleStyle(EnrichedSpans.ORDERED_LIST)
445
551
  EnrichedSpans.UNORDERED_LIST -> listStyles?.toggleStyle(EnrichedSpans.UNORDERED_LIST)
552
+ EnrichedSpans.CHECKBOX_LIST -> listStyles?.toggleStyle(EnrichedSpans.CHECKBOX_LIST)
446
553
  else -> Log.w("EnrichedTextInputView", "Unknown style: $name")
447
554
  }
448
555
 
449
556
  layoutManager.invalidateLayout()
450
557
  }
451
558
 
452
- private fun removeStyle(name: String, start: Int, end: Int): Boolean {
453
- val removed = when (name) {
454
- EnrichedSpans.BOLD -> inlineStyles?.removeStyle(EnrichedSpans.BOLD, start, end)
455
- EnrichedSpans.ITALIC -> inlineStyles?.removeStyle(EnrichedSpans.ITALIC, start, end)
456
- EnrichedSpans.UNDERLINE -> inlineStyles?.removeStyle(EnrichedSpans.UNDERLINE, start, end)
457
- EnrichedSpans.STRIKETHROUGH -> inlineStyles?.removeStyle(EnrichedSpans.STRIKETHROUGH, start, end)
458
- EnrichedSpans.INLINE_CODE -> inlineStyles?.removeStyle(EnrichedSpans.INLINE_CODE, start, end)
459
- EnrichedSpans.H1 -> paragraphStyles?.removeStyle(EnrichedSpans.H1, start, end)
460
- EnrichedSpans.H2 -> paragraphStyles?.removeStyle(EnrichedSpans.H2, start, end)
461
- EnrichedSpans.H3 -> paragraphStyles?.removeStyle(EnrichedSpans.H3, start, end)
462
- EnrichedSpans.CODE_BLOCK -> paragraphStyles?.removeStyle(EnrichedSpans.CODE_BLOCK, start, end)
463
- EnrichedSpans.BLOCK_QUOTE -> paragraphStyles?.removeStyle(EnrichedSpans.BLOCK_QUOTE, start, end)
464
- EnrichedSpans.ORDERED_LIST -> listStyles?.removeStyle(EnrichedSpans.ORDERED_LIST, start, end)
465
- EnrichedSpans.UNORDERED_LIST -> listStyles?.removeStyle(EnrichedSpans.UNORDERED_LIST, start, end)
466
- EnrichedSpans.LINK -> parametrizedStyles?.removeStyle(EnrichedSpans.LINK, start, end)
467
- EnrichedSpans.IMAGE -> parametrizedStyles?.removeStyle(EnrichedSpans.IMAGE, start, end)
468
- EnrichedSpans.MENTION -> parametrizedStyles?.removeStyle(EnrichedSpans.MENTION, start, end)
469
- else -> false
470
- }
559
+ private fun removeStyle(
560
+ name: String,
561
+ start: Int,
562
+ end: Int,
563
+ ): Boolean {
564
+ val removed =
565
+ when (name) {
566
+ EnrichedSpans.BOLD -> inlineStyles?.removeStyle(EnrichedSpans.BOLD, start, end)
567
+ EnrichedSpans.ITALIC -> inlineStyles?.removeStyle(EnrichedSpans.ITALIC, start, end)
568
+ EnrichedSpans.UNDERLINE -> inlineStyles?.removeStyle(EnrichedSpans.UNDERLINE, start, end)
569
+ EnrichedSpans.STRIKETHROUGH -> inlineStyles?.removeStyle(EnrichedSpans.STRIKETHROUGH, start, end)
570
+ EnrichedSpans.INLINE_CODE -> inlineStyles?.removeStyle(EnrichedSpans.INLINE_CODE, start, end)
571
+ EnrichedSpans.H1 -> paragraphStyles?.removeStyle(EnrichedSpans.H1, start, end)
572
+ EnrichedSpans.H2 -> paragraphStyles?.removeStyle(EnrichedSpans.H2, start, end)
573
+ EnrichedSpans.H3 -> paragraphStyles?.removeStyle(EnrichedSpans.H3, start, end)
574
+ EnrichedSpans.H4 -> paragraphStyles?.removeStyle(EnrichedSpans.H4, start, end)
575
+ EnrichedSpans.H5 -> paragraphStyles?.removeStyle(EnrichedSpans.H5, start, end)
576
+ EnrichedSpans.H6 -> paragraphStyles?.removeStyle(EnrichedSpans.H6, start, end)
577
+ EnrichedSpans.CODE_BLOCK -> paragraphStyles?.removeStyle(EnrichedSpans.CODE_BLOCK, start, end)
578
+ EnrichedSpans.BLOCK_QUOTE -> paragraphStyles?.removeStyle(EnrichedSpans.BLOCK_QUOTE, start, end)
579
+ EnrichedSpans.ORDERED_LIST -> listStyles?.removeStyle(EnrichedSpans.ORDERED_LIST, start, end)
580
+ EnrichedSpans.UNORDERED_LIST -> listStyles?.removeStyle(EnrichedSpans.UNORDERED_LIST, start, end)
581
+ EnrichedSpans.CHECKBOX_LIST -> listStyles?.removeStyle(EnrichedSpans.CHECKBOX_LIST, start, end)
582
+ EnrichedSpans.LINK -> parametrizedStyles?.removeStyle(EnrichedSpans.LINK, start, end)
583
+ EnrichedSpans.IMAGE -> parametrizedStyles?.removeStyle(EnrichedSpans.IMAGE, start, end)
584
+ EnrichedSpans.MENTION -> parametrizedStyles?.removeStyle(EnrichedSpans.MENTION, start, end)
585
+ else -> false
586
+ }
471
587
 
472
588
  return removed == true
473
589
  }
474
590
 
475
591
  private fun getTargetRange(name: String): Pair<Int, Int> {
476
- val result = when (name) {
477
- EnrichedSpans.BOLD -> inlineStyles?.getStyleRange()
478
- EnrichedSpans.ITALIC -> inlineStyles?.getStyleRange()
479
- EnrichedSpans.UNDERLINE -> inlineStyles?.getStyleRange()
480
- EnrichedSpans.STRIKETHROUGH -> inlineStyles?.getStyleRange()
481
- EnrichedSpans.INLINE_CODE -> inlineStyles?.getStyleRange()
482
- EnrichedSpans.H1 -> paragraphStyles?.getStyleRange()
483
- EnrichedSpans.H2 -> paragraphStyles?.getStyleRange()
484
- EnrichedSpans.H3 -> paragraphStyles?.getStyleRange()
485
- EnrichedSpans.CODE_BLOCK -> paragraphStyles?.getStyleRange()
486
- EnrichedSpans.BLOCK_QUOTE -> paragraphStyles?.getStyleRange()
487
- EnrichedSpans.ORDERED_LIST -> listStyles?.getStyleRange()
488
- EnrichedSpans.UNORDERED_LIST -> listStyles?.getStyleRange()
489
- EnrichedSpans.LINK -> parametrizedStyles?.getStyleRange()
490
- EnrichedSpans.IMAGE -> parametrizedStyles?.getStyleRange()
491
- EnrichedSpans.MENTION -> parametrizedStyles?.getStyleRange()
492
- else -> Pair(0, 0)
493
- }
592
+ val result =
593
+ when (name) {
594
+ EnrichedSpans.BOLD -> inlineStyles?.getStyleRange()
595
+ EnrichedSpans.ITALIC -> inlineStyles?.getStyleRange()
596
+ EnrichedSpans.UNDERLINE -> inlineStyles?.getStyleRange()
597
+ EnrichedSpans.STRIKETHROUGH -> inlineStyles?.getStyleRange()
598
+ EnrichedSpans.INLINE_CODE -> inlineStyles?.getStyleRange()
599
+ EnrichedSpans.H1 -> paragraphStyles?.getStyleRange()
600
+ EnrichedSpans.H2 -> paragraphStyles?.getStyleRange()
601
+ EnrichedSpans.H3 -> paragraphStyles?.getStyleRange()
602
+ EnrichedSpans.H4 -> paragraphStyles?.getStyleRange()
603
+ EnrichedSpans.H5 -> paragraphStyles?.getStyleRange()
604
+ EnrichedSpans.H6 -> paragraphStyles?.getStyleRange()
605
+ EnrichedSpans.CODE_BLOCK -> paragraphStyles?.getStyleRange()
606
+ EnrichedSpans.BLOCK_QUOTE -> paragraphStyles?.getStyleRange()
607
+ EnrichedSpans.ORDERED_LIST -> listStyles?.getStyleRange()
608
+ EnrichedSpans.UNORDERED_LIST -> listStyles?.getStyleRange()
609
+ EnrichedSpans.CHECKBOX_LIST -> listStyles?.getStyleRange()
610
+ EnrichedSpans.LINK -> parametrizedStyles?.getStyleRange()
611
+ EnrichedSpans.IMAGE -> parametrizedStyles?.getStyleRange()
612
+ EnrichedSpans.MENTION -> parametrizedStyles?.getStyleRange()
613
+ else -> Pair(0, 0)
614
+ }
494
615
 
495
616
  return result ?: Pair(0, 0)
496
617
  }
@@ -524,11 +645,12 @@ class EnrichedTextInputView : AppCompatEditText {
524
645
 
525
646
  val lengthAfter = text?.length ?: 0
526
647
  val charactersRemoved = lengthBefore - lengthAfter
527
- val finalEnd = if (charactersRemoved > 0) {
528
- (end - charactersRemoved).coerceAtLeast(0)
529
- } else {
530
- end
531
- }
648
+ val finalEnd =
649
+ if (charactersRemoved > 0) {
650
+ (end - charactersRemoved).coerceAtLeast(0)
651
+ } else {
652
+ end
653
+ }
532
654
 
533
655
  val finalStart = start.coerceAtLeast(0).coerceAtMost(finalEnd)
534
656
  selection?.onSelection(finalStart, finalEnd)
@@ -537,12 +659,6 @@ class EnrichedTextInputView : AppCompatEditText {
537
659
  return true
538
660
  }
539
661
 
540
- private fun addSpanWatcher(watcher: EnrichedSpanWatcher) {
541
- val spannable = text as Spannable
542
- spannable.setSpan(watcher, 0, spannable.length, Spannable.SPAN_INCLUSIVE_INCLUSIVE)
543
- spanWatcher = watcher
544
- }
545
-
546
662
  fun verifyAndToggleStyle(name: String) {
547
663
  val isValid = verifyStyle(name)
548
664
  if (!isValid) return
@@ -550,14 +666,30 @@ class EnrichedTextInputView : AppCompatEditText {
550
666
  toggleStyle(name)
551
667
  }
552
668
 
553
- fun addLink(start: Int, end: Int, text: String, url: String) {
669
+ fun toggleCheckboxListItem(checked: Boolean) {
670
+ val isValid = verifyStyle(EnrichedSpans.CHECKBOX_LIST)
671
+ if (!isValid) return
672
+
673
+ listStyles?.toggleCheckboxListStyle(checked)
674
+ }
675
+
676
+ fun addLink(
677
+ start: Int,
678
+ end: Int,
679
+ text: String,
680
+ url: String,
681
+ ) {
554
682
  val isValid = verifyStyle(EnrichedSpans.LINK)
555
683
  if (!isValid) return
556
684
 
557
685
  parametrizedStyles?.setLinkSpan(start, end, text, url)
558
686
  }
559
687
 
560
- fun addImage(src: String, width: Float, height: Float) {
688
+ fun addImage(
689
+ src: String,
690
+ width: Float,
691
+ height: Float,
692
+ ) {
561
693
  val isValid = verifyStyle(EnrichedSpans.IMAGE)
562
694
  if (!isValid) return
563
695
 
@@ -572,7 +704,11 @@ class EnrichedTextInputView : AppCompatEditText {
572
704
  parametrizedStyles?.startMention(indicator)
573
705
  }
574
706
 
575
- fun addMention(indicator: String, text: String, attributes: Map<String, String>) {
707
+ fun addMention(
708
+ indicator: String,
709
+ text: String,
710
+ attributes: Map<String, String>,
711
+ ) {
576
712
  val isValid = verifyStyle(EnrichedSpans.MENTION)
577
713
  if (!isValid) return
578
714
 
@@ -580,11 +716,12 @@ class EnrichedTextInputView : AppCompatEditText {
580
716
  }
581
717
 
582
718
  fun requestHTML(requestId: Int) {
583
- val html = try {
584
- EnrichedParser.toHtmlWithDefault(text)
585
- } catch (e: Exception) {
586
- null
587
- }
719
+ val html =
720
+ try {
721
+ EnrichedParser.toHtmlWithDefault(text)
722
+ } catch (e: Exception) {
723
+ null
724
+ }
588
725
 
589
726
  val reactContext = context as ReactContext
590
727
  val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
@@ -604,45 +741,69 @@ class EnrichedTextInputView : AppCompatEditText {
604
741
  }
605
742
  }
606
743
 
607
- private fun forceScrollToSelection() {
608
- val textLayout = layout ?: return
609
- val cursorOffset = selectionStart
610
- if (cursorOffset <= 0) return
744
+ private fun forceScrollToSelection() {
745
+ val textLayout = layout ?: return
746
+ val cursorOffset = selectionStart
747
+ if (cursorOffset <= 0) return
611
748
 
612
- val selectedLineIndex = textLayout.getLineForOffset(cursorOffset)
613
- val selectedLineTop = textLayout.getLineTop(selectedLineIndex)
614
- val selectedLineBottom = textLayout.getLineBottom(selectedLineIndex)
615
- val visibleTextHeight = height - paddingTop - paddingBottom
749
+ val selectedLineIndex = textLayout.getLineForOffset(cursorOffset)
750
+ val selectedLineTop = textLayout.getLineTop(selectedLineIndex)
751
+ val selectedLineBottom = textLayout.getLineBottom(selectedLineIndex)
752
+ val visibleTextHeight = height - paddingTop - paddingBottom
616
753
 
617
- if (visibleTextHeight <= 0) return
754
+ if (visibleTextHeight <= 0) return
618
755
 
619
- val visibleTop = scrollY
620
- val visibleBottom = scrollY + visibleTextHeight
621
- var targetScrollY = scrollY
756
+ val visibleTop = scrollY
757
+ val visibleBottom = scrollY + visibleTextHeight
758
+ var targetScrollY = scrollY
622
759
 
623
- if (selectedLineTop < visibleTop) {
624
- targetScrollY = selectedLineTop
625
- } else if (selectedLineBottom > visibleBottom) {
626
- targetScrollY = selectedLineBottom - visibleTextHeight
627
- }
760
+ if (selectedLineTop < visibleTop) {
761
+ targetScrollY = selectedLineTop
762
+ } else if (selectedLineBottom > visibleBottom) {
763
+ targetScrollY = selectedLineBottom - visibleTextHeight
764
+ }
628
765
 
629
- val maxScrollY = (textLayout.height - visibleTextHeight).coerceAtLeast(0)
630
- targetScrollY = targetScrollY.coerceIn(0, maxScrollY)
631
- scrollTo(scrollX, targetScrollY)
766
+ val maxScrollY = (textLayout.height - visibleTextHeight).coerceAtLeast(0)
767
+ targetScrollY = targetScrollY.coerceIn(0, maxScrollY)
768
+ scrollTo(scrollX, targetScrollY)
769
+ }
770
+
771
+ private fun isHeadingBold(
772
+ style: HtmlStyle,
773
+ span: EnrichedInputSpan,
774
+ ): Boolean =
775
+ when (span) {
776
+ is EnrichedInputH1Span -> style.h1Bold
777
+ is EnrichedInputH2Span -> style.h2Bold
778
+ is EnrichedInputH3Span -> style.h3Bold
779
+ is EnrichedInputH4Span -> style.h4Bold
780
+ is EnrichedInputH5Span -> style.h5Bold
781
+ is EnrichedInputH6Span -> style.h6Bold
782
+ else -> false
632
783
  }
633
784
 
634
- private fun reApplyHtmlStyleForSpans(previousHtmlStyle: HtmlStyle, nextHtmlStyle: HtmlStyle) {
635
- val shouldRemoveBoldSpanFromH1Span = !previousHtmlStyle.h1Bold && nextHtmlStyle.h1Bold
636
- val shouldRemoveBoldSpanFromH2Span = !previousHtmlStyle.h2Bold && nextHtmlStyle.h2Bold
637
- val shouldRemoveBoldSpanFromH3Span = !previousHtmlStyle.h3Bold && nextHtmlStyle.h3Bold
785
+ private fun shouldRemoveBoldFromHeading(
786
+ span: EnrichedInputSpan,
787
+ prevStyle: HtmlStyle,
788
+ nextStyle: HtmlStyle,
789
+ ): Boolean {
790
+ val wasBold = isHeadingBold(prevStyle, span)
791
+ val isNowBold = isHeadingBold(nextStyle, span)
638
792
 
793
+ return !wasBold && isNowBold
794
+ }
795
+
796
+ private fun reApplyHtmlStyleForSpans(
797
+ previousHtmlStyle: HtmlStyle,
798
+ nextHtmlStyle: HtmlStyle,
799
+ ) {
639
800
  val spannable = text as? Spannable ?: return
640
801
  if (spannable.isEmpty()) return
641
802
 
642
803
  var shouldEmitStateChange = false
643
804
 
644
805
  runAsATransaction {
645
- val spans = spannable.getSpans(0, spannable.length, EnrichedSpan::class.java)
806
+ val spans = spannable.getSpans(0, spannable.length, EnrichedInputSpan::class.java)
646
807
  for (span in spans) {
647
808
  if (!span.dependsOnHtmlStyle) continue
648
809
 
@@ -652,7 +813,8 @@ class EnrichedTextInputView : AppCompatEditText {
652
813
 
653
814
  if (start == -1 || end == -1) continue
654
815
 
655
- if ((span is EnrichedH1Span && shouldRemoveBoldSpanFromH1Span) || (span is EnrichedH2Span && shouldRemoveBoldSpanFromH2Span) || (span is EnrichedH3Span && shouldRemoveBoldSpanFromH3Span)) {
816
+ // Check if we need to remove explicit bold spans
817
+ if (shouldRemoveBoldFromHeading(span, previousHtmlStyle, nextHtmlStyle)) {
656
818
  val isRemoved = removeStyle(EnrichedSpans.BOLD, start, end)
657
819
  if (isRemoved) shouldEmitStateChange = true
658
820
  }
@@ -673,6 +835,9 @@ class EnrichedTextInputView : AppCompatEditText {
673
835
  override fun onAttachedToWindow() {
674
836
  super.onAttachedToWindow()
675
837
 
838
+ // https://github.com/facebook/react-native/blob/36df97f500aa0aa8031098caf7526db358b6ddc1/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.kt#L946
839
+ super.setTextIsSelectable(true)
840
+
676
841
  if (autoFocus && !didAttachToWindow) {
677
842
  requestFocusProgrammatically()
678
843
  }