@apollohg/react-native-prose-editor 0.5.4 → 0.5.5
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/NativeProseViewerExpoView.kt +43 -21
- package/dist/NativeProseViewer.js +4 -15
- 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/NativeProseViewerExpoView.swift +3 -11
- 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
|
@@ -10,6 +10,7 @@ import android.view.ViewGroup
|
|
|
10
10
|
import expo.modules.kotlin.AppContext
|
|
11
11
|
import expo.modules.kotlin.viewevent.EventDispatcher
|
|
12
12
|
import expo.modules.kotlin.views.ExpoView
|
|
13
|
+
import kotlin.math.abs
|
|
13
14
|
import org.json.JSONArray
|
|
14
15
|
|
|
15
16
|
class NativeProseViewerExpoView(
|
|
@@ -31,6 +32,7 @@ class NativeProseViewerExpoView(
|
|
|
31
32
|
private var isCollapsedEmptyContent = false
|
|
32
33
|
private var enableLinkTaps = true
|
|
33
34
|
private var interceptLinkTaps = false
|
|
35
|
+
internal var suppressContentHeightEventsForTesting = false
|
|
34
36
|
|
|
35
37
|
init {
|
|
36
38
|
proseView.setBaseStyle(
|
|
@@ -39,6 +41,9 @@ class NativeProseViewerExpoView(
|
|
|
39
41
|
Color.TRANSPARENT
|
|
40
42
|
)
|
|
41
43
|
proseView.isEditable = false
|
|
44
|
+
proseView.inputType = android.text.InputType.TYPE_CLASS_TEXT or
|
|
45
|
+
android.text.InputType.TYPE_TEXT_FLAG_MULTI_LINE or
|
|
46
|
+
android.text.InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
|
|
42
47
|
proseView.setImageResizingEnabled(false)
|
|
43
48
|
proseView.setHeightBehavior(EditorHeightBehavior.AUTO_GROW)
|
|
44
49
|
proseView.isFocusable = false
|
|
@@ -88,10 +93,7 @@ class NativeProseViewerExpoView(
|
|
|
88
93
|
if (lastRenderJson == renderJson) return
|
|
89
94
|
lastRenderJson = renderJson
|
|
90
95
|
applyRenderJson()
|
|
91
|
-
|
|
92
|
-
requestLayout()
|
|
93
|
-
emitContentHeightIfNeeded(force = true)
|
|
94
|
-
}
|
|
96
|
+
requestLayout()
|
|
95
97
|
}
|
|
96
98
|
|
|
97
99
|
fun setThemeJson(themeJson: String?) {
|
|
@@ -99,10 +101,7 @@ class NativeProseViewerExpoView(
|
|
|
99
101
|
lastThemeJson = themeJson
|
|
100
102
|
proseView.applyTheme(EditorTheme.fromJson(themeJson))
|
|
101
103
|
applyRenderJson()
|
|
102
|
-
|
|
103
|
-
requestLayout()
|
|
104
|
-
emitContentHeightIfNeeded(force = true)
|
|
105
|
-
}
|
|
104
|
+
requestLayout()
|
|
106
105
|
}
|
|
107
106
|
|
|
108
107
|
fun setCollapsesWhenEmpty(collapsesWhenEmpty: Boolean?) {
|
|
@@ -125,6 +124,7 @@ class NativeProseViewerExpoView(
|
|
|
125
124
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
126
125
|
if (isCollapsedEmptyContent) {
|
|
127
126
|
setMeasuredDimension(resolveSize(0, widthMeasureSpec), 0)
|
|
127
|
+
emitContentHeightIfNeeded()
|
|
128
128
|
return
|
|
129
129
|
}
|
|
130
130
|
|
|
@@ -139,12 +139,20 @@ class NativeProseViewerExpoView(
|
|
|
139
139
|
)
|
|
140
140
|
proseView.measure(childWidthSpec, childHeightSpec)
|
|
141
141
|
|
|
142
|
+
val resolvedContentHeight = proseView.resolveAutoGrowHeight()
|
|
142
143
|
val desiredWidth = proseView.measuredWidth + paddingLeft + paddingRight
|
|
143
|
-
val desiredHeight =
|
|
144
|
+
val desiredHeight = resolvedContentHeight + paddingTop + paddingBottom
|
|
145
|
+
val measuredHeight = when (View.MeasureSpec.getMode(heightMeasureSpec)) {
|
|
146
|
+
View.MeasureSpec.AT_MOST -> desiredHeight.coerceAtMost(
|
|
147
|
+
View.MeasureSpec.getSize(heightMeasureSpec)
|
|
148
|
+
)
|
|
149
|
+
else -> desiredHeight
|
|
150
|
+
}
|
|
144
151
|
setMeasuredDimension(
|
|
145
152
|
resolveSize(desiredWidth, widthMeasureSpec),
|
|
146
|
-
|
|
153
|
+
measuredHeight
|
|
147
154
|
)
|
|
155
|
+
emitContentHeightIfNeeded(measuredContentHeight = desiredHeight)
|
|
148
156
|
}
|
|
149
157
|
|
|
150
158
|
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
|
@@ -177,11 +185,16 @@ class NativeProseViewerExpoView(
|
|
|
177
185
|
proseView.visibility = if (isCollapsedEmptyContent) View.GONE else View.VISIBLE
|
|
178
186
|
}
|
|
179
187
|
|
|
180
|
-
private fun emitContentHeightIfNeeded(
|
|
188
|
+
private fun emitContentHeightIfNeeded(
|
|
189
|
+
force: Boolean = false,
|
|
190
|
+
measuredContentHeight: Int? = null
|
|
191
|
+
) {
|
|
181
192
|
val contentHeight = if (isCollapsedEmptyContent) {
|
|
182
193
|
0
|
|
183
194
|
} else {
|
|
184
|
-
(
|
|
195
|
+
(
|
|
196
|
+
measuredContentHeight ?: (measureContentHeightPx() + paddingTop + paddingBottom)
|
|
197
|
+
).coerceAtLeast(0)
|
|
185
198
|
}
|
|
186
199
|
if (contentHeight <= 0 && !isCollapsedEmptyContent) {
|
|
187
200
|
return
|
|
@@ -190,6 +203,9 @@ class NativeProseViewerExpoView(
|
|
|
190
203
|
return
|
|
191
204
|
}
|
|
192
205
|
lastEmittedContentHeight = contentHeight
|
|
206
|
+
if (suppressContentHeightEventsForTesting) {
|
|
207
|
+
return
|
|
208
|
+
}
|
|
193
209
|
onContentHeightChange(mapOf("contentHeight" to contentHeight))
|
|
194
210
|
}
|
|
195
211
|
|
|
@@ -198,16 +214,22 @@ class NativeProseViewerExpoView(
|
|
|
198
214
|
return 0
|
|
199
215
|
}
|
|
200
216
|
|
|
201
|
-
val currentMeasuredHeight = proseView.measuredHeight
|
|
202
|
-
if (currentMeasuredHeight > 0 && proseView.layout != null) {
|
|
203
|
-
return currentMeasuredHeight
|
|
204
|
-
}
|
|
205
|
-
|
|
206
217
|
val availableWidthPx = resolveAvailableWidthPx()
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
218
|
+
if (
|
|
219
|
+
proseView.measuredWidth <= 0 ||
|
|
220
|
+
abs(proseView.measuredWidth - availableWidthPx) > 1
|
|
221
|
+
) {
|
|
222
|
+
val childWidthSpec = View.MeasureSpec.makeMeasureSpec(
|
|
223
|
+
availableWidthPx,
|
|
224
|
+
View.MeasureSpec.EXACTLY
|
|
225
|
+
)
|
|
226
|
+
val childHeightSpec = View.MeasureSpec.makeMeasureSpec(
|
|
227
|
+
0,
|
|
228
|
+
View.MeasureSpec.UNSPECIFIED
|
|
229
|
+
)
|
|
230
|
+
proseView.measure(childWidthSpec, childHeightSpec)
|
|
231
|
+
}
|
|
232
|
+
return proseView.resolveAutoGrowHeight()
|
|
211
233
|
}
|
|
212
234
|
|
|
213
235
|
private fun resolveAvailableWidthPx(): Int {
|
|
@@ -4,6 +4,7 @@ exports.NativeProseViewer = NativeProseViewer;
|
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
5
|
const react_1 = require("react");
|
|
6
6
|
const expo_modules_core_1 = require("expo-modules-core");
|
|
7
|
+
const react_native_1 = require("react-native");
|
|
7
8
|
const addons_1 = require("./addons");
|
|
8
9
|
const EditorTheme_1 = require("./EditorTheme");
|
|
9
10
|
const schemas_1 = require("./schemas");
|
|
@@ -381,12 +382,9 @@ function NativeProseViewer({ ...props }) {
|
|
|
381
382
|
const renderJsonIsCollapsedEmpty = (0, react_1.useMemo)(() => collapseTrailingEmptyParagraphs &&
|
|
382
383
|
renderElementsJsonContainsOnlyEmptyParagraphs(renderJson), [collapseTrailingEmptyParagraphs, renderJson]);
|
|
383
384
|
const [contentHeight, setContentHeight] = (0, react_1.useState)(null);
|
|
384
|
-
const allowContentHeightShrinkRef = (0, react_1.useRef)(true);
|
|
385
|
-
(0, react_1.useEffect)(() => {
|
|
386
|
-
allowContentHeightShrinkRef.current = true;
|
|
387
|
-
}, [resolvedContentRevision, renderJson, themeJson]);
|
|
388
385
|
const handleContentHeightChange = (0, react_1.useCallback)((event) => {
|
|
389
|
-
const
|
|
386
|
+
const density = react_native_1.Platform.OS === 'android' ? react_native_1.PixelRatio.get() : 1;
|
|
387
|
+
const nextHeight = Math.ceil(event.nativeEvent.contentHeight / density);
|
|
390
388
|
if (nextHeight < 0)
|
|
391
389
|
return;
|
|
392
390
|
if (nextHeight === 0 && !renderJsonIsCollapsedEmpty)
|
|
@@ -395,16 +393,7 @@ function NativeProseViewer({ ...props }) {
|
|
|
395
393
|
setContentHeight((currentHeight) => currentHeight === 0 ? currentHeight : 0);
|
|
396
394
|
return;
|
|
397
395
|
}
|
|
398
|
-
setContentHeight((currentHeight) => currentHeight
|
|
399
|
-
nextHeight >= currentHeight ||
|
|
400
|
-
allowContentHeightShrinkRef.current
|
|
401
|
-
? (() => {
|
|
402
|
-
allowContentHeightShrinkRef.current = false;
|
|
403
|
-
return currentHeight === nextHeight
|
|
404
|
-
? currentHeight
|
|
405
|
-
: nextHeight;
|
|
406
|
-
})()
|
|
407
|
-
: currentHeight);
|
|
396
|
+
setContentHeight((currentHeight) => currentHeight === nextHeight ? currentHeight : nextHeight);
|
|
408
397
|
}, [renderJsonIsCollapsedEmpty]);
|
|
409
398
|
const handlePressMention = (0, react_1.useCallback)((event) => {
|
|
410
399
|
if (!onPressMention)
|
|
@@ -8,32 +8,32 @@
|
|
|
8
8
|
<key>BinaryPath</key>
|
|
9
9
|
<string>libeditor_core.a</string>
|
|
10
10
|
<key>LibraryIdentifier</key>
|
|
11
|
-
<string>ios-
|
|
11
|
+
<string>ios-arm64</string>
|
|
12
12
|
<key>LibraryPath</key>
|
|
13
13
|
<string>libeditor_core.a</string>
|
|
14
14
|
<key>SupportedArchitectures</key>
|
|
15
15
|
<array>
|
|
16
16
|
<string>arm64</string>
|
|
17
|
-
<string>x86_64</string>
|
|
18
17
|
</array>
|
|
19
18
|
<key>SupportedPlatform</key>
|
|
20
19
|
<string>ios</string>
|
|
21
|
-
<key>SupportedPlatformVariant</key>
|
|
22
|
-
<string>simulator</string>
|
|
23
20
|
</dict>
|
|
24
21
|
<dict>
|
|
25
22
|
<key>BinaryPath</key>
|
|
26
23
|
<string>libeditor_core.a</string>
|
|
27
24
|
<key>LibraryIdentifier</key>
|
|
28
|
-
<string>ios-
|
|
25
|
+
<string>ios-arm64_x86_64-simulator</string>
|
|
29
26
|
<key>LibraryPath</key>
|
|
30
27
|
<string>libeditor_core.a</string>
|
|
31
28
|
<key>SupportedArchitectures</key>
|
|
32
29
|
<array>
|
|
33
30
|
<string>arm64</string>
|
|
31
|
+
<string>x86_64</string>
|
|
34
32
|
</array>
|
|
35
33
|
<key>SupportedPlatform</key>
|
|
36
34
|
<string>ios</string>
|
|
35
|
+
<key>SupportedPlatformVariant</key>
|
|
36
|
+
<string>simulator</string>
|
|
37
37
|
</dict>
|
|
38
38
|
</array>
|
|
39
39
|
<key>CFBundlePackageType</key>
|
|
Binary file
|
|
Binary file
|
|
@@ -11,7 +11,6 @@ final class NativeProseViewerExpoView: ExpoView {
|
|
|
11
11
|
private var lastThemeJSON: String?
|
|
12
12
|
private var lastEmittedContentHeight: CGFloat = 0
|
|
13
13
|
private var lastMeasuredWidth: CGFloat = 0
|
|
14
|
-
private var allowContentHeightShrink = true
|
|
15
14
|
private var collapsesWhenEmpty = true
|
|
16
15
|
private var isCollapsedEmptyContent = false
|
|
17
16
|
private var enableLinkTaps = true
|
|
@@ -57,7 +56,6 @@ final class NativeProseViewerExpoView: ExpoView {
|
|
|
57
56
|
let nextValue = collapses ?? true
|
|
58
57
|
guard collapsesWhenEmpty != nextValue else { return }
|
|
59
58
|
collapsesWhenEmpty = nextValue
|
|
60
|
-
allowContentHeightShrink = true
|
|
61
59
|
updateCollapsedEmptyState()
|
|
62
60
|
setNeedsLayout()
|
|
63
61
|
emitContentHeightIfNeeded(force: true)
|
|
@@ -66,14 +64,12 @@ final class NativeProseViewerExpoView: ExpoView {
|
|
|
66
64
|
func setRenderJson(_ renderJson: String?) {
|
|
67
65
|
guard lastRenderJSON != renderJson else { return }
|
|
68
66
|
lastRenderJSON = renderJson
|
|
69
|
-
allowContentHeightShrink = true
|
|
70
67
|
applyRenderJSON()
|
|
71
68
|
}
|
|
72
69
|
|
|
73
70
|
func setThemeJson(_ themeJson: String?) {
|
|
74
71
|
guard lastThemeJSON != themeJson else { return }
|
|
75
72
|
lastThemeJSON = themeJson
|
|
76
|
-
allowContentHeightShrink = true
|
|
77
73
|
let theme = EditorTheme.from(json: themeJson)
|
|
78
74
|
textView.applyTheme(theme)
|
|
79
75
|
let cornerRadius = theme?.borderRadius ?? 0
|
|
@@ -112,9 +108,9 @@ final class NativeProseViewerExpoView: ExpoView {
|
|
|
112
108
|
updateCollapsedEmptyState()
|
|
113
109
|
textView.applyRenderJSON(lastRenderJSON ?? "[]")
|
|
114
110
|
textView.isHidden = isCollapsedEmptyContent
|
|
111
|
+
lastMeasuredWidth = 0
|
|
115
112
|
invalidateIntrinsicContentSize()
|
|
116
113
|
setNeedsLayout()
|
|
117
|
-
emitContentHeightIfNeeded(force: true)
|
|
118
114
|
}
|
|
119
115
|
|
|
120
116
|
private func updateCollapsedEmptyState() {
|
|
@@ -131,16 +127,12 @@ final class NativeProseViewerExpoView: ExpoView {
|
|
|
131
127
|
if isCollapsedEmptyContent {
|
|
132
128
|
contentHeight = 0
|
|
133
129
|
} else {
|
|
134
|
-
|
|
135
|
-
? bounds.width
|
|
136
|
-
: (superview?.bounds.width ?? UIScreen.main.bounds.width)
|
|
130
|
+
guard bounds.width > 0 else { return }
|
|
137
131
|
let fittedHeight = measuredHeight
|
|
138
|
-
?? textView.measuredAutoGrowHeightForTesting(width:
|
|
132
|
+
?? textView.measuredAutoGrowHeightForTesting(width: bounds.width)
|
|
139
133
|
contentHeight = ceil(fittedHeight)
|
|
140
134
|
guard contentHeight > 0 else { return }
|
|
141
135
|
}
|
|
142
|
-
guard allowContentHeightShrink || contentHeight >= lastEmittedContentHeight else { return }
|
|
143
|
-
allowContentHeightShrink = false
|
|
144
136
|
guard force || abs(contentHeight - lastEmittedContentHeight) > 0.5 else { return }
|
|
145
137
|
lastEmittedContentHeight = contentHeight
|
|
146
138
|
invalidateIntrinsicContentSize()
|
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.5",
|
|
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
|