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

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.
@@ -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
  }
@@ -10,6 +10,7 @@ final class NativeProseViewerExpoView: ExpoView {
10
10
  private var lastThemeJSON: String?
11
11
  private var lastEmittedContentHeight: CGFloat = 0
12
12
  private var lastMeasuredWidth: CGFloat = 0
13
+ private var allowContentHeightShrink = true
13
14
 
14
15
  private lazy var mentionTapRecognizer: UITapGestureRecognizer = {
15
16
  let recognizer = UITapGestureRecognizer(
@@ -42,12 +43,14 @@ final class NativeProseViewerExpoView: ExpoView {
42
43
  func setRenderJson(_ renderJson: String?) {
43
44
  guard lastRenderJSON != renderJson else { return }
44
45
  lastRenderJSON = renderJson
46
+ allowContentHeightShrink = true
45
47
  applyRenderJSON()
46
48
  }
47
49
 
48
50
  func setThemeJson(_ themeJson: String?) {
49
51
  guard lastThemeJSON != themeJson else { return }
50
52
  lastThemeJSON = themeJson
53
+ allowContentHeightShrink = true
51
54
  let theme = EditorTheme.from(json: themeJson)
52
55
  textView.applyTheme(theme)
53
56
  let cornerRadius = theme?.borderRadius ?? 0
@@ -87,11 +90,11 @@ final class NativeProseViewerExpoView: ExpoView {
87
90
  ? bounds.width
88
91
  : (superview?.bounds.width ?? UIScreen.main.bounds.width)
89
92
  let fittedHeight = measuredHeight
90
- ?? textView.sizeThatFits(
91
- CGSize(width: resolvedWidth, height: CGFloat.greatestFiniteMagnitude)
92
- ).height
93
+ ?? textView.measuredAutoGrowHeightForTesting(width: resolvedWidth)
93
94
  let contentHeight = ceil(fittedHeight)
94
95
  guard contentHeight > 0 else { return }
96
+ guard allowContentHeightShrink || contentHeight >= lastEmittedContentHeight else { return }
97
+ allowContentHeightShrink = false
95
98
  guard force || abs(contentHeight - lastEmittedContentHeight) > 0.5 else { return }
96
99
  lastEmittedContentHeight = contentHeight
97
100
  invalidateIntrinsicContentSize()
@@ -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
 
@@ -242,7 +245,8 @@ final class RenderBridge {
242
245
  let attrs = applyBlockStyle(
243
246
  to: baseAttrs,
244
247
  blockStack: blockStack,
245
- theme: theme
248
+ theme: theme,
249
+ blockBaseFont: blockFont
246
250
  )
247
251
  let attributedText = NSAttributedString(string: text, attributes: attrs)
248
252
  result.append(
@@ -322,6 +326,9 @@ final class RenderBridge {
322
326
  let nodeType = element["nodeType"] as? String ?? ""
323
327
  let label = element["label"] as? String ?? "?"
324
328
  let docPos = jsonUInt32(element["docPos"])
329
+ let mentionTheme = (element["mentionTheme"] as? [String: Any]).map(
330
+ EditorMentionTheme.init(dictionary:)
331
+ )
325
332
  let attrStr = attributedStringForOpaqueInlineAtom(
326
333
  nodeType: nodeType,
327
334
  label: label,
@@ -330,7 +337,8 @@ final class RenderBridge {
330
337
  textColor: textColor,
331
338
  blockStack: blockStack,
332
339
  topLevelChildIndex: topLevelChildIndex,
333
- theme: theme
340
+ theme: theme,
341
+ mentionTheme: mentionTheme
334
342
  )
335
343
  result.append(
336
344
  attributedStringApplyingLeadingTopLevelChildIndexIfNeeded(
@@ -635,7 +643,8 @@ final class RenderBridge {
635
643
  let styledAttrs = applyBlockStyle(
636
644
  to: attrs,
637
645
  blockStack: blockStack,
638
- theme: theme
646
+ theme: theme,
647
+ blockBaseFont: blockFont
639
648
  )
640
649
 
641
650
  switch nodeType {
@@ -731,7 +740,8 @@ final class RenderBridge {
731
740
  textColor: UIColor,
732
741
  blockStack: [BlockContext],
733
742
  topLevelChildIndex _: Int?,
734
- theme: EditorTheme?
743
+ theme: EditorTheme?,
744
+ mentionTheme: EditorMentionTheme?
735
745
  ) -> NSAttributedString {
736
746
  let blockFont = resolvedFont(for: blockStack, baseFont: baseFont, theme: theme)
737
747
  let blockColor = resolvedTextColor(for: blockStack, textColor: textColor, theme: theme)
@@ -739,9 +749,11 @@ final class RenderBridge {
739
749
  attrs[RenderBridgeAttributes.voidNodeType] = nodeType
740
750
  attrs[RenderBridgeAttributes.docPos] = docPos
741
751
  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) {
752
+ let resolvedMentionTheme = theme?.mentions?.merged(with: mentionTheme) ?? mentionTheme
753
+ attrs[.foregroundColor] = resolvedMentionTheme?.textColor ?? blockColor
754
+ attrs[.backgroundColor] =
755
+ resolvedMentionTheme?.backgroundColor ?? UIColor.systemBlue.withAlphaComponent(0.12)
756
+ if let mentionFont = mentionFont(from: blockFont, theme: resolvedMentionTheme) {
745
757
  attrs[.font] = mentionFont
746
758
  }
747
759
  } else {
@@ -750,7 +762,8 @@ final class RenderBridge {
750
762
  let styledAttrs = applyBlockStyle(
751
763
  to: attrs,
752
764
  blockStack: blockStack,
753
- theme: theme
765
+ theme: theme,
766
+ blockBaseFont: blockFont
754
767
  )
755
768
 
756
769
  let visibleText = nodeType == "mention" ? label : "[\(label)]"
@@ -923,16 +936,18 @@ final class RenderBridge {
923
936
  private static func applyBlockStyle(
924
937
  to attrs: [NSAttributedString.Key: Any],
925
938
  blockStack: [BlockContext],
926
- theme: EditorTheme?
939
+ theme: EditorTheme?,
940
+ blockBaseFont: UIFont? = nil
927
941
  ) -> [NSAttributedString.Key: Any] {
928
942
  guard let currentBlock = effectiveBlockContext(blockStack) else { return attrs }
929
943
  var mutableAttrs = attrs
930
- let blockFont = mutableAttrs[.font] as? UIFont ?? .systemFont(ofSize: 16)
944
+ let renderedFont = mutableAttrs[.font] as? UIFont ?? .systemFont(ofSize: 16)
945
+ let paragraphBaseFont = blockBaseFont ?? renderedFont
931
946
  mutableAttrs[.paragraphStyle] = paragraphStyleForBlock(
932
947
  currentBlock,
933
948
  blockStack: blockStack,
934
949
  theme: theme,
935
- baseFont: blockFont
950
+ baseFont: paragraphBaseFont
936
951
  )
937
952
  mutableAttrs[RenderBridgeAttributes.blockNodeType] = currentBlock.nodeType
938
953
  mutableAttrs[RenderBridgeAttributes.blockDepth] = currentBlock.depth
@@ -943,10 +958,11 @@ final class RenderBridge {
943
958
  mutableAttrs[RenderBridgeAttributes.listMarkerContext] = markerContext
944
959
  mutableAttrs[RenderBridgeAttributes.listMarkerColor] = theme?.list?.markerColor
945
960
  mutableAttrs[RenderBridgeAttributes.listMarkerScale] = theme?.list?.markerScale
961
+ mutableAttrs[RenderBridgeAttributes.listMarkerBaseFont] = paragraphBaseFont
946
962
  mutableAttrs[RenderBridgeAttributes.listMarkerWidth] = listMarkerWidth(
947
963
  for: currentBlock,
948
964
  theme: theme,
949
- baseFont: blockFont
965
+ baseFont: paragraphBaseFont
950
966
  )
951
967
  }
952
968
  if blockquoteDepth(in: blockStack) > 0 {
@@ -977,7 +993,8 @@ final class RenderBridge {
977
993
  var attrs = applyBlockStyle(
978
994
  to: defaultAttributes(baseFont: baseFont, textColor: textColor),
979
995
  blockStack: blockStack,
980
- theme: theme
996
+ theme: theme,
997
+ blockBaseFont: baseFont
981
998
  )
982
999
  if let topLevelChildIndex {
983
1000
  attrs[RenderBridgeAttributes.topLevelChildIndex] = NSNumber(value: topLevelChildIndex)
@@ -1280,7 +1297,8 @@ final class RenderBridge {
1280
1297
  var styledAttrs = applyBlockStyle(
1281
1298
  to: attrs,
1282
1299
  blockStack: placeholderBlockStack,
1283
- theme: theme
1300
+ theme: theme,
1301
+ blockBaseFont: blockFont
1284
1302
  )
1285
1303
  if let paragraphStyle = (styledAttrs[.paragraphStyle] as? NSParagraphStyle)?.mutableCopy()
1286
1304
  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.2",
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",