@swmansion/react-native-bottom-sheet 0.8.0 → 0.8.1
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/README.md
CHANGED
|
@@ -42,7 +42,8 @@ React Native.
|
|
|
42
42
|
The library provides two components: `BottomSheet` (inline) and
|
|
43
43
|
`ModalBottomSheet` (modal). Both render their children as the sheet content
|
|
44
44
|
(including any background) and are controlled via `detents`, `index`,
|
|
45
|
-
and `onIndexChange`.
|
|
45
|
+
and `onIndexChange`. Use `onSettle` for
|
|
46
|
+
post‍-‍snap observability.
|
|
46
47
|
|
|
47
48
|
### Inline
|
|
48
49
|
|
|
@@ -108,13 +109,18 @@ its color:
|
|
|
108
109
|
### Detents and index
|
|
109
110
|
|
|
110
111
|
Detents are the points to which the sheet snaps. Each detent is either a number
|
|
111
|
-
(a fixed height in pixels) or `'
|
|
112
|
-
available screen height). The default detents are `[0, '
|
|
112
|
+
(a fixed height in pixels) or `'content'` (the sheet’s content height, capped by
|
|
113
|
+
the available screen height). The default detents are `[0, 'content']`.
|
|
113
114
|
|
|
114
115
|
The `index` prop is a zero‍-‍based index into the `detents` array.
|
|
115
|
-
`onIndexChange`
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
`onIndexChange` and `onSettle` have different responsibilities:
|
|
117
|
+
|
|
118
|
+
- `onIndexChange` is for user‍-‍triggered snaps. Treat it as the signal
|
|
119
|
+
to update your controlled `index` state.
|
|
120
|
+
- `onSettle` fires when the sheet finishes snapping to a detent, regardless of
|
|
121
|
+
whether that snap was user‍-‍triggered or programmatic. Use it for
|
|
122
|
+
observability or side effects (analytics, reacting to collapse, etc.), not for
|
|
123
|
+
updating the controlled `index` state.
|
|
118
124
|
|
|
119
125
|
```tsx
|
|
120
126
|
const [index, setIndex] = useState(0);
|
|
@@ -122,9 +128,12 @@ const [index, setIndex] = useState(0);
|
|
|
122
128
|
|
|
123
129
|
```tsx
|
|
124
130
|
<BottomSheet // Or `ModalBottomSheet`.
|
|
125
|
-
detents={[0, 300, '
|
|
131
|
+
detents={[0, 300, 'content']} // Collapsed, 300 px, content height.
|
|
126
132
|
index={index}
|
|
127
|
-
onIndexChange={setIndex}
|
|
133
|
+
onIndexChange={setIndex} // Keep controlled state in sync.
|
|
134
|
+
onSettle={(nextIndex) => {
|
|
135
|
+
if (nextIndex === 0) console.log('Sheet collapsed.');
|
|
136
|
+
}}
|
|
128
137
|
>
|
|
129
138
|
{/* ... */}
|
|
130
139
|
</BottomSheet>
|
|
@@ -138,9 +147,12 @@ drag snapping but can still be targeted via `index` updates.
|
|
|
138
147
|
|
|
139
148
|
```tsx
|
|
140
149
|
<BottomSheet
|
|
141
|
-
detents={[0, programmatic(300), '
|
|
150
|
+
detents={[0, programmatic(300), 'content']}
|
|
142
151
|
index={index}
|
|
143
152
|
onIndexChange={setIndex}
|
|
153
|
+
onSettle={(nextIndex) => {
|
|
154
|
+
console.log(`Settled at ${nextIndex}.`);
|
|
155
|
+
}}
|
|
144
156
|
>
|
|
145
157
|
{/* ... */}
|
|
146
158
|
</BottomSheet>
|
|
@@ -56,6 +56,7 @@ class BottomSheetView(context: Context) : ReactViewGroup(context) {
|
|
|
56
56
|
private val sheetContainer = FrameLayout(context)
|
|
57
57
|
private val scrimPaint = Paint(Paint.ANTI_ALIAS_FLAG)
|
|
58
58
|
private var activeAnimation: SpringAnimation? = null
|
|
59
|
+
private var activeAnimationEmitsSettle = false
|
|
59
60
|
private var velocityTracker: VelocityTracker? = null
|
|
60
61
|
private var choreographerCallback: Choreographer.FrameCallback? = null
|
|
61
62
|
private val density = context.resources.displayMetrics.density
|
|
@@ -188,13 +189,15 @@ class BottomSheetView(context: Context) : ReactViewGroup(context) {
|
|
|
188
189
|
targetIndex = targetIndex.coerceIn(0, detentSpecs.size - 1)
|
|
189
190
|
if (activeAnimation != null) {
|
|
190
191
|
val currentTy = sheetContainer.translationY
|
|
192
|
+
val shouldEmitSettle = activeAnimationEmitsSettle
|
|
191
193
|
activeAnimation?.cancel()
|
|
192
194
|
activeAnimation = null
|
|
195
|
+
activeAnimationEmitsSettle = false
|
|
193
196
|
stopChoreographer()
|
|
194
197
|
sheetContainer.translationY =
|
|
195
198
|
currentTy.coerceIn(0f, detentSpecs.lastOrNull()?.height ?: currentTy)
|
|
196
199
|
emitPosition()
|
|
197
|
-
snapToIndex(targetIndex, 0f, emitIndexChange = false, emitSettle =
|
|
200
|
+
snapToIndex(targetIndex, 0f, emitIndexChange = false, emitSettle = shouldEmitSettle)
|
|
198
201
|
} else {
|
|
199
202
|
sheetContainer.translationY = translationY(targetIndex)
|
|
200
203
|
emitPosition()
|
|
@@ -308,6 +311,7 @@ class BottomSheetView(context: Context) : ReactViewGroup(context) {
|
|
|
308
311
|
targetIndex = index
|
|
309
312
|
|
|
310
313
|
val targetTy = translationY(index)
|
|
314
|
+
activeAnimationEmitsSettle = emitSettle
|
|
311
315
|
activeAnimation?.cancel()
|
|
312
316
|
|
|
313
317
|
val spring = SpringAnimation(sheetContainer, DynamicAnimation.TRANSLATION_Y, targetTy).apply {
|
|
@@ -323,6 +327,7 @@ class BottomSheetView(context: Context) : ReactViewGroup(context) {
|
|
|
323
327
|
stopChoreographer()
|
|
324
328
|
emitPosition()
|
|
325
329
|
activeAnimation = null
|
|
330
|
+
activeAnimationEmitsSettle = false
|
|
326
331
|
updateInteractionState()
|
|
327
332
|
if (emitIndexChange) listener?.onIndexChange(index)
|
|
328
333
|
if (emitSettle) listener?.onSettle(index)
|
|
@@ -35,6 +35,7 @@ public final class RNSBottomSheetHostingView: UIView {
|
|
|
35
35
|
private let scrimView = UIControl()
|
|
36
36
|
private var panGesture: UIPanGestureRecognizer!
|
|
37
37
|
private var activeAnimator: UIViewPropertyAnimator?
|
|
38
|
+
private var activeAnimatorEmitsSettle = false
|
|
38
39
|
private var displayLink: CADisplayLink?
|
|
39
40
|
private var pendingIndex: Int?
|
|
40
41
|
private var hasLaidOut = false
|
|
@@ -173,14 +174,16 @@ public final class RNSBottomSheetHostingView: UIView {
|
|
|
173
174
|
if let animator = activeAnimator {
|
|
174
175
|
stopDisplayLink()
|
|
175
176
|
let visualTy = sheetContainer.layer.presentation()?.affineTransform().ty ?? sheetContainer.transform.ty
|
|
177
|
+
let shouldEmitSettle = activeAnimatorEmitsSettle
|
|
176
178
|
animator.stopAnimation(true)
|
|
177
179
|
activeAnimator = nil
|
|
180
|
+
activeAnimatorEmitsSettle = false
|
|
178
181
|
sheetContainer.transform = CGAffineTransform(
|
|
179
182
|
translationX: 0,
|
|
180
183
|
y: min(max(visualTy, 0), detentSpecs.last?.height ?? visualTy)
|
|
181
184
|
)
|
|
182
185
|
emitPosition()
|
|
183
|
-
snapToIndex(targetIndex, velocity: 0, emitIndexChange: false, emitSettle:
|
|
186
|
+
snapToIndex(targetIndex, velocity: 0, emitIndexChange: false, emitSettle: shouldEmitSettle)
|
|
184
187
|
} else {
|
|
185
188
|
sheetContainer.transform = CGAffineTransform(translationX: 0, y: translationY(for: targetIndex))
|
|
186
189
|
emitPosition()
|
|
@@ -337,6 +340,7 @@ public final class RNSBottomSheetHostingView: UIView {
|
|
|
337
340
|
let clampedRatio = min(max(velocityRatio, -5), 5)
|
|
338
341
|
let initialVelocity = CGVector(dx: 0, dy: clampedRatio)
|
|
339
342
|
|
|
343
|
+
activeAnimatorEmitsSettle = emitSettle
|
|
340
344
|
activeAnimator?.stopAnimation(true)
|
|
341
345
|
|
|
342
346
|
let spring = UISpringTimingParameters(dampingRatio: 1.0, initialVelocity: initialVelocity)
|
|
@@ -350,6 +354,7 @@ public final class RNSBottomSheetHostingView: UIView {
|
|
|
350
354
|
self.stopDisplayLink()
|
|
351
355
|
self.emitPosition()
|
|
352
356
|
self.activeAnimator = nil
|
|
357
|
+
self.activeAnimatorEmitsSettle = false
|
|
353
358
|
self.setContentInteractionEnabled(true)
|
|
354
359
|
self.updateInteractionState()
|
|
355
360
|
if emitIndexChange {
|
package/package.json
CHANGED