@swmansion/react-native-bottom-sheet 0.7.0-next.8 → 0.7.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 +35 -55
- package/android/src/main/java/com/swmansion/reactnativebottomsheet/BottomSheetView.kt +12 -14
- package/common/cpp/react/renderer/components/ReactNativeBottomSheetSpec/BottomSheetStateHelper.h +12 -0
- package/common/cpp/react/renderer/components/ReactNativeBottomSheetSpec/BottomSheetViewShadowNode.cpp +19 -17
- package/common/cpp/react/renderer/components/ReactNativeBottomSheetSpec/ComponentDescriptors.h +0 -6
- package/common/cpp/react/renderer/components/ReactNativeBottomSheetSpec/ShadowNodes.h +1 -1
- package/ios/BottomSheetComponentView.mm +20 -0
- package/ios/RNSBottomSheetHostingView.swift +16 -17
- package/lib/module/BottomSheet.js +7 -5
- package/lib/module/BottomSheet.js.map +1 -1
- package/package.json +1 -1
- package/src/BottomSheet.tsx +9 -9
package/README.md
CHANGED
|
@@ -7,6 +7,15 @@
|
|
|
7
7
|
React Native Bottom Sheet provides bottom‍-‍sheet components for
|
|
8
8
|
React Native.
|
|
9
9
|
|
|
10
|
+
## Highlights
|
|
11
|
+
|
|
12
|
+
- Native implementation for optimal performance.
|
|
13
|
+
- Bring your own sheet surface.
|
|
14
|
+
- Dynamic, content‍-‍based sizing out of the box.
|
|
15
|
+
- Position tracking for driving UI tied to sheets.
|
|
16
|
+
- Programmatic‍-‍only detents for snap points unreachable
|
|
17
|
+
by dragging.
|
|
18
|
+
|
|
10
19
|
## Getting started
|
|
11
20
|
|
|
12
21
|
1. Install React Native Bottom Sheet:
|
|
@@ -15,20 +24,16 @@ React Native.
|
|
|
15
24
|
npm i @swmansion/react-native-bottom-sheet
|
|
16
25
|
```
|
|
17
26
|
|
|
18
|
-
2. Ensure the peer
|
|
27
|
+
2. Ensure the peer dependency is installed:
|
|
19
28
|
|
|
20
29
|
```sh
|
|
21
|
-
npm i react-native-
|
|
30
|
+
npm i react-native-safe-area-context@^4.0.0
|
|
22
31
|
```
|
|
23
32
|
|
|
24
|
-
3. Wrap your app with
|
|
33
|
+
3. Wrap your app with `BottomSheetProvider`:
|
|
25
34
|
|
|
26
35
|
```tsx
|
|
27
|
-
const App = () =>
|
|
28
|
-
<GestureHandlerRootView>
|
|
29
|
-
<BottomSheetProvider>{/* ... */}</BottomSheetProvider>
|
|
30
|
-
</GestureHandlerRootView>
|
|
31
|
-
);
|
|
36
|
+
const App = () => <BottomSheetProvider>{/* ... */}</BottomSheetProvider>;
|
|
32
37
|
```
|
|
33
38
|
|
|
34
39
|
## Usage
|
|
@@ -86,22 +91,14 @@ const insets = useSafeAreaInsets();
|
|
|
86
91
|
|
|
87
92
|
#### Scrim
|
|
88
93
|
|
|
89
|
-
Tapping the scrim collapses the sheet.
|
|
90
|
-
|
|
91
|
-
1 when the first nonzero detent is reached:
|
|
94
|
+
Tapping the scrim collapses the sheet. Use `scrimColor` to customize
|
|
95
|
+
its color:
|
|
92
96
|
|
|
93
97
|
```tsx
|
|
94
98
|
<ModalBottomSheet
|
|
95
99
|
index={index}
|
|
96
100
|
onIndexChange={setIndex}
|
|
97
|
-
|
|
98
|
-
<Animated.View
|
|
99
|
-
style={useAnimatedStyle(() => ({
|
|
100
|
-
backgroundColor: `rgba(0, 0, 255, ${0.3 * progress.value})`,
|
|
101
|
-
flex: 1,
|
|
102
|
-
}))}
|
|
103
|
-
/>
|
|
104
|
-
)}
|
|
101
|
+
scrimColor="rgba(0, 0, 0, 0.3)"
|
|
105
102
|
>
|
|
106
103
|
{/* ... */}
|
|
107
104
|
</ModalBottomSheet>
|
|
@@ -115,8 +112,8 @@ available screen height). The default detents are `[0, 'max']`.
|
|
|
115
112
|
|
|
116
113
|
The `index` prop is a zero‍-‍based index into the `detents` array.
|
|
117
114
|
`onIndexChange` is called when the sheet snaps to a different detent after
|
|
118
|
-
a
|
|
119
|
-
state.
|
|
115
|
+
a drag. You can also control the sheet externally by updating the
|
|
116
|
+
index state.
|
|
120
117
|
|
|
121
118
|
```tsx
|
|
122
119
|
const [index, setIndex] = useState(0);
|
|
@@ -150,55 +147,38 @@ drag snapping but can still be targeted via `index` updates.
|
|
|
150
147
|
|
|
151
148
|
### Position tracking
|
|
152
149
|
|
|
153
|
-
|
|
154
|
-
the
|
|
155
|
-
screen to the top of the sheet). Use it to drive animations tied to the
|
|
156
|
-
sheet position.
|
|
157
|
-
|
|
158
|
-
```tsx
|
|
159
|
-
const position = useSharedValue(0);
|
|
160
|
-
```
|
|
150
|
+
Use `onPositionChange` to observe the sheet’s current position (the distance in
|
|
151
|
+
pixels from the bottom of the screen to the top of the sheet).
|
|
161
152
|
|
|
162
153
|
```tsx
|
|
163
154
|
<BottomSheet // Or `ModalBottomSheet`.
|
|
164
155
|
index={index}
|
|
165
156
|
onIndexChange={setIndex}
|
|
166
|
-
|
|
157
|
+
onPositionChange={(position) => {
|
|
158
|
+
console.log(position);
|
|
159
|
+
}}
|
|
167
160
|
>
|
|
168
161
|
{/* ... */}
|
|
169
162
|
</BottomSheet>
|
|
170
163
|
```
|
|
171
164
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
Use the `openAnimationConfig` and `closeAnimationConfig` props to tweak the
|
|
175
|
-
spring parameters for opening and closing.
|
|
176
|
-
|
|
177
|
-
### Scrollable content
|
|
178
|
-
|
|
179
|
-
For scrollable sheet content, use `BottomSheetScrollView` or
|
|
180
|
-
`BottomSheetFlatList` instead of the standard React Native components. These
|
|
181
|
-
integrate scrolling with the sheet’s drag gesture so that dragging down while
|
|
182
|
-
scrolled to the top collapses the sheet.
|
|
183
|
-
|
|
184
|
-
If you need a custom scrollable (for example, a `FlashList`), wrap it with
|
|
185
|
-
`bottomSheetScrollable`. The returned component forwards the underlying
|
|
186
|
-
scrollable ref, so you can call any imperative methods supported by the
|
|
187
|
-
wrapped component.
|
|
165
|
+
If you want to keep the latest position in a Reanimated shared value, update it
|
|
166
|
+
from the callback:
|
|
188
167
|
|
|
189
168
|
```tsx
|
|
190
|
-
const
|
|
191
|
-
FlashListProps<string>,
|
|
192
|
-
FlashListRef<string>
|
|
193
|
-
>(FlashList);
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
```tsx
|
|
197
|
-
const listRef = useRef<FlashListRef<string>>(null);
|
|
169
|
+
const position = useSharedValue(0);
|
|
198
170
|
```
|
|
199
171
|
|
|
200
172
|
```tsx
|
|
201
|
-
<
|
|
173
|
+
<BottomSheet
|
|
174
|
+
index={index}
|
|
175
|
+
onIndexChange={setIndex}
|
|
176
|
+
onPositionChange={(nextPosition) => {
|
|
177
|
+
position.value = nextPosition;
|
|
178
|
+
}}
|
|
179
|
+
>
|
|
180
|
+
{/* ... */}
|
|
181
|
+
</BottomSheet>;
|
|
202
182
|
```
|
|
203
183
|
|
|
204
184
|
## By [Software Mansion](https://swmansion.com)
|
|
@@ -15,6 +15,7 @@ import com.facebook.react.bridge.Arguments
|
|
|
15
15
|
import com.facebook.react.uimanager.PointerEvents
|
|
16
16
|
import com.facebook.react.uimanager.StateWrapper
|
|
17
17
|
import com.facebook.react.views.view.ReactViewGroup
|
|
18
|
+
import com.facebook.react.uimanager.events.NativeGestureUtil
|
|
18
19
|
import kotlin.math.abs
|
|
19
20
|
|
|
20
21
|
private data class DetentSpec(val height: Float, val programmatic: Boolean)
|
|
@@ -218,7 +219,9 @@ class BottomSheetView(context: Context) : ReactViewGroup(context) {
|
|
|
218
219
|
private var lastShadowOffsetY = Float.NaN
|
|
219
220
|
|
|
220
221
|
private fun updateShadowState(translationY: Float) {
|
|
221
|
-
val
|
|
222
|
+
val maxDetentHeight = detentSpecs.lastOrNull()?.height ?: height.toFloat()
|
|
223
|
+
val containerTop = height.toFloat() - maxDetentHeight
|
|
224
|
+
val offsetY = ((containerTop + translationY) / density).toDouble()
|
|
222
225
|
if (offsetY.toFloat() == lastShadowOffsetY) return
|
|
223
226
|
lastShadowOffsetY = offsetY.toFloat()
|
|
224
227
|
val sw = stateWrapper ?: return
|
|
@@ -320,21 +323,24 @@ class BottomSheetView(context: Context) : ReactViewGroup(context) {
|
|
|
320
323
|
val dx = x - initialTouchX
|
|
321
324
|
val dy = y - initialTouchY
|
|
322
325
|
|
|
323
|
-
if (abs(dy) > touchSlop && abs(dy) > abs(dx)) {
|
|
326
|
+
if (abs(dy) > touchSlop && abs(dy) > abs(dx) && draggableMinTy < draggableMaxTy) {
|
|
324
327
|
if (!isAtMaxDraggable) {
|
|
325
328
|
lastTouchY = y
|
|
326
329
|
requestDisallowInterceptTouchEvent(false)
|
|
330
|
+
NativeGestureUtil.notifyNativeGestureStarted(this, ev)
|
|
327
331
|
return true
|
|
328
332
|
}
|
|
329
333
|
if (dy > 0 && isScrollViewAtTop()) {
|
|
330
334
|
lastTouchY = y
|
|
331
335
|
requestDisallowInterceptTouchEvent(false)
|
|
336
|
+
NativeGestureUtil.notifyNativeGestureStarted(this, ev)
|
|
332
337
|
return true
|
|
333
338
|
}
|
|
334
339
|
}
|
|
335
340
|
}
|
|
336
341
|
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
|
337
342
|
initialTouchX = 0f
|
|
343
|
+
initialTouchY = 0f
|
|
338
344
|
activePointerId = MotionEvent.INVALID_POINTER_ID
|
|
339
345
|
}
|
|
340
346
|
}
|
|
@@ -342,16 +348,7 @@ class BottomSheetView(context: Context) : ReactViewGroup(context) {
|
|
|
342
348
|
}
|
|
343
349
|
|
|
344
350
|
override fun onTouchEvent(event: MotionEvent): Boolean {
|
|
345
|
-
val sheetTop = sheetContainer.top + sheetContainer.translationY
|
|
346
|
-
if (event.actionMasked == MotionEvent.ACTION_DOWN && event.y < sheetTop) {
|
|
347
|
-
return false
|
|
348
|
-
}
|
|
349
|
-
|
|
350
351
|
when (event.actionMasked) {
|
|
351
|
-
MotionEvent.ACTION_DOWN -> {
|
|
352
|
-
beginPan(event)
|
|
353
|
-
return true
|
|
354
|
-
}
|
|
355
352
|
MotionEvent.ACTION_MOVE -> {
|
|
356
353
|
if (!isPanning) beginPan(event)
|
|
357
354
|
val pointerIndex = event.findPointerIndex(activePointerId)
|
|
@@ -385,9 +382,10 @@ class BottomSheetView(context: Context) : ReactViewGroup(context) {
|
|
|
385
382
|
MotionEvent.ACTION_POINTER_UP -> {
|
|
386
383
|
val actionIndex = event.actionIndex
|
|
387
384
|
if (event.getPointerId(actionIndex) == activePointerId) {
|
|
388
|
-
val
|
|
389
|
-
activePointerId = event.getPointerId(
|
|
390
|
-
lastTouchY = event.getY(
|
|
385
|
+
val newPointerIndex = if (actionIndex == 0) 1 else 0
|
|
386
|
+
activePointerId = event.getPointerId(newPointerIndex)
|
|
387
|
+
lastTouchY = event.getY(newPointerIndex)
|
|
388
|
+
velocityTracker?.clear()
|
|
391
389
|
}
|
|
392
390
|
return true
|
|
393
391
|
}
|
|
@@ -1,28 +1,30 @@
|
|
|
1
|
+
#include "BottomSheetStateHelper.h"
|
|
1
2
|
#include "ShadowNodes.h"
|
|
2
3
|
|
|
3
|
-
#include <yoga/Yoga.h>
|
|
4
|
-
|
|
5
4
|
namespace facebook::react {
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
ensureUnsealed();
|
|
10
|
-
|
|
6
|
+
Point BottomSheetViewShadowNode::getContentOriginOffset(
|
|
7
|
+
bool /*includeTransform*/) const {
|
|
11
8
|
auto state =
|
|
12
9
|
std::static_pointer_cast<const BottomSheetViewShadowNode::ConcreteState>(
|
|
13
10
|
getState());
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
auto adjustedStyle = getConcreteProps().yogaStyle;
|
|
17
|
-
auto newPaddingTop =
|
|
18
|
-
yoga::Style::Length::points(stateData.contentOffsetY);
|
|
11
|
+
return {0, state->getData().contentOffsetY};
|
|
12
|
+
}
|
|
19
13
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
14
|
+
void updateBottomSheetContentOffsetY(
|
|
15
|
+
const State::Shared& state,
|
|
16
|
+
float contentOffsetY) {
|
|
17
|
+
auto concreteState =
|
|
18
|
+
std::static_pointer_cast<const BottomSheetViewShadowNode::ConcreteState>(
|
|
19
|
+
state);
|
|
20
|
+
concreteState->updateState(
|
|
21
|
+
[contentOffsetY](const BottomSheetViewState& /*oldState*/)
|
|
22
|
+
-> BottomSheetViewShadowNode::ConcreteState::SharedData {
|
|
23
|
+
auto newState = std::make_shared<BottomSheetViewState>();
|
|
24
|
+
const_cast<BottomSheetViewState&>(*newState).contentOffsetY =
|
|
25
|
+
contentOffsetY;
|
|
26
|
+
return newState;
|
|
27
|
+
});
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
} // namespace facebook::react
|
package/common/cpp/react/renderer/components/ReactNativeBottomSheetSpec/ComponentDescriptors.h
CHANGED
|
@@ -8,12 +8,6 @@ namespace facebook::react {
|
|
|
8
8
|
class BottomSheetViewComponentDescriptor final
|
|
9
9
|
: public ConcreteComponentDescriptor<BottomSheetViewShadowNode> {
|
|
10
10
|
using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
|
|
11
|
-
|
|
12
|
-
void adopt(ShadowNode& shadowNode) const override {
|
|
13
|
-
auto& node = static_cast<BottomSheetViewShadowNode&>(shadowNode);
|
|
14
|
-
node.adjustLayoutWithState();
|
|
15
|
-
ConcreteComponentDescriptor::adopt(shadowNode);
|
|
16
|
-
}
|
|
17
11
|
};
|
|
18
12
|
|
|
19
13
|
} // namespace facebook::react
|
|
@@ -19,7 +19,7 @@ class JSI_EXPORT BottomSheetViewShadowNode final
|
|
|
19
19
|
using ConcreteViewShadowNode::ConcreteViewShadowNode;
|
|
20
20
|
|
|
21
21
|
public:
|
|
22
|
-
|
|
22
|
+
Point getContentOriginOffset(bool includeTransform) const override;
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
} // namespace facebook::react
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
#import <React/RCTConversions.h>
|
|
5
5
|
#import <React/RCTFabricComponentsPlugins.h>
|
|
6
|
+
#import <react/renderer/components/ReactNativeBottomSheetSpec/BottomSheetStateHelper.h>
|
|
6
7
|
#import <react/renderer/components/ReactNativeBottomSheetSpec/ComponentDescriptors.h>
|
|
7
8
|
#import <react/renderer/components/ReactNativeBottomSheetSpec/EventEmitters.h>
|
|
8
9
|
#import <react/renderer/components/ReactNativeBottomSheetSpec/Props.h>
|
|
@@ -15,6 +16,8 @@ using namespace facebook::react;
|
|
|
15
16
|
|
|
16
17
|
@implementation BottomSheetComponentView {
|
|
17
18
|
BottomSheetContentView *_sheetView;
|
|
19
|
+
State::Shared _sheetState;
|
|
20
|
+
float _lastContentOffsetY;
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
+ (ComponentDescriptorProvider)componentDescriptorProvider
|
|
@@ -64,6 +67,11 @@ using namespace facebook::react;
|
|
|
64
67
|
[super updateProps:props oldProps:oldProps];
|
|
65
68
|
}
|
|
66
69
|
|
|
70
|
+
- (void)updateState:(const State::Shared &)state oldState:(const State::Shared &)oldState
|
|
71
|
+
{
|
|
72
|
+
_sheetState = state;
|
|
73
|
+
}
|
|
74
|
+
|
|
67
75
|
- (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
|
|
68
76
|
{
|
|
69
77
|
[_sheetView mountChildComponentView:childComponentView atIndex:index];
|
|
@@ -90,12 +98,24 @@ using namespace facebook::react;
|
|
|
90
98
|
auto emitter = std::static_pointer_cast<const BottomSheetViewEventEmitter>(_eventEmitter);
|
|
91
99
|
emitter->onPositionChange({.position = static_cast<double>(position)});
|
|
92
100
|
}
|
|
101
|
+
|
|
102
|
+
float contentOffsetY = static_cast<float>(self.bounds.size.height - position);
|
|
103
|
+
if (contentOffsetY == _lastContentOffsetY) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
_lastContentOffsetY = contentOffsetY;
|
|
107
|
+
|
|
108
|
+
if (_sheetState) {
|
|
109
|
+
updateBottomSheetContentOffsetY(_sheetState, contentOffsetY);
|
|
110
|
+
}
|
|
93
111
|
}
|
|
94
112
|
|
|
95
113
|
- (void)prepareForRecycle
|
|
96
114
|
{
|
|
97
115
|
[super prepareForRecycle];
|
|
98
116
|
[_sheetView resetSheetState];
|
|
117
|
+
_sheetState.reset();
|
|
118
|
+
_lastContentOffsetY = 0;
|
|
99
119
|
}
|
|
100
120
|
|
|
101
121
|
@end
|
|
@@ -43,7 +43,7 @@ public final class RNSBottomSheetHostingView: UIView {
|
|
|
43
43
|
panGesture.delegate = self
|
|
44
44
|
panGesture.cancelsTouchesInView = true
|
|
45
45
|
panGesture.delaysTouchesBegan = true
|
|
46
|
-
panGesture.delaysTouchesEnded =
|
|
46
|
+
panGesture.delaysTouchesEnded = false
|
|
47
47
|
sheetContainer.addGestureRecognizer(panGesture)
|
|
48
48
|
}
|
|
49
49
|
|
|
@@ -258,13 +258,21 @@ public final class RNSBottomSheetHostingView: UIView {
|
|
|
258
258
|
sheetContainer.transform = CGAffineTransform(translationX: 0, y: newTy)
|
|
259
259
|
emitPosition()
|
|
260
260
|
|
|
261
|
-
case .ended
|
|
261
|
+
case .ended:
|
|
262
262
|
isPanning = false
|
|
263
263
|
let velocity = gesture.velocity(in: self).y
|
|
264
264
|
let currentHeight = maxHeight - sheetContainer.transform.ty
|
|
265
265
|
let index = bestSnapIndex(for: currentHeight, velocity: velocity)
|
|
266
266
|
snapToIndex(index, velocity: velocity)
|
|
267
267
|
|
|
268
|
+
case .cancelled:
|
|
269
|
+
isPanning = false
|
|
270
|
+
setContentInteractionEnabled(true)
|
|
271
|
+
let cancelVelocity = gesture.velocity(in: self).y
|
|
272
|
+
let cancelHeight = maxHeight - sheetContainer.transform.ty
|
|
273
|
+
let cancelIndex = bestSnapIndex(for: cancelHeight, velocity: cancelVelocity)
|
|
274
|
+
snapToIndex(cancelIndex, velocity: cancelVelocity)
|
|
275
|
+
|
|
268
276
|
case .failed:
|
|
269
277
|
isPanning = false
|
|
270
278
|
setContentInteractionEnabled(true)
|
|
@@ -312,7 +320,10 @@ public final class RNSBottomSheetHostingView: UIView {
|
|
|
312
320
|
let velocity = panGesture.velocity(in: self)
|
|
313
321
|
guard abs(velocity.y) > abs(velocity.x) else { return false }
|
|
314
322
|
|
|
315
|
-
let
|
|
323
|
+
let draggable = detentSpecs.enumerated().filter { !$0.element.programmatic }
|
|
324
|
+
guard draggable.count > 1 else { return false }
|
|
325
|
+
|
|
326
|
+
let maxDraggableIndex = draggable.last?.offset ?? 0
|
|
316
327
|
guard targetIndex >= maxDraggableIndex else { return true }
|
|
317
328
|
|
|
318
329
|
if velocity.y < 0 {
|
|
@@ -325,24 +336,12 @@ public final class RNSBottomSheetHostingView: UIView {
|
|
|
325
336
|
}
|
|
326
337
|
|
|
327
338
|
extension RNSBottomSheetHostingView: UIGestureRecognizerDelegate {
|
|
328
|
-
public func gestureRecognizer(
|
|
329
|
-
_ gestureRecognizer: UIGestureRecognizer,
|
|
330
|
-
shouldRequireFailureOf other: UIGestureRecognizer
|
|
331
|
-
) -> Bool {
|
|
332
|
-
guard gestureRecognizer === panGesture else { return false }
|
|
333
|
-
|
|
334
|
-
if other is UITapGestureRecognizer {
|
|
335
|
-
return true
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
return false
|
|
339
|
-
}
|
|
340
|
-
|
|
341
339
|
public func gestureRecognizer(
|
|
342
340
|
_ gestureRecognizer: UIGestureRecognizer,
|
|
343
341
|
shouldBeRequiredToFailBy other: UIGestureRecognizer
|
|
344
342
|
) -> Bool {
|
|
345
|
-
|
|
343
|
+
guard gestureRecognizer === panGesture else { return false }
|
|
344
|
+
return other is UIPanGestureRecognizer || other is UITapGestureRecognizer
|
|
346
345
|
}
|
|
347
346
|
|
|
348
347
|
public func gestureRecognizer(
|
|
@@ -39,6 +39,7 @@ export const BottomSheet = ({
|
|
|
39
39
|
const [contentHeight, setContentHeight] = useState(0);
|
|
40
40
|
const currentPositionRef = useRef(0);
|
|
41
41
|
const scrimProgress = useRef(new Animated.Value(0)).current;
|
|
42
|
+
const sheetOpacity = useRef(new Animated.Value(0)).current;
|
|
42
43
|
const resolvedDetents = detents.map(detent => {
|
|
43
44
|
const value = resolveDetent(detent, contentHeight, maxHeight);
|
|
44
45
|
return {
|
|
@@ -51,7 +52,6 @@ export const BottomSheet = ({
|
|
|
51
52
|
};
|
|
52
53
|
const clampedIndex = Math.max(0, Math.min(index, resolvedDetents.length - 1));
|
|
53
54
|
const isCollapsed = (resolvedDetents[clampedIndex]?.height ?? 0) === 0;
|
|
54
|
-
const sheetPointerEvents = isCollapsed ? 'none' : 'box-none';
|
|
55
55
|
const scrimPressEnabledRef = useRef(!modal || isCollapsed);
|
|
56
56
|
const previousIsCollapsedRef = useRef(isCollapsed);
|
|
57
57
|
const firstNonzeroDetent = resolvedDetents.find(detent => detent.height > 0)?.height ?? 0;
|
|
@@ -85,6 +85,7 @@ export const BottomSheet = ({
|
|
|
85
85
|
currentPositionRef.current = height;
|
|
86
86
|
const progress = firstNonzeroDetent <= 0 ? 0 : Math.min(1, Math.max(0, height / firstNonzeroDetent));
|
|
87
87
|
scrimProgress.setValue(progress);
|
|
88
|
+
sheetOpacity.setValue(height === 0 ? 0 : 1);
|
|
88
89
|
onPositionChange?.(height);
|
|
89
90
|
};
|
|
90
91
|
const closedIndex = resolvedDetents.findIndex(detent => detent.height === 0);
|
|
@@ -105,11 +106,13 @@ export const BottomSheet = ({
|
|
|
105
106
|
style: StyleSheet.absoluteFill,
|
|
106
107
|
onPress: handleScrimPress,
|
|
107
108
|
children: scrimElement
|
|
108
|
-
}) : null, /*#__PURE__*/_jsx(View, {
|
|
109
|
+
}) : null, /*#__PURE__*/_jsx(Animated.View, {
|
|
109
110
|
pointerEvents: "box-none",
|
|
110
|
-
style: StyleSheet.absoluteFill,
|
|
111
|
+
style: [StyleSheet.absoluteFill, {
|
|
112
|
+
opacity: sheetOpacity
|
|
113
|
+
}],
|
|
111
114
|
children: /*#__PURE__*/_jsx(BottomSheetNativeComponent, {
|
|
112
|
-
pointerEvents:
|
|
115
|
+
pointerEvents: "box-none",
|
|
113
116
|
style: [{
|
|
114
117
|
position: 'absolute',
|
|
115
118
|
left: 0,
|
|
@@ -127,7 +130,6 @@ export const BottomSheet = ({
|
|
|
127
130
|
style: {
|
|
128
131
|
flex: 1
|
|
129
132
|
},
|
|
130
|
-
pointerEvents: "box-none",
|
|
131
133
|
children: [children, /*#__PURE__*/_jsx(View, {
|
|
132
134
|
onLayout: handleSentinelLayout,
|
|
133
135
|
pointerEvents: "none"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["useEffect","useRef","useState","Animated","Pressable","StyleSheet","View","useWindowDimensions","useSafeAreaInsets","BottomSheetNativeComponent","Portal","resolveDetent","jsx","_jsx","jsxs","_jsxs","programmatic","DefaultScrim","progress","color","style","absoluteFill","flex","backgroundColor","opacity","BottomSheet","children","detents","index","animateIn","onIndexChange","onPositionChange","modal","scrimColor","height","screenHeight","insets","maxHeight","top","contentHeight","setContentHeight","currentPositionRef","scrimProgress","Value","current","resolvedDetents","map","detent","value","Math","max","min","isDetentProgrammatic","handleSentinelLayout","event","nativeEvent","layout","y","clampedIndex","length","isCollapsed","
|
|
1
|
+
{"version":3,"names":["useEffect","useRef","useState","Animated","Pressable","StyleSheet","View","useWindowDimensions","useSafeAreaInsets","BottomSheetNativeComponent","Portal","resolveDetent","jsx","_jsx","jsxs","_jsxs","programmatic","DefaultScrim","progress","color","style","absoluteFill","flex","backgroundColor","opacity","BottomSheet","children","detents","index","animateIn","onIndexChange","onPositionChange","modal","scrimColor","height","screenHeight","insets","maxHeight","top","contentHeight","setContentHeight","currentPositionRef","scrimProgress","Value","current","sheetOpacity","resolvedDetents","map","detent","value","Math","max","min","isDetentProgrammatic","handleSentinelLayout","event","nativeEvent","layout","y","clampedIndex","length","isCollapsed","scrimPressEnabledRef","previousIsCollapsedRef","firstNonzeroDetent","find","setValue","undefined","frame","requestAnimationFrame","cancelAnimationFrame","handleIndexChange","handlePositionChange","position","closedIndex","findIndex","handleScrimPress","scrimElement","sheet","pointerEvents","onPress","left","right","bottom","collapsable","onLayout"],"sourceRoot":"../../src","sources":["BottomSheet.tsx"],"mappings":"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","ignoreList":[]}
|
package/package.json
CHANGED
package/src/BottomSheet.tsx
CHANGED
|
@@ -61,6 +61,7 @@ export const BottomSheet = ({
|
|
|
61
61
|
const [contentHeight, setContentHeight] = useState(0);
|
|
62
62
|
const currentPositionRef = useRef(0);
|
|
63
63
|
const scrimProgress = useRef(new Animated.Value(0)).current;
|
|
64
|
+
const sheetOpacity = useRef(new Animated.Value(0)).current;
|
|
64
65
|
|
|
65
66
|
const resolvedDetents = detents.map((detent) => {
|
|
66
67
|
const value = resolveDetent(detent, contentHeight, maxHeight);
|
|
@@ -76,7 +77,6 @@ export const BottomSheet = ({
|
|
|
76
77
|
|
|
77
78
|
const clampedIndex = Math.max(0, Math.min(index, resolvedDetents.length - 1));
|
|
78
79
|
const isCollapsed = (resolvedDetents[clampedIndex]?.height ?? 0) === 0;
|
|
79
|
-
const sheetPointerEvents = isCollapsed ? 'none' : 'box-none';
|
|
80
80
|
const scrimPressEnabledRef = useRef(!modal || isCollapsed);
|
|
81
81
|
const previousIsCollapsedRef = useRef(isCollapsed);
|
|
82
82
|
const firstNonzeroDetent =
|
|
@@ -130,6 +130,7 @@ export const BottomSheet = ({
|
|
|
130
130
|
? 0
|
|
131
131
|
: Math.min(1, Math.max(0, height / firstNonzeroDetent));
|
|
132
132
|
scrimProgress.setValue(progress);
|
|
133
|
+
sheetOpacity.setValue(height === 0 ? 0 : 1);
|
|
133
134
|
onPositionChange?.(height);
|
|
134
135
|
};
|
|
135
136
|
|
|
@@ -162,9 +163,12 @@ export const BottomSheet = ({
|
|
|
162
163
|
{scrimElement}
|
|
163
164
|
</Pressable>
|
|
164
165
|
) : null}
|
|
165
|
-
<View
|
|
166
|
+
<Animated.View
|
|
167
|
+
pointerEvents="box-none"
|
|
168
|
+
style={[StyleSheet.absoluteFill, { opacity: sheetOpacity }]}
|
|
169
|
+
>
|
|
166
170
|
<BottomSheetNativeComponent
|
|
167
|
-
pointerEvents=
|
|
171
|
+
pointerEvents="box-none"
|
|
168
172
|
style={[
|
|
169
173
|
{
|
|
170
174
|
position: 'absolute',
|
|
@@ -181,16 +185,12 @@ export const BottomSheet = ({
|
|
|
181
185
|
onIndexChange={handleIndexChange}
|
|
182
186
|
onPositionChange={handlePositionChange}
|
|
183
187
|
>
|
|
184
|
-
<View
|
|
185
|
-
collapsable={false}
|
|
186
|
-
style={{ flex: 1 }}
|
|
187
|
-
pointerEvents="box-none"
|
|
188
|
-
>
|
|
188
|
+
<View collapsable={false} style={{ flex: 1 }}>
|
|
189
189
|
{children}
|
|
190
190
|
<View onLayout={handleSentinelLayout} pointerEvents="none" />
|
|
191
191
|
</View>
|
|
192
192
|
</BottomSheetNativeComponent>
|
|
193
|
-
</View>
|
|
193
|
+
</Animated.View>
|
|
194
194
|
</Animated.View>
|
|
195
195
|
);
|
|
196
196
|
|