@openeditor/react-native-prose-editor 0.0.12 → 0.0.14
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/android/src/main/java/com/apollohg/editor/NativeBlockEditorSurface.kt +47 -26
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoView.kt +8 -0
- package/android/src/main/java/com/apollohg/editor/RichTextEditorView.kt +2 -0
- package/dist/NativeRichTextEditor.d.ts +8 -0
- package/dist/NativeRichTextEditor.js +11 -1
- package/dist/addons.d.ts +7 -0
- package/ios/NativeBlockEditorSurface.swift +72 -31
- package/ios/NativeEditorExpoView.swift +10 -0
- package/ios/RichTextEditorView.swift +4 -0
- package/package.json +1 -1
|
@@ -48,6 +48,7 @@ internal data class SelectedImageGeometry(val docPos: Int, val rect: RectF)
|
|
|
48
48
|
|
|
49
49
|
class NativeBlockEditorSurface(context: Context) : ScrollView(context) {
|
|
50
50
|
var onPageOpen: ((String, String, String?, String?) -> Unit)? = null
|
|
51
|
+
var onAttachmentOpen: ((String?, String, String?, Long?, String?) -> Unit)? = null
|
|
51
52
|
var isScrollingEnabled: Boolean = true
|
|
52
53
|
|
|
53
54
|
internal var geometryRevision: Long = 0
|
|
@@ -618,6 +619,9 @@ class NativeBlockEditorSurface(context: Context) : ScrollView(context) {
|
|
|
618
619
|
internal fun tableFillColorsForTesting(): List<Int> = tableCells.map { it.baseFillColor }
|
|
619
620
|
|
|
620
621
|
private fun viewForNode(node: JSONObject, depth: Int, path: List<Int>): View {
|
|
622
|
+
if (node.optString("nodeType") == "attachment") {
|
|
623
|
+
return attachmentView(node, depth).also { nodeViews[path] = it }
|
|
624
|
+
}
|
|
621
625
|
if (node.optString("nodeType") == "page") {
|
|
622
626
|
return pageView(node, depth).also { nodeViews[path] = it }
|
|
623
627
|
}
|
|
@@ -772,36 +776,31 @@ class NativeBlockEditorSurface(context: Context) : ScrollView(context) {
|
|
|
772
776
|
|
|
773
777
|
private fun calloutView(node: JSONObject, depth: Int, path: List<Int>): View {
|
|
774
778
|
val density = resources.displayMetrics.density
|
|
775
|
-
val tone = node.optJSONObject("attrs")?.optString("tone", "info") ?: "info"
|
|
776
|
-
val accent = when (tone) {
|
|
777
|
-
"success" -> 0xFF16A34A.toInt()
|
|
778
|
-
"warning" -> 0xFFD97706.toInt()
|
|
779
|
-
"danger" -> 0xFFDC2626.toInt()
|
|
780
|
-
else -> 0xFF2563EB.toInt()
|
|
781
|
-
}
|
|
782
779
|
val container = LinearLayout(context).apply {
|
|
783
|
-
orientation = LinearLayout.
|
|
780
|
+
orientation = LinearLayout.HORIZONTAL
|
|
781
|
+
gravity = android.view.Gravity.TOP
|
|
784
782
|
val padding = (12 * density).toInt()
|
|
785
783
|
setPadding(padding, padding, padding, padding)
|
|
786
784
|
background = roundedDrawable(
|
|
787
|
-
|
|
788
|
-
Color.
|
|
789
|
-
|
|
785
|
+
currentTheme?.codeBlock?.backgroundColor ?: 0xFFF5F5F5.toInt(),
|
|
786
|
+
Color.TRANSPARENT,
|
|
787
|
+
0f,
|
|
790
788
|
10f
|
|
791
789
|
)
|
|
792
790
|
}
|
|
793
791
|
container.addView(TextView(context).apply {
|
|
794
|
-
text =
|
|
795
|
-
|
|
796
|
-
setTextSize(TypedValue.COMPLEX_UNIT_SP, 12f)
|
|
797
|
-
setTypeface(typeface, Typeface.BOLD)
|
|
798
|
-
isClickable = this@NativeBlockEditorSurface.isEditable
|
|
799
|
-
setOnClickListener { cycleCalloutTone(node) }
|
|
792
|
+
text = node.optJSONObject("attrs")?.optString("emoji", "💡") ?: "💡"
|
|
793
|
+
setTextSize(TypedValue.COMPLEX_UNIT_SP, 20f)
|
|
800
794
|
})
|
|
795
|
+
val content = LinearLayout(context).apply {
|
|
796
|
+
orientation = LinearLayout.VERTICAL
|
|
797
|
+
setPadding((10 * density).toInt(), 0, 0, 0)
|
|
798
|
+
}
|
|
801
799
|
val children = node.optJSONArray("children")
|
|
802
800
|
for (index in 0 until (children?.length() ?: 0)) {
|
|
803
|
-
children?.optJSONObject(index)?.let {
|
|
801
|
+
children?.optJSONObject(index)?.let { content.addView(viewForNode(it, depth + 1, path + index)) }
|
|
804
802
|
}
|
|
803
|
+
container.addView(content, LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f))
|
|
805
804
|
return container
|
|
806
805
|
}
|
|
807
806
|
|
|
@@ -829,14 +828,6 @@ class NativeBlockEditorSurface(context: Context) : ScrollView(context) {
|
|
|
829
828
|
}
|
|
830
829
|
}
|
|
831
830
|
|
|
832
|
-
private fun cycleCalloutTone(node: JSONObject) {
|
|
833
|
-
if (!isEditable) return
|
|
834
|
-
val tones = listOf("info", "success", "warning", "danger")
|
|
835
|
-
val current = node.optJSONObject("attrs")?.optString("tone", "info") ?: "info"
|
|
836
|
-
val next = tones[(tones.indexOf(current).coerceAtLeast(0) + 1) % tones.size]
|
|
837
|
-
updateNodeAttrs(node, JSONObject().put("tone", next))
|
|
838
|
-
}
|
|
839
|
-
|
|
840
831
|
private fun updateNodeAttrs(node: JSONObject, patch: JSONObject) {
|
|
841
832
|
if (currentEditorId == 0L) return
|
|
842
833
|
val attrs = JSONObject(node.optJSONObject("attrs")?.toString() ?: "{}")
|
|
@@ -960,6 +951,36 @@ class NativeBlockEditorSurface(context: Context) : ScrollView(context) {
|
|
|
960
951
|
return row
|
|
961
952
|
}
|
|
962
953
|
|
|
954
|
+
private fun attachmentView(node: JSONObject, depth: Int): View {
|
|
955
|
+
val density = resources.displayMetrics.density
|
|
956
|
+
val attrs = node.optJSONObject("attrs")
|
|
957
|
+
return LinearLayout(context).apply {
|
|
958
|
+
orientation = LinearLayout.HORIZONTAL
|
|
959
|
+
gravity = android.view.Gravity.CENTER_VERTICAL
|
|
960
|
+
val padding = (10 * density).toInt()
|
|
961
|
+
setPadding(padding + (depth * 8 * density).toInt(), padding, padding, padding)
|
|
962
|
+
background = borderDrawable(Color.TRANSPARENT, currentTheme?.table?.borderColor ?: baseTextColor, 1f)
|
|
963
|
+
addView(TextView(context).apply { text = "📎"; setTextSize(TypedValue.COMPLEX_UNIT_SP, 22f) }, LinearLayout.LayoutParams((38 * density).toInt(), ViewGroup.LayoutParams.WRAP_CONTENT))
|
|
964
|
+
addView(LinearLayout(context).apply {
|
|
965
|
+
orientation = LinearLayout.VERTICAL
|
|
966
|
+
addView(TextView(context).apply { text = attrs?.optString("name").orEmpty().ifEmpty { "Attachment" }; setTextColor(baseTextColor); setTextSize(TypedValue.COMPLEX_UNIT_SP, 15f); setTypeface(typeface, Typeface.BOLD) })
|
|
967
|
+
addView(TextView(context).apply { text = attrs?.optString("mimeType").orEmpty().ifEmpty { "File" }; setTextColor(currentTheme?.placeholderColor ?: baseTextColor); setTextSize(TypedValue.COMPLEX_UNIT_SP, 12f) })
|
|
968
|
+
}, LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f))
|
|
969
|
+
isClickable = true
|
|
970
|
+
isFocusable = true
|
|
971
|
+
contentDescription = "Open ${attrs?.optString("name").orEmpty().ifEmpty { "attachment" }}"
|
|
972
|
+
setOnClickListener {
|
|
973
|
+
onAttachmentOpen?.invoke(
|
|
974
|
+
attrs?.optString("attachmentId")?.takeIf(String::isNotEmpty),
|
|
975
|
+
attrs?.optString("name").orEmpty().ifEmpty { "Attachment" },
|
|
976
|
+
attrs?.optString("mimeType")?.takeIf(String::isNotEmpty),
|
|
977
|
+
attrs?.optLong("size")?.takeIf { attrs.has("size") && !attrs.isNull("size") },
|
|
978
|
+
attrs?.optString("url")?.takeIf(String::isNotEmpty),
|
|
979
|
+
)
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
|
|
963
984
|
private fun configureTextLeaf(leaf: TextLeaf, node: JSONObject) {
|
|
964
985
|
val rendered = attributedInlineContent(node)
|
|
965
986
|
val hadFocus = leaf.hasFocus()
|
|
@@ -733,6 +733,14 @@ class NativeEditorExpoView(
|
|
|
733
733
|
href?.let { event["href"] = it }
|
|
734
734
|
emitAddonEvent(mapOf("eventJson" to JSONObject(event).toString()))
|
|
735
735
|
}
|
|
736
|
+
richTextView.onAttachmentOpen = { attachmentId, name, mimeType, size, url ->
|
|
737
|
+
val event = mutableMapOf<String, Any>("type" to "attachmentOpen", "name" to name)
|
|
738
|
+
attachmentId?.let { event["attachmentId"] = it }
|
|
739
|
+
mimeType?.let { event["mimeType"] = it }
|
|
740
|
+
size?.let { event["size"] = it }
|
|
741
|
+
url?.let { event["url"] = it }
|
|
742
|
+
emitAddonEvent(mapOf("eventJson" to JSONObject(event).toString()))
|
|
743
|
+
}
|
|
736
744
|
richTextView.onBeforeDetachedFromWindow = {
|
|
737
745
|
prepareForDetachFromWindow()
|
|
738
746
|
}
|
|
@@ -39,6 +39,7 @@ class RichTextEditorView @JvmOverloads constructor(
|
|
|
39
39
|
var onEditorUpdate: ((String) -> Unit)? = null
|
|
40
40
|
var onFocusChange: ((Boolean) -> Unit)? = null
|
|
41
41
|
var onPageOpen: ((String, String, String?, String?) -> Unit)? = null
|
|
42
|
+
var onAttachmentOpen: ((String?, String, String?, Long?, String?) -> Unit)? = null
|
|
42
43
|
|
|
43
44
|
var editorId: Long
|
|
44
45
|
get() = currentEditorId
|
|
@@ -62,6 +63,7 @@ class RichTextEditorView @JvmOverloads constructor(
|
|
|
62
63
|
blockSurface.onEditorUpdate = { update -> onEditorUpdate?.invoke(update) }
|
|
63
64
|
blockSurface.onFocusChange = { focused -> onFocusChange?.invoke(focused) }
|
|
64
65
|
blockSurface.onPageOpen = { pageId, title, icon, href -> onPageOpen?.invoke(pageId, title, icon, href) }
|
|
66
|
+
blockSurface.onAttachmentOpen = { attachmentId, name, mimeType, size, url -> onAttachmentOpen?.invoke(attachmentId, name, mimeType, size, url) }
|
|
65
67
|
updateAppearance()
|
|
66
68
|
}
|
|
67
69
|
|
|
@@ -88,6 +88,14 @@ export interface NativeRichTextEditorProps {
|
|
|
88
88
|
icon?: string | null;
|
|
89
89
|
href?: string | null;
|
|
90
90
|
}) => void;
|
|
91
|
+
/** Called when an Attachment block is pressed. */
|
|
92
|
+
onOpenAttachment?: (attachment: {
|
|
93
|
+
attachmentId: string | null;
|
|
94
|
+
name: string;
|
|
95
|
+
mimeType: string | null;
|
|
96
|
+
size: number | null;
|
|
97
|
+
url: string | null;
|
|
98
|
+
}) => void;
|
|
91
99
|
/** Whether plain URLs typed or pasted into the editor should be converted into link marks automatically. */
|
|
92
100
|
autoDetectLinks?: boolean;
|
|
93
101
|
/** Whether `data:image/...` sources are accepted for image insertion and HTML parsing. */
|
|
@@ -559,7 +559,7 @@ function doesLiveMentionQueryConflictWithNativeSelectRequest(request, currentQue
|
|
|
559
559
|
currentQuery.range.anchor !== request.range.anchor ||
|
|
560
560
|
currentQuery.range.head !== request.range.head);
|
|
561
561
|
}
|
|
562
|
-
exports.NativeRichTextEditor = (0, react_1.forwardRef)(function NativeRichTextEditor({ initialContent, initialJSON, value, valueJSON, valueJSONRevision, valueJSONUpdateMode = 'replace', preserveSelectionOnValueJSONReset = false, selectionOnValueJSONReset, schema, placeholder, editable = true, maxLength, autoFocus = false, autoCapitalize, autoCorrect, keyboardType, keyboardAppearance, heightBehavior = 'autoGrow', showToolbar = true, toolbarPlacement = 'keyboard', toolbarItems = EditorToolbar_1.DEFAULT_EDITOR_TOOLBAR_ITEMS, onToolbarAction, onRequestLink, onRequestImage, onOpenPage, autoDetectLinks = false, onContentChange, onContentChangeJSON, onSelectionChange, onActiveStateChange, onHistoryStateChange, onFocus, onBlur, style, containerStyle, theme, addons, remoteSelections, allowBase64Images = false, allowImageResizing = true, }, ref) {
|
|
562
|
+
exports.NativeRichTextEditor = (0, react_1.forwardRef)(function NativeRichTextEditor({ initialContent, initialJSON, value, valueJSON, valueJSONRevision, valueJSONUpdateMode = 'replace', preserveSelectionOnValueJSONReset = false, selectionOnValueJSONReset, schema, placeholder, editable = true, maxLength, autoFocus = false, autoCapitalize, autoCorrect, keyboardType, keyboardAppearance, heightBehavior = 'autoGrow', showToolbar = true, toolbarPlacement = 'keyboard', toolbarItems = EditorToolbar_1.DEFAULT_EDITOR_TOOLBAR_ITEMS, onToolbarAction, onRequestLink, onRequestImage, onOpenPage, onOpenAttachment, autoDetectLinks = false, onContentChange, onContentChangeJSON, onSelectionChange, onActiveStateChange, onHistoryStateChange, onFocus, onBlur, style, containerStyle, theme, addons, remoteSelections, allowBase64Images = false, allowImageResizing = true, }, ref) {
|
|
563
563
|
const bridgeRef = (0, react_1.useRef)(null);
|
|
564
564
|
const nativeViewRef = (0, react_1.useRef)(null);
|
|
565
565
|
const [isReady, setIsReady] = (0, react_1.useState)(false);
|
|
@@ -2054,6 +2054,16 @@ exports.NativeRichTextEditor = (0, react_1.forwardRef)(function NativeRichTextEd
|
|
|
2054
2054
|
}
|
|
2055
2055
|
if (!parsed)
|
|
2056
2056
|
return;
|
|
2057
|
+
if (parsed.type === 'attachmentOpen') {
|
|
2058
|
+
onOpenAttachment?.({
|
|
2059
|
+
attachmentId: parsed.attachmentId ?? null,
|
|
2060
|
+
name: parsed.name,
|
|
2061
|
+
mimeType: parsed.mimeType ?? null,
|
|
2062
|
+
size: parsed.size ?? null,
|
|
2063
|
+
url: parsed.url ?? null,
|
|
2064
|
+
});
|
|
2065
|
+
return;
|
|
2066
|
+
}
|
|
2057
2067
|
if (parsed.type === 'pageOpen') {
|
|
2058
2068
|
onOpenPage?.({
|
|
2059
2069
|
pageId: parsed.pageId,
|
package/dist/addons.d.ts
CHANGED
|
@@ -65,6 +65,13 @@ export interface SerializedEditorAddons {
|
|
|
65
65
|
mentions?: SerializedMentionsAddonConfig;
|
|
66
66
|
}
|
|
67
67
|
export type EditorAddonEvent = {
|
|
68
|
+
type: 'attachmentOpen';
|
|
69
|
+
attachmentId?: string | null;
|
|
70
|
+
name: string;
|
|
71
|
+
mimeType?: string | null;
|
|
72
|
+
size?: number | null;
|
|
73
|
+
url?: string | null;
|
|
74
|
+
} | {
|
|
68
75
|
type: 'pageOpen';
|
|
69
76
|
pageId: string;
|
|
70
77
|
title: string;
|
|
@@ -2,6 +2,17 @@ import UIKit
|
|
|
2
2
|
|
|
3
3
|
final class NativeBlockEditorSurface: UIScrollView, UITextViewDelegate, UIGestureRecognizerDelegate {
|
|
4
4
|
var onPageOpen: ((String, String, String?, String?) -> Void)?
|
|
5
|
+
var onAttachmentOpen: ((String?, String, String?, NSNumber?, String?) -> Void)?
|
|
6
|
+
private final class AttachmentRow: UIStackView {
|
|
7
|
+
var open: (() -> Void)?
|
|
8
|
+
override init(frame: CGRect) {
|
|
9
|
+
super.init(frame: frame)
|
|
10
|
+
isUserInteractionEnabled = true
|
|
11
|
+
addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleOpen)))
|
|
12
|
+
}
|
|
13
|
+
required init(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
|
14
|
+
@objc private func handleOpen() { open?() }
|
|
15
|
+
}
|
|
5
16
|
private final class TextLeafView: UITextView {
|
|
6
17
|
struct PositionSegment {
|
|
7
18
|
let range: NSRange
|
|
@@ -697,6 +708,11 @@ final class NativeBlockEditorSurface: UIScrollView, UITextViewDelegate, UIGestur
|
|
|
697
708
|
}
|
|
698
709
|
|
|
699
710
|
private func view(for node: [String: Any], depth: Int, path: [Int]) -> UIView {
|
|
711
|
+
if node["nodeType"] as? String == "attachment" {
|
|
712
|
+
let rendered = attachmentView(for: node, depth: depth)
|
|
713
|
+
nodeViews[path] = rendered
|
|
714
|
+
return rendered
|
|
715
|
+
}
|
|
700
716
|
if node["nodeType"] as? String == "page" {
|
|
701
717
|
let rendered = pageView(for: node, depth: depth)
|
|
702
718
|
nodeViews[path] = rendered
|
|
@@ -848,37 +864,27 @@ final class NativeBlockEditorSurface: UIScrollView, UITextViewDelegate, UIGestur
|
|
|
848
864
|
}
|
|
849
865
|
|
|
850
866
|
private func calloutView(for node: [String: Any], depth: Int, path: [Int]) -> UIView {
|
|
851
|
-
let tone = stringAttr(node, "tone") ?? "info"
|
|
852
|
-
let accent: UIColor = switch tone {
|
|
853
|
-
case "success": .systemGreen
|
|
854
|
-
case "warning": .systemOrange
|
|
855
|
-
case "danger": .systemRed
|
|
856
|
-
default: .systemBlue
|
|
857
|
-
}
|
|
858
867
|
let container = UIStackView()
|
|
859
|
-
container.axis = .
|
|
860
|
-
container.
|
|
868
|
+
container.axis = .horizontal
|
|
869
|
+
container.alignment = .top
|
|
870
|
+
container.spacing = 10
|
|
861
871
|
container.layoutMargins = UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 12)
|
|
862
872
|
container.isLayoutMarginsRelativeArrangement = true
|
|
863
|
-
container.backgroundColor =
|
|
864
|
-
container.layer.borderColor = accent.withAlphaComponent(0.55).cgColor
|
|
865
|
-
container.layer.borderWidth = 1
|
|
873
|
+
container.backgroundColor = UIColor.secondarySystemBackground
|
|
866
874
|
container.layer.cornerRadius = 10
|
|
867
875
|
|
|
868
|
-
let
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
toneButton.contentHorizontalAlignment = .leading
|
|
873
|
-
toneButton.isEnabled = isEditable
|
|
874
|
-
toneButton.addAction(UIAction { [weak self] _ in
|
|
875
|
-
self?.cycleCalloutTone(node)
|
|
876
|
-
}, for: .touchUpInside)
|
|
877
|
-
container.addArrangedSubview(toneButton)
|
|
876
|
+
let emoji = UILabel()
|
|
877
|
+
emoji.text = stringAttr(node, "emoji") ?? "💡"
|
|
878
|
+
emoji.font = .systemFont(ofSize: 20)
|
|
879
|
+
container.addArrangedSubview(emoji)
|
|
878
880
|
|
|
881
|
+
let content = UIStackView()
|
|
882
|
+
content.axis = .vertical
|
|
883
|
+
content.spacing = 4
|
|
879
884
|
for (index, child) in (node["children"] as? [[String: Any]] ?? []).enumerated() {
|
|
880
|
-
|
|
885
|
+
content.addArrangedSubview(view(for: child, depth: depth + 1, path: path + [index]))
|
|
881
886
|
}
|
|
887
|
+
container.addArrangedSubview(content)
|
|
882
888
|
return container
|
|
883
889
|
}
|
|
884
890
|
|
|
@@ -916,14 +922,6 @@ final class NativeBlockEditorSurface: UIScrollView, UITextViewDelegate, UIGestur
|
|
|
916
922
|
}
|
|
917
923
|
}
|
|
918
924
|
|
|
919
|
-
private func cycleCalloutTone(_ node: [String: Any]) {
|
|
920
|
-
guard isEditable else { return }
|
|
921
|
-
let tones = ["info", "success", "warning", "danger"]
|
|
922
|
-
let current = stringAttr(node, "tone") ?? "info"
|
|
923
|
-
let next = tones[((tones.firstIndex(of: current) ?? 0) + 1) % tones.count]
|
|
924
|
-
updateNodeAttrs(node, patch: ["tone": next])
|
|
925
|
-
}
|
|
926
|
-
|
|
927
925
|
private func updateNodeAttrs(_ node: [String: Any], patch: [String: Any]) {
|
|
928
926
|
guard currentEditorId != 0 else { return }
|
|
929
927
|
var attrs = node["attrs"] as? [String: Any] ?? [:]
|
|
@@ -1088,6 +1086,49 @@ final class NativeBlockEditorSurface: UIScrollView, UITextViewDelegate, UIGestur
|
|
|
1088
1086
|
return row
|
|
1089
1087
|
}
|
|
1090
1088
|
|
|
1089
|
+
private func attachmentView(for node: [String: Any], depth: Int) -> UIView {
|
|
1090
|
+
let row = AttachmentRow()
|
|
1091
|
+
row.axis = .horizontal
|
|
1092
|
+
row.alignment = .center
|
|
1093
|
+
row.spacing = 10
|
|
1094
|
+
row.layoutMargins = UIEdgeInsets(top: 10, left: CGFloat(depth) * 8 + 10, bottom: 10, right: 10)
|
|
1095
|
+
row.isLayoutMarginsRelativeArrangement = true
|
|
1096
|
+
row.layer.cornerRadius = 10
|
|
1097
|
+
row.layer.borderWidth = 1
|
|
1098
|
+
row.layer.borderColor = UIColor.separator.cgColor
|
|
1099
|
+
let icon = UILabel()
|
|
1100
|
+
icon.text = "📎"
|
|
1101
|
+
icon.font = .systemFont(ofSize: 22)
|
|
1102
|
+
row.addArrangedSubview(icon)
|
|
1103
|
+
let details = UIStackView()
|
|
1104
|
+
details.axis = .vertical
|
|
1105
|
+
details.spacing = 2
|
|
1106
|
+
let name = UILabel()
|
|
1107
|
+
name.text = stringAttr(node, "name") ?? "Attachment"
|
|
1108
|
+
name.font = .systemFont(ofSize: 15, weight: .semibold)
|
|
1109
|
+
name.textColor = baseTextColor
|
|
1110
|
+
name.lineBreakMode = .byTruncatingMiddle
|
|
1111
|
+
let meta = UILabel()
|
|
1112
|
+
meta.text = stringAttr(node, "mimeType") ?? "File"
|
|
1113
|
+
meta.font = .systemFont(ofSize: 12)
|
|
1114
|
+
meta.textColor = .secondaryLabel
|
|
1115
|
+
details.addArrangedSubview(name)
|
|
1116
|
+
details.addArrangedSubview(meta)
|
|
1117
|
+
row.addArrangedSubview(details)
|
|
1118
|
+
row.accessibilityTraits = .button
|
|
1119
|
+
row.accessibilityLabel = "Open \(name.text ?? "attachment")"
|
|
1120
|
+
row.open = { [weak self] in
|
|
1121
|
+
self?.onAttachmentOpen?(
|
|
1122
|
+
self?.stringAttr(node, "attachmentId"),
|
|
1123
|
+
self?.stringAttr(node, "name") ?? "Attachment",
|
|
1124
|
+
self?.stringAttr(node, "mimeType"),
|
|
1125
|
+
(node["attrs"] as? [String: Any])?["size"] as? NSNumber,
|
|
1126
|
+
self?.stringAttr(node, "url")
|
|
1127
|
+
)
|
|
1128
|
+
}
|
|
1129
|
+
return row
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1091
1132
|
private func configureTextLeaf(_ leaf: TextLeafView, for node: [String: Any]) {
|
|
1092
1133
|
let wasFirstResponder = leaf.isFirstResponder
|
|
1093
1134
|
let previousSelection = leaf.selectedRange
|
|
@@ -2109,6 +2109,16 @@ class NativeEditorExpoView: ExpoView, UIGestureRecognizerDelegate {
|
|
|
2109
2109
|
let json = String(data: data, encoding: .utf8) else { return }
|
|
2110
2110
|
self?.onAddonEvent(["eventJson": json])
|
|
2111
2111
|
}
|
|
2112
|
+
richTextView.onAttachmentOpen = { [weak self] attachmentId, name, mimeType, size, url in
|
|
2113
|
+
var event: [String: Any] = ["type": "attachmentOpen", "name": name]
|
|
2114
|
+
if let attachmentId { event["attachmentId"] = attachmentId }
|
|
2115
|
+
if let mimeType { event["mimeType"] = mimeType }
|
|
2116
|
+
if let size { event["size"] = size }
|
|
2117
|
+
if let url { event["url"] = url }
|
|
2118
|
+
guard let data = try? JSONSerialization.data(withJSONObject: event),
|
|
2119
|
+
let json = String(data: data, encoding: .utf8) else { return }
|
|
2120
|
+
self?.onAddonEvent(["eventJson": json])
|
|
2121
|
+
}
|
|
2112
2122
|
configureAccessoryToolbar()
|
|
2113
2123
|
|
|
2114
2124
|
addSubview(richTextView)
|
|
@@ -647,6 +647,7 @@ final class RichTextEditorView: UIView {
|
|
|
647
647
|
var onEditorUpdate: ((String) -> Void)?
|
|
648
648
|
var onFocusChange: ((Bool) -> Void)?
|
|
649
649
|
var onPageOpen: ((String, String, String?, String?) -> Void)?
|
|
650
|
+
var onAttachmentOpen: ((String?, String, String?, NSNumber?, String?) -> Void)?
|
|
650
651
|
private var lastAutoGrowWidth: CGFloat = 0
|
|
651
652
|
private var cachedAutoGrowMeasuredHeight: CGFloat = 0
|
|
652
653
|
private var remoteSelections: [RemoteSelectionDecoration] = []
|
|
@@ -764,6 +765,9 @@ final class RichTextEditorView: UIView {
|
|
|
764
765
|
blockSurface.onPageOpen = { [weak self] pageId, title, icon, href in
|
|
765
766
|
self?.onPageOpen?(pageId, title, icon, href)
|
|
766
767
|
}
|
|
768
|
+
blockSurface.onAttachmentOpen = { [weak self] attachmentId, name, mimeType, size, url in
|
|
769
|
+
self?.onAttachmentOpen?(attachmentId, name, mimeType, size, url)
|
|
770
|
+
}
|
|
767
771
|
blockSurface.onContentHeightMayChange = { [weak self] measuredHeight in
|
|
768
772
|
guard let self, self.heightBehavior == .autoGrow else { return }
|
|
769
773
|
self.cachedAutoGrowMeasuredHeight = measuredHeight
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openeditor/react-native-prose-editor",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.14",
|
|
4
4
|
"description": "OpenEditor-owned native rich text editor engine for React Native",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://github.com/EasyLink-HQ/openeditor",
|