react-native-nitro-modal 0.3.0-rc → 0.3.1-rc

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.
Files changed (53) hide show
  1. package/README.md +5 -1
  2. package/android/src/main/java/com/margelo/nitro/nitromodal/BottomSheetPresenter.kt +73 -9
  3. package/android/src/main/java/com/margelo/nitro/nitromodal/HybridNitroModal.kt +4 -0
  4. package/android/src/main/java/com/margelo/nitro/nitromodal/ModalConfig.kt +4 -0
  5. package/ios/HybridNitroModal.swift +5 -1
  6. package/ios/ModalConfig.swift +4 -0
  7. package/ios/SheetView.swift +82 -10
  8. package/lib/module/NitroModal.js +16 -1
  9. package/lib/module/NitroModal.js.map +1 -1
  10. package/lib/module/NitroModalView.web.js +32 -2
  11. package/lib/module/NitroModalView.web.js.map +1 -1
  12. package/lib/module/web/ModalConfig.js +3 -1
  13. package/lib/module/web/ModalConfig.js.map +1 -1
  14. package/lib/module/web/ModalPresenter.js.map +1 -1
  15. package/lib/module/web/SheetPresenter.js +66 -7
  16. package/lib/module/web/SheetPresenter.js.map +1 -1
  17. package/lib/module/web/dom.js +3 -0
  18. package/lib/module/web/dom.js.map +1 -1
  19. package/lib/typescript/src/NitroModal.d.ts +10 -1
  20. package/lib/typescript/src/NitroModal.d.ts.map +1 -1
  21. package/lib/typescript/src/NitroModal.nitro.d.ts +8 -0
  22. package/lib/typescript/src/NitroModal.nitro.d.ts.map +1 -1
  23. package/lib/typescript/src/NitroModalView.web.d.ts.map +1 -1
  24. package/lib/typescript/src/web/ModalConfig.d.ts +4 -0
  25. package/lib/typescript/src/web/ModalConfig.d.ts.map +1 -1
  26. package/lib/typescript/src/web/ModalPresenter.d.ts +4 -0
  27. package/lib/typescript/src/web/ModalPresenter.d.ts.map +1 -1
  28. package/lib/typescript/src/web/SheetPresenter.d.ts +12 -1
  29. package/lib/typescript/src/web/SheetPresenter.d.ts.map +1 -1
  30. package/lib/typescript/src/web/dom.d.ts +2 -0
  31. package/lib/typescript/src/web/dom.d.ts.map +1 -1
  32. package/nitrogen/generated/android/c++/JHybridNitroModalSpec.cpp +18 -0
  33. package/nitrogen/generated/android/c++/JHybridNitroModalSpec.hpp +4 -0
  34. package/nitrogen/generated/android/c++/views/JHybridNitroModalStateUpdater.cpp +10 -0
  35. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromodal/HybridNitroModalSpec.kt +12 -0
  36. package/nitrogen/generated/ios/NitroModal-Swift-Cxx-Bridge.hpp +15 -0
  37. package/nitrogen/generated/ios/c++/HybridNitroModalSpecSwift.hpp +14 -0
  38. package/nitrogen/generated/ios/c++/views/HybridNitroModalComponent.mm +12 -0
  39. package/nitrogen/generated/ios/swift/HybridNitroModalSpec.swift +2 -0
  40. package/nitrogen/generated/ios/swift/HybridNitroModalSpec_cxx.swift +48 -0
  41. package/nitrogen/generated/shared/c++/HybridNitroModalSpec.cpp +4 -0
  42. package/nitrogen/generated/shared/c++/HybridNitroModalSpec.hpp +4 -0
  43. package/nitrogen/generated/shared/c++/views/HybridNitroModalComponent.cpp +4 -0
  44. package/nitrogen/generated/shared/c++/views/HybridNitroModalComponent.hpp +6 -0
  45. package/nitrogen/generated/shared/json/NitroModalConfig.json +2 -0
  46. package/package.json +1 -1
  47. package/src/NitroModal.nitro.ts +8 -0
  48. package/src/NitroModal.tsx +25 -0
  49. package/src/NitroModalView.web.tsx +26 -1
  50. package/src/web/ModalConfig.ts +6 -0
  51. package/src/web/ModalPresenter.ts +4 -0
  52. package/src/web/SheetPresenter.ts +72 -5
  53. package/src/web/dom.ts +3 -0
package/README.md CHANGED
@@ -19,7 +19,7 @@
19
19
 
20
20
  - 📄 **Native bottom sheets** with multiple detents (`small`, `medium`, `large`, `fitContent` or a height) and swipe-to-dismiss
21
21
  - 📌 **Inline sheets** — a persistent sheet that is part of your screen, with the views behind it still usable
22
- - 🔄 **Pull to refresh** — pulling a sheet down past its lowest detent
22
+ - 🔄 **Pull to refresh** — pulling a sheet down past its lowest detent, with a native activity indicator
23
23
  - 🪟 **Centered popups** with native `scale` / `fade` transitions
24
24
  - 📏 **Content-sized sheets** — `fitContent` measures your React content and grows with it
25
25
  - ⌨️ **Keyboard aware** — the sheet or card follows the keyboard animation (`pan` or `resize`)
@@ -126,6 +126,7 @@ Detents are listed smallest first. `initialDetentIndex` selects where the sheet
126
126
  presentation="inline"
127
127
  style={{ top: headerHeight }}
128
128
  detents={[320, 'large']}
129
+ refreshing={isRefreshing}
129
130
  onPullToRefresh={refresh}
130
131
  >
131
132
  <FlatList data={rows} renderItem={renderRow} nestedScrollEnabled />
@@ -142,6 +143,7 @@ A sheet hands drags off to the scroll view under the finger:
142
143
  - Dragging up expands the sheet first, then scrolls the content.
143
144
  - Dragging down scrolls the content back to its top. Once the content has scrolled during a drag, that drag never moves the sheet; the next drag collapses it.
144
145
  - With `onPullToRefresh` set, a drag that starts with the sheet resting on its lowest detent can pull it further down. Letting go past the threshold calls `onPullToRefresh` (with haptic feedback when it is crossed). This needs a sheet that can't be swiped away: inline, or `dismissOnSwipe={false}`.
146
+ - With `refreshing` set too, an activity indicator shows in the gap the pull uncovers, and the sheet stays pulled down while `refreshing` is `true`, like `RefreshControl`. Set it to `true` in `onPullToRefresh` and back to `false` when the refresh is done. Setting it to `true` yourself shows the indicator without a pull.
145
147
 
146
148
  On Android, scrollable content takes part through nested scrolling, so set `nestedScrollEnabled` on it.
147
149
 
@@ -217,6 +219,8 @@ const sheet = useRef<NitroModalRef>(null);
217
219
  | `backgroundColor` | `ColorValue` | system surface | Background of the sheet/card. |
218
220
  | `keyboardBehavior` | `'pan' \| 'resize' \| 'none'` | `'pan'` | How the modal reacts to the software keyboard. |
219
221
  | `popupAnimation` | `'scale' \| 'fade' \| 'none'` | `'scale'` | Enter/exit transition of a popup. |
222
+ | `refreshing` | `boolean` | — | A refresh is in progress: the sheet stays pulled down with an activity indicator. See [pull to refresh](#scrolling-content-and-pull-to-refresh). |
223
+ | `refreshIndicatorColor` | `ColorValue` | platform default | Color of the refresh indicator. |
220
224
  | `contentContainerStyle` | `StyleProp<ViewStyle>` | — | Style of the view that wraps `children` inside the modal. |
221
225
  | `testID` | `string` | — | Applied to the content container. |
222
226
  | `ref` | `Ref<NitroModalRef>` | — | Imperative handle. |
@@ -4,6 +4,7 @@ import android.animation.ValueAnimator
4
4
  import android.annotation.SuppressLint
5
5
  import android.app.Activity
6
6
  import android.content.Context
7
+ import android.content.res.ColorStateList
7
8
  import android.graphics.Color
8
9
  import android.graphics.Outline
9
10
  import android.graphics.drawable.GradientDrawable
@@ -18,7 +19,9 @@ import android.view.ViewOutlineProvider
18
19
  import android.view.animation.DecelerateInterpolator
19
20
  import android.view.animation.PathInterpolator
20
21
  import android.widget.FrameLayout
22
+ import android.widget.ProgressBar
21
23
  import androidx.activity.BackEventCompat
24
+ import androidx.core.animation.doOnEnd
22
25
  import androidx.coordinatorlayout.widget.CoordinatorLayout
23
26
  import androidx.core.graphics.Insets
24
27
  import androidx.core.view.ViewCompat
@@ -56,6 +59,7 @@ internal class BottomSheetPresenter(
56
59
  private val coordinator = SheetLayout(activity)
57
60
  private val sheet = FrameLayout(activity)
58
61
  private val grabber = View(activity)
62
+ private val refreshIndicator = ProgressBar(activity)
59
63
  private val behavior = SheetBehavior()
60
64
  private val sheetBackground = GradientDrawable()
61
65
  private var cornerRadiusPx = 0f
@@ -72,6 +76,11 @@ internal class BottomSheetPresenter(
72
76
 
73
77
  /** How far the sheet is pulled below its lowest detent, in px. */
74
78
  private var pullOffset = 0f
79
+ private var pullAnimator: ValueAnimator? = null
80
+ private val refreshThreshold = activity.dpToPx(REFRESH_THRESHOLD_DP)
81
+
82
+ /** The sheet rests below its lowest detent, the indicator spinning above it. */
83
+ private var refreshing = config.refreshing ?: false
75
84
 
76
85
  /** A predictive back gesture was committed; the behavior is sliding the sheet out. */
77
86
  private var committingBack = false
@@ -86,6 +95,8 @@ internal class BottomSheetPresenter(
86
95
  if (newState == BottomSheetBehavior.STATE_DRAGGING) {
87
96
  sheetDragging = true
88
97
  coordinator.notifyNativeGesture(started = true)
98
+ // A refreshing sheet follows the finger from its lowest detent.
99
+ animatePullOffset(0f)
89
100
  } else if (sheetDragging) {
90
101
  sheetDragging = false
91
102
  coordinator.notifyNativeGesture(started = false)
@@ -97,6 +108,7 @@ internal class BottomSheetPresenter(
97
108
  behavior.isHideable = this@BottomSheetPresenter.config.dismissOnSwipe
98
109
  notifyPresented()
99
110
  reportRestingTop()
111
+ animatePullOffset(restingPullOffset())
100
112
  val index = sortedDetents.getOrNull(position)?.index ?: return
101
113
  if (index != lastDetentIndex) {
102
114
  lastDetentIndex = index
@@ -107,6 +119,7 @@ internal class BottomSheetPresenter(
107
119
  override fun onSlide(bottomSheet: View, slideOffset: Float) {
108
120
  // slideOffset is -1 when hidden and 0 at the lowest detent.
109
121
  backdrop.alpha = (1f + slideOffset).coerceIn(0f, 1f)
122
+ layoutRefreshIndicator()
110
123
  }
111
124
  }
112
125
 
@@ -117,8 +130,20 @@ internal class BottomSheetPresenter(
117
130
  if (r - l != oldR - oldL || b - t != oldB - oldT) coordinator.post { updateDetents() }
118
131
  // Detent heights can move the resting sheet without a state change.
119
132
  reportRestingTop()
133
+ layoutRefreshIndicator()
120
134
  }
121
135
 
136
+ // Behind the sheet, which uncovers it as it's pulled down.
137
+ refreshIndicator.isIndeterminate = true
138
+ refreshIndicator.visibility = View.INVISIBLE
139
+ refreshIndicator.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
140
+ coordinator.addView(
141
+ refreshIndicator,
142
+ CoordinatorLayout.LayoutParams(dp(REFRESH_INDICATOR_SIZE_DP), dp(REFRESH_INDICATOR_SIZE_DP)).apply {
143
+ gravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL
144
+ },
145
+ )
146
+
122
147
  sheet.background = sheetBackground
123
148
  sheet.outlineProvider = object : ViewOutlineProvider() {
124
149
  override fun getOutline(view: View, outline: Outline) {
@@ -207,6 +232,8 @@ internal class BottomSheetPresenter(
207
232
 
208
233
  override fun onConfigChanged(previous: ModalConfig) {
209
234
  applyAppearance()
235
+ if (previous.refreshing != config.refreshing) setRefreshing(config.refreshing ?: false)
236
+ layoutRefreshIndicator()
210
237
  if (presented && !isDismissing) behavior.isHideable = config.dismissOnSwipe
211
238
  if (previous.detents != config.detents || previous.keyboardBehavior != config.keyboardBehavior) {
212
239
  updateDetents()
@@ -245,6 +272,7 @@ internal class BottomSheetPresenter(
245
272
  cornerRadius = activity.dpToPx(2f)
246
273
  setColor(Color.argb(0x66, 0x80, 0x80, 0x80))
247
274
  }
275
+ refreshIndicator.indeterminateTintList = config.refreshIndicatorColor?.let(ColorStateList::valueOf)
248
276
  }
249
277
 
250
278
  /** Recomputes detent heights and maps them onto the behavior's states. */
@@ -364,6 +392,43 @@ internal class BottomSheetPresenter(
364
392
  private fun setPullOffset(offset: Float) {
365
393
  pullOffset = offset
366
394
  applyTranslation()
395
+ layoutRefreshIndicator()
396
+ }
397
+
398
+ private fun animatePullOffset(target: Float) {
399
+ if (coordinator.isPulling) return
400
+ if (pullAnimator == null && pullOffset == target) return
401
+ pullAnimator?.cancel()
402
+ pullAnimator = ValueAnimator.ofFloat(pullOffset, target).apply {
403
+ duration = PULL_RELEASE_DURATION
404
+ interpolator = DecelerateInterpolator()
405
+ addUpdateListener { setPullOffset(it.animatedValue as Float) }
406
+ doOnEnd { if (pullAnimator === it) pullAnimator = null }
407
+ start()
408
+ }
409
+ }
410
+
411
+ /** Where the sheet rests below its lowest detent: held there while refreshing. */
412
+ private fun restingPullOffset(): Float =
413
+ if (refreshing && behavior.state == states().firstOrNull()) refreshThreshold else 0f
414
+
415
+ private fun setRefreshing(value: Boolean) {
416
+ if (value == refreshing) return
417
+ refreshing = value
418
+ if (presented && !isDismissing) animatePullOffset(restingPullOffset())
419
+ }
420
+
421
+ /**
422
+ * Centered in the gap a pull uncovers below the lowest detent, fading in
423
+ * as the pull nears the threshold.
424
+ */
425
+ private fun layoutRefreshIndicator() {
426
+ val size = refreshIndicator.layoutParams?.height ?: return
427
+ refreshIndicator.translationY = sheet.top + sheet.translationY - (refreshThreshold + size) / 2
428
+ val progress = if (config.refreshing == null || isDismissing) 0f else (pullOffset / refreshThreshold).coerceIn(0f, 1f)
429
+ refreshIndicator.alpha = progress
430
+ // Hidden, the indeterminate animation stops.
431
+ refreshIndicator.visibility = if (progress > 0f) View.VISIBLE else View.INVISIBLE
367
432
  }
368
433
 
369
434
  /**
@@ -435,14 +500,15 @@ internal class BottomSheetPresenter(
435
500
  @SuppressLint("ViewConstructor")
436
501
  private inner class SheetLayout(context: Context) : CoordinatorLayout(context), ReactPointerEventsView {
437
502
  private val touchSlop = ViewConfiguration.get(context).scaledTouchSlop
438
- private val refreshThreshold = context.dpToPx(REFRESH_THRESHOLD_DP)
439
503
  private var lastEvent: MotionEvent? = null
440
504
  private var downX = 0f
441
505
  private var downY = 0f
442
506
  private var canPull = false
443
507
  private var pulling = false
444
508
  private var armed = false
445
- private var pullAnimator: ValueAnimator? = null
509
+
510
+ val isPulling: Boolean
511
+ get() = pulling
446
512
 
447
513
  override val pointerEvents: PointerEvents
448
514
  get() = PointerEvents.BOX_NONE
@@ -513,7 +579,7 @@ internal class BottomSheetPresenter(
513
579
  */
514
580
  private fun canStartPull(x: Float, y: Float): Boolean {
515
581
  if (!config.pullToRefreshEnabled || config.dismissOnSwipe || !presented || isDismissing) return false
516
- if (pullOffset != 0f || behavior.state != states().firstOrNull()) return false
582
+ if (refreshing || pullOffset != 0f || behavior.state != states().firstOrNull()) return false
517
583
  val top = sheet.top + sheet.translationY
518
584
  if (y < top || x < sheet.left || x >= sheet.right) return false
519
585
  return !canScrollUpAt(sheet, x - sheet.left, y - top)
@@ -524,12 +590,9 @@ internal class BottomSheetPresenter(
524
590
  armed = false
525
591
  canPull = false
526
592
  notifyNativeGesture(started = false, event = ev)
527
- pullAnimator = ValueAnimator.ofFloat(pullOffset, 0f).apply {
528
- duration = PULL_RELEASE_DURATION
529
- interpolator = DecelerateInterpolator()
530
- addUpdateListener { setPullOffset(it.animatedValue as Float) }
531
- start()
532
- }
593
+ // Settles on the refreshing position; React confirms or ends it.
594
+ if (refresh && config.refreshing != null) refreshing = true
595
+ animatePullOffset(restingPullOffset())
533
596
  if (refresh) listener.onPullToRefresh()
534
597
  }
535
598
 
@@ -572,6 +635,7 @@ internal class BottomSheetPresenter(
572
635
 
573
636
  /** How far below its lowest detent the sheet must be pulled to refresh. */
574
637
  private const val REFRESH_THRESHOLD_DP = 56f
638
+ private const val REFRESH_INDICATOR_SIZE_DP = 32f
575
639
  private const val PULL_RELEASE_DURATION = 300L
576
640
  private val THRESHOLD_FEEDBACK =
577
641
  if (Build.VERSION.SDK_INT >= 34) {
@@ -52,6 +52,8 @@ class HybridNitroModal(private val reactContext: ThemedReactContext) :
52
52
  override var keyboardBehavior: KeyboardBehavior = KeyboardBehavior.PAN
53
53
  override var popupAnimation: PopupAnimation = PopupAnimation.SCALE
54
54
  override var pullToRefreshEnabled: Boolean = false
55
+ override var refreshing: Boolean? = null
56
+ override var refreshIndicatorColor: Double? = null
55
57
 
56
58
  override var onPresent: (() -> Unit)? = null
57
59
  override var onDismiss: ((reason: DismissReason) -> Unit)? = null
@@ -337,6 +339,8 @@ class HybridNitroModal(private val reactContext: ThemedReactContext) :
337
339
  keyboardBehavior = keyboardBehavior,
338
340
  popupAnimation = popupAnimation,
339
341
  pullToRefreshEnabled = pullToRefreshEnabled,
342
+ refreshing = refreshing,
343
+ refreshIndicatorColor = refreshIndicatorColor?.toProcessedColor(),
340
344
  )
341
345
  }
342
346
 
@@ -49,6 +49,10 @@ internal data class ModalConfig(
49
49
  val keyboardBehavior: KeyboardBehavior = KeyboardBehavior.PAN,
50
50
  val popupAnimation: PopupAnimation = PopupAnimation.SCALE,
51
51
  val pullToRefreshEnabled: Boolean = false,
52
+ /** A refresh is in progress; `null` shows no refresh indicator. */
53
+ val refreshing: Boolean? = null,
54
+ /** `null` uses the platform default. */
55
+ val refreshIndicatorColor: Int? = null,
52
56
  ) {
53
57
  /** A `bottomSheet` living in the host view's bounds. Popups are always modal. */
54
58
  val isInline: Boolean
@@ -47,6 +47,8 @@ final class HybridNitroModal: HybridNitroModalSpec {
47
47
  var keyboardBehavior: KeyboardBehavior = .pan
48
48
  var popupAnimation: PopupAnimation = .scale
49
49
  var pullToRefreshEnabled = false
50
+ var refreshing: Bool?
51
+ var refreshIndicatorColor: Double?
50
52
 
51
53
  var onPresent: (() -> Void)?
52
54
  var onDismiss: ((DismissReason) -> Void)?
@@ -326,7 +328,9 @@ final class HybridNitroModal: HybridNitroModalSpec {
326
328
  contentBackgroundColor: contentBackgroundColor.map(UIColor.init(processedColor:)) ?? .systemBackground,
327
329
  keyboardBehavior: keyboardBehavior,
328
330
  popupAnimation: popupAnimation,
329
- pullToRefreshEnabled: pullToRefreshEnabled
331
+ pullToRefreshEnabled: pullToRefreshEnabled,
332
+ refreshing: refreshing,
333
+ refreshIndicatorColor: refreshIndicatorColor.map(UIColor.init(processedColor:))
330
334
  )
331
335
  }
332
336
 
@@ -53,6 +53,10 @@ struct ModalConfig: Equatable {
53
53
  var keyboardBehavior: KeyboardBehavior = .pan
54
54
  var popupAnimation: PopupAnimation = .scale
55
55
  var pullToRefreshEnabled = false
56
+ /// A refresh is in progress; `nil` shows no refresh indicator.
57
+ var refreshing: Bool?
58
+ /// `nil` uses the platform default.
59
+ var refreshIndicatorColor: UIColor?
56
60
 
57
61
  /// A `bottomSheet` living in the host view's bounds. Popups are always modal.
58
62
  var isInline: Bool {
@@ -27,7 +27,8 @@ final class SheetView: UIView, UIGestureRecognizerDelegate {
27
27
  private static let defaultCornerRadius: CGFloat = 16
28
28
  private static let regularMaxWidth: CGFloat = 704
29
29
  private static let grabberSize = CGSize(width: 36, height: 5)
30
- /// How far below its lowest detent the sheet must be pulled to refresh.
30
+ /// How far below its lowest detent the sheet must be pulled to refresh,
31
+ /// and where it rests while refreshing.
31
32
  private static let refreshThreshold: CGFloat = 56
32
33
  private static let touchHandlerClass: AnyClass? = NSClassFromString("RCTSurfaceTouchHandler")
33
34
 
@@ -36,6 +37,7 @@ final class SheetView: UIView, UIGestureRecognizerDelegate {
36
37
  private let backdrop = BackdropView()
37
38
  private let sheetView = UIView()
38
39
  private let grabber = UIView()
40
+ private let refreshIndicator = UIActivityIndicatorView(style: .medium)
39
41
  private var config: ModalConfig
40
42
  private var selectedIndex: Int
41
43
  private var keyboardHeight: CGFloat = 0
@@ -49,6 +51,8 @@ final class SheetView: UIView, UIGestureRecognizerDelegate {
49
51
  private var releaseVelocity: CGFloat = 0
50
52
  private var reportedRestingTop: CGFloat?
51
53
  private var refreshFeedback: UIImpactFeedbackGenerator?
54
+ /// The sheet rests below its lowest detent, the indicator spinning above it.
55
+ private var isRefreshing = false
52
56
 
53
57
  private var lockedScrollView: UIScrollView?
54
58
  private var lockedOffsetY: CGFloat = 0
@@ -78,6 +82,7 @@ final class SheetView: UIView, UIGestureRecognizerDelegate {
78
82
  self.config = config
79
83
  self.isInline = isInline
80
84
  selectedIndex = config.clampedInitialDetentIndex
85
+ isRefreshing = config.refreshing ?? false
81
86
  super.init(frame: .zero)
82
87
  backgroundColor = .clear
83
88
 
@@ -104,6 +109,12 @@ final class SheetView: UIView, UIGestureRecognizerDelegate {
104
109
  grabber.backgroundColor = .tertiaryLabel
105
110
  grabber.layer.cornerRadius = Self.grabberSize.height / 2
106
111
  sheetView.addSubview(grabber)
112
+
113
+ // Behind the sheet, which uncovers it as it's pulled down.
114
+ refreshIndicator.hidesWhenStopped = false
115
+ refreshIndicator.isUserInteractionEnabled = false
116
+ refreshIndicator.alpha = 0
117
+ addSubview(refreshIndicator)
107
118
  addSubview(sheetView)
108
119
 
109
120
  let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
@@ -138,8 +149,12 @@ final class SheetView: UIView, UIGestureRecognizerDelegate {
138
149
  // MARK: Presentation
139
150
 
140
151
  func apply(_ newConfig: ModalConfig) {
152
+ let refreshingChanged = newConfig.refreshing != config.refreshing
141
153
  config = newConfig
142
154
  selectedIndex = min(selectedIndex, max(detents.count - 1, 0))
155
+ if refreshingChanged {
156
+ setRefreshing(newConfig.refreshing ?? false)
157
+ }
143
158
  applyAppearance()
144
159
  animateToRest()
145
160
  }
@@ -181,7 +196,10 @@ final class SheetView: UIView, UIGestureRecognizerDelegate {
181
196
  damping: offscreen ? 1 : 0.88,
182
197
  duration: duration,
183
198
  animations: { self.layoutSheet() },
184
- completion: completion
199
+ completion: {
200
+ self.stopRefreshIndicatorIfHidden()
201
+ completion()
202
+ }
185
203
  )
186
204
  }
187
205
 
@@ -227,9 +245,21 @@ final class SheetView: UIView, UIGestureRecognizerDelegate {
227
245
  detents.indices.map(restingTop(for:))
228
246
  }
229
247
 
248
+ /// Where the sheet settles on `detents[index]`: held below the lowest
249
+ /// detent while refreshing.
250
+ private func settledTop(for index: Int) -> CGFloat {
251
+ let top = restingTop(for: index)
252
+ guard isRefreshing, let lowest = detentTops.max(), abs(top - lowest) < 1 else { return top }
253
+ return top + Self.refreshThreshold
254
+ }
255
+
256
+ private var settledTops: [CGFloat] {
257
+ detents.indices.map(settledTop(for:))
258
+ }
259
+
230
260
  private var currentTop: CGFloat {
231
261
  if isOffscreen { return bounds.height }
232
- return dragTop ?? restingTop(for: selectedIndex)
262
+ return dragTop ?? settledTop(for: selectedIndex)
233
263
  }
234
264
 
235
265
  private func layoutSheet() {
@@ -243,9 +273,42 @@ final class SheetView: UIView, UIGestureRecognizerDelegate {
243
273
  size: Self.grabberSize
244
274
  )
245
275
  backdrop.alpha = backdropAlpha(forTop: top)
276
+ layoutRefreshIndicator(sheetTop: top)
246
277
  reportRestingTop()
247
278
  }
248
279
 
280
+ /// Centered in the gap a pull uncovers below the lowest detent, fading in
281
+ /// as the pull nears the threshold.
282
+ private func layoutRefreshIndicator(sheetTop top: CGFloat) {
283
+ let size = refreshIndicator.intrinsicContentSize
284
+ refreshIndicator.bounds = CGRect(origin: .zero, size: size)
285
+ refreshIndicator.center = CGPoint(x: bounds.midX, y: top - Self.refreshThreshold / 2)
286
+ guard config.refreshing != nil, !isOffscreen, let lowest = detentTops.max() else {
287
+ refreshIndicator.alpha = 0
288
+ return
289
+ }
290
+ let progress = min(max((top - lowest) / Self.refreshThreshold, 0), 1)
291
+ refreshIndicator.alpha = progress
292
+ if progress > 0, !refreshIndicator.isAnimating {
293
+ refreshIndicator.startAnimating()
294
+ }
295
+ }
296
+
297
+ /// Called once the sheet settles, so a fading indicator keeps spinning.
298
+ private func stopRefreshIndicatorIfHidden() {
299
+ if drag == nil, refreshIndicator.alpha == 0 {
300
+ refreshIndicator.stopAnimating()
301
+ }
302
+ }
303
+
304
+ private func setRefreshing(_ refreshing: Bool) {
305
+ guard refreshing != isRefreshing else { return }
306
+ isRefreshing = refreshing
307
+ if refreshing, !isOffscreen, config.refreshing != nil {
308
+ refreshIndicator.startAnimating()
309
+ }
310
+ }
311
+
249
312
  /// Fully dimmed at every detent; fades out below the lowest one.
250
313
  private func backdropAlpha(forTop top: CGFloat) -> CGFloat {
251
314
  let bottom = bounds.height
@@ -259,15 +322,18 @@ final class SheetView: UIView, UIGestureRecognizerDelegate {
259
322
  sheetView.backgroundColor = config.contentBackgroundColor
260
323
  sheetView.layer.cornerRadius = config.cornerRadius ?? Self.defaultCornerRadius
261
324
  grabber.isHidden = !config.grabberVisible
325
+ refreshIndicator.color = config.refreshIndicatorColor
262
326
  }
263
327
 
264
328
  private func animateToRest() {
265
329
  guard drag == nil, !isOffscreen else { return }
266
330
  dragTop = nil
267
- UIView.animate(withDuration: 0.3, delay: 0, options: [.beginFromCurrentState, .allowUserInteraction]) {
331
+ UIView.animate(withDuration: 0.3, delay: 0, options: [.beginFromCurrentState, .allowUserInteraction], animations: {
268
332
  self.setNeedsLayout()
269
333
  self.layoutIfNeeded()
270
- }
334
+ }, completion: { _ in
335
+ self.stopRefreshIndicatorIfHidden()
336
+ })
271
337
  }
272
338
 
273
339
  private func animate(
@@ -372,7 +438,7 @@ final class SheetView: UIView, UIGestureRecognizerDelegate {
372
438
  if let scrollView = state.scrollView {
373
439
  lockedOffsetY = scrollView.contentOffset.y
374
440
  }
375
- if config.pullToRefreshEnabled, !config.dismissOnSwipe, isSettled,
441
+ if config.pullToRefreshEnabled, !config.dismissOnSwipe, !isRefreshing, isSettled,
376
442
  let lowest = detentTops.max(), abs(top - lowest) < 1 {
377
443
  state.canRefresh = true
378
444
  refreshFeedback = UIImpactFeedbackGenerator(style: .medium)
@@ -430,8 +496,13 @@ final class SheetView: UIView, UIGestureRecognizerDelegate {
430
496
  drag = nil
431
497
  refreshFeedback = nil
432
498
  let velocity = state.sheetDrove ? pan.velocity(in: self).y : 0
499
+ let refresh = state.refreshArmed && pan.state == .ended
500
+ if refresh {
501
+ // Settles on the refreshing position; React confirms or ends it.
502
+ setRefreshing(config.refreshing != nil)
503
+ }
433
504
  endDrag(at: dragTop ?? currentTop, velocity: velocity, holdingScroll: state.sheetDrove)
434
- if state.refreshArmed, pan.state == .ended {
505
+ if refresh {
435
506
  delegate?.modalPresentationDidPullToRefresh()
436
507
  }
437
508
 
@@ -444,7 +515,7 @@ final class SheetView: UIView, UIGestureRecognizerDelegate {
444
515
  if !holdingScroll {
445
516
  unlockScroll()
446
517
  }
447
- let tops = detentTops
518
+ let tops = settledTops
448
519
  guard let lowest = tops.max() else { return }
449
520
  // Where a fling of this velocity would come to rest.
450
521
  let projected = top + velocity * 0.2
@@ -465,16 +536,17 @@ final class SheetView: UIView, UIGestureRecognizerDelegate {
465
536
  if changed {
466
537
  delegate?.modalPresentationDidChangeDetent(index)
467
538
  }
468
- animate(distance: restingTop(for: index) - top, velocity: velocity, animations: { self.layoutSheet() }) { [weak self] in
539
+ animate(distance: settledTop(for: index) - top, velocity: velocity, animations: { self.layoutSheet() }) { [weak self] in
469
540
  guard let self, self.drag == nil else { return }
470
541
  self.unlockScroll()
542
+ self.stopRefreshIndicatorIfHidden()
471
543
  }
472
544
  }
473
545
 
474
546
  /// Rubber-bands above the tallest detent, and below the lowest one when
475
547
  /// swiping cannot dismiss.
476
548
  private func constrained(_ top: CGFloat) -> CGFloat {
477
- let tops = detentTops
549
+ let tops = settledTops
478
550
  guard let minTop = tops.min(), let maxTop = tops.max() else { return top }
479
551
  let dimension = max(bounds.height, 1)
480
552
  if top < minTop {
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
 
3
- import { useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from 'react';
3
+ import { useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from 'react';
4
4
  import { processColor, StyleSheet, useWindowDimensions, View } from 'react-native';
5
5
  import { callback, NitroModalView } from './NitroModalView';
6
6
 
@@ -49,6 +49,8 @@ export function NitroModal({
49
49
  onBackdropPress,
50
50
  onBackButtonPress,
51
51
  onPullToRefresh,
52
+ refreshing,
53
+ refreshIndicatorColor,
52
54
  children,
53
55
  testID,
54
56
  ref
@@ -72,12 +74,21 @@ export function NitroModal({
72
74
  // is laid out there so `measure()`, which presses rely on, matches the
73
75
  // screen; native cancels the offset out on screen.
74
76
  const [restingTop, setRestingTop] = useState(0);
77
+ // Native starts refreshing as soon as a pull is released. Reporting `true`
78
+ // for the render that follows, then `refreshing` again, stops it if the
79
+ // handler didn't start a refresh (unchanged props never reach native).
80
+ const isRefreshControlled = refreshing !== undefined;
81
+ const [pullPending, setPullPending] = useState(false);
82
+ useEffect(() => {
83
+ if (pullPending) setPullPending(false);
84
+ }, [pullPending]);
75
85
  const hybridRef = useRef(null);
76
86
 
77
87
  // Native callbacks are created once (stable props, no native updates on
78
88
  // re-render) and read the latest handlers from this ref.
79
89
  const latest = useRef({
80
90
  isControlled,
91
+ isRefreshControlled,
81
92
  onPresent,
82
93
  onDismiss,
83
94
  onDetentChange,
@@ -88,6 +99,7 @@ export function NitroModal({
88
99
  useLayoutEffect(() => {
89
100
  latest.current = {
90
101
  isControlled,
102
+ isRefreshControlled,
91
103
  onPresent,
92
104
  onDismiss,
93
105
  onDetentChange,
@@ -119,6 +131,7 @@ export function NitroModal({
119
131
  latest.current.onBackButtonPress?.();
120
132
  }),
121
133
  onPullToRefresh: callback(() => {
134
+ if (latest.current.isRefreshControlled) setPullPending(true);
122
135
  latest.current.onPullToRefresh?.();
123
136
  }),
124
137
  onRestingTopChange: callback(top => {
@@ -188,6 +201,8 @@ export function NitroModal({
188
201
  keyboardBehavior: keyboardBehavior,
189
202
  popupAnimation: popupAnimation,
190
203
  pullToRefreshEnabled: onPullToRefresh != null,
204
+ refreshing: isRefreshControlled ? refreshing || pullPending : undefined,
205
+ refreshIndicatorColor: toNativeColor(refreshIndicatorColor),
191
206
  ...nativeCallbacks,
192
207
  children: renderContent ? /*#__PURE__*/_jsx(View, {
193
208
  collapsable: false,
@@ -1 +1 @@
1
- {"version":3,"names":["useImperativeHandle","useLayoutEffect","useMemo","useRef","useState","processColor","StyleSheet","useWindowDimensions","View","callback","NitroModalView","jsx","_jsx","DEFAULT_DETENTS","BLACK","parseDetent","value","points","Number","isFinite","toNativeColor","color","undefined","processed","NitroModal","isOpen","mode","presentation","style","detents","initialDetentIndex","backdropColor","backdropOpacity","backdropBlur","dismissOnBackdropPress","dismissOnSwipe","dismissOnBackButton","showGrabber","cornerRadius","backgroundColor","keyboardBehavior","popupAnimation","contentContainerStyle","onPresent","onDismiss","onDetentChange","onBackdropPress","onBackButtonPress","onPullToRefresh","children","testID","ref","isControlled","isInline","internalOpen","setInternalOpen","open","sessionActive","setSessionActive","prevOpen","setPrevOpen","renderContent","area","setArea","restingTop","setRestingTop","hybridRef","latest","current","nativeCallbacks","instance","reason","index","onRestingTopChange","top","onContentAreaChange","next","width","height","present","console","warn","dismiss","snapToDetent","detentsKey","join","nativeDetents","Set","split","map","windowSize","contentArea","containerStyle","maxWidth","maxHeight","includes","absoluteFill","styles","host","pointerEvents","backdropBlurRadius","grabberVisible","contentBackgroundColor","pullToRefreshEnabled","collapsable","container","create","position","left"],"sourceRoot":"../../src","sources":["NitroModal.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["useEffect","useImperativeHandle","useLayoutEffect","useMemo","useRef","useState","processColor","StyleSheet","useWindowDimensions","View","callback","NitroModalView","jsx","_jsx","DEFAULT_DETENTS","BLACK","parseDetent","value","points","Number","isFinite","toNativeColor","color","undefined","processed","NitroModal","isOpen","mode","presentation","style","detents","initialDetentIndex","backdropColor","backdropOpacity","backdropBlur","dismissOnBackdropPress","dismissOnSwipe","dismissOnBackButton","showGrabber","cornerRadius","backgroundColor","keyboardBehavior","popupAnimation","contentContainerStyle","onPresent","onDismiss","onDetentChange","onBackdropPress","onBackButtonPress","onPullToRefresh","refreshing","refreshIndicatorColor","children","testID","ref","isControlled","isInline","internalOpen","setInternalOpen","open","sessionActive","setSessionActive","prevOpen","setPrevOpen","renderContent","area","setArea","restingTop","setRestingTop","isRefreshControlled","pullPending","setPullPending","hybridRef","latest","current","nativeCallbacks","instance","reason","index","onRestingTopChange","top","onContentAreaChange","next","width","height","present","console","warn","dismiss","snapToDetent","detentsKey","join","nativeDetents","Set","split","map","windowSize","contentArea","containerStyle","maxWidth","maxHeight","includes","absoluteFill","styles","host","pointerEvents","backdropBlurRadius","grabberVisible","contentBackgroundColor","pullToRefreshEnabled","collapsable","container","create","position","left"],"sourceRoot":"../../src","sources":["NitroModal.tsx"],"mappings":"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","ignoreList":[]}