react-native-enriched 0.3.0 → 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 (202) hide show
  1. package/README.md +6 -4
  2. package/android/build.gradle +3 -3
  3. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerDelegate.java +3 -0
  4. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerInterface.java +1 -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/{ios/generated/RNEnrichedTextInputViewSpec → android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec}/EventEmitters.cpp +31 -29
  8. package/{ios/generated/RNEnrichedTextInputViewSpec → android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec}/EventEmitters.h +22 -25
  9. package/android/generated/jni/react/renderer/components/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/Props.h +57 -0
  10. package/android/gradle.properties +5 -5
  11. package/android/src/main/java/com/swmansion/enriched/{EnrichedTextInputViewPackage.kt → ReactNativeEnrichedPackage.kt} +3 -2
  12. package/android/src/main/java/com/swmansion/enriched/{utils → common}/AsyncDrawable.kt +40 -8
  13. package/android/src/main/java/com/swmansion/enriched/common/CheckboxDrawable.kt +81 -0
  14. package/android/src/main/java/com/swmansion/enriched/{utils → common}/EnrichedConstants.kt +1 -1
  15. package/android/src/main/java/com/swmansion/enriched/common/EnrichedStyle.kt +57 -0
  16. package/android/src/main/java/com/swmansion/enriched/{spans/utils → common}/ForceRedrawSpan.kt +1 -1
  17. package/android/src/main/java/com/swmansion/enriched/common/MentionStyle.kt +7 -0
  18. package/android/src/main/java/com/swmansion/enriched/{utils → common}/ResourceManager.kt +1 -1
  19. package/android/src/main/java/com/swmansion/enriched/{utils → common/parser}/EnrichedParser.java +126 -99
  20. package/android/src/main/java/com/swmansion/enriched/common/parser/EnrichedSpanFactory.kt +79 -0
  21. package/android/src/main/java/com/swmansion/enriched/{spans → common/spans}/EnrichedBlockQuoteSpan.kt +9 -13
  22. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedBoldSpan.kt +12 -0
  23. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedCheckboxListSpan.kt +91 -0
  24. package/android/src/main/java/com/swmansion/enriched/{spans → common/spans}/EnrichedCodeBlockSpan.kt +8 -12
  25. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH1Span.kt +20 -0
  26. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH2Span.kt +20 -0
  27. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH3Span.kt +20 -0
  28. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH4Span.kt +21 -0
  29. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH5Span.kt +20 -0
  30. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH6Span.kt +20 -0
  31. package/android/src/main/java/com/swmansion/enriched/{spans → common/spans}/EnrichedImageSpan.kt +43 -38
  32. package/android/src/main/java/com/swmansion/enriched/{spans → common/spans}/EnrichedInlineCodeSpan.kt +7 -11
  33. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedItalicSpan.kt +12 -0
  34. package/android/src/main/java/com/swmansion/enriched/{spans → common/spans}/EnrichedLinkSpan.kt +7 -11
  35. package/android/src/main/java/com/swmansion/enriched/{spans → common/spans}/EnrichedMentionSpan.kt +6 -10
  36. package/android/src/main/java/com/swmansion/enriched/{spans → common/spans}/EnrichedOrderedListSpan.kt +12 -21
  37. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedStrikeThroughSpan.kt +11 -0
  38. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedUnderlineSpan.kt +11 -0
  39. package/android/src/main/java/com/swmansion/enriched/{spans → common/spans}/EnrichedUnorderedListSpan.kt +9 -13
  40. package/android/src/main/java/com/swmansion/enriched/{spans → common/spans}/interfaces/EnrichedBlockSpan.kt +1 -1
  41. package/android/src/main/java/com/swmansion/enriched/{spans → common/spans}/interfaces/EnrichedHeadingSpan.kt +1 -1
  42. package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedInlineSpan.kt +3 -0
  43. package/android/src/main/java/com/swmansion/enriched/{spans → common/spans}/interfaces/EnrichedParagraphSpan.kt +1 -1
  44. package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedSpan.kt +3 -0
  45. package/android/src/main/java/com/swmansion/enriched/{spans → common/spans}/interfaces/EnrichedZeroWidthSpaceSpan.kt +1 -1
  46. package/android/src/main/java/com/swmansion/enriched/{EnrichedTextInputConnectionWrapper.kt → textinput/EnrichedTextInputConnectionWrapper.kt} +2 -2
  47. package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputSpannableFactory.kt +83 -0
  48. package/android/src/main/java/com/swmansion/enriched/{EnrichedTextInputView.kt → textinput/EnrichedTextInputView.kt} +87 -51
  49. package/android/src/main/java/com/swmansion/enriched/{EnrichedTextInputViewLayoutManager.kt → textinput/EnrichedTextInputViewLayoutManager.kt} +1 -1
  50. package/android/src/main/java/com/swmansion/enriched/{EnrichedTextInputViewManager.kt → textinput/EnrichedTextInputViewManager.kt} +24 -17
  51. package/android/src/main/java/com/swmansion/enriched/{MeasurementStore.kt → textinput/MeasurementStore.kt} +5 -4
  52. package/android/src/main/java/com/swmansion/enriched/{events → textinput/events}/MentionHandler.kt +2 -2
  53. package/android/src/main/java/com/swmansion/enriched/{events → textinput/events}/OnChangeHtmlEvent.kt +1 -1
  54. package/android/src/main/java/com/swmansion/enriched/{events → textinput/events}/OnChangeSelectionEvent.kt +1 -1
  55. package/android/src/main/java/com/swmansion/enriched/{events → textinput/events}/OnChangeStateEvent.kt +1 -1
  56. package/android/src/main/java/com/swmansion/enriched/{events → textinput/events}/OnChangeTextEvent.kt +2 -2
  57. package/android/src/main/java/com/swmansion/enriched/{events → textinput/events}/OnInputBlurEvent.kt +1 -1
  58. package/android/src/main/java/com/swmansion/enriched/{events → textinput/events}/OnInputFocusEvent.kt +1 -1
  59. package/android/src/main/java/com/swmansion/enriched/{events → textinput/events}/OnInputKeyPressEvent.kt +1 -1
  60. package/android/src/main/java/com/swmansion/enriched/{events → textinput/events}/OnLinkDetectedEvent.kt +1 -1
  61. package/android/src/main/java/com/swmansion/enriched/{events → textinput/events}/OnMentionDetectedEvent.kt +1 -1
  62. package/android/src/main/java/com/swmansion/enriched/{events → textinput/events}/OnMentionEvent.kt +1 -1
  63. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnPasteImagesEvent.kt +47 -0
  64. package/android/src/main/java/com/swmansion/enriched/{events → textinput/events}/OnRequestHtmlResultEvent.kt +1 -1
  65. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputBlockQuoteSpan.kt +14 -0
  66. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputBoldSpan.kt +14 -0
  67. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputCheckboxListSpan.kt +15 -0
  68. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputCodeBlockSpan.kt +14 -0
  69. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH1Span.kt +14 -0
  70. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH2Span.kt +14 -0
  71. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH3Span.kt +14 -0
  72. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH4Span.kt +14 -0
  73. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH5Span.kt +14 -0
  74. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH6Span.kt +14 -0
  75. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputImageSpan.kt +36 -0
  76. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputInlineCodeSpan.kt +14 -0
  77. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputItalicSpan.kt +14 -0
  78. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputLinkSpan.kt +15 -0
  79. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputMentionSpan.kt +18 -0
  80. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputOrderedListSpan.kt +21 -0
  81. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputStrikeThroughSpan.kt +14 -0
  82. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputUnderlineSpan.kt +14 -0
  83. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputUnorderedListSpan.kt +14 -0
  84. package/android/src/main/java/com/swmansion/enriched/{spans → textinput/spans}/EnrichedSpans.kt +39 -30
  85. package/android/src/main/java/com/swmansion/enriched/textinput/spans/interfaces/EnrichedInputSpan.kt +10 -0
  86. package/android/src/main/java/com/swmansion/enriched/{styles → textinput/styles}/HtmlStyle.kt +58 -43
  87. package/android/src/main/java/com/swmansion/enriched/{styles → textinput/styles}/InlineStyles.kt +4 -4
  88. package/android/src/main/java/com/swmansion/enriched/{styles → textinput/styles}/ListStyles.kt +77 -26
  89. package/android/src/main/java/com/swmansion/enriched/{styles → textinput/styles}/ParagraphStyles.kt +11 -11
  90. package/android/src/main/java/com/swmansion/enriched/{styles → textinput/styles}/ParametrizedStyles.kt +19 -19
  91. package/android/src/main/java/com/swmansion/enriched/{utils → textinput/utils}/EnrichedEditableFactory.kt +2 -2
  92. package/android/src/main/java/com/swmansion/enriched/{utils → textinput/utils}/EnrichedSelection.kt +15 -14
  93. package/android/src/main/java/com/swmansion/enriched/{utils → textinput/utils}/EnrichedSpanState.kt +15 -50
  94. package/android/src/main/java/com/swmansion/enriched/{utils → textinput/utils}/EnrichedSpannable.kt +3 -3
  95. package/android/src/main/java/com/swmansion/enriched/{utils → textinput/utils}/EnrichedSpannableStringBuilder.kt +2 -1
  96. package/android/src/main/java/com/swmansion/enriched/textinput/utils/RichContentReceiver.kt +127 -0
  97. package/android/src/main/java/com/swmansion/enriched/textinput/utils/Utils.kt +106 -0
  98. package/android/src/main/java/com/swmansion/enriched/{watchers → textinput/watchers}/EnrichedSpanWatcher.kt +10 -10
  99. package/android/src/main/java/com/swmansion/enriched/{watchers → textinput/watchers}/EnrichedTextWatcher.kt +3 -3
  100. package/android/src/main/new_arch/CMakeLists.txt +1 -1
  101. package/android/src/main/new_arch/ReactNativeEnrichedSpec.cpp +11 -0
  102. package/android/src/main/new_arch/ReactNativeEnrichedSpec.h +15 -0
  103. package/android/src/main/new_arch/react/renderer/components/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/EnrichedTextInputMeasurementManager.h +1 -1
  104. package/android/src/main/new_arch/react/renderer/components/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/EnrichedTextInputShadowNode.h +2 -2
  105. package/android/src/main/new_arch/react/renderer/components/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/conversions.h +1 -1
  106. package/ios/EnrichedTextInputView.h +1 -0
  107. package/ios/EnrichedTextInputView.mm +266 -55
  108. package/ios/config/InputConfig.h +10 -0
  109. package/ios/config/InputConfig.mm +119 -0
  110. package/ios/extensions/ImageExtension.h +35 -0
  111. package/ios/extensions/ImageExtension.mm +156 -0
  112. package/ios/extensions/LayoutManagerExtension.mm +115 -95
  113. package/ios/generated/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/ComponentDescriptors.cpp +1 -1
  114. package/ios/generated/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/ComponentDescriptors.h +1 -1
  115. package/{android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec → ios/generated/ReactNativeEnrichedSpec}/EventEmitters.cpp +31 -29
  116. package/{android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec → ios/generated/ReactNativeEnrichedSpec}/EventEmitters.h +22 -25
  117. package/ios/generated/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/Props.h +57 -0
  118. package/ios/generated/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/RCTComponentViewHelpers.h +21 -0
  119. package/ios/inputParser/InputParser.mm +135 -8
  120. package/ios/inputTextView/InputTextView.mm +118 -0
  121. package/ios/interfaces/ImageAttachment.h +1 -0
  122. package/ios/interfaces/ImageAttachment.mm +14 -4
  123. package/ios/interfaces/StyleHeaders.h +13 -2
  124. package/ios/interfaces/StyleTypeEnum.h +1 -0
  125. package/ios/internals/EnrichedTextInputViewState.cpp +6 -6
  126. package/ios/styles/CheckboxListStyle.mm +321 -0
  127. package/ios/styles/H1Style.mm +3 -0
  128. package/ios/styles/H2Style.mm +3 -0
  129. package/ios/styles/H3Style.mm +3 -0
  130. package/ios/styles/H4Style.mm +3 -0
  131. package/ios/styles/H5Style.mm +3 -0
  132. package/ios/styles/H6Style.mm +3 -0
  133. package/ios/styles/HeadingStyleBase.mm +150 -78
  134. package/ios/utils/CheckboxHitTestUtils.h +10 -0
  135. package/ios/utils/CheckboxHitTestUtils.mm +123 -0
  136. package/ios/utils/ParagraphAttributesUtils.mm +83 -53
  137. package/ios/utils/TextBlockTapGestureRecognizer.h +17 -0
  138. package/ios/utils/TextBlockTapGestureRecognizer.mm +56 -0
  139. package/ios/utils/ZeroWidthSpaceUtils.mm +14 -3
  140. package/lib/module/EnrichedTextInput.js +36 -11
  141. package/lib/module/EnrichedTextInput.js.map +1 -1
  142. package/lib/module/{EnrichedTextInputNativeComponent.ts → spec/EnrichedTextInputNativeComponent.ts} +40 -9
  143. package/lib/module/types.js +4 -0
  144. package/lib/module/types.js.map +1 -0
  145. package/lib/module/utils/normalizeHtmlStyle.js +6 -0
  146. package/lib/module/utils/normalizeHtmlStyle.js.map +1 -1
  147. package/lib/module/utils/nullthrows.js +9 -0
  148. package/lib/module/utils/nullthrows.js.map +1 -0
  149. package/lib/typescript/src/EnrichedTextInput.d.ts +9 -49
  150. package/lib/typescript/src/EnrichedTextInput.d.ts.map +1 -1
  151. package/lib/typescript/src/index.d.ts +2 -1
  152. package/lib/typescript/src/index.d.ts.map +1 -1
  153. package/lib/typescript/src/{EnrichedTextInputNativeComponent.d.ts → spec/EnrichedTextInputNativeComponent.d.ts} +33 -8
  154. package/lib/typescript/src/spec/EnrichedTextInputNativeComponent.d.ts.map +1 -0
  155. package/lib/typescript/src/types.d.ts +58 -0
  156. package/lib/typescript/src/types.d.ts.map +1 -0
  157. package/lib/typescript/src/utils/normalizeHtmlStyle.d.ts +2 -2
  158. package/lib/typescript/src/utils/normalizeHtmlStyle.d.ts.map +1 -1
  159. package/lib/typescript/src/utils/nullthrows.d.ts +2 -0
  160. package/lib/typescript/src/utils/nullthrows.d.ts.map +1 -0
  161. package/lib/typescript/src/utils/regexParser.d.ts +1 -1
  162. package/lib/typescript/src/utils/regexParser.d.ts.map +1 -1
  163. package/package.json +4 -4
  164. package/src/EnrichedTextInput.tsx +49 -62
  165. package/src/index.tsx +3 -1
  166. package/src/{EnrichedTextInputNativeComponent.ts → spec/EnrichedTextInputNativeComponent.ts} +40 -9
  167. package/src/types.ts +59 -0
  168. package/src/utils/normalizeHtmlStyle.ts +8 -5
  169. package/src/utils/nullthrows.ts +7 -0
  170. package/src/utils/regexParser.ts +1 -1
  171. package/android/src/main/java/com/swmansion/enriched/events/OnChangeStateDeprecatedEvent.kt +0 -21
  172. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedBoldSpan.kt +0 -17
  173. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH1Span.kt +0 -24
  174. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH2Span.kt +0 -24
  175. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH3Span.kt +0 -24
  176. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH4Span.kt +0 -24
  177. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH5Span.kt +0 -24
  178. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH6Span.kt +0 -24
  179. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedItalicSpan.kt +0 -16
  180. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedStrikeThroughSpan.kt +0 -15
  181. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedUnderlineSpan.kt +0 -15
  182. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedInlineSpan.kt +0 -3
  183. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedSpan.kt +0 -9
  184. package/android/src/main/java/com/swmansion/enriched/utils/Utils.kt +0 -21
  185. package/android/src/main/new_arch/RNEnrichedTextInputViewSpec.cpp +0 -22
  186. package/android/src/main/new_arch/RNEnrichedTextInputViewSpec.h +0 -26
  187. package/lib/typescript/src/EnrichedTextInputNativeComponent.d.ts.map +0 -1
  188. /package/android/generated/jni/react/renderer/components/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/Props.cpp +0 -0
  189. /package/android/generated/jni/react/renderer/components/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/ShadowNodes.cpp +0 -0
  190. /package/android/generated/jni/react/renderer/components/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/ShadowNodes.h +0 -0
  191. /package/android/generated/jni/react/renderer/components/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/States.cpp +0 -0
  192. /package/android/generated/jni/react/renderer/components/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/States.h +0 -0
  193. /package/android/src/main/new_arch/react/renderer/components/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/EnrichedTextInputComponentDescriptor.h +0 -0
  194. /package/android/src/main/new_arch/react/renderer/components/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/EnrichedTextInputMeasurementManager.cpp +0 -0
  195. /package/android/src/main/new_arch/react/renderer/components/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/EnrichedTextInputShadowNode.cpp +0 -0
  196. /package/android/src/main/new_arch/react/renderer/components/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/EnrichedTextInputState.cpp +0 -0
  197. /package/android/src/main/new_arch/react/renderer/components/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/EnrichedTextInputState.h +0 -0
  198. /package/ios/generated/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/Props.cpp +0 -0
  199. /package/ios/generated/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/ShadowNodes.cpp +0 -0
  200. /package/ios/generated/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/ShadowNodes.h +0 -0
  201. /package/ios/generated/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/States.cpp +0 -0
  202. /package/ios/generated/{RNEnrichedTextInputViewSpec → ReactNativeEnrichedSpec}/States.h +0 -0
@@ -1,10 +1,12 @@
1
1
  #import "EnrichedTextInputView.h"
2
2
  #import "CoreText/CoreText.h"
3
+ #import "ImageAttachment.h"
3
4
  #import "LayoutManagerExtension.h"
4
5
  #import "ParagraphAttributesUtils.h"
5
6
  #import "RCTFabricComponentsPlugins.h"
6
7
  #import "StringExtension.h"
7
8
  #import "StyleHeaders.h"
9
+ #import "TextBlockTapGestureRecognizer.h"
8
10
  #import "UIView+React.h"
9
11
  #import "WordsUtils.h"
10
12
  #import "ZeroWidthSpaceUtils.h"
@@ -26,7 +28,8 @@
26
28
  using namespace facebook::react;
27
29
 
28
30
  @interface EnrichedTextInputView () <RCTEnrichedTextInputViewViewProtocol,
29
- UITextViewDelegate, NSObject>
31
+ UITextViewDelegate,
32
+ UIGestureRecognizerDelegate, NSObject>
30
33
 
31
34
  @end
32
35
 
@@ -46,6 +49,7 @@ using namespace facebook::react;
46
49
  UIColor *_placeholderColor;
47
50
  BOOL _emitFocusBlur;
48
51
  BOOL _emitTextChange;
52
+ NSMutableDictionary<NSValue *, UIImageView *> *_attachmentViews;
49
53
  }
50
54
 
51
55
  // MARK: - Component utils
@@ -116,6 +120,8 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
116
120
  [[UnorderedListStyle alloc] initWithInput:self],
117
121
  @([OrderedListStyle getStyleType]) :
118
122
  [[OrderedListStyle alloc] initWithInput:self],
123
+ @([CheckboxListStyle getStyleType]) :
124
+ [[CheckboxListStyle alloc] initWithInput:self],
119
125
  @([BlockQuoteStyle getStyleType]) :
120
126
  [[BlockQuoteStyle alloc] initWithInput:self],
121
127
  @([CodeBlockStyle getStyleType]) :
@@ -141,63 +147,70 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
141
147
  @([H4Style getStyleType]), @([H5Style getStyleType]),
142
148
  @([H6Style getStyleType]), @([UnorderedListStyle getStyleType]),
143
149
  @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]),
144
- @([CodeBlockStyle getStyleType])
150
+ @([CodeBlockStyle getStyleType]), @([CheckboxListStyle getStyleType])
145
151
  ],
146
152
  @([H2Style getStyleType]) : @[
147
153
  @([H1Style getStyleType]), @([H3Style getStyleType]),
148
154
  @([H4Style getStyleType]), @([H5Style getStyleType]),
149
155
  @([H6Style getStyleType]), @([UnorderedListStyle getStyleType]),
150
156
  @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]),
151
- @([CodeBlockStyle getStyleType])
157
+ @([CodeBlockStyle getStyleType]), @([CheckboxListStyle getStyleType])
152
158
  ],
153
159
  @([H3Style getStyleType]) : @[
154
160
  @([H1Style getStyleType]), @([H2Style getStyleType]),
155
161
  @([H4Style getStyleType]), @([H5Style getStyleType]),
156
162
  @([H6Style getStyleType]), @([UnorderedListStyle getStyleType]),
157
163
  @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]),
158
- @([CodeBlockStyle getStyleType])
164
+ @([CodeBlockStyle getStyleType]), @([CheckboxListStyle getStyleType])
159
165
  ],
160
166
  @([H4Style getStyleType]) : @[
161
167
  @([H1Style getStyleType]), @([H2Style getStyleType]),
162
168
  @([H3Style getStyleType]), @([H5Style getStyleType]),
163
169
  @([H6Style getStyleType]), @([UnorderedListStyle getStyleType]),
164
170
  @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]),
165
- @([CodeBlockStyle getStyleType])
171
+ @([CodeBlockStyle getStyleType]), @([CheckboxListStyle getStyleType])
166
172
  ],
167
173
  @([H5Style getStyleType]) : @[
168
174
  @([H1Style getStyleType]), @([H2Style getStyleType]),
169
175
  @([H3Style getStyleType]), @([H4Style getStyleType]),
170
176
  @([H6Style getStyleType]), @([UnorderedListStyle getStyleType]),
171
177
  @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]),
172
- @([CodeBlockStyle getStyleType])
178
+ @([CodeBlockStyle getStyleType]), @([CheckboxListStyle getStyleType])
173
179
  ],
174
180
  @([H6Style getStyleType]) : @[
175
181
  @([H1Style getStyleType]), @([H2Style getStyleType]),
176
182
  @([H3Style getStyleType]), @([H4Style getStyleType]),
177
183
  @([H5Style getStyleType]), @([UnorderedListStyle getStyleType]),
178
184
  @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]),
179
- @([CodeBlockStyle getStyleType])
185
+ @([CodeBlockStyle getStyleType]), @([CheckboxListStyle getStyleType])
180
186
  ],
181
187
  @([UnorderedListStyle getStyleType]) : @[
182
188
  @([H1Style getStyleType]), @([H2Style getStyleType]),
183
189
  @([H3Style getStyleType]), @([H4Style getStyleType]),
184
190
  @([H5Style getStyleType]), @([H6Style getStyleType]),
185
191
  @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]),
186
- @([CodeBlockStyle getStyleType])
192
+ @([CodeBlockStyle getStyleType]), @([CheckboxListStyle getStyleType])
187
193
  ],
188
194
  @([OrderedListStyle getStyleType]) : @[
189
195
  @([H1Style getStyleType]), @([H2Style getStyleType]),
190
196
  @([H3Style getStyleType]), @([H4Style getStyleType]),
191
197
  @([H5Style getStyleType]), @([H6Style getStyleType]),
192
198
  @([UnorderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]),
193
- @([CodeBlockStyle getStyleType])
199
+ @([CodeBlockStyle getStyleType]), @([CheckboxListStyle getStyleType])
200
+ ],
201
+ @([CheckboxListStyle getStyleType]) : @[
202
+ @([H1Style getStyleType]), @([H2Style getStyleType]),
203
+ @([H3Style getStyleType]), @([H4Style getStyleType]),
204
+ @([H5Style getStyleType]), @([H6Style getStyleType]),
205
+ @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]),
206
+ @([BlockQuoteStyle getStyleType]), @([CodeBlockStyle getStyleType])
194
207
  ],
195
208
  @([BlockQuoteStyle getStyleType]) : @[
196
209
  @([H1Style getStyleType]), @([H2Style getStyleType]),
197
210
  @([H3Style getStyleType]), @([H4Style getStyleType]),
198
211
  @([H5Style getStyleType]), @([H6Style getStyleType]),
199
212
  @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]),
200
- @([CodeBlockStyle getStyleType])
213
+ @([CodeBlockStyle getStyleType]), @([CheckboxListStyle getStyleType])
201
214
  ],
202
215
  @([CodeBlockStyle getStyleType]) : @[
203
216
  @([H1Style getStyleType]), @([H2Style getStyleType]),
@@ -207,7 +220,8 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
207
220
  @([UnderlineStyle getStyleType]), @([StrikethroughStyle getStyleType]),
208
221
  @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]),
209
222
  @([BlockQuoteStyle getStyleType]), @([InlineCodeStyle getStyleType]),
210
- @([MentionStyle getStyleType]), @([LinkStyle getStyleType])
223
+ @([MentionStyle getStyleType]), @([LinkStyle getStyleType]),
224
+ @([CheckboxListStyle getStyleType])
211
225
  ],
212
226
  @([ImageStyle getStyleType]) :
213
227
  @[ @([LinkStyle getStyleType]), @([MentionStyle getStyleType]) ]
@@ -233,12 +247,14 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
233
247
  @([H6Style getStyleType]) : @[],
234
248
  @([UnorderedListStyle getStyleType]) : @[],
235
249
  @([OrderedListStyle getStyleType]) : @[],
250
+ @([CheckboxListStyle getStyleType]) : @[],
236
251
  @([BlockQuoteStyle getStyleType]) : @[],
237
252
  @([CodeBlockStyle getStyleType]) : @[],
238
253
  @([ImageStyle getStyleType]) : @[ @([InlineCodeStyle getStyleType]) ]
239
254
  } mutableCopy];
240
255
 
241
256
  parser = [[InputParser alloc] initWithInput:self];
257
+ _attachmentViews = [[NSMutableDictionary alloc] init];
242
258
  }
243
259
 
244
260
  - (void)setupTextView {
@@ -250,6 +266,9 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
250
266
  textView.input = self;
251
267
  textView.layoutManager.input = self;
252
268
  textView.adjustsFontForContentSizeCategory = YES;
269
+ [textView addGestureRecognizer:[[TextBlockTapGestureRecognizer alloc]
270
+ initWithInput:self
271
+ action:@selector(onTextBlockTap:)]];
253
272
  }
254
273
 
255
274
  - (void)setupPlaceholderLabel {
@@ -596,6 +615,37 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
596
615
  stylePropChanged = YES;
597
616
  }
598
617
 
618
+ if (newViewProps.htmlStyle.ulCheckbox.boxSize !=
619
+ oldViewProps.htmlStyle.ulCheckbox.boxSize) {
620
+ [newConfig
621
+ setCheckboxListBoxSize:newViewProps.htmlStyle.ulCheckbox.boxSize];
622
+ stylePropChanged = YES;
623
+ }
624
+
625
+ if (newViewProps.htmlStyle.ulCheckbox.gapWidth !=
626
+ oldViewProps.htmlStyle.ulCheckbox.gapWidth) {
627
+ [newConfig
628
+ setCheckboxListGapWidth:newViewProps.htmlStyle.ulCheckbox.gapWidth];
629
+ stylePropChanged = YES;
630
+ }
631
+
632
+ if (newViewProps.htmlStyle.ulCheckbox.marginLeft !=
633
+ oldViewProps.htmlStyle.ulCheckbox.marginLeft) {
634
+ [newConfig
635
+ setCheckboxListMarginLeft:newViewProps.htmlStyle.ulCheckbox.marginLeft];
636
+ stylePropChanged = YES;
637
+ }
638
+
639
+ if (newViewProps.htmlStyle.ulCheckbox.boxColor !=
640
+ oldViewProps.htmlStyle.ulCheckbox.boxColor) {
641
+ if (isColorMeaningful(newViewProps.htmlStyle.ulCheckbox.boxColor)) {
642
+ [newConfig setCheckboxListBoxColor:RCTUIColorFromSharedColor(
643
+ newViewProps.htmlStyle.ulCheckbox
644
+ .boxColor)];
645
+ stylePropChanged = YES;
646
+ }
647
+ }
648
+
599
649
  if (newViewProps.htmlStyle.a.textDecorationLine !=
600
650
  oldViewProps.htmlStyle.a.textDecorationLine) {
601
651
  NSString *objcString =
@@ -1024,28 +1074,6 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
1024
1074
  _activeStyles = newActiveStyles;
1025
1075
  _blockedStyles = newBlockedStyles;
1026
1076
 
1027
- emitter->onChangeStateDeprecated({
1028
- .isBold = [self isStyleActive:[BoldStyle getStyleType]],
1029
- .isItalic = [self isStyleActive:[ItalicStyle getStyleType]],
1030
- .isUnderline = [self isStyleActive:[UnderlineStyle getStyleType]],
1031
- .isStrikeThrough =
1032
- [self isStyleActive:[StrikethroughStyle getStyleType]],
1033
- .isInlineCode = [self isStyleActive:[InlineCodeStyle getStyleType]],
1034
- .isLink = [self isStyleActive:[LinkStyle getStyleType]],
1035
- .isMention = [self isStyleActive:[MentionStyle getStyleType]],
1036
- .isH1 = [self isStyleActive:[H1Style getStyleType]],
1037
- .isH2 = [self isStyleActive:[H2Style getStyleType]],
1038
- .isH3 = [self isStyleActive:[H3Style getStyleType]],
1039
- .isH4 = [self isStyleActive:[H4Style getStyleType]],
1040
- .isH5 = [self isStyleActive:[H5Style getStyleType]],
1041
- .isH6 = [self isStyleActive:[H6Style getStyleType]],
1042
- .isUnorderedList =
1043
- [self isStyleActive:[UnorderedListStyle getStyleType]],
1044
- .isOrderedList = [self isStyleActive:[OrderedListStyle getStyleType]],
1045
- .isBlockQuote = [self isStyleActive:[BlockQuoteStyle getStyleType]],
1046
- .isCodeBlock = [self isStyleActive:[CodeBlockStyle getStyleType]],
1047
- .isImage = [self isStyleActive:[ImageStyle getStyleType]],
1048
- });
1049
1077
  emitter->onChangeState(
1050
1078
  {.bold = GET_STYLE_STATE([BoldStyle getStyleType]),
1051
1079
  .italic = GET_STYLE_STATE([ItalicStyle getStyleType]),
@@ -1064,7 +1092,8 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
1064
1092
  .orderedList = GET_STYLE_STATE([OrderedListStyle getStyleType]),
1065
1093
  .blockQuote = GET_STYLE_STATE([BlockQuoteStyle getStyleType]),
1066
1094
  .codeBlock = GET_STYLE_STATE([CodeBlockStyle getStyleType]),
1067
- .image = GET_STYLE_STATE([ImageStyle getStyleType])});
1095
+ .image = GET_STYLE_STATE([ImageStyle getStyleType]),
1096
+ .checkboxList = GET_STYLE_STATE([CheckboxListStyle getStyleType])});
1068
1097
  }
1069
1098
  }
1070
1099
 
@@ -1175,6 +1204,9 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
1175
1204
  [self toggleParagraphStyle:[UnorderedListStyle getStyleType]];
1176
1205
  } else if ([commandName isEqualToString:@"toggleOrderedList"]) {
1177
1206
  [self toggleParagraphStyle:[OrderedListStyle getStyleType]];
1207
+ } else if ([commandName isEqualToString:@"toggleCheckboxList"]) {
1208
+ BOOL checked = [args[0] boolValue];
1209
+ [self toggleCheckboxList:checked];
1178
1210
  } else if ([commandName isEqualToString:@"toggleBlockQuote"]) {
1179
1211
  [self toggleParagraphStyle:[BlockQuoteStyle getStyleType]];
1180
1212
  } else if ([commandName isEqualToString:@"toggleCodeBlock"]) {
@@ -1282,6 +1314,32 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
1282
1314
  }
1283
1315
  }
1284
1316
 
1317
+ - (void)emitOnPasteImagesEvent:(NSArray<NSDictionary *> *)images {
1318
+ auto emitter = [self getEventEmitter];
1319
+ if (emitter != nullptr) {
1320
+ std::vector<EnrichedTextInputViewEventEmitter::OnPasteImagesImages>
1321
+ imagesVector;
1322
+ imagesVector.reserve(images.count);
1323
+
1324
+ for (NSDictionary *img in images) {
1325
+ NSString *uri = img[@"uri"];
1326
+ NSString *type = img[@"type"];
1327
+ double width = [img[@"width"] doubleValue];
1328
+ double height = [img[@"height"] doubleValue];
1329
+
1330
+ EnrichedTextInputViewEventEmitter::OnPasteImagesImages imageStruct = {
1331
+ .uri = [uri toCppString],
1332
+ .type = [type toCppString],
1333
+ .width = width,
1334
+ .height = height};
1335
+
1336
+ imagesVector.push_back(imageStruct);
1337
+ }
1338
+
1339
+ emitter->onPasteImages({.images = imagesVector});
1340
+ }
1341
+ }
1342
+
1285
1343
  - (void)emitOnMentionDetectedEvent:(NSString *)text
1286
1344
  indicator:(NSString *)indicator
1287
1345
  attributes:(NSString *)attributes {
@@ -1369,6 +1427,24 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
1369
1427
  }
1370
1428
  }
1371
1429
 
1430
+ - (void)toggleCheckboxList:(BOOL)checked {
1431
+ CheckboxListStyle *checkboxListStyleClass =
1432
+ (CheckboxListStyle *)stylesDict[@([CheckboxListStyle getStyleType])];
1433
+ if (checkboxListStyleClass == nullptr) {
1434
+ return;
1435
+ }
1436
+ // we always pass whole paragraph/s range to these styles
1437
+ NSRange paragraphRange = [textView.textStorage.string
1438
+ paragraphRangeForRange:textView.selectedRange];
1439
+
1440
+ if ([self handleStyleBlocksAndConflicts:[CheckboxListStyle getStyleType]
1441
+ range:paragraphRange]) {
1442
+ [checkboxListStyleClass applyStyleWithCheckedValue:checked
1443
+ inRange:paragraphRange];
1444
+ [self anyTextMayHaveBeenModified];
1445
+ }
1446
+ }
1447
+
1372
1448
  - (void)addLinkAt:(NSInteger)start
1373
1449
  end:(NSInteger)end
1374
1450
  text:(NSString *)text
@@ -1587,27 +1663,6 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
1587
1663
  [codeBlockStyle manageCodeBlockFontAndColor];
1588
1664
  }
1589
1665
 
1590
- // improper headings fix
1591
- H1Style *h1Style = stylesDict[@([H1Style getStyleType])];
1592
- H2Style *h2Style = stylesDict[@([H2Style getStyleType])];
1593
- H3Style *h3Style = stylesDict[@([H3Style getStyleType])];
1594
- H4Style *h4Style = stylesDict[@([H4Style getStyleType])];
1595
- H5Style *h5Style = stylesDict[@([H5Style getStyleType])];
1596
- H6Style *h6Style = stylesDict[@([H6Style getStyleType])];
1597
-
1598
- bool headingStylesDefined = h1Style != nullptr && h2Style != nullptr &&
1599
- h3Style != nullptr && h4Style != nullptr &&
1600
- h5Style != nullptr && h6Style != nullptr;
1601
-
1602
- if (headingStylesDefined) {
1603
- [h1Style handleImproperHeadings];
1604
- [h2Style handleImproperHeadings];
1605
- [h3Style handleImproperHeadings];
1606
- [h4Style handleImproperHeadings];
1607
- [h5Style handleImproperHeadings];
1608
- [h6Style handleImproperHeadings];
1609
- }
1610
-
1611
1666
  // mentions management: removal and editing
1612
1667
  MentionStyle *mentionStyleClass =
1613
1668
  (MentionStyle *)stylesDict[@([MentionStyle getStyleType])];
@@ -1662,6 +1717,7 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
1662
1717
  [self tryUpdatingHeight];
1663
1718
  // update active styles as well
1664
1719
  [self tryUpdatingActiveStyles];
1720
+ [self layoutAttachments];
1665
1721
  // update drawing - schedule debounced relayout
1666
1722
  [self scheduleRelayoutIfNeeded];
1667
1723
  }
@@ -1768,6 +1824,7 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
1768
1824
 
1769
1825
  UnorderedListStyle *uStyle = stylesDict[@([UnorderedListStyle getStyleType])];
1770
1826
  OrderedListStyle *oStyle = stylesDict[@([OrderedListStyle getStyleType])];
1827
+ CheckboxListStyle *cbLStyle = stylesDict[@([CheckboxListStyle getStyleType])];
1771
1828
  BlockQuoteStyle *bqStyle = stylesDict[@([BlockQuoteStyle getStyleType])];
1772
1829
  CodeBlockStyle *cbStyle = stylesDict[@([CodeBlockStyle getStyleType])];
1773
1830
  LinkStyle *linkStyle = stylesDict[@([LinkStyle getStyleType])];
@@ -1787,6 +1844,8 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
1787
1844
  [uStyle tryHandlingListShorcutInRange:range replacementText:text] ||
1788
1845
  [oStyle handleBackspaceInRange:range replacementText:text] ||
1789
1846
  [oStyle tryHandlingListShorcutInRange:range replacementText:text] ||
1847
+ [cbLStyle handleBackspaceInRange:range replacementText:text] ||
1848
+ [cbLStyle handleNewlinesInRange:range replacementText:text] ||
1790
1849
  [bqStyle handleBackspaceInRange:range replacementText:text] ||
1791
1850
  [cbStyle handleBackspaceInRange:range replacementText:text] ||
1792
1851
  [linkStyle handleLeadingLinkReplacement:range replacementText:text] ||
@@ -1798,6 +1857,12 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
1798
1857
  [h4Style handleNewlinesInRange:range replacementText:text] ||
1799
1858
  [h5Style handleNewlinesInRange:range replacementText:text] ||
1800
1859
  [h6Style handleNewlinesInRange:range replacementText:text] ||
1860
+ [h1Style handleBackspaceInRange:range replacementText:text] ||
1861
+ [h2Style handleBackspaceInRange:range replacementText:text] ||
1862
+ [h3Style handleBackspaceInRange:range replacementText:text] ||
1863
+ [h4Style handleBackspaceInRange:range replacementText:text] ||
1864
+ [h5Style handleBackspaceInRange:range replacementText:text] ||
1865
+ [h6Style handleBackspaceInRange:range replacementText:text] ||
1801
1866
  [ZeroWidthSpaceUtils handleBackspaceInRange:range
1802
1867
  replacementText:text
1803
1868
  input:self] ||
@@ -1890,6 +1955,51 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
1890
1955
  }
1891
1956
  }
1892
1957
 
1958
+ - (void)onTextBlockTap:(TextBlockTapGestureRecognizer *)gr {
1959
+ if (gr.state != UIGestureRecognizerStateEnded)
1960
+ return;
1961
+ if (![self->textView isFirstResponder]) {
1962
+ [self->textView becomeFirstResponder];
1963
+ }
1964
+
1965
+ switch (gr.tapKind) {
1966
+
1967
+ case TextBlockTapKindCheckbox: {
1968
+ CheckboxListStyle *checkboxStyle =
1969
+ (CheckboxListStyle *)stylesDict[@([CheckboxListStyle getStyleType])];
1970
+
1971
+ if (checkboxStyle) {
1972
+ NSUInteger charIndex = (NSUInteger)gr.characterIndex;
1973
+ [checkboxStyle toggleCheckedAt:charIndex];
1974
+ [self anyTextMayHaveBeenModified];
1975
+
1976
+ NSString *fullText = textView.textStorage.string;
1977
+ NSRange paragraphRange =
1978
+ [fullText paragraphRangeForRange:NSMakeRange(charIndex, 0)];
1979
+ NSUInteger endOfLineIndex = NSMaxRange(paragraphRange);
1980
+
1981
+ // If the paragraph ends with a newline, step back by 1 so the cursor
1982
+ // stays on the current line instead of jumping to the next one.
1983
+ if (endOfLineIndex > 0 && endOfLineIndex <= fullText.length) {
1984
+ unichar lastChar = [fullText characterAtIndex:endOfLineIndex - 1];
1985
+ if ([[NSCharacterSet newlineCharacterSet] characterIsMember:lastChar]) {
1986
+ endOfLineIndex--;
1987
+ }
1988
+ }
1989
+
1990
+ // Move the cursor to the end of the currently tapped checkbox line.
1991
+ // Without this, the cursor may remain at its previous position,
1992
+ // potentially inside a different checkbox line.
1993
+ textView.selectedRange = NSMakeRange(endOfLineIndex, 0);
1994
+ }
1995
+ break;
1996
+ }
1997
+
1998
+ default:
1999
+ break;
2000
+ }
2001
+ }
2002
+
1893
2003
  // MARK: - Media attachments delegate
1894
2004
 
1895
2005
  - (void)mediaAttachmentDidUpdate:(NSTextAttachment *)attachment {
@@ -1915,6 +2025,107 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
1915
2025
  [storage edited:NSTextStorageEditedAttributes
1916
2026
  range:foundRange
1917
2027
  changeInLength:0];
2028
+
2029
+ dispatch_async(dispatch_get_main_queue(), ^{
2030
+ [self layoutAttachments];
2031
+ });
1918
2032
  }
1919
2033
 
2034
+ // MARK: - Image/GIF Overlay Management
2035
+
2036
+ - (void)layoutAttachments {
2037
+ NSTextStorage *storage = textView.textStorage;
2038
+ NSMutableDictionary<NSValue *, UIImageView *> *activeAttachmentViews =
2039
+ [NSMutableDictionary dictionary];
2040
+
2041
+ // Iterate over the entire text to find ImageAttachments
2042
+ [storage enumerateAttribute:NSAttachmentAttributeName
2043
+ inRange:NSMakeRange(0, storage.length)
2044
+ options:0
2045
+ usingBlock:^(id value, NSRange range, BOOL *stop) {
2046
+ if ([value isKindOfClass:[ImageAttachment class]]) {
2047
+ ImageAttachment *attachment = (ImageAttachment *)value;
2048
+
2049
+ CGRect rect = [self frameForAttachment:attachment
2050
+ atRange:range];
2051
+
2052
+ // Get or Create the UIImageView for this specific
2053
+ // attachment key
2054
+ NSValue *key =
2055
+ [NSValue valueWithNonretainedObject:attachment];
2056
+ UIImageView *imgView = _attachmentViews[key];
2057
+
2058
+ if (!imgView) {
2059
+ // It doesn't exist yet, create it
2060
+ imgView = [[UIImageView alloc] initWithFrame:rect];
2061
+ imgView.contentMode = UIViewContentModeScaleAspectFit;
2062
+ imgView.tintColor = [UIColor labelColor];
2063
+
2064
+ // Add it directly to the TextView
2065
+ [textView addSubview:imgView];
2066
+ }
2067
+
2068
+ // Update position (in case text moved/scrolled)
2069
+ if (!CGRectEqualToRect(imgView.frame, rect)) {
2070
+ imgView.frame = rect;
2071
+ }
2072
+ UIImage *targetImage =
2073
+ attachment.storedAnimatedImage ?: attachment.image;
2074
+
2075
+ // Only set if different to avoid resetting the animation
2076
+ // loop
2077
+ if (imgView.image != targetImage) {
2078
+ imgView.image = targetImage;
2079
+ }
2080
+
2081
+ // Ensure it is visible on top
2082
+ imgView.hidden = NO;
2083
+ [textView bringSubviewToFront:imgView];
2084
+
2085
+ activeAttachmentViews[key] = imgView;
2086
+ // Remove from the old map so we know it has been claimed
2087
+ [_attachmentViews removeObjectForKey:key];
2088
+ }
2089
+ }];
2090
+
2091
+ // Everything remaining in _attachmentViews is dead or off-screen
2092
+ for (UIImageView *danglingView in _attachmentViews.allValues) {
2093
+ [danglingView removeFromSuperview];
2094
+ }
2095
+ _attachmentViews = activeAttachmentViews;
2096
+ }
2097
+
2098
+ - (CGRect)frameForAttachment:(ImageAttachment *)attachment
2099
+ atRange:(NSRange)range {
2100
+ NSLayoutManager *layoutManager = textView.layoutManager;
2101
+ NSTextContainer *textContainer = textView.textContainer;
2102
+ NSTextStorage *storage = textView.textStorage;
2103
+
2104
+ NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:range
2105
+ actualCharacterRange:NULL];
2106
+ CGRect glyphRect = [layoutManager boundingRectForGlyphRange:glyphRange
2107
+ inTextContainer:textContainer];
2108
+
2109
+ CGRect lineRect =
2110
+ [layoutManager lineFragmentRectForGlyphAtIndex:glyphRange.location
2111
+ effectiveRange:NULL];
2112
+ CGSize attachmentSize = attachment.bounds.size;
2113
+
2114
+ UIFont *font = [storage attribute:NSFontAttributeName
2115
+ atIndex:range.location
2116
+ effectiveRange:NULL];
2117
+ if (!font) {
2118
+ font = [config primaryFont];
2119
+ }
2120
+
2121
+ // Calculate (Baseline Alignment)
2122
+ CGFloat targetY =
2123
+ CGRectGetMaxY(lineRect) + font.descender - attachmentSize.height;
2124
+ CGRect rect =
2125
+ CGRectMake(glyphRect.origin.x + textView.textContainerInset.left,
2126
+ targetY + textView.textContainerInset.top,
2127
+ attachmentSize.width, attachmentSize.height);
2128
+
2129
+ return CGRectIntegral(rect);
2130
+ }
1920
2131
  @end
@@ -88,4 +88,14 @@
88
88
  - (NSRegularExpression *)parsedLinkRegex;
89
89
  - (void)invalidateFonts;
90
90
  - (NSNumber *)scaledPrimaryFontSize;
91
+ - (CGFloat)checkboxListBoxSize;
92
+ - (void)setCheckboxListBoxSize:(CGFloat)newValue;
93
+ - (CGFloat)checkboxListGapWidth;
94
+ - (void)setCheckboxListGapWidth:(CGFloat)newValue;
95
+ - (CGFloat)checkboxListMarginLeft;
96
+ - (void)setCheckboxListMarginLeft:(CGFloat)newValue;
97
+ - (UIColor *)checkboxListBoxColor;
98
+ - (void)setCheckboxListBoxColor:(UIColor *)newValue;
99
+ - (UIImage *)checkboxCheckedImage;
100
+ - (UIImage *)checkboxUncheckedImage;
91
101
  @end
@@ -47,6 +47,12 @@
47
47
  UIColor *_codeBlockBgColor;
48
48
  LinkRegexConfig *_linkRegexConfig;
49
49
  NSRegularExpression *_parsedLinkRegex;
50
+ CGFloat _checkboxListBoxSize;
51
+ CGFloat _checkboxListGapWidth;
52
+ CGFloat _checkboxListMarginLeft;
53
+ UIColor *_checkboxListBoxColor;
54
+ UIImage *_checkboxCheckedImage;
55
+ UIImage *_checkboxUncheckedImage;
50
56
  }
51
57
 
52
58
  - (instancetype)init {
@@ -101,6 +107,12 @@
101
107
  copy->_codeBlockBorderRadius = _codeBlockBorderRadius;
102
108
  copy->_linkRegexConfig = [_linkRegexConfig copy];
103
109
  copy->_parsedLinkRegex = [_parsedLinkRegex copy];
110
+ copy->_checkboxListBoxSize = _checkboxListBoxSize;
111
+ copy->_checkboxListGapWidth = _checkboxListGapWidth;
112
+ copy->_checkboxListMarginLeft = _checkboxListMarginLeft;
113
+ copy->_checkboxListBoxColor = [_checkboxListBoxColor copy];
114
+ copy->_checkboxCheckedImage = _checkboxCheckedImage;
115
+ copy->_checkboxUncheckedImage = _checkboxUncheckedImage;
104
116
  return copy;
105
117
  }
106
118
 
@@ -527,4 +539,111 @@
527
539
  return @(scaledSize);
528
540
  }
529
541
 
542
+ - (CGFloat)checkboxListBoxSize {
543
+ return _checkboxListBoxSize;
544
+ }
545
+
546
+ - (void)setCheckboxListBoxSize:(CGFloat)newValue {
547
+ if (_checkboxListBoxSize != newValue) {
548
+ _checkboxListBoxSize = newValue;
549
+ // Invalidate checkbox images because box size changed
550
+ _checkboxCheckedImage = nil;
551
+ _checkboxUncheckedImage = nil;
552
+ }
553
+ }
554
+
555
+ - (CGFloat)checkboxListGapWidth {
556
+ return _checkboxListGapWidth;
557
+ }
558
+
559
+ - (void)setCheckboxListGapWidth:(CGFloat)newValue {
560
+ _checkboxListGapWidth = newValue;
561
+ }
562
+
563
+ - (CGFloat)checkboxListMarginLeft {
564
+ return _checkboxListMarginLeft;
565
+ }
566
+
567
+ - (void)setCheckboxListMarginLeft:(CGFloat)newValue {
568
+ _checkboxListMarginLeft = newValue;
569
+ }
570
+
571
+ - (UIColor *)checkboxListBoxColor {
572
+ return _checkboxListBoxColor;
573
+ }
574
+
575
+ - (void)setCheckboxListBoxColor:(UIColor *)newValue {
576
+ if (_checkboxListBoxColor != newValue) {
577
+ _checkboxListBoxColor = newValue;
578
+ // Invalidate checkbox images because color changed
579
+ _checkboxCheckedImage = nil;
580
+ _checkboxUncheckedImage = nil;
581
+ }
582
+ }
583
+
584
+ - (UIImage *)checkboxCheckedImage {
585
+ if (!_checkboxCheckedImage) {
586
+ _checkboxCheckedImage = [self generateCheckboxImage:YES];
587
+ }
588
+ return _checkboxCheckedImage;
589
+ }
590
+
591
+ - (UIImage *)checkboxUncheckedImage {
592
+ if (!_checkboxUncheckedImage) {
593
+ _checkboxUncheckedImage = [self generateCheckboxImage:NO];
594
+ }
595
+ return _checkboxUncheckedImage;
596
+ }
597
+
598
+ - (UIImage *)generateCheckboxImage:(BOOL)isChecked {
599
+ CGFloat boxSize = self.checkboxListBoxSize;
600
+ UIColor *boxColor = self.checkboxListBoxColor ?: [UIColor blackColor];
601
+
602
+ UIGraphicsBeginImageContextWithOptions(CGSizeMake(boxSize, boxSize), NO, 0.0);
603
+ CGRect localRect = CGRectMake(0, 0, boxSize, boxSize);
604
+ CGFloat cornerRadius = boxSize * 0.15f;
605
+ CGFloat strokeWidth = boxSize * 0.1f;
606
+ CGRect insetRect =
607
+ CGRectInset(localRect, strokeWidth / 2.0, strokeWidth / 2.0);
608
+
609
+ // Draw Box
610
+ UIBezierPath *boxPath = [UIBezierPath bezierPathWithRoundedRect:insetRect
611
+ cornerRadius:cornerRadius];
612
+ [boxPath setLineWidth:strokeWidth];
613
+
614
+ if (isChecked) {
615
+ [[boxColor colorWithAlphaComponent:1.0] setFill];
616
+ [boxPath fill];
617
+ [[boxColor colorWithAlphaComponent:1.0] setStroke];
618
+ [boxPath stroke];
619
+
620
+ // Draw Checkmark
621
+ UIBezierPath *checkPath = [UIBezierPath bezierPath];
622
+ CGFloat startX = insetRect.origin.x + insetRect.size.width * 0.25;
623
+ CGFloat startY = insetRect.origin.y + insetRect.size.height * 0.5;
624
+ CGFloat midX = insetRect.origin.x + insetRect.size.width * 0.45;
625
+ CGFloat midY = insetRect.origin.y + insetRect.size.height * 0.65;
626
+ CGFloat endX = insetRect.origin.x + insetRect.size.width * 0.75;
627
+ CGFloat endY = insetRect.origin.y + insetRect.size.height * 0.35;
628
+
629
+ [checkPath moveToPoint:CGPointMake(startX, startY)];
630
+ [checkPath addLineToPoint:CGPointMake(midX, midY)];
631
+ [checkPath addLineToPoint:CGPointMake(endX, endY)];
632
+
633
+ [checkPath setLineWidth:strokeWidth * 1.5];
634
+ [[UIColor whiteColor] setStroke];
635
+ [checkPath setLineCapStyle:kCGLineCapRound];
636
+ [checkPath setLineJoinStyle:kCGLineJoinRound];
637
+ [checkPath stroke];
638
+ } else {
639
+ [[boxColor colorWithAlphaComponent:1.0] setStroke];
640
+ [boxPath stroke];
641
+ }
642
+
643
+ UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
644
+ UIGraphicsEndImageContext();
645
+
646
+ return result;
647
+ }
648
+
530
649
  @end