@apollohg/react-native-prose-editor 0.5.1 → 0.5.3

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 (31) hide show
  1. package/README.md +18 -15
  2. package/android/src/main/java/com/apollohg/editor/EditorAddons.kt +4 -2
  3. package/android/src/main/java/com/apollohg/editor/EditorEditText.kt +33 -1
  4. package/android/src/main/java/com/apollohg/editor/EditorTheme.kt +23 -0
  5. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoView.kt +39 -6
  6. package/android/src/main/java/com/apollohg/editor/NativeEditorModule.kt +15 -1
  7. package/android/src/main/java/com/apollohg/editor/NativeProseViewerExpoView.kt +44 -7
  8. package/android/src/main/java/com/apollohg/editor/RenderBridge.kt +24 -4
  9. package/dist/NativeEditorBridge.d.ts +8 -0
  10. package/dist/NativeEditorBridge.js +16 -0
  11. package/dist/NativeProseViewer.d.ts +25 -5
  12. package/dist/NativeProseViewer.js +212 -13
  13. package/dist/NativeRichTextEditor.d.ts +2 -0
  14. package/dist/NativeRichTextEditor.js +417 -31
  15. package/dist/addons.d.ts +20 -0
  16. package/dist/addons.js +4 -0
  17. package/dist/index.d.ts +2 -2
  18. package/ios/EditorAddons.swift +2 -0
  19. package/ios/EditorCore.xcframework/ios-arm64/libeditor_core.a +0 -0
  20. package/ios/EditorCore.xcframework/ios-arm64_x86_64-simulator/libeditor_core.a +0 -0
  21. package/ios/EditorLayoutManager.swift +10 -1
  22. package/ios/EditorTheme.swift +25 -0
  23. package/ios/NativeEditorExpoView.swift +56 -6
  24. package/ios/NativeEditorModule.swift +14 -1
  25. package/ios/NativeProseViewerExpoView.swift +62 -11
  26. package/ios/RenderBridge.swift +40 -16
  27. package/ios/RichTextEditorView.swift +4 -0
  28. package/package.json +1 -1
  29. package/rust/android/arm64-v8a/libeditor_core.so +0 -0
  30. package/rust/android/armeabi-v7a/libeditor_core.so +0 -0
  31. package/rust/android/x86_64/libeditor_core.so +0 -0
@@ -2062,12 +2062,54 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
2062
2062
  onAddonEvent(["eventJson": json])
2063
2063
  }
2064
2064
 
2065
- private func emitMentionSelect(trigger: String, suggestion: NativeMentionSuggestion) {
2065
+ private func resolvedMentionAttrs(
2066
+ trigger: String,
2067
+ suggestion: NativeMentionSuggestion
2068
+ ) -> [String: Any] {
2069
+ var attrs = suggestion.attrs
2070
+ if attrs["label"] == nil {
2071
+ attrs["label"] = suggestion.label
2072
+ }
2073
+ if attrs["mentionSuggestionChar"] == nil {
2074
+ attrs["mentionSuggestionChar"] = trigger
2075
+ }
2076
+ return attrs
2077
+ }
2078
+
2079
+ private func emitMentionSelect(
2080
+ trigger: String,
2081
+ suggestion: NativeMentionSuggestion,
2082
+ attrs: [String: Any]
2083
+ ) {
2066
2084
  let payload: [String: Any] = [
2067
2085
  "type": "mentionsSelect",
2068
2086
  "trigger": trigger,
2069
2087
  "suggestionKey": suggestion.key,
2070
- "attrs": suggestion.attrs,
2088
+ "attrs": attrs,
2089
+ ]
2090
+ guard let data = try? JSONSerialization.data(withJSONObject: payload),
2091
+ let json = String(data: data, encoding: .utf8)
2092
+ else {
2093
+ return
2094
+ }
2095
+ onAddonEvent(["eventJson": json])
2096
+ }
2097
+
2098
+ private func emitMentionSelectRequest(
2099
+ trigger: String,
2100
+ suggestion: NativeMentionSuggestion,
2101
+ attrs: [String: Any],
2102
+ range: MentionQueryState
2103
+ ) {
2104
+ let payload: [String: Any] = [
2105
+ "type": "mentionsSelectRequest",
2106
+ "trigger": trigger,
2107
+ "suggestionKey": suggestion.key,
2108
+ "attrs": attrs,
2109
+ "range": [
2110
+ "anchor": Int(range.anchor),
2111
+ "head": Int(range.head),
2112
+ ],
2071
2113
  ]
2072
2114
  guard let data = try? JSONSerialization.data(withJSONObject: payload),
2073
2115
  let json = String(data: data, encoding: .utf8)
@@ -2175,9 +2217,17 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
2175
2217
  return
2176
2218
  }
2177
2219
 
2178
- var attrs = suggestion.attrs
2179
- if attrs["label"] == nil {
2180
- attrs["label"] = suggestion.label
2220
+ let attrs = resolvedMentionAttrs(trigger: mentions.trigger, suggestion: suggestion)
2221
+ if mentions.resolveSelectionAttrs {
2222
+ emitMentionSelectRequest(
2223
+ trigger: mentions.trigger,
2224
+ suggestion: suggestion,
2225
+ attrs: attrs,
2226
+ range: queryState
2227
+ )
2228
+ lastMentionEventJSON = nil
2229
+ clearMentionQueryStateAndHidePopover()
2230
+ return
2181
2231
  }
2182
2232
  let payload: [String: Any] = [
2183
2233
  "type": "doc",
@@ -2199,7 +2249,7 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
2199
2249
  json: json
2200
2250
  )
2201
2251
  richTextView.textView.applyUpdateJSON(updateJSON)
2202
- emitMentionSelect(trigger: mentions.trigger, suggestion: suggestion)
2252
+ emitMentionSelect(trigger: mentions.trigger, suggestion: suggestion, attrs: attrs)
2203
2253
  lastMentionEventJSON = nil
2204
2254
  clearMentionQueryStateAndHidePopover()
2205
2255
  }
@@ -268,6 +268,13 @@ public class NativeEditorModule: Module {
268
268
  }
269
269
  return editorSetJson(id: editorId, json: json)
270
270
  }
271
+ Function("renderDocumentHtml") { (configJson: String, html: String) -> String in
272
+ let editorId = editorCreate(configJson: configJson)
273
+ defer {
274
+ editorDestroy(id: editorId)
275
+ }
276
+ return editorSetHtml(id: editorId, html: html)
277
+ }
271
278
  Function("editorReplaceHtml") { (id: Int, html: String) -> String in
272
279
  editorReplaceHtml(id: UInt64(id), html: html)
273
280
  }
@@ -375,7 +382,7 @@ public class NativeEditorModule: Module {
375
382
 
376
383
  View(NativeProseViewerExpoView.self) {
377
384
  ViewName("NativeProseViewer")
378
- Events("onContentHeightChange", "onPressMention")
385
+ Events("onContentHeightChange", "onPressLink", "onPressMention")
379
386
 
380
387
  Prop("renderJson") { (view: NativeProseViewerExpoView, renderJson: String?) in
381
388
  view.setRenderJson(renderJson)
@@ -383,6 +390,12 @@ public class NativeEditorModule: Module {
383
390
  Prop("themeJson") { (view: NativeProseViewerExpoView, themeJson: String?) in
384
391
  view.setThemeJson(themeJson)
385
392
  }
393
+ Prop("enableLinkTaps") { (view: NativeProseViewerExpoView, enableLinkTaps: Bool?) in
394
+ view.setEnableLinkTaps(enableLinkTaps)
395
+ }
396
+ Prop("interceptLinkTaps") { (view: NativeProseViewerExpoView, interceptLinkTaps: Bool?) in
397
+ view.setInterceptLinkTaps(interceptLinkTaps)
398
+ }
386
399
  }
387
400
  }
388
401
  }
@@ -3,6 +3,7 @@ import UIKit
3
3
 
4
4
  final class NativeProseViewerExpoView: ExpoView {
5
5
  let onContentHeightChange = EventDispatcher()
6
+ let onPressLink = EventDispatcher()
6
7
  let onPressMention = EventDispatcher()
7
8
 
8
9
  private let textView = EditorTextView(frame: .zero, textContainer: nil)
@@ -10,11 +11,14 @@ final class NativeProseViewerExpoView: ExpoView {
10
11
  private var lastThemeJSON: String?
11
12
  private var lastEmittedContentHeight: CGFloat = 0
12
13
  private var lastMeasuredWidth: CGFloat = 0
14
+ private var allowContentHeightShrink = true
15
+ private var enableLinkTaps = true
16
+ private var interceptLinkTaps = false
13
17
 
14
- private lazy var mentionTapRecognizer: UITapGestureRecognizer = {
18
+ private lazy var interactiveTapRecognizer: UITapGestureRecognizer = {
15
19
  let recognizer = UITapGestureRecognizer(
16
20
  target: self,
17
- action: #selector(handleMentionTap(_:))
21
+ action: #selector(handleInteractiveTap(_:))
18
22
  )
19
23
  recognizer.cancelsTouchesInView = false
20
24
  return recognizer
@@ -35,19 +39,29 @@ final class NativeProseViewerExpoView: ExpoView {
35
39
  textView.onHeightMayChange = { [weak self] measuredHeight in
36
40
  self?.emitContentHeightIfNeeded(measuredHeight: measuredHeight, force: true)
37
41
  }
38
- textView.addGestureRecognizer(mentionTapRecognizer)
42
+ textView.addGestureRecognizer(interactiveTapRecognizer)
39
43
  addSubview(textView)
40
44
  }
41
45
 
46
+ func setEnableLinkTaps(_ enabled: Bool?) {
47
+ enableLinkTaps = enabled ?? true
48
+ }
49
+
50
+ func setInterceptLinkTaps(_ intercept: Bool?) {
51
+ interceptLinkTaps = intercept ?? false
52
+ }
53
+
42
54
  func setRenderJson(_ renderJson: String?) {
43
55
  guard lastRenderJSON != renderJson else { return }
44
56
  lastRenderJSON = renderJson
57
+ allowContentHeightShrink = true
45
58
  applyRenderJSON()
46
59
  }
47
60
 
48
61
  func setThemeJson(_ themeJson: String?) {
49
62
  guard lastThemeJSON != themeJson else { return }
50
63
  lastThemeJSON = themeJson
64
+ allowContentHeightShrink = true
51
65
  let theme = EditorTheme.from(json: themeJson)
52
66
  textView.applyTheme(theme)
53
67
  let cornerRadius = theme?.borderRadius ?? 0
@@ -87,31 +101,43 @@ final class NativeProseViewerExpoView: ExpoView {
87
101
  ? bounds.width
88
102
  : (superview?.bounds.width ?? UIScreen.main.bounds.width)
89
103
  let fittedHeight = measuredHeight
90
- ?? textView.sizeThatFits(
91
- CGSize(width: resolvedWidth, height: CGFloat.greatestFiniteMagnitude)
92
- ).height
104
+ ?? textView.measuredAutoGrowHeightForTesting(width: resolvedWidth)
93
105
  let contentHeight = ceil(fittedHeight)
94
106
  guard contentHeight > 0 else { return }
107
+ guard allowContentHeightShrink || contentHeight >= lastEmittedContentHeight else { return }
108
+ allowContentHeightShrink = false
95
109
  guard force || abs(contentHeight - lastEmittedContentHeight) > 0.5 else { return }
96
110
  lastEmittedContentHeight = contentHeight
97
111
  invalidateIntrinsicContentSize()
98
112
  onContentHeightChange(["contentHeight": contentHeight])
99
113
  }
100
114
 
101
- @objc private func handleMentionTap(_ recognizer: UITapGestureRecognizer) {
102
- guard recognizer.state == .ended,
103
- let mention = mentionHit(at: recognizer.location(in: textView))
104
- else {
115
+ @objc private func handleInteractiveTap(_ recognizer: UITapGestureRecognizer) {
116
+ guard recognizer.state == .ended else {
117
+ return
118
+ }
119
+
120
+ let location = recognizer.location(in: textView)
121
+ if enableLinkTaps, let link = linkHit(at: location) {
122
+ if interceptLinkTaps {
123
+ onPressLink([
124
+ "href": link.href,
125
+ "text": link.text,
126
+ ])
127
+ } else {
128
+ openLink(link.href)
129
+ }
105
130
  return
106
131
  }
107
132
 
133
+ guard let mention = mentionHit(at: location) else { return }
108
134
  onPressMention([
109
135
  "docPos": mention.docPos,
110
136
  "label": mention.label,
111
137
  ])
112
138
  }
113
139
 
114
- private func mentionHit(at location: CGPoint) -> (docPos: Int, label: String)? {
140
+ private func characterIndex(at location: CGPoint) -> Int? {
115
141
  let textStorage = textView.textStorage
116
142
  guard textStorage.length > 0 else { return nil }
117
143
 
@@ -130,6 +156,26 @@ final class NativeProseViewerExpoView: ExpoView {
130
156
  guard glyphIndex < layoutManager.numberOfGlyphs else { return nil }
131
157
  let characterIndex = layoutManager.characterIndexForGlyph(at: glyphIndex)
132
158
  guard characterIndex < textStorage.length else { return nil }
159
+ return characterIndex
160
+ }
161
+
162
+ private func linkHit(at location: CGPoint) -> (href: String, text: String)? {
163
+ let textStorage = textView.textStorage
164
+ guard let characterIndex = characterIndex(at: location) else { return nil }
165
+
166
+ var effectiveRange = NSRange(location: 0, length: 0)
167
+ let attrs = textStorage.attributes(at: characterIndex, effectiveRange: &effectiveRange)
168
+ guard let href = attrs[RenderBridgeAttributes.linkHref] as? String, !href.isEmpty else {
169
+ return nil
170
+ }
171
+
172
+ let text = (textStorage.string as NSString).substring(with: effectiveRange)
173
+ return (href: href, text: text)
174
+ }
175
+
176
+ private func mentionHit(at location: CGPoint) -> (docPos: Int, label: String)? {
177
+ let textStorage = textView.textStorage
178
+ guard let characterIndex = characterIndex(at: location) else { return nil }
133
179
 
134
180
  var effectiveRange = NSRange(location: 0, length: 0)
135
181
  let attrs = textStorage.attributes(at: characterIndex, effectiveRange: &effectiveRange)
@@ -143,4 +189,9 @@ final class NativeProseViewerExpoView: ExpoView {
143
189
  let label = (textStorage.string as NSString).substring(with: effectiveRange)
144
190
  return (docPos: docPos, label: label)
145
191
  }
192
+
193
+ private func openLink(_ href: String) {
194
+ guard let url = URL(string: href) else { return }
195
+ UIApplication.shared.open(url, options: [:], completionHandler: nil)
196
+ }
146
197
  }
@@ -89,6 +89,9 @@ enum RenderBridgeAttributes {
89
89
  /// Stores the rendered list marker scale for unordered bullets.
90
90
  static let listMarkerScale = NSAttributedString.Key("com.apollohg.editor.listMarkerScale")
91
91
 
92
+ /// Stores the paragraph base font used to render the list marker.
93
+ static let listMarkerBaseFont = NSAttributedString.Key("com.apollohg.editor.listMarkerBaseFont")
94
+
92
95
  /// Stores the reserved list marker gutter width.
93
96
  static let listMarkerWidth = NSAttributedString.Key("com.apollohg.editor.listMarkerWidth")
94
97
 
@@ -104,6 +107,9 @@ enum RenderBridgeAttributes {
104
107
  /// Marks synthetic zero-width placeholders used only for UIKit layout.
105
108
  static let syntheticPlaceholder = NSAttributedString.Key("com.apollohg.editor.syntheticPlaceholder")
106
109
 
110
+ /// Stores the link href for visually styled link text without enabling UITextView's default link interaction.
111
+ static let linkHref = NSAttributedString.Key("com.apollohg.editor.linkHref")
112
+
107
113
  /// Stores the owning top-level document child index for partial native patching.
108
114
  static let topLevelChildIndex = NSAttributedString.Key("com.apollohg.editor.topLevelChildIndex")
109
115
  }
@@ -242,7 +248,8 @@ final class RenderBridge {
242
248
  let attrs = applyBlockStyle(
243
249
  to: baseAttrs,
244
250
  blockStack: blockStack,
245
- theme: theme
251
+ theme: theme,
252
+ blockBaseFont: blockFont
246
253
  )
247
254
  let attributedText = NSAttributedString(string: text, attributes: attrs)
248
255
  result.append(
@@ -322,6 +329,9 @@ final class RenderBridge {
322
329
  let nodeType = element["nodeType"] as? String ?? ""
323
330
  let label = element["label"] as? String ?? "?"
324
331
  let docPos = jsonUInt32(element["docPos"])
332
+ let mentionTheme = (element["mentionTheme"] as? [String: Any]).map(
333
+ EditorMentionTheme.init(dictionary:)
334
+ )
325
335
  let attrStr = attributedStringForOpaqueInlineAtom(
326
336
  nodeType: nodeType,
327
337
  label: label,
@@ -330,7 +340,8 @@ final class RenderBridge {
330
340
  textColor: textColor,
331
341
  blockStack: blockStack,
332
342
  topLevelChildIndex: topLevelChildIndex,
333
- theme: theme
343
+ theme: theme,
344
+ mentionTheme: mentionTheme
334
345
  )
335
346
  result.append(
336
347
  attributedStringApplyingLeadingTopLevelChildIndexIfNeeded(
@@ -556,11 +567,11 @@ final class RenderBridge {
556
567
  var traits: UIFontDescriptor.SymbolicTraits = []
557
568
  var useMonospace = false
558
569
  for mark in marks {
570
+ let markObject = mark as? [String: Any]
559
571
  let markType: String
560
572
  if let markName = mark as? String {
561
573
  markType = markName
562
- } else if let markObject = mark as? [String: Any],
563
- let resolvedType = markObject["type"] as? String {
574
+ } else if let resolvedType = markObject?["type"] as? String {
564
575
  markType = resolvedType
565
576
  } else {
566
577
  continue
@@ -580,6 +591,9 @@ final class RenderBridge {
580
591
  case "link":
581
592
  attrs[.underlineStyle] = NSUnderlineStyle.single.rawValue
582
593
  attrs[.foregroundColor] = UIColor.systemBlue
594
+ if let href = markObject?["href"] as? String, !href.isEmpty {
595
+ attrs[RenderBridgeAttributes.linkHref] = href
596
+ }
583
597
  default:
584
598
  break
585
599
  }
@@ -635,7 +649,8 @@ final class RenderBridge {
635
649
  let styledAttrs = applyBlockStyle(
636
650
  to: attrs,
637
651
  blockStack: blockStack,
638
- theme: theme
652
+ theme: theme,
653
+ blockBaseFont: blockFont
639
654
  )
640
655
 
641
656
  switch nodeType {
@@ -731,7 +746,8 @@ final class RenderBridge {
731
746
  textColor: UIColor,
732
747
  blockStack: [BlockContext],
733
748
  topLevelChildIndex _: Int?,
734
- theme: EditorTheme?
749
+ theme: EditorTheme?,
750
+ mentionTheme: EditorMentionTheme?
735
751
  ) -> NSAttributedString {
736
752
  let blockFont = resolvedFont(for: blockStack, baseFont: baseFont, theme: theme)
737
753
  let blockColor = resolvedTextColor(for: blockStack, textColor: textColor, theme: theme)
@@ -739,9 +755,11 @@ final class RenderBridge {
739
755
  attrs[RenderBridgeAttributes.voidNodeType] = nodeType
740
756
  attrs[RenderBridgeAttributes.docPos] = docPos
741
757
  if nodeType == "mention" {
742
- attrs[.foregroundColor] = theme?.mentions?.textColor ?? blockColor
743
- attrs[.backgroundColor] = theme?.mentions?.backgroundColor ?? UIColor.systemBlue.withAlphaComponent(0.12)
744
- if let mentionFont = mentionFont(from: blockFont, theme: theme?.mentions) {
758
+ let resolvedMentionTheme = theme?.mentions?.merged(with: mentionTheme) ?? mentionTheme
759
+ attrs[.foregroundColor] = resolvedMentionTheme?.textColor ?? blockColor
760
+ attrs[.backgroundColor] =
761
+ resolvedMentionTheme?.backgroundColor ?? UIColor.systemBlue.withAlphaComponent(0.12)
762
+ if let mentionFont = mentionFont(from: blockFont, theme: resolvedMentionTheme) {
745
763
  attrs[.font] = mentionFont
746
764
  }
747
765
  } else {
@@ -750,7 +768,8 @@ final class RenderBridge {
750
768
  let styledAttrs = applyBlockStyle(
751
769
  to: attrs,
752
770
  blockStack: blockStack,
753
- theme: theme
771
+ theme: theme,
772
+ blockBaseFont: blockFont
754
773
  )
755
774
 
756
775
  let visibleText = nodeType == "mention" ? label : "[\(label)]"
@@ -923,16 +942,18 @@ final class RenderBridge {
923
942
  private static func applyBlockStyle(
924
943
  to attrs: [NSAttributedString.Key: Any],
925
944
  blockStack: [BlockContext],
926
- theme: EditorTheme?
945
+ theme: EditorTheme?,
946
+ blockBaseFont: UIFont? = nil
927
947
  ) -> [NSAttributedString.Key: Any] {
928
948
  guard let currentBlock = effectiveBlockContext(blockStack) else { return attrs }
929
949
  var mutableAttrs = attrs
930
- let blockFont = mutableAttrs[.font] as? UIFont ?? .systemFont(ofSize: 16)
950
+ let renderedFont = mutableAttrs[.font] as? UIFont ?? .systemFont(ofSize: 16)
951
+ let paragraphBaseFont = blockBaseFont ?? renderedFont
931
952
  mutableAttrs[.paragraphStyle] = paragraphStyleForBlock(
932
953
  currentBlock,
933
954
  blockStack: blockStack,
934
955
  theme: theme,
935
- baseFont: blockFont
956
+ baseFont: paragraphBaseFont
936
957
  )
937
958
  mutableAttrs[RenderBridgeAttributes.blockNodeType] = currentBlock.nodeType
938
959
  mutableAttrs[RenderBridgeAttributes.blockDepth] = currentBlock.depth
@@ -943,10 +964,11 @@ final class RenderBridge {
943
964
  mutableAttrs[RenderBridgeAttributes.listMarkerContext] = markerContext
944
965
  mutableAttrs[RenderBridgeAttributes.listMarkerColor] = theme?.list?.markerColor
945
966
  mutableAttrs[RenderBridgeAttributes.listMarkerScale] = theme?.list?.markerScale
967
+ mutableAttrs[RenderBridgeAttributes.listMarkerBaseFont] = paragraphBaseFont
946
968
  mutableAttrs[RenderBridgeAttributes.listMarkerWidth] = listMarkerWidth(
947
969
  for: currentBlock,
948
970
  theme: theme,
949
- baseFont: blockFont
971
+ baseFont: paragraphBaseFont
950
972
  )
951
973
  }
952
974
  if blockquoteDepth(in: blockStack) > 0 {
@@ -977,7 +999,8 @@ final class RenderBridge {
977
999
  var attrs = applyBlockStyle(
978
1000
  to: defaultAttributes(baseFont: baseFont, textColor: textColor),
979
1001
  blockStack: blockStack,
980
- theme: theme
1002
+ theme: theme,
1003
+ blockBaseFont: baseFont
981
1004
  )
982
1005
  if let topLevelChildIndex {
983
1006
  attrs[RenderBridgeAttributes.topLevelChildIndex] = NSNumber(value: topLevelChildIndex)
@@ -1280,7 +1303,8 @@ final class RenderBridge {
1280
1303
  var styledAttrs = applyBlockStyle(
1281
1304
  to: attrs,
1282
1305
  blockStack: placeholderBlockStack,
1283
- theme: theme
1306
+ theme: theme,
1307
+ blockBaseFont: blockFont
1284
1308
  )
1285
1309
  if let paragraphStyle = (styledAttrs[.paragraphStyle] as? NSParagraphStyle)?.mutableCopy()
1286
1310
  as? NSMutableParagraphStyle
@@ -858,6 +858,7 @@ final class EditorTextView: UITextView, UITextViewDelegate, UIGestureRecognizerD
858
858
  /// The base background color before theme overrides.
859
859
  var baseBackgroundColor: UIColor = .systemBackground
860
860
  var baseTextContainerInset: UIEdgeInsets = .zero
861
+ var baseLineFragmentPadding: CGFloat = 0
861
862
 
862
863
  /// Optional render theme supplied by React.
863
864
  var theme: EditorTheme? {
@@ -872,8 +873,10 @@ final class EditorTextView: UITextView, UITextViewDelegate, UIGestureRecognizerD
872
873
  bottom: contentInsets.bottom ?? 0,
873
874
  right: contentInsets.right ?? 0
874
875
  )
876
+ textContainer.lineFragmentPadding = 0
875
877
  } else {
876
878
  textContainerInset = baseTextContainerInset
879
+ textContainer.lineFragmentPadding = baseLineFragmentPadding
877
880
  }
878
881
  invalidateAutoGrowHeightMeasurement()
879
882
  setNeedsLayout()
@@ -1040,6 +1043,7 @@ final class EditorTextView: UITextView, UITextViewDelegate, UIGestureRecognizerD
1040
1043
  textColor = baseTextColor
1041
1044
  backgroundColor = baseBackgroundColor
1042
1045
  baseTextContainerInset = textContainerInset
1046
+ baseLineFragmentPadding = textContainer.lineFragmentPadding
1043
1047
  visibleSelectionTintColor = tintColor
1044
1048
 
1045
1049
  // Register as the text storage delegate so we can detect unauthorized
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apollohg/react-native-prose-editor",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "description": "Native rich text editor with Rust core for React Native",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/apollohg/react-native-prose-editor",