@apollohg/react-native-prose-editor 0.5.2 → 0.5.4
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/EditorEditText.kt +33 -1
- package/android/src/main/java/com/apollohg/editor/EditorTheme.kt +6 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorModule.kt +20 -1
- package/android/src/main/java/com/apollohg/editor/NativeProseViewerExpoView.kt +148 -12
- package/android/src/main/java/com/apollohg/editor/RenderBridge.kt +35 -3
- package/dist/EditorTheme.d.ts +3 -0
- package/dist/EditorToolbar.js +11 -6
- package/dist/NativeProseViewer.d.ts +19 -3
- package/dist/NativeProseViewer.js +193 -13
- package/dist/NativeRichTextEditor.js +189 -30
- package/dist/index.d.ts +1 -1
- package/ios/EditorCore.xcframework/Info.plist +5 -5
- 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/EditorTheme.swift +6 -0
- package/ios/NativeEditorModule.swift +18 -1
- package/ios/NativeProseViewerExpoView.swift +152 -17
- package/ios/RenderBridge.swift +13 -2
- 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
|
@@ -77,6 +77,11 @@ class EditorEditText @JvmOverloads constructor(
|
|
|
77
77
|
val label: String
|
|
78
78
|
)
|
|
79
79
|
|
|
80
|
+
data class LinkHit(
|
|
81
|
+
val href: String,
|
|
82
|
+
val text: String
|
|
83
|
+
)
|
|
84
|
+
|
|
80
85
|
private data class ParsedRenderPatch(
|
|
81
86
|
val startIndex: Int,
|
|
82
87
|
val deleteCount: Int,
|
|
@@ -1583,7 +1588,7 @@ class EditorEditText @JvmOverloads constructor(
|
|
|
1583
1588
|
}
|
|
1584
1589
|
}
|
|
1585
1590
|
|
|
1586
|
-
fun
|
|
1591
|
+
private fun textOffsetHitAt(x: Float, y: Float): Pair<Spanned, Int>? {
|
|
1587
1592
|
val spannable = text as? Spanned ?: return null
|
|
1588
1593
|
val layout = layout ?: return null
|
|
1589
1594
|
if (spannable.isEmpty()) return null
|
|
@@ -1603,6 +1608,11 @@ class EditorEditText @JvmOverloads constructor(
|
|
|
1603
1608
|
|
|
1604
1609
|
val offset = layout.getOffsetForHorizontal(line, localX)
|
|
1605
1610
|
.coerceIn(0, maxOf(spannable.length - 1, 0))
|
|
1611
|
+
return spannable to offset
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
fun mentionHitAt(x: Float, y: Float): MentionHit? {
|
|
1615
|
+
val (spannable, offset) = textOffsetHitAt(x, y) ?: return null
|
|
1606
1616
|
val annotations = spannable.getSpans(
|
|
1607
1617
|
offset,
|
|
1608
1618
|
(offset + 1).coerceAtMost(spannable.length),
|
|
@@ -1626,6 +1636,28 @@ class EditorEditText @JvmOverloads constructor(
|
|
|
1626
1636
|
)
|
|
1627
1637
|
}
|
|
1628
1638
|
|
|
1639
|
+
fun linkHitAt(x: Float, y: Float): LinkHit? {
|
|
1640
|
+
val (spannable, offset) = textOffsetHitAt(x, y) ?: return null
|
|
1641
|
+
val annotations = spannable.getSpans(
|
|
1642
|
+
offset,
|
|
1643
|
+
(offset + 1).coerceAtMost(spannable.length),
|
|
1644
|
+
Annotation::class.java
|
|
1645
|
+
)
|
|
1646
|
+
val linkAnnotation = annotations.firstOrNull {
|
|
1647
|
+
it.key == RenderBridge.NATIVE_LINK_HREF_ANNOTATION && it.value.isNotBlank()
|
|
1648
|
+
} ?: return null
|
|
1649
|
+
val start = spannable.getSpanStart(linkAnnotation)
|
|
1650
|
+
val end = spannable.getSpanEnd(linkAnnotation)
|
|
1651
|
+
if (start < 0 || end <= start) {
|
|
1652
|
+
return null
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
return LinkHit(
|
|
1656
|
+
href = linkAnnotation.value,
|
|
1657
|
+
text = spannable.subSequence(start, end).toString()
|
|
1658
|
+
)
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1629
1661
|
private fun handleImageTap(event: MotionEvent): Boolean {
|
|
1630
1662
|
if (!imageResizingEnabled) {
|
|
1631
1663
|
return false
|
|
@@ -55,6 +55,7 @@ data class EditorTextStyle(
|
|
|
55
55
|
|
|
56
56
|
data class EditorListTheme(
|
|
57
57
|
val indent: Float? = null,
|
|
58
|
+
val baseIndentMultiplier: Float? = null,
|
|
58
59
|
val itemSpacing: Float? = null,
|
|
59
60
|
val markerColor: Int? = null,
|
|
60
61
|
val markerScale: Float? = null
|
|
@@ -64,6 +65,7 @@ data class EditorListTheme(
|
|
|
64
65
|
json ?: return null
|
|
65
66
|
return EditorListTheme(
|
|
66
67
|
indent = json.optNullableFloat("indent"),
|
|
68
|
+
baseIndentMultiplier = json.optNullableFloat("baseIndentMultiplier"),
|
|
67
69
|
itemSpacing = json.optNullableFloat("itemSpacing"),
|
|
68
70
|
markerColor = parseColor(json.optNullableString("markerColor")),
|
|
69
71
|
markerScale = json.optNullableFloat("markerScale")
|
|
@@ -194,6 +196,8 @@ data class EditorToolbarTheme(
|
|
|
194
196
|
val borderColor: Int? = null,
|
|
195
197
|
val borderWidth: Float? = null,
|
|
196
198
|
val borderRadius: Float? = null,
|
|
199
|
+
val marginTop: Float? = null,
|
|
200
|
+
val showTopBorder: Boolean? = null,
|
|
197
201
|
val keyboardOffset: Float? = null,
|
|
198
202
|
val horizontalInset: Float? = null,
|
|
199
203
|
val separatorColor: Int? = null,
|
|
@@ -222,6 +226,8 @@ data class EditorToolbarTheme(
|
|
|
222
226
|
borderColor = parseColor(json.optNullableString("borderColor")),
|
|
223
227
|
borderWidth = json.optNullableFloat("borderWidth"),
|
|
224
228
|
borderRadius = json.optNullableFloat("borderRadius"),
|
|
229
|
+
marginTop = json.optNullableFloat("marginTop"),
|
|
230
|
+
showTopBorder = if (json.has("showTopBorder")) json.optBoolean("showTopBorder") else null,
|
|
225
231
|
keyboardOffset = json.optNullableFloat("keyboardOffset"),
|
|
226
232
|
horizontalInset = json.optNullableFloat("horizontalInset"),
|
|
227
233
|
separatorColor = parseColor(json.optNullableString("separatorColor")),
|
|
@@ -295,6 +295,14 @@ class NativeEditorModule : Module() {
|
|
|
295
295
|
editorDestroy(editorId)
|
|
296
296
|
}
|
|
297
297
|
}
|
|
298
|
+
Function("renderDocumentHtml") { configJson: String, html: String ->
|
|
299
|
+
val editorId = editorCreate(configJson)
|
|
300
|
+
try {
|
|
301
|
+
editorSetHtml(editorId, html)
|
|
302
|
+
} finally {
|
|
303
|
+
editorDestroy(editorId)
|
|
304
|
+
}
|
|
305
|
+
}
|
|
298
306
|
|
|
299
307
|
View(NativeEditorExpoView::class) {
|
|
300
308
|
Events(
|
|
@@ -370,7 +378,7 @@ class NativeEditorModule : Module() {
|
|
|
370
378
|
|
|
371
379
|
View(NativeProseViewerExpoView::class) {
|
|
372
380
|
Name("NativeProseViewer")
|
|
373
|
-
Events("onContentHeightChange", "onPressMention")
|
|
381
|
+
Events("onContentHeightChange", "onPressLink", "onPressMention")
|
|
374
382
|
|
|
375
383
|
Prop("renderJson") { view: NativeProseViewerExpoView, renderJson: String? ->
|
|
376
384
|
view.setRenderJson(renderJson)
|
|
@@ -378,6 +386,17 @@ class NativeEditorModule : Module() {
|
|
|
378
386
|
Prop("themeJson") { view: NativeProseViewerExpoView, themeJson: String? ->
|
|
379
387
|
view.setThemeJson(themeJson)
|
|
380
388
|
}
|
|
389
|
+
Prop("collapsesWhenEmpty") {
|
|
390
|
+
view: NativeProseViewerExpoView,
|
|
391
|
+
collapsesWhenEmpty: Boolean? ->
|
|
392
|
+
view.setCollapsesWhenEmpty(collapsesWhenEmpty)
|
|
393
|
+
}
|
|
394
|
+
Prop("enableLinkTaps") { view: NativeProseViewerExpoView, enableLinkTaps: Boolean? ->
|
|
395
|
+
view.setEnableLinkTaps(enableLinkTaps)
|
|
396
|
+
}
|
|
397
|
+
Prop("interceptLinkTaps") { view: NativeProseViewerExpoView, interceptLinkTaps: Boolean? ->
|
|
398
|
+
view.setInterceptLinkTaps(interceptLinkTaps)
|
|
399
|
+
}
|
|
381
400
|
}
|
|
382
401
|
}
|
|
383
402
|
}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
package com.apollohg.editor
|
|
2
2
|
|
|
3
|
+
import android.content.Intent
|
|
3
4
|
import android.content.Context
|
|
4
5
|
import android.graphics.Color
|
|
6
|
+
import android.net.Uri
|
|
5
7
|
import android.view.MotionEvent
|
|
6
8
|
import android.view.View
|
|
7
9
|
import android.view.ViewGroup
|
|
8
10
|
import expo.modules.kotlin.AppContext
|
|
9
11
|
import expo.modules.kotlin.viewevent.EventDispatcher
|
|
10
12
|
import expo.modules.kotlin.views.ExpoView
|
|
13
|
+
import org.json.JSONArray
|
|
11
14
|
|
|
12
15
|
class NativeProseViewerExpoView(
|
|
13
16
|
context: Context,
|
|
@@ -17,11 +20,17 @@ class NativeProseViewerExpoView(
|
|
|
17
20
|
private val proseView = EditorEditText(context)
|
|
18
21
|
private val onContentHeightChange by EventDispatcher<Map<String, Any>>()
|
|
19
22
|
@Suppress("unused")
|
|
23
|
+
private val onPressLink by EventDispatcher<Map<String, Any>>()
|
|
24
|
+
@Suppress("unused")
|
|
20
25
|
private val onPressMention by EventDispatcher<Map<String, Any>>()
|
|
21
26
|
|
|
22
27
|
private var lastRenderJson: String? = null
|
|
23
28
|
private var lastThemeJson: String? = null
|
|
24
29
|
private var lastEmittedContentHeight = 0
|
|
30
|
+
private var collapsesWhenEmpty = true
|
|
31
|
+
private var isCollapsedEmptyContent = false
|
|
32
|
+
private var enableLinkTaps = true
|
|
33
|
+
private var interceptLinkTaps = false
|
|
25
34
|
|
|
26
35
|
init {
|
|
27
36
|
proseView.setBaseStyle(
|
|
@@ -43,14 +52,27 @@ class NativeProseViewerExpoView(
|
|
|
43
52
|
return@setOnTouchListener false
|
|
44
53
|
}
|
|
45
54
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
55
|
+
proseView.mentionHitAt(event.x, event.y)?.let { mention ->
|
|
56
|
+
onPressMention(mapOf("docPos" to mention.docPos, "label" to mention.label))
|
|
57
|
+
return@setOnTouchListener true
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!enableLinkTaps) {
|
|
61
|
+
return@setOnTouchListener false
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
val link = proseView.linkHitAt(event.x, event.y) ?: return@setOnTouchListener false
|
|
65
|
+
if (interceptLinkTaps) {
|
|
66
|
+
onPressLink(
|
|
67
|
+
mapOf(
|
|
68
|
+
"href" to link.href,
|
|
69
|
+
"text" to link.text
|
|
70
|
+
)
|
|
51
71
|
)
|
|
52
|
-
|
|
53
|
-
|
|
72
|
+
return@setOnTouchListener true
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return@setOnTouchListener openLink(link.href)
|
|
54
76
|
}
|
|
55
77
|
|
|
56
78
|
addView(
|
|
@@ -65,7 +87,7 @@ class NativeProseViewerExpoView(
|
|
|
65
87
|
fun setRenderJson(renderJson: String?) {
|
|
66
88
|
if (lastRenderJson == renderJson) return
|
|
67
89
|
lastRenderJson = renderJson
|
|
68
|
-
|
|
90
|
+
applyRenderJson()
|
|
69
91
|
post {
|
|
70
92
|
requestLayout()
|
|
71
93
|
emitContentHeightIfNeeded(force = true)
|
|
@@ -76,14 +98,36 @@ class NativeProseViewerExpoView(
|
|
|
76
98
|
if (lastThemeJson == themeJson) return
|
|
77
99
|
lastThemeJson = themeJson
|
|
78
100
|
proseView.applyTheme(EditorTheme.fromJson(themeJson))
|
|
79
|
-
|
|
101
|
+
applyRenderJson()
|
|
80
102
|
post {
|
|
81
103
|
requestLayout()
|
|
82
104
|
emitContentHeightIfNeeded(force = true)
|
|
83
105
|
}
|
|
84
106
|
}
|
|
85
107
|
|
|
108
|
+
fun setCollapsesWhenEmpty(collapsesWhenEmpty: Boolean?) {
|
|
109
|
+
val nextValue = collapsesWhenEmpty ?: true
|
|
110
|
+
if (this.collapsesWhenEmpty == nextValue) return
|
|
111
|
+
this.collapsesWhenEmpty = nextValue
|
|
112
|
+
updateCollapsedEmptyState()
|
|
113
|
+
requestLayout()
|
|
114
|
+
emitContentHeightIfNeeded(force = true)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
fun setEnableLinkTaps(enableLinkTaps: Boolean?) {
|
|
118
|
+
this.enableLinkTaps = enableLinkTaps ?: true
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
fun setInterceptLinkTaps(interceptLinkTaps: Boolean?) {
|
|
122
|
+
this.interceptLinkTaps = interceptLinkTaps ?: false
|
|
123
|
+
}
|
|
124
|
+
|
|
86
125
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
126
|
+
if (isCollapsedEmptyContent) {
|
|
127
|
+
setMeasuredDimension(resolveSize(0, widthMeasureSpec), 0)
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
|
|
87
131
|
val childWidthSpec = getChildMeasureSpec(
|
|
88
132
|
widthMeasureSpec,
|
|
89
133
|
paddingLeft + paddingRight,
|
|
@@ -104,6 +148,12 @@ class NativeProseViewerExpoView(
|
|
|
104
148
|
}
|
|
105
149
|
|
|
106
150
|
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
|
151
|
+
if (isCollapsedEmptyContent) {
|
|
152
|
+
proseView.layout(paddingLeft, paddingTop, right - left - paddingRight, paddingTop)
|
|
153
|
+
emitContentHeightIfNeeded()
|
|
154
|
+
return
|
|
155
|
+
}
|
|
156
|
+
|
|
107
157
|
val childLeft = paddingLeft
|
|
108
158
|
val childTop = paddingTop
|
|
109
159
|
proseView.layout(
|
|
@@ -115,10 +165,25 @@ class NativeProseViewerExpoView(
|
|
|
115
165
|
emitContentHeightIfNeeded()
|
|
116
166
|
}
|
|
117
167
|
|
|
168
|
+
private fun applyRenderJson() {
|
|
169
|
+
updateCollapsedEmptyState()
|
|
170
|
+
proseView.applyRenderJSON(lastRenderJson ?: "[]")
|
|
171
|
+
proseView.visibility = if (isCollapsedEmptyContent) View.GONE else View.VISIBLE
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
private fun updateCollapsedEmptyState() {
|
|
175
|
+
isCollapsedEmptyContent = collapsesWhenEmpty &&
|
|
176
|
+
renderJsonContainsOnlyEmptyParagraphs(lastRenderJson ?: "[]")
|
|
177
|
+
proseView.visibility = if (isCollapsedEmptyContent) View.GONE else View.VISIBLE
|
|
178
|
+
}
|
|
179
|
+
|
|
118
180
|
private fun emitContentHeightIfNeeded(force: Boolean = false) {
|
|
119
|
-
val contentHeight = (
|
|
120
|
-
|
|
121
|
-
|
|
181
|
+
val contentHeight = if (isCollapsedEmptyContent) {
|
|
182
|
+
0
|
|
183
|
+
} else {
|
|
184
|
+
(measureContentHeightPx() + paddingTop + paddingBottom).coerceAtLeast(0)
|
|
185
|
+
}
|
|
186
|
+
if (contentHeight <= 0 && !isCollapsedEmptyContent) {
|
|
122
187
|
return
|
|
123
188
|
}
|
|
124
189
|
if (!force && contentHeight == lastEmittedContentHeight) {
|
|
@@ -129,6 +194,10 @@ class NativeProseViewerExpoView(
|
|
|
129
194
|
}
|
|
130
195
|
|
|
131
196
|
private fun measureContentHeightPx(): Int {
|
|
197
|
+
if (isCollapsedEmptyContent) {
|
|
198
|
+
return 0
|
|
199
|
+
}
|
|
200
|
+
|
|
132
201
|
val currentMeasuredHeight = proseView.measuredHeight
|
|
133
202
|
if (currentMeasuredHeight > 0 && proseView.layout != null) {
|
|
134
203
|
return currentMeasuredHeight
|
|
@@ -154,4 +223,71 @@ class NativeProseViewerExpoView(
|
|
|
154
223
|
|
|
155
224
|
return (resources.displayMetrics.widthPixels - paddingLeft - paddingRight).coerceAtLeast(1)
|
|
156
225
|
}
|
|
226
|
+
|
|
227
|
+
private fun openLink(href: String): Boolean {
|
|
228
|
+
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(href)).apply {
|
|
229
|
+
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
230
|
+
}
|
|
231
|
+
return runCatching {
|
|
232
|
+
context.startActivity(intent)
|
|
233
|
+
true
|
|
234
|
+
}.getOrDefault(false)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
companion object {
|
|
238
|
+
private const val EMPTY_TEXT_BLOCK_PLACEHOLDER = '\u200B'
|
|
239
|
+
|
|
240
|
+
internal fun renderJsonContainsOnlyEmptyParagraphs(renderJson: String): Boolean {
|
|
241
|
+
val elements = try {
|
|
242
|
+
JSONArray(renderJson)
|
|
243
|
+
} catch (_: Exception) {
|
|
244
|
+
return false
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (elements.length() == 0) {
|
|
248
|
+
return true
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
var hasParagraph = false
|
|
252
|
+
var paragraphIsOpen = false
|
|
253
|
+
|
|
254
|
+
for (index in 0 until elements.length()) {
|
|
255
|
+
val element = elements.optJSONObject(index) ?: return false
|
|
256
|
+
when (element.optString("type", "")) {
|
|
257
|
+
"blockStart" -> {
|
|
258
|
+
if (
|
|
259
|
+
paragraphIsOpen ||
|
|
260
|
+
element.optString("nodeType", "") != "paragraph" ||
|
|
261
|
+
element.optInt("depth", 0) != 0
|
|
262
|
+
) {
|
|
263
|
+
return false
|
|
264
|
+
}
|
|
265
|
+
paragraphIsOpen = true
|
|
266
|
+
hasParagraph = true
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
"textRun" -> {
|
|
270
|
+
val text = element.optString("text", "")
|
|
271
|
+
if (
|
|
272
|
+
!paragraphIsOpen ||
|
|
273
|
+
!text.all { it == EMPTY_TEXT_BLOCK_PLACEHOLDER }
|
|
274
|
+
) {
|
|
275
|
+
return false
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
"blockEnd" -> {
|
|
280
|
+
if (!paragraphIsOpen) {
|
|
281
|
+
return false
|
|
282
|
+
}
|
|
283
|
+
paragraphIsOpen = false
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
else -> return false
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return hasParagraph && !paragraphIsOpen
|
|
291
|
+
}
|
|
292
|
+
}
|
|
157
293
|
}
|
|
@@ -752,6 +752,7 @@ class CenteredBulletSpan(
|
|
|
752
752
|
object RenderBridge {
|
|
753
753
|
internal const val NATIVE_BLOCKQUOTE_ANNOTATION = "nativeBlockquote"
|
|
754
754
|
internal const val NATIVE_TOP_LEVEL_CHILD_INDEX_ANNOTATION = "nativeTopLevelChildIndex"
|
|
755
|
+
internal const val NATIVE_LINK_HREF_ANNOTATION = "nativeLinkHref"
|
|
755
756
|
private const val NATIVE_SYNTHETIC_PLACEHOLDER_ANNOTATION = "nativeSyntheticPlaceholder"
|
|
756
757
|
|
|
757
758
|
private data class RenderBuildState(
|
|
@@ -1174,6 +1175,15 @@ object RenderBridge {
|
|
|
1174
1175
|
end,
|
|
1175
1176
|
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
|
1176
1177
|
)
|
|
1178
|
+
val href = mark.optString("href", "")
|
|
1179
|
+
if (href.isNotBlank()) {
|
|
1180
|
+
builder.setSpan(
|
|
1181
|
+
Annotation(NATIVE_LINK_HREF_ANNOTATION, href),
|
|
1182
|
+
start,
|
|
1183
|
+
end,
|
|
1184
|
+
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
|
1185
|
+
)
|
|
1186
|
+
}
|
|
1177
1187
|
}
|
|
1178
1188
|
}
|
|
1179
1189
|
}
|
|
@@ -1455,6 +1465,9 @@ object RenderBridge {
|
|
|
1455
1465
|
val indent = calculateIndent(currentBlock, blockStack, theme, density)
|
|
1456
1466
|
val markerWidth = calculateMarkerWidth(density)
|
|
1457
1467
|
val quoteDepth = blockquoteDepth(blockStack)
|
|
1468
|
+
val indentPerDepth = (theme?.list?.indent ?: LayoutConstants.INDENT_PER_DEPTH) * density
|
|
1469
|
+
val listBaseIndentAdjustment =
|
|
1470
|
+
calculateListBaseIndentAdjustment(currentBlock, theme, density)
|
|
1458
1471
|
val quoteStripeColor = if (quoteDepth > 0) {
|
|
1459
1472
|
theme?.blockquote?.borderColor ?: Color.argb(
|
|
1460
1473
|
(Color.alpha(resolveInlineTextColor(blockStack, Color.BLACK, theme)) * 0.3f).toInt(),
|
|
@@ -1476,8 +1489,9 @@ object RenderBridge {
|
|
|
1476
1489
|
) * density
|
|
1477
1490
|
val blockquoteIndentPx = (quoteDepth * quoteIndent).toInt()
|
|
1478
1491
|
val quoteBaseIndent = if (quoteDepth > 0) {
|
|
1479
|
-
((currentBlock.depth *
|
|
1480
|
-
- (quoteDepth *
|
|
1492
|
+
((currentBlock.depth * indentPerDepth)
|
|
1493
|
+
- (quoteDepth * indentPerDepth)
|
|
1494
|
+
+ listBaseIndentAdjustment
|
|
1481
1495
|
+ ((quoteDepth - 1f) * quoteIndent)).toInt()
|
|
1482
1496
|
} else {
|
|
1483
1497
|
0
|
|
@@ -1656,7 +1670,25 @@ object RenderBridge {
|
|
|
1656
1670
|
(theme?.blockquote?.markerGap ?: LayoutConstants.BLOCKQUOTE_MARKER_GAP) +
|
|
1657
1671
|
(theme?.blockquote?.borderWidth ?: LayoutConstants.BLOCKQUOTE_BORDER_WIDTH)
|
|
1658
1672
|
) * density
|
|
1659
|
-
|
|
1673
|
+
val listBaseIndentAdjustment = calculateListBaseIndentAdjustment(context, theme, density)
|
|
1674
|
+
return (context.depth * indentPerDepth) -
|
|
1675
|
+
(quoteDepth * indentPerDepth) +
|
|
1676
|
+
listBaseIndentAdjustment +
|
|
1677
|
+
(quoteDepth * quoteIndent)
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
private fun calculateListBaseIndentAdjustment(
|
|
1681
|
+
context: BlockContext,
|
|
1682
|
+
theme: EditorTheme?,
|
|
1683
|
+
density: Float
|
|
1684
|
+
): Float {
|
|
1685
|
+
if (context.listContext == null) {
|
|
1686
|
+
return 0f
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
val indentPerDepth = (theme?.list?.indent ?: LayoutConstants.INDENT_PER_DEPTH) * density
|
|
1690
|
+
val listBaseIndentMultiplier = maxOf(theme?.list?.baseIndentMultiplier ?: 1f, 0f)
|
|
1691
|
+
return (listBaseIndentMultiplier - 1f) * indentPerDepth
|
|
1660
1692
|
}
|
|
1661
1693
|
|
|
1662
1694
|
private fun effectiveBlockContext(blockStack: List<BlockContext>): BlockContext? {
|
package/dist/EditorTheme.d.ts
CHANGED
|
@@ -36,6 +36,7 @@ export interface EditorHeadingTheme {
|
|
|
36
36
|
}
|
|
37
37
|
export interface EditorListTheme {
|
|
38
38
|
indent?: number;
|
|
39
|
+
baseIndentMultiplier?: number;
|
|
39
40
|
itemSpacing?: number;
|
|
40
41
|
markerColor?: string;
|
|
41
42
|
markerScale?: number;
|
|
@@ -59,6 +60,8 @@ export interface EditorToolbarTheme {
|
|
|
59
60
|
borderColor?: string;
|
|
60
61
|
borderWidth?: number;
|
|
61
62
|
borderRadius?: number;
|
|
63
|
+
marginTop?: number;
|
|
64
|
+
showTopBorder?: boolean;
|
|
62
65
|
keyboardOffset?: number;
|
|
63
66
|
horizontalInset?: number;
|
|
64
67
|
separatorColor?: string;
|
package/dist/EditorToolbar.js
CHANGED
|
@@ -109,7 +109,7 @@ const DEFAULT_MATERIAL_ICONS = {
|
|
|
109
109
|
undo: 'undo',
|
|
110
110
|
redo: 'redo',
|
|
111
111
|
};
|
|
112
|
-
function EditorToolbar({ activeState, historyState, onToggleBold, onToggleItalic, onToggleUnderline, onToggleStrike, onToggleBulletList, onToggleHeading, onToggleBlockquote, onToggleOrderedList, onIndentList, onOutdentList, onInsertHorizontalRule, onInsertLineBreak, onUndo, onRedo, onToggleMark, onToggleListType, onInsertNodeType, onRunCommand, onToolbarAction, onRequestLink, onRequestImage, toolbarItems = exports.DEFAULT_EDITOR_TOOLBAR_ITEMS, theme, showTopBorder
|
|
112
|
+
function EditorToolbar({ activeState, historyState, onToggleBold, onToggleItalic, onToggleUnderline, onToggleStrike, onToggleBulletList, onToggleHeading, onToggleBlockquote, onToggleOrderedList, onIndentList, onOutdentList, onInsertHorizontalRule, onInsertLineBreak, onUndo, onRedo, onToggleMark, onToggleListType, onInsertNodeType, onRunCommand, onToolbarAction, onRequestLink, onRequestImage, toolbarItems = exports.DEFAULT_EDITOR_TOOLBAR_ITEMS, theme, showTopBorder, }) {
|
|
113
113
|
const marks = activeState.marks ?? {};
|
|
114
114
|
const nodes = activeState.nodes ?? {};
|
|
115
115
|
const commands = activeState.commands ?? {};
|
|
@@ -371,6 +371,7 @@ function EditorToolbar({ activeState, historyState, onToggleBold, onToggleItalic
|
|
|
371
371
|
groupsByKey: nextGroups,
|
|
372
372
|
};
|
|
373
373
|
}, [expandedGroupKey, menuState?.groupKey, resolveButton, toolbarItems]);
|
|
374
|
+
const resolvedShowTopBorder = showTopBorder ?? theme?.showTopBorder ?? true;
|
|
374
375
|
(0, react_1.useEffect)(() => {
|
|
375
376
|
if (expandedGroupKey != null && !groupsByKey.has(expandedGroupKey)) {
|
|
376
377
|
setExpandedGroupKey(null);
|
|
@@ -414,7 +415,7 @@ function EditorToolbar({ activeState, historyState, onToggleBold, onToggleItalic
|
|
|
414
415
|
});
|
|
415
416
|
});
|
|
416
417
|
}, []);
|
|
417
|
-
const menuGroup = menuState != null ? groupsByKey.get(menuState.groupKey) ?? null : null;
|
|
418
|
+
const menuGroup = menuState != null ? (groupsByKey.get(menuState.groupKey) ?? null) : null;
|
|
418
419
|
const menuHeight = menuGroup ? menuGroup.children.length * 40 + 16 : 0;
|
|
419
420
|
const menuTop = menuState == null
|
|
420
421
|
? 0
|
|
@@ -426,7 +427,11 @@ function EditorToolbar({ activeState, historyState, onToggleBold, onToggleItalic
|
|
|
426
427
|
const activeColor = theme?.buttonActiveColor ?? ACTIVE_COLOR;
|
|
427
428
|
const defaultColor = theme?.buttonColor ?? DEFAULT_COLOR;
|
|
428
429
|
const disabledColor = theme?.buttonDisabledColor ?? DISABLED_COLOR;
|
|
429
|
-
const color = button.isActive
|
|
430
|
+
const color = button.isActive
|
|
431
|
+
? activeColor
|
|
432
|
+
: button.isDisabled
|
|
433
|
+
? disabledColor
|
|
434
|
+
: defaultColor;
|
|
430
435
|
const anchorGroupKey = options?.anchorGroupKey;
|
|
431
436
|
return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { ref: anchorGroupKey == null
|
|
432
437
|
? undefined
|
|
@@ -457,15 +462,15 @@ function EditorToolbar({ activeState, historyState, onToggleBold, onToggleItalic
|
|
|
457
462
|
] }, key));
|
|
458
463
|
return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [
|
|
459
464
|
styles.container,
|
|
460
|
-
!
|
|
465
|
+
!resolvedShowTopBorder && styles.containerWithoutTopBorder,
|
|
461
466
|
theme?.backgroundColor != null ? { backgroundColor: theme.backgroundColor } : null,
|
|
462
467
|
theme?.borderColor != null
|
|
463
|
-
?
|
|
468
|
+
? resolvedShowTopBorder
|
|
464
469
|
? { borderTopColor: theme.borderColor }
|
|
465
470
|
: null
|
|
466
471
|
: null,
|
|
467
472
|
theme?.borderWidth != null
|
|
468
|
-
?
|
|
473
|
+
? resolvedShowTopBorder
|
|
469
474
|
? { borderTopWidth: theme.borderWidth }
|
|
470
475
|
: null
|
|
471
476
|
: null,
|
|
@@ -9,17 +9,33 @@ export interface NativeProseViewerMentionRenderContext {
|
|
|
9
9
|
}
|
|
10
10
|
export interface NativeProseViewerMentionPressEvent extends NativeProseViewerMentionRenderContext {
|
|
11
11
|
}
|
|
12
|
+
export interface NativeProseViewerLinkPressEvent {
|
|
13
|
+
href: string;
|
|
14
|
+
text: string;
|
|
15
|
+
}
|
|
12
16
|
type NativeProseViewerContent = DocumentJSON | string;
|
|
13
|
-
|
|
14
|
-
|
|
17
|
+
interface NativeProseViewerBaseProps {
|
|
18
|
+
contentRevision?: string | number;
|
|
15
19
|
contentJSONRevision?: string | number;
|
|
16
20
|
schema?: SchemaDefinition;
|
|
17
21
|
theme?: EditorTheme;
|
|
18
22
|
style?: StyleProp<ViewStyle>;
|
|
19
23
|
allowBase64Images?: boolean;
|
|
24
|
+
collapseTrailingEmptyParagraphs?: boolean;
|
|
25
|
+
enableLinkTaps?: boolean;
|
|
20
26
|
mentionPrefix?: string | ((mention: NativeProseViewerMentionRenderContext) => string | null | undefined);
|
|
21
27
|
resolveMentionTheme?: (mention: NativeProseViewerMentionRenderContext) => EditorMentionTheme | null | undefined;
|
|
28
|
+
onPressLink?: (event: NativeProseViewerLinkPressEvent) => void;
|
|
22
29
|
onPressMention?: (event: NativeProseViewerMentionPressEvent) => void;
|
|
23
30
|
}
|
|
24
|
-
|
|
31
|
+
interface NativeProseViewerJsonProps extends NativeProseViewerBaseProps {
|
|
32
|
+
contentJSON: NativeProseViewerContent;
|
|
33
|
+
contentHTML?: never;
|
|
34
|
+
}
|
|
35
|
+
interface NativeProseViewerHtmlProps extends NativeProseViewerBaseProps {
|
|
36
|
+
contentHTML: string;
|
|
37
|
+
contentJSON?: never;
|
|
38
|
+
}
|
|
39
|
+
export type NativeProseViewerProps = NativeProseViewerJsonProps | NativeProseViewerHtmlProps;
|
|
40
|
+
export declare function NativeProseViewer({ ...props }: NativeProseViewerProps): import("react/jsx-runtime").JSX.Element;
|
|
25
41
|
export {};
|