@fendent/react-native-enriched 0.5.2-fork.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (278) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +343 -0
  3. package/ReactNativeEnriched.podspec +31 -0
  4. package/android/build.gradle +106 -0
  5. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerDelegate.java +197 -0
  6. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerInterface.java +72 -0
  7. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/ComponentDescriptors.cpp +22 -0
  8. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/ComponentDescriptors.h +24 -0
  9. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/EventEmitters.cpp +434 -0
  10. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/EventEmitters.h +391 -0
  11. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/Props.cpp +173 -0
  12. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/Props.h +833 -0
  13. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/ShadowNodes.cpp +17 -0
  14. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/ShadowNodes.h +23 -0
  15. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/States.cpp +16 -0
  16. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/States.h +20 -0
  17. package/android/gradle.properties +5 -0
  18. package/android/lint.gradle +70 -0
  19. package/android/src/main/AndroidManifest.xml +3 -0
  20. package/android/src/main/AndroidManifestNew.xml +2 -0
  21. package/android/src/main/java/com/swmansion/enriched/ReactNativeEnrichedPackage.kt +20 -0
  22. package/android/src/main/java/com/swmansion/enriched/common/AsyncDrawable.kt +126 -0
  23. package/android/src/main/java/com/swmansion/enriched/common/CheckboxDrawable.kt +81 -0
  24. package/android/src/main/java/com/swmansion/enriched/common/EnrichedConstants.kt +11 -0
  25. package/android/src/main/java/com/swmansion/enriched/common/EnrichedStyle.kt +57 -0
  26. package/android/src/main/java/com/swmansion/enriched/common/ForceRedrawSpan.kt +14 -0
  27. package/android/src/main/java/com/swmansion/enriched/common/GumboNormalizer.kt +5 -0
  28. package/android/src/main/java/com/swmansion/enriched/common/MentionStyle.kt +7 -0
  29. package/android/src/main/java/com/swmansion/enriched/common/ResourceManager.kt +26 -0
  30. package/android/src/main/java/com/swmansion/enriched/common/parser/EnrichedParser.java +956 -0
  31. package/android/src/main/java/com/swmansion/enriched/common/parser/EnrichedSpanFactory.kt +79 -0
  32. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedBlockQuoteSpan.kt +53 -0
  33. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedBoldSpan.kt +12 -0
  34. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedCheckboxListSpan.kt +92 -0
  35. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedCodeBlockSpan.kt +81 -0
  36. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH1Span.kt +20 -0
  37. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH2Span.kt +20 -0
  38. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH3Span.kt +20 -0
  39. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH4Span.kt +21 -0
  40. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH5Span.kt +20 -0
  41. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH6Span.kt +20 -0
  42. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedImageSpan.kt +184 -0
  43. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedInlineCodeSpan.kt +24 -0
  44. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedItalicSpan.kt +12 -0
  45. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedLinkSpan.kt +29 -0
  46. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedMentionSpan.kt +35 -0
  47. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedOrderedListSpan.kt +79 -0
  48. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedStrikeThroughSpan.kt +11 -0
  49. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedUnderlineSpan.kt +11 -0
  50. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedUnorderedListSpan.kt +62 -0
  51. package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedBlockSpan.kt +5 -0
  52. package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedHeadingSpan.kt +3 -0
  53. package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedInlineSpan.kt +3 -0
  54. package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedParagraphSpan.kt +5 -0
  55. package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedSpan.kt +3 -0
  56. package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedZeroWidthSpaceSpan.kt +4 -0
  57. package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputConnectionWrapper.kt +140 -0
  58. package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputSpannableFactory.kt +83 -0
  59. package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputView.kt +1120 -0
  60. package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputViewLayoutManager.kt +27 -0
  61. package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputViewManager.kt +478 -0
  62. package/android/src/main/java/com/swmansion/enriched/textinput/MeasurementStore.kt +225 -0
  63. package/android/src/main/java/com/swmansion/enriched/textinput/events/MentionHandler.kt +55 -0
  64. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnChangeHtmlEvent.kt +27 -0
  65. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnChangeSelectionEvent.kt +30 -0
  66. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnChangeStateEvent.kt +21 -0
  67. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnChangeTextEvent.kt +30 -0
  68. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnContextMenuItemPressEvent.kt +35 -0
  69. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnInputBlurEvent.kt +25 -0
  70. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnInputFocusEvent.kt +25 -0
  71. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnInputKeyPressEvent.kt +27 -0
  72. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnLinkDetectedEvent.kt +32 -0
  73. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnMentionDetectedEvent.kt +30 -0
  74. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnMentionEvent.kt +34 -0
  75. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnPasteImagesEvent.kt +47 -0
  76. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnRequestHtmlResultEvent.kt +32 -0
  77. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnSubmitEditingEvent.kt +29 -0
  78. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputBlockQuoteSpan.kt +14 -0
  79. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputBoldSpan.kt +14 -0
  80. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputCheckboxListSpan.kt +15 -0
  81. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputCodeBlockSpan.kt +14 -0
  82. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH1Span.kt +14 -0
  83. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH2Span.kt +14 -0
  84. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH3Span.kt +14 -0
  85. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH4Span.kt +14 -0
  86. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH5Span.kt +14 -0
  87. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH6Span.kt +14 -0
  88. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputImageSpan.kt +36 -0
  89. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputInlineCodeSpan.kt +14 -0
  90. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputItalicSpan.kt +14 -0
  91. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputLinkSpan.kt +16 -0
  92. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputMentionSpan.kt +18 -0
  93. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputOrderedListSpan.kt +21 -0
  94. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputStrikeThroughSpan.kt +14 -0
  95. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputUnderlineSpan.kt +14 -0
  96. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputUnorderedListSpan.kt +14 -0
  97. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedLineHeightSpan.kt +44 -0
  98. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedSpans.kt +241 -0
  99. package/android/src/main/java/com/swmansion/enriched/textinput/spans/interfaces/EnrichedInputSpan.kt +10 -0
  100. package/android/src/main/java/com/swmansion/enriched/textinput/styles/HtmlStyle.kt +372 -0
  101. package/android/src/main/java/com/swmansion/enriched/textinput/styles/InlineStyles.kt +164 -0
  102. package/android/src/main/java/com/swmansion/enriched/textinput/styles/ListStyles.kt +263 -0
  103. package/android/src/main/java/com/swmansion/enriched/textinput/styles/ParagraphStyles.kt +434 -0
  104. package/android/src/main/java/com/swmansion/enriched/textinput/styles/ParametrizedStyles.kt +394 -0
  105. package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedEditableFactory.kt +17 -0
  106. package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSelection.kt +320 -0
  107. package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSpanState.kt +310 -0
  108. package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSpannable.kt +106 -0
  109. package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSpannableStringBuilder.kt +24 -0
  110. package/android/src/main/java/com/swmansion/enriched/textinput/utils/RichContentReceiver.kt +127 -0
  111. package/android/src/main/java/com/swmansion/enriched/textinput/utils/Utils.kt +106 -0
  112. package/android/src/main/java/com/swmansion/enriched/textinput/watchers/EnrichedSpanWatcher.kt +107 -0
  113. package/android/src/main/java/com/swmansion/enriched/textinput/watchers/EnrichedTextWatcher.kt +74 -0
  114. package/android/src/main/new_arch/CMakeLists.txt +62 -0
  115. package/android/src/main/new_arch/GumboNormalizerJni.cpp +14 -0
  116. package/android/src/main/new_arch/ReactNativeEnrichedSpec.cpp +11 -0
  117. package/android/src/main/new_arch/ReactNativeEnrichedSpec.h +15 -0
  118. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputComponentDescriptor.h +35 -0
  119. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputMeasurementManager.cpp +53 -0
  120. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputMeasurementManager.h +25 -0
  121. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputShadowNode.cpp +35 -0
  122. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputShadowNode.h +53 -0
  123. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputState.cpp +9 -0
  124. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputState.h +24 -0
  125. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/conversions.h +27 -0
  126. package/android/src/main/res/drawable/broken_image.xml +10 -0
  127. package/cpp/CMakeLists.txt +50 -0
  128. package/cpp/GumboParser/GumboParser.h +34043 -0
  129. package/cpp/README.md +59 -0
  130. package/cpp/parser/GumboNormalizer.c +915 -0
  131. package/cpp/parser/GumboParser.cpp +16 -0
  132. package/cpp/parser/GumboParser.hpp +23 -0
  133. package/cpp/tests/GumboParserTest.cpp +457 -0
  134. package/ios/EnrichedTextInputView.h +53 -0
  135. package/ios/EnrichedTextInputView.mm +2360 -0
  136. package/ios/EnrichedTextInputViewManager.mm +13 -0
  137. package/ios/attributesManager/AttributesManager.h +17 -0
  138. package/ios/attributesManager/AttributesManager.mm +195 -0
  139. package/ios/config/InputConfig.h +104 -0
  140. package/ios/config/InputConfig.mm +664 -0
  141. package/ios/extensions/ColorExtension.h +7 -0
  142. package/ios/extensions/ColorExtension.mm +38 -0
  143. package/ios/extensions/FontExtension.h +11 -0
  144. package/ios/extensions/FontExtension.mm +72 -0
  145. package/ios/extensions/ImageExtension.h +34 -0
  146. package/ios/extensions/ImageExtension.mm +165 -0
  147. package/ios/extensions/LayoutManagerExtension.h +6 -0
  148. package/ios/extensions/LayoutManagerExtension.mm +443 -0
  149. package/ios/extensions/StringExtension.h +15 -0
  150. package/ios/extensions/StringExtension.mm +69 -0
  151. package/ios/generated/ReactNativeEnrichedSpec/ComponentDescriptors.cpp +22 -0
  152. package/ios/generated/ReactNativeEnrichedSpec/ComponentDescriptors.h +24 -0
  153. package/ios/generated/ReactNativeEnrichedSpec/EventEmitters.cpp +434 -0
  154. package/ios/generated/ReactNativeEnrichedSpec/EventEmitters.h +391 -0
  155. package/ios/generated/ReactNativeEnrichedSpec/Props.cpp +173 -0
  156. package/ios/generated/ReactNativeEnrichedSpec/Props.h +833 -0
  157. package/ios/generated/ReactNativeEnrichedSpec/RCTComponentViewHelpers.h +582 -0
  158. package/ios/generated/ReactNativeEnrichedSpec/ShadowNodes.cpp +17 -0
  159. package/ios/generated/ReactNativeEnrichedSpec/ShadowNodes.h +23 -0
  160. package/ios/generated/ReactNativeEnrichedSpec/States.cpp +16 -0
  161. package/ios/generated/ReactNativeEnrichedSpec/States.h +20 -0
  162. package/ios/inputParser/InputParser.h +11 -0
  163. package/ios/inputParser/InputParser.mm +1463 -0
  164. package/ios/inputTextView/InputTextView.h +6 -0
  165. package/ios/inputTextView/InputTextView.mm +285 -0
  166. package/ios/interfaces/AttributeEntry.h +9 -0
  167. package/ios/interfaces/AttributeEntry.mm +4 -0
  168. package/ios/interfaces/BaseStyleProtocol.h +17 -0
  169. package/ios/interfaces/ImageAttachment.h +11 -0
  170. package/ios/interfaces/ImageAttachment.mm +107 -0
  171. package/ios/interfaces/ImageData.h +10 -0
  172. package/ios/interfaces/ImageData.mm +4 -0
  173. package/ios/interfaces/LinkData.h +11 -0
  174. package/ios/interfaces/LinkData.mm +29 -0
  175. package/ios/interfaces/LinkRegexConfig.h +19 -0
  176. package/ios/interfaces/LinkRegexConfig.mm +37 -0
  177. package/ios/interfaces/MediaAttachment.h +23 -0
  178. package/ios/interfaces/MediaAttachment.mm +31 -0
  179. package/ios/interfaces/MentionParams.h +8 -0
  180. package/ios/interfaces/MentionParams.mm +4 -0
  181. package/ios/interfaces/MentionStyleProps.h +13 -0
  182. package/ios/interfaces/MentionStyleProps.mm +63 -0
  183. package/ios/interfaces/StyleBase.h +36 -0
  184. package/ios/interfaces/StyleBase.mm +256 -0
  185. package/ios/interfaces/StyleHeaders.h +102 -0
  186. package/ios/interfaces/StylePair.h +9 -0
  187. package/ios/interfaces/StylePair.mm +4 -0
  188. package/ios/interfaces/StyleTypeEnum.h +26 -0
  189. package/ios/interfaces/TextDecorationLineEnum.h +6 -0
  190. package/ios/interfaces/TextDecorationLineEnum.mm +4 -0
  191. package/ios/internals/EnrichedTextInputViewComponentDescriptor.h +19 -0
  192. package/ios/internals/EnrichedTextInputViewShadowNode.h +44 -0
  193. package/ios/internals/EnrichedTextInputViewShadowNode.mm +103 -0
  194. package/ios/internals/EnrichedTextInputViewState.cpp +10 -0
  195. package/ios/internals/EnrichedTextInputViewState.h +22 -0
  196. package/ios/styles/BlockQuoteStyle.mm +55 -0
  197. package/ios/styles/BoldStyle.mm +37 -0
  198. package/ios/styles/CheckboxListStyle.mm +153 -0
  199. package/ios/styles/CodeBlockStyle.mm +49 -0
  200. package/ios/styles/H1Style.mm +20 -0
  201. package/ios/styles/H2Style.mm +20 -0
  202. package/ios/styles/H3Style.mm +20 -0
  203. package/ios/styles/H4Style.mm +20 -0
  204. package/ios/styles/H5Style.mm +20 -0
  205. package/ios/styles/H6Style.mm +20 -0
  206. package/ios/styles/HeadingStyleBase.mm +65 -0
  207. package/ios/styles/ImageStyle.mm +146 -0
  208. package/ios/styles/InlineCodeStyle.mm +65 -0
  209. package/ios/styles/ItalicStyle.mm +37 -0
  210. package/ios/styles/LinkStyle.mm +532 -0
  211. package/ios/styles/MentionStyle.mm +538 -0
  212. package/ios/styles/OrderedListStyle.mm +86 -0
  213. package/ios/styles/StrikethroughStyle.mm +25 -0
  214. package/ios/styles/UnderlineStyle.mm +24 -0
  215. package/ios/styles/UnorderedListStyle.mm +86 -0
  216. package/ios/utils/CheckboxHitTestUtils.h +10 -0
  217. package/ios/utils/CheckboxHitTestUtils.mm +122 -0
  218. package/ios/utils/DotReplacementUtils.h +10 -0
  219. package/ios/utils/DotReplacementUtils.mm +68 -0
  220. package/ios/utils/KeyboardUtils.h +7 -0
  221. package/ios/utils/KeyboardUtils.mm +31 -0
  222. package/ios/utils/OccurenceUtils.h +44 -0
  223. package/ios/utils/OccurenceUtils.mm +179 -0
  224. package/ios/utils/ParagraphAttributesUtils.h +15 -0
  225. package/ios/utils/ParagraphAttributesUtils.mm +257 -0
  226. package/ios/utils/RangeUtils.h +12 -0
  227. package/ios/utils/RangeUtils.mm +183 -0
  228. package/ios/utils/TextBlockTapGestureRecognizer.h +17 -0
  229. package/ios/utils/TextBlockTapGestureRecognizer.mm +56 -0
  230. package/ios/utils/TextInsertionUtils.h +17 -0
  231. package/ios/utils/TextInsertionUtils.mm +64 -0
  232. package/ios/utils/WordsUtils.h +7 -0
  233. package/ios/utils/WordsUtils.mm +98 -0
  234. package/ios/utils/ZeroWidthSpaceUtils.h +9 -0
  235. package/ios/utils/ZeroWidthSpaceUtils.mm +270 -0
  236. package/lib/module/index.js +4 -0
  237. package/lib/module/index.js.map +1 -0
  238. package/lib/module/native/EnrichedTextInput.js +304 -0
  239. package/lib/module/native/EnrichedTextInput.js.map +1 -0
  240. package/lib/module/package.json +1 -0
  241. package/lib/module/spec/EnrichedTextInputNativeComponent.ts +517 -0
  242. package/lib/module/types.js +4 -0
  243. package/lib/module/types.js.map +1 -0
  244. package/lib/module/utils/EnrichedTextInputDefaultProps.js +12 -0
  245. package/lib/module/utils/EnrichedTextInputDefaultProps.js.map +1 -0
  246. package/lib/module/utils/normalizeHtmlStyle.js +155 -0
  247. package/lib/module/utils/normalizeHtmlStyle.js.map +1 -0
  248. package/lib/module/utils/nullthrows.js +9 -0
  249. package/lib/module/utils/nullthrows.js.map +1 -0
  250. package/lib/module/utils/regexParser.js +46 -0
  251. package/lib/module/utils/regexParser.js.map +1 -0
  252. package/lib/typescript/package.json +1 -0
  253. package/lib/typescript/src/index.d.ts +3 -0
  254. package/lib/typescript/src/index.d.ts.map +1 -0
  255. package/lib/typescript/src/native/EnrichedTextInput.d.ts +3 -0
  256. package/lib/typescript/src/native/EnrichedTextInput.d.ts.map +1 -0
  257. package/lib/typescript/src/spec/EnrichedTextInputNativeComponent.d.ts +397 -0
  258. package/lib/typescript/src/spec/EnrichedTextInputNativeComponent.d.ts.map +1 -0
  259. package/lib/typescript/src/types.d.ts +447 -0
  260. package/lib/typescript/src/types.d.ts.map +1 -0
  261. package/lib/typescript/src/utils/EnrichedTextInputDefaultProps.d.ts +10 -0
  262. package/lib/typescript/src/utils/EnrichedTextInputDefaultProps.d.ts.map +1 -0
  263. package/lib/typescript/src/utils/normalizeHtmlStyle.d.ts +4 -0
  264. package/lib/typescript/src/utils/normalizeHtmlStyle.d.ts.map +1 -0
  265. package/lib/typescript/src/utils/nullthrows.d.ts +2 -0
  266. package/lib/typescript/src/utils/nullthrows.d.ts.map +1 -0
  267. package/lib/typescript/src/utils/regexParser.d.ts +3 -0
  268. package/lib/typescript/src/utils/regexParser.d.ts.map +1 -0
  269. package/package.json +226 -0
  270. package/react-native.config.js +13 -0
  271. package/src/index.tsx +20 -0
  272. package/src/native/EnrichedTextInput.tsx +370 -0
  273. package/src/spec/EnrichedTextInputNativeComponent.ts +517 -0
  274. package/src/types.ts +499 -0
  275. package/src/utils/EnrichedTextInputDefaultProps.ts +9 -0
  276. package/src/utils/normalizeHtmlStyle.ts +199 -0
  277. package/src/utils/nullthrows.ts +7 -0
  278. package/src/utils/regexParser.ts +56 -0
@@ -0,0 +1,263 @@
1
+ package com.swmansion.enriched.textinput.styles
2
+
3
+ import android.text.Editable
4
+ import android.text.Spannable
5
+ import android.text.SpannableStringBuilder
6
+ import android.text.Spanned
7
+ import com.swmansion.enriched.common.EnrichedConstants
8
+ import com.swmansion.enriched.textinput.EnrichedTextInputView
9
+ import com.swmansion.enriched.textinput.spans.EnrichedInputCheckboxListSpan
10
+ import com.swmansion.enriched.textinput.spans.EnrichedInputOrderedListSpan
11
+ import com.swmansion.enriched.textinput.spans.EnrichedInputUnorderedListSpan
12
+ import com.swmansion.enriched.textinput.spans.EnrichedSpans
13
+ import com.swmansion.enriched.textinput.utils.getParagraphBounds
14
+ import com.swmansion.enriched.textinput.utils.getSafeSpanBoundaries
15
+ import com.swmansion.enriched.textinput.utils.removeZWS
16
+
17
+ class ListStyles(
18
+ private val view: EnrichedTextInputView,
19
+ ) {
20
+ private fun <T> getPreviousParagraphSpan(
21
+ spannable: Spannable,
22
+ s: Int,
23
+ type: Class<T>,
24
+ ): T? {
25
+ if (s <= 0) return null
26
+
27
+ val (previousParagraphStart, previousParagraphEnd) = spannable.getParagraphBounds(s - 1)
28
+ val spans = spannable.getSpans(previousParagraphStart, previousParagraphEnd, type)
29
+
30
+ if (spans.isNotEmpty()) {
31
+ return spans.last()
32
+ }
33
+
34
+ return null
35
+ }
36
+
37
+ private fun <T> isPreviousParagraphList(
38
+ spannable: Spannable,
39
+ s: Int,
40
+ type: Class<T>,
41
+ ): Boolean {
42
+ val previousSpan = getPreviousParagraphSpan(spannable, s, type)
43
+
44
+ return previousSpan != null
45
+ }
46
+
47
+ private fun getOrderedListIndex(
48
+ spannable: Spannable,
49
+ s: Int,
50
+ ): Int {
51
+ val span = getPreviousParagraphSpan(spannable, s, EnrichedInputOrderedListSpan::class.java)
52
+ val index = span?.getListIndex() ?: 0
53
+ return index + 1
54
+ }
55
+
56
+ private fun setSpan(
57
+ spannable: Spannable,
58
+ name: String,
59
+ start: Int,
60
+ end: Int,
61
+ isChecked: Boolean? = false,
62
+ ) {
63
+ val (safeStart, safeEnd) = spannable.getSafeSpanBoundaries(start, end)
64
+
65
+ when (name) {
66
+ EnrichedSpans.UNORDERED_LIST -> {
67
+ val span = EnrichedInputUnorderedListSpan(view.htmlStyle)
68
+ spannable.setSpan(span, safeStart, safeEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
69
+ }
70
+
71
+ EnrichedSpans.ORDERED_LIST -> {
72
+ val index = getOrderedListIndex(spannable, safeStart)
73
+ val span = EnrichedInputOrderedListSpan(index, view.htmlStyle)
74
+ spannable.setSpan(span, safeStart, safeEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
75
+ }
76
+
77
+ EnrichedSpans.CHECKBOX_LIST -> {
78
+ val span = EnrichedInputCheckboxListSpan(isChecked ?: false, view.htmlStyle)
79
+ spannable.setSpan(span, safeStart, safeEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
80
+
81
+ // Invalidate layout to update checkbox drawing in case checkbox is bigger than line height
82
+ view.layoutManager.invalidateLayout()
83
+ }
84
+ }
85
+ }
86
+
87
+ private fun <T> removeSpansForRange(
88
+ spannable: Spannable,
89
+ start: Int,
90
+ end: Int,
91
+ clazz: Class<T>,
92
+ ): Boolean {
93
+ val ssb = spannable as SpannableStringBuilder
94
+ val spans = ssb.getSpans(start, end, clazz)
95
+ if (spans.isEmpty()) return false
96
+
97
+ for (span in spans) {
98
+ ssb.removeSpan(span)
99
+ }
100
+
101
+ ssb.removeZWS(start, end)
102
+ return true
103
+ }
104
+
105
+ fun updateOrderedListIndexes(
106
+ text: Spannable,
107
+ position: Int,
108
+ ) {
109
+ val spans = text.getSpans(position + 1, text.length, EnrichedInputOrderedListSpan::class.java)
110
+ val sortedSpans = spans.sortedBy { text.getSpanStart(it) }
111
+ for (span in sortedSpans) {
112
+ val spanStart = text.getSpanStart(span)
113
+ val index = getOrderedListIndex(text, spanStart)
114
+ span.setListIndex(index)
115
+ }
116
+ }
117
+
118
+ private fun toggleStyle(
119
+ name: String,
120
+ checkboxState: Boolean?,
121
+ ) {
122
+ if (view.selection == null) return
123
+ val config = EnrichedSpans.listSpans[name] ?: return
124
+ val spannable = view.text as SpannableStringBuilder
125
+ val (start, end) = view.selection.getParagraphSelection()
126
+ val styleStart = view.spanState?.getStart(name)
127
+
128
+ if (styleStart != null) {
129
+ view.spanState.setStart(name, null)
130
+ removeSpansForRange(spannable, start, end, config.clazz)
131
+ view.selection.validateStyles()
132
+
133
+ return
134
+ }
135
+
136
+ if (start == end) {
137
+ spannable.insert(start, EnrichedConstants.ZWS_STRING)
138
+ view.spanState?.setStart(name, start + 1)
139
+ removeSpansForRange(spannable, start, end, config.clazz)
140
+ setSpan(spannable, name, start, end + 1, checkboxState)
141
+
142
+ return
143
+ }
144
+
145
+ var currentStart = start
146
+ val paragraphs = spannable.substring(start, end).split("\n")
147
+ removeSpansForRange(spannable, start, end, config.clazz)
148
+
149
+ for (paragraph in paragraphs) {
150
+ spannable.insert(currentStart, EnrichedConstants.ZWS_STRING)
151
+ val currentEnd = currentStart + paragraph.length + 1
152
+ setSpan(spannable, name, currentStart, currentEnd, checkboxState)
153
+
154
+ currentStart = currentEnd + 1
155
+ }
156
+
157
+ view.spanState?.setStart(name, currentStart)
158
+ }
159
+
160
+ fun toggleStyle(name: String) {
161
+ toggleStyle(name, false)
162
+ }
163
+
164
+ fun toggleCheckboxListStyle(checked: Boolean) {
165
+ toggleStyle(EnrichedSpans.CHECKBOX_LIST, checked)
166
+ }
167
+
168
+ private fun handleAfterTextChanged(
169
+ s: Editable,
170
+ name: String,
171
+ endCursorPosition: Int,
172
+ previousTextLength: Int,
173
+ ) {
174
+ val config = EnrichedSpans.listSpans[name] ?: return
175
+ val cursorPosition = endCursorPosition.coerceAtMost(s.length)
176
+ val (start, end) = s.getParagraphBounds(cursorPosition)
177
+
178
+ val isBackspace = previousTextLength > s.length
179
+ val isNewLine = cursorPosition > 0 && s[cursorPosition - 1] == '\n'
180
+ val isShortcut = config.shortcut?.let { s.substring(start, end).startsWith(it) } ?: false
181
+ val spans = s.getSpans(start, end, config.clazz)
182
+
183
+ // Remove spans if cursor is at the start of the paragraph and spans exist
184
+ if (isBackspace && start == cursorPosition && spans.isNotEmpty()) {
185
+ removeSpansForRange(s, start, end, config.clazz)
186
+ return
187
+ }
188
+
189
+ if (!isBackspace && isShortcut) {
190
+ s.replace(start, cursorPosition, EnrichedConstants.ZWS_STRING)
191
+ setSpan(s, name, start, start + 1)
192
+ // Inform that new span has been added
193
+ view.selection?.validateStyles()
194
+ return
195
+ }
196
+
197
+ if (!isBackspace && isNewLine && isPreviousParagraphList(s, start, config.clazz)) {
198
+ // Check if the span from the previous line "leaked" into this one
199
+ if (spans.isNotEmpty()) {
200
+ val existingSpan = spans[0]
201
+ val spanStart = s.getSpanStart(existingSpan)
202
+
203
+ // If the span started before the current paragraph (belongs to the previous item)
204
+ // update it to end at the newline (start - 1)
205
+ if (spanStart < start) {
206
+ val spanFlags = s.getSpanFlags(existingSpan)
207
+ s.setSpan(existingSpan, spanStart, start - 1, spanFlags)
208
+ }
209
+ }
210
+
211
+ s.insert(cursorPosition, EnrichedConstants.ZWS_STRING)
212
+ setSpan(s, name, start, end + 1)
213
+ // Inform that new span has been added
214
+ view.selection?.validateStyles()
215
+ return
216
+ }
217
+
218
+ if (name === EnrichedSpans.CHECKBOX_LIST) {
219
+ if (spans.isNotEmpty()) {
220
+ val previousSpan = spans[0] as EnrichedInputCheckboxListSpan
221
+ val isChecked = previousSpan.isChecked
222
+
223
+ for (span in spans) {
224
+ s.removeSpan(span)
225
+ }
226
+
227
+ setSpan(s, EnrichedSpans.CHECKBOX_LIST, start, end, isChecked)
228
+ }
229
+
230
+ return
231
+ }
232
+
233
+ if (spans.isNotEmpty()) {
234
+ for (span in spans) {
235
+ s.removeSpan(span)
236
+ }
237
+
238
+ setSpan(s, name, start, end)
239
+ }
240
+ }
241
+
242
+ fun afterTextChanged(
243
+ s: Editable,
244
+ endCursorPosition: Int,
245
+ previousTextLength: Int,
246
+ ) {
247
+ handleAfterTextChanged(s, EnrichedSpans.ORDERED_LIST, endCursorPosition, previousTextLength)
248
+ handleAfterTextChanged(s, EnrichedSpans.UNORDERED_LIST, endCursorPosition, previousTextLength)
249
+ handleAfterTextChanged(s, EnrichedSpans.CHECKBOX_LIST, endCursorPosition, previousTextLength)
250
+ }
251
+
252
+ fun getStyleRange(): Pair<Int, Int> = view.selection?.getParagraphSelection() ?: Pair(0, 0)
253
+
254
+ fun removeStyle(
255
+ name: String,
256
+ start: Int,
257
+ end: Int,
258
+ ): Boolean {
259
+ val config = EnrichedSpans.listSpans[name] ?: return false
260
+ val spannable = view.text as Spannable
261
+ return removeSpansForRange(spannable, start, end, config.clazz)
262
+ }
263
+ }
@@ -0,0 +1,434 @@
1
+ package com.swmansion.enriched.textinput.styles
2
+
3
+ import android.text.Editable
4
+ import android.text.Spannable
5
+ import android.text.SpannableStringBuilder
6
+ import android.util.Log
7
+ import com.swmansion.enriched.common.EnrichedConstants
8
+ import com.swmansion.enriched.textinput.EnrichedTextInputView
9
+ import com.swmansion.enriched.textinput.spans.EnrichedSpans
10
+ import com.swmansion.enriched.textinput.spans.interfaces.EnrichedInputSpan
11
+ import com.swmansion.enriched.textinput.utils.getParagraphBounds
12
+ import com.swmansion.enriched.textinput.utils.getSafeSpanBoundaries
13
+ import com.swmansion.enriched.textinput.utils.removeZWS
14
+
15
+ class ParagraphStyles(
16
+ private val view: EnrichedTextInputView,
17
+ ) {
18
+ private fun <T> getPreviousParagraphSpan(
19
+ spannable: Spannable,
20
+ paragraphStart: Int,
21
+ type: Class<T>,
22
+ ): T? {
23
+ if (paragraphStart <= 0) return null
24
+
25
+ val (previousParagraphStart, previousParagraphEnd) = spannable.getParagraphBounds(paragraphStart - 1)
26
+ val spans = spannable.getSpans(previousParagraphStart, previousParagraphEnd, type)
27
+
28
+ // A paragraph implies a single cohesive style. having multiple spans of the
29
+ // same type (e.g., two codeblock spans) in one paragraph is an invalid state in current library logic
30
+ if (spans.size > 1) {
31
+ Log.w("ParagraphStyles", "getPreviousParagraphSpan(): Found more than one span in the paragraph!")
32
+ }
33
+
34
+ if (spans.isNotEmpty()) {
35
+ return spans.first()
36
+ }
37
+
38
+ return null
39
+ }
40
+
41
+ private fun <T> getNextParagraphSpan(
42
+ spannable: Spannable,
43
+ paragraphEnd: Int,
44
+ type: Class<T>,
45
+ ): T? {
46
+ if (paragraphEnd >= spannable.length - 1) return null
47
+
48
+ val (nextParagraphStart, nextParagraphEnd) = spannable.getParagraphBounds(paragraphEnd + 1)
49
+
50
+ val spans = spannable.getSpans(nextParagraphStart, nextParagraphEnd, type)
51
+
52
+ // A paragraph implies a single cohesive style. having multiple spans of the
53
+ // same type (e.g., two codeblock spans) in one paragraph is an invalid state in current library logic
54
+ if (spans.size > 1) {
55
+ Log.w("ParagraphStyles", "getNextParagraphSpan(): Found more than one span in the paragraph!")
56
+ }
57
+
58
+ if (spans.isNotEmpty()) {
59
+ return spans.first()
60
+ }
61
+
62
+ return null
63
+ }
64
+
65
+ /**
66
+ * Applies a continuous span to the specified range.
67
+ * If the new range touches existing continuous spans, they are coalesced into a single span
68
+ */
69
+ private fun <T> setContinuousSpan(
70
+ spannable: Spannable,
71
+ start: Int,
72
+ end: Int,
73
+ type: Class<T>,
74
+ ) {
75
+ val span = type.getDeclaredConstructor(HtmlStyle::class.java).newInstance(view.htmlStyle)
76
+ val previousSpan = getPreviousParagraphSpan(spannable, start, type)
77
+ val nextSpan = getNextParagraphSpan(spannable, end, type)
78
+ var newStart = start
79
+ var newEnd = end
80
+
81
+ if (previousSpan != null) {
82
+ newStart = spannable.getSpanStart(previousSpan)
83
+ spannable.removeSpan(previousSpan)
84
+ }
85
+
86
+ if (nextSpan != null && start != end) {
87
+ newEnd = spannable.getSpanEnd(nextSpan)
88
+ spannable.removeSpan(nextSpan)
89
+ }
90
+
91
+ val (safeStart, safeEnd) = spannable.getSafeSpanBoundaries(newStart, newEnd)
92
+ spannable.setSpan(span, safeStart, safeEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
93
+ }
94
+
95
+ private fun <T> setSpan(
96
+ spannable: Spannable,
97
+ type: Class<T>,
98
+ start: Int,
99
+ end: Int,
100
+ ) {
101
+ if (EnrichedSpans.isTypeContinuous(type)) {
102
+ setContinuousSpan(spannable, start, end, type)
103
+ return
104
+ }
105
+
106
+ val span = type.getDeclaredConstructor(HtmlStyle::class.java).newInstance(view.htmlStyle)
107
+ val (safeStart, safeEnd) = spannable.getSafeSpanBoundaries(start, end)
108
+ spannable.setSpan(span, safeStart, safeEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
109
+ }
110
+
111
+ // Removes spans of the given type in the specified range.
112
+ // If the removed span intersects with the range, it will be split and the remaining part will be re-applied after the removal
113
+ // Returns true if any spans were removed, false otherwise
114
+ private fun <T> removeSpansForRange(
115
+ spannable: Spannable,
116
+ start: Int,
117
+ end: Int,
118
+ clazz: Class<T>,
119
+ ): Boolean {
120
+ val ssb = spannable as SpannableStringBuilder
121
+ val spans = ssb.getSpans(start, end, clazz)
122
+ if (spans.isEmpty()) return false
123
+
124
+ for (span in spans) {
125
+ val spanStart = ssb.getSpanStart(span)
126
+ val spanEnd = ssb.getSpanEnd(span)
127
+ ssb.removeSpan(span)
128
+
129
+ if (spanStart < start) {
130
+ setSpan(ssb, clazz, spanStart, start - 1)
131
+ }
132
+
133
+ if (spanEnd > end) {
134
+ setSpan(ssb, clazz, end + 1, spanEnd)
135
+ }
136
+ }
137
+
138
+ ssb.removeZWS(start, end)
139
+ return true
140
+ }
141
+
142
+ private fun <T> setAndMergeSpans(
143
+ spannable: Spannable,
144
+ type: Class<T>,
145
+ start: Int,
146
+ end: Int,
147
+ ) {
148
+ val spans = spannable.getSpans(start, end, type)
149
+
150
+ // No spans setup for current selection, means we just need to assign new span
151
+ if (spans.isEmpty()) {
152
+ setSpan(spannable, type, start, end)
153
+ return
154
+ }
155
+
156
+ var setSpanOnFinish = false
157
+
158
+ // Some spans are present, we have to remove spans and (optionally) apply new spans
159
+ for (span in spans) {
160
+ val spanStart = spannable.getSpanStart(span)
161
+ val spanEnd = spannable.getSpanEnd(span)
162
+ var finalStart: Int? = null
163
+ var finalEnd: Int? = null
164
+
165
+ spannable.removeSpan(span)
166
+
167
+ if (start == spanStart && end == spanEnd) {
168
+ setSpanOnFinish = false
169
+ } else if (start > spanStart && end < spanEnd) {
170
+ setSpan(spannable, type, spanStart, start)
171
+ setSpan(spannable, type, end, spanEnd)
172
+ } else if (start == spanStart && end < spanEnd) {
173
+ finalStart = end
174
+ finalEnd = spanEnd
175
+ } else if (start > spanStart && end == spanEnd) {
176
+ finalStart = spanStart
177
+ finalEnd = start
178
+ } else if (start > spanStart) {
179
+ finalStart = spanStart
180
+ finalEnd = end
181
+ } else if (start < spanStart && end < spanEnd) {
182
+ finalStart = start
183
+ finalEnd = spanEnd
184
+ } else {
185
+ setSpanOnFinish = true
186
+ }
187
+
188
+ if (!setSpanOnFinish && finalStart != null && finalEnd != null) {
189
+ setSpan(spannable, type, finalStart, finalEnd)
190
+ }
191
+ }
192
+
193
+ if (setSpanOnFinish) {
194
+ setSpan(spannable, type, start, end)
195
+ }
196
+ }
197
+
198
+ private fun <T> isSpanEnabledInNextLine(
199
+ spannable: Spannable,
200
+ index: Int,
201
+ type: Class<T>,
202
+ ): Boolean {
203
+ val selection = view.selection ?: return false
204
+ if (index + 1 >= spannable.length) return false
205
+ val (start, end) = selection.getParagraphSelection()
206
+
207
+ val spans = spannable.getSpans(start, end, type)
208
+ return spans.isNotEmpty()
209
+ }
210
+
211
+ private fun <T> mergeAdjacentStyleSpans(
212
+ s: Editable,
213
+ endCursorPosition: Int,
214
+ type: Class<T>,
215
+ ) {
216
+ val (start, end) = s.getParagraphBounds(endCursorPosition)
217
+ val currParagraphSpans = s.getSpans(start, end, type)
218
+
219
+ if (currParagraphSpans.isEmpty()) {
220
+ return
221
+ }
222
+
223
+ val currSpan = currParagraphSpans[0]
224
+ val nextSpan = getNextParagraphSpan(s, end, type) ?: return
225
+
226
+ val newStart = s.getSpanStart(currSpan)
227
+ val newEnd = s.getSpanEnd(nextSpan)
228
+
229
+ s.removeSpan(nextSpan)
230
+ s.removeSpan(currSpan)
231
+
232
+ val (safeStart, safeEnd) = s.getSafeSpanBoundaries(newStart, newEnd)
233
+ val span = type.getDeclaredConstructor(HtmlStyle::class.java).newInstance(view.htmlStyle)
234
+
235
+ s.setSpan(span, safeStart, safeEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
236
+ }
237
+
238
+ private fun handleConflictsDuringNewlineDeletion(
239
+ s: Editable,
240
+ style: String,
241
+ paragraphStart: Int,
242
+ paragraphEnd: Int,
243
+ ): Boolean {
244
+ val spanState = view.spanState ?: return false
245
+ val mergingConfig = EnrichedSpans.getMergingConfigForStyle(style, view.htmlStyle) ?: return false
246
+ var isConflicting = false
247
+ val stylesToCheck = mergingConfig.blockingStyles + mergingConfig.conflictingStyles
248
+
249
+ for (styleToCheck in stylesToCheck) {
250
+ val conflictingType = EnrichedSpans.allSpans[styleToCheck]?.clazz ?: continue
251
+
252
+ val spans = s.getSpans(paragraphStart, paragraphEnd, conflictingType)
253
+ if (spans.isEmpty()) {
254
+ continue
255
+ }
256
+ isConflicting = true
257
+
258
+ val isParagraphStyle = EnrichedSpans.paragraphSpans[styleToCheck] != null
259
+ if (!isParagraphStyle) {
260
+ continue
261
+ }
262
+
263
+ for (span in spans) {
264
+ extendStyleOnWholeParagraph(s, span as EnrichedInputSpan, conflictingType, paragraphEnd)
265
+ }
266
+ }
267
+
268
+ if (isConflicting) {
269
+ val styleStart = spanState.getStart(style) ?: return false
270
+ spanState.setStart(style, null)
271
+ removeStyle(style, styleStart, paragraphEnd)
272
+ return true
273
+ }
274
+
275
+ return false
276
+ }
277
+
278
+ private fun deleteConflictingAndBlockingStyles(
279
+ s: Editable,
280
+ style: String,
281
+ paragraphStart: Int,
282
+ paragraphEnd: Int,
283
+ ) {
284
+ val mergingConfig = EnrichedSpans.getMergingConfigForStyle(style, view.htmlStyle) ?: return
285
+ val stylesToCheck = mergingConfig.blockingStyles + mergingConfig.conflictingStyles
286
+
287
+ for (styleToCheck in stylesToCheck) {
288
+ val conflictingType = EnrichedSpans.allSpans[styleToCheck]?.clazz ?: continue
289
+
290
+ val spans = s.getSpans(paragraphStart, paragraphEnd, conflictingType)
291
+ for (span in spans) {
292
+ s.removeSpan(span)
293
+ }
294
+ }
295
+ }
296
+
297
+ private fun <T> extendStyleOnWholeParagraph(
298
+ s: Editable,
299
+ span: EnrichedInputSpan,
300
+ type: Class<T>,
301
+ paragraphEnd: Int,
302
+ ) {
303
+ val currStyleStart = s.getSpanStart(span)
304
+ s.removeSpan(span)
305
+ val (safeStart, safeEnd) = s.getSafeSpanBoundaries(currStyleStart, paragraphEnd)
306
+ setSpan(s, type, safeStart, safeEnd)
307
+ }
308
+
309
+ fun afterTextChanged(
310
+ s: Editable,
311
+ endPosition: Int,
312
+ previousTextLength: Int,
313
+ ) {
314
+ var endCursorPosition = endPosition
315
+ val isBackspace = s.length < previousTextLength
316
+ val isNewLine = endCursorPosition == 0 || (endCursorPosition > 0 && s[endCursorPosition - 1] == '\n')
317
+ val spanState = view.spanState ?: return
318
+
319
+ for ((style, config) in EnrichedSpans.paragraphSpans) {
320
+ val styleStart = spanState.getStart(style)
321
+
322
+ if (styleStart == null) {
323
+ if (isBackspace) {
324
+ val (start, end) = s.getParagraphBounds(endCursorPosition)
325
+ val spans = s.getSpans(start, end, config.clazz)
326
+
327
+ for (span in spans) {
328
+ // handle conflicts when entering paragraph with some paragraph style applied
329
+ deleteConflictingAndBlockingStyles(s, style, start, end)
330
+ extendStyleOnWholeParagraph(s, span as EnrichedInputSpan, config.clazz, end)
331
+ }
332
+ }
333
+
334
+ if (config.isContinuous) {
335
+ mergeAdjacentStyleSpans(s, endCursorPosition, config.clazz)
336
+ }
337
+ continue
338
+ }
339
+
340
+ if (isNewLine) {
341
+ if (!config.isContinuous) {
342
+ spanState.setStart(style, null)
343
+ continue
344
+ }
345
+
346
+ // If removing text at the beginning of the line, we want to remove the span for the whole paragraph
347
+ if (isBackspace) {
348
+ val currentParagraphBounds = s.getParagraphBounds(endCursorPosition)
349
+ removeSpansForRange(s, currentParagraphBounds.first, currentParagraphBounds.second, config.clazz)
350
+ spanState.setStart(style, null)
351
+ continue
352
+ } else {
353
+ s.insert(endCursorPosition, EnrichedConstants.ZWS_STRING)
354
+ endCursorPosition += 1
355
+ }
356
+ }
357
+
358
+ var (start, end) = s.getParagraphBounds(styleStart, endCursorPosition)
359
+
360
+ // handle conflicts when deleting newline from paragraph style (going back to previous line)
361
+ if (isBackspace && styleStart != start) {
362
+ val isConflicting = handleConflictsDuringNewlineDeletion(s, style, start, end)
363
+ if (isConflicting) {
364
+ continue
365
+ }
366
+ }
367
+
368
+ val isNotEndLineSpan = isSpanEnabledInNextLine(s, end, config.clazz)
369
+ val spans = s.getSpans(start, end, config.clazz)
370
+
371
+ for (span in spans) {
372
+ if (isNotEndLineSpan) {
373
+ start = s.getSpanStart(span).coerceAtMost(start)
374
+ end = s.getSpanEnd(span).coerceAtLeast(end)
375
+ }
376
+
377
+ s.removeSpan(span)
378
+ }
379
+
380
+ setSpan(s, config.clazz, start, end)
381
+ }
382
+ }
383
+
384
+ fun toggleStyle(name: String) {
385
+ if (view.selection == null) return
386
+ val spannable = view.text as SpannableStringBuilder
387
+ val (start, end) = view.selection.getParagraphSelection()
388
+ val config = EnrichedSpans.paragraphSpans[name] ?: return
389
+ val type = config.clazz
390
+
391
+ val styleStart = view.spanState?.getStart(name)
392
+
393
+ if (styleStart != null) {
394
+ view.spanState.setStart(name, null)
395
+ removeSpansForRange(spannable, start, end, type)
396
+ view.selection.validateStyles()
397
+
398
+ return
399
+ }
400
+
401
+ if (start == end) {
402
+ spannable.insert(start, EnrichedConstants.ZWS_STRING)
403
+ setAndMergeSpans(spannable, type, start, end + 1)
404
+ view.selection.validateStyles()
405
+
406
+ return
407
+ }
408
+
409
+ var currentStart = start
410
+ var currentEnd = currentStart
411
+ val paragraphs = spannable.substring(start, end).split("\n")
412
+
413
+ for (paragraph in paragraphs) {
414
+ spannable.insert(currentStart, EnrichedConstants.ZWS_STRING)
415
+ currentEnd = currentStart + paragraph.length + 1
416
+ currentStart = currentEnd + 1
417
+ }
418
+
419
+ setAndMergeSpans(spannable, type, start, currentEnd)
420
+ view.selection.validateStyles()
421
+ }
422
+
423
+ fun getStyleRange(): Pair<Int, Int> = view.selection?.getParagraphSelection() ?: Pair(0, 0)
424
+
425
+ fun removeStyle(
426
+ name: String,
427
+ start: Int,
428
+ end: Int,
429
+ ): Boolean {
430
+ val config = EnrichedSpans.paragraphSpans[name] ?: return false
431
+ val spannable = view.text as Spannable
432
+ return removeSpansForRange(spannable, start, end, config.clazz)
433
+ }
434
+ }