@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.
- package/README.md +18 -15
- package/android/src/main/java/com/apollohg/editor/EditorAddons.kt +4 -2
- package/android/src/main/java/com/apollohg/editor/EditorEditText.kt +33 -1
- package/android/src/main/java/com/apollohg/editor/EditorTheme.kt +23 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoView.kt +39 -6
- package/android/src/main/java/com/apollohg/editor/NativeEditorModule.kt +15 -1
- package/android/src/main/java/com/apollohg/editor/NativeProseViewerExpoView.kt +44 -7
- package/android/src/main/java/com/apollohg/editor/RenderBridge.kt +24 -4
- package/dist/NativeEditorBridge.d.ts +8 -0
- package/dist/NativeEditorBridge.js +16 -0
- package/dist/NativeProseViewer.d.ts +25 -5
- package/dist/NativeProseViewer.js +212 -13
- package/dist/NativeRichTextEditor.d.ts +2 -0
- package/dist/NativeRichTextEditor.js +417 -31
- package/dist/addons.d.ts +20 -0
- package/dist/addons.js +4 -0
- package/dist/index.d.ts +2 -2
- package/ios/EditorAddons.swift +2 -0
- package/ios/EditorCore.xcframework/ios-arm64/libeditor_core.a +0 -0
- package/ios/EditorCore.xcframework/ios-arm64_x86_64-simulator/libeditor_core.a +0 -0
- package/ios/EditorLayoutManager.swift +10 -1
- package/ios/EditorTheme.swift +25 -0
- package/ios/NativeEditorExpoView.swift +56 -6
- package/ios/NativeEditorModule.swift +14 -1
- package/ios/NativeProseViewerExpoView.swift +62 -11
- package/ios/RenderBridge.swift +40 -16
- package/ios/RichTextEditorView.swift +4 -0
- package/package.json +1 -1
- package/rust/android/arm64-v8a/libeditor_core.so +0 -0
- package/rust/android/armeabi-v7a/libeditor_core.so +0 -0
- 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
|
|
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":
|
|
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
|
-
|
|
2179
|
-
if
|
|
2180
|
-
|
|
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
|
|
18
|
+
private lazy var interactiveTapRecognizer: UITapGestureRecognizer = {
|
|
15
19
|
let recognizer = UITapGestureRecognizer(
|
|
16
20
|
target: self,
|
|
17
|
-
action: #selector(
|
|
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(
|
|
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.
|
|
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
|
|
102
|
-
guard recognizer.state == .ended
|
|
103
|
-
|
|
104
|
-
|
|
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
|
|
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
|
}
|
package/ios/RenderBridge.swift
CHANGED
|
@@ -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
|
|
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
|
-
|
|
743
|
-
attrs[.
|
|
744
|
-
|
|
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
|
|
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:
|
|
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:
|
|
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.
|
|
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",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|