@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.
@@ -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
- post {
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
- post {
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 = proseView.measuredHeight + paddingTop + paddingBottom
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
- resolveSize(desiredHeight, heightMeasureSpec)
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(force: Boolean = false) {
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
- (measureContentHeightPx() + paddingTop + paddingBottom).coerceAtLeast(0)
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
- val childWidthSpec = View.MeasureSpec.makeMeasureSpec(availableWidthPx, View.MeasureSpec.EXACTLY)
208
- val childHeightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
209
- proseView.measure(childWidthSpec, childHeightSpec)
210
- return proseView.measuredHeight
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 nextHeight = event.nativeEvent.contentHeight;
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 == null ||
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-arm64_x86_64-simulator</string>
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-arm64</string>
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>
@@ -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
- let resolvedWidth = bounds.width > 0
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: resolvedWidth)
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.4",
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",