@swmansion/react-native-bottom-sheet 0.11.0 → 0.13.0-next.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
@@ -84,8 +84,8 @@ relative to the provider.
84
84
  The library provides two components: `BottomSheet` (inline) and
85
85
  `ModalBottomSheet` (modal). Both render their children as the sheet content,
86
86
  with a `surface` prop for the background behind it, and are controlled via
87
- `detents`, `index`, and `onIndexChange`. Use `onSettle` for
88
- post‍-‍snap observability.
87
+ `detents`, `index`, and `onIndexChange`. Use `onSettle` to observe when the
88
+ sheet finishes moving.
89
89
 
90
90
  ### Inline
91
91
 
@@ -150,6 +150,31 @@ its color:
150
150
  </ModalBottomSheet>
151
151
  ```
152
152
 
153
+ By default, the scrim fades in as the sheet opens and then holds at full
154
+ opacity, so detents above the first share the same scrim. Use `scrimOpacities`
155
+ to control the opacity at each detent: It takes one value in 0–1 per detent,
156
+ indexed to match `detents`, and interpolates linearly as the sheet is dragged
157
+ between them. A shorter array reuses its last value for any remaining detents.
158
+
159
+ The default maps each detent to 0 when it is closed and 1 otherwise, so the
160
+ scrim is transparent at any closed detent and fully opaque at every open one,
161
+ whatever order the detents are passed in.
162
+
163
+ To keep the scrim deepening across every detent, pass one value per detent:
164
+
165
+ ```tsx
166
+ <ModalBottomSheet
167
+ index={index}
168
+ onIndexChange={setIndex}
169
+ detents={[0, 300, 'content']}
170
+ scrimColor="rgba(0, 0, 0, 0.3)"
171
+ scrimOpacities={[0, 0.5, 1]}
172
+ surface={/* ... */}
173
+ >
174
+ {/* ... */}
175
+ </ModalBottomSheet>
176
+ ```
177
+
153
178
  ### Surface
154
179
 
155
180
  Provide the sheet’s background through the `surface` prop. The library renders
@@ -223,12 +248,15 @@ content, never on the&nbsp;surface:
223
248
  The `index` prop is a zero&zwj;-&zwj;based index into the `detents` array.
224
249
  `onIndexChange` and `onSettle` have different&nbsp;responsibilities:
225
250
 
226
- - `onIndexChange` is for user&zwj;-&zwj;triggered snaps. Treat it as the signal
227
- to update your controlled `index`&nbsp;state.
251
+ - `onIndexChange` fires when a user&zwj;-&zwj;triggered snap is _initiated_: the
252
+ moment a drag commits to a detent, before the animation settles. It does not
253
+ fire for programmatic `index` changes; you already know when you make those.
254
+ Treat it as the signal to update your controlled `index`&nbsp;state.
228
255
  - `onSettle` fires when the sheet finishes snapping to a detent, regardless of
229
- whether that snap was user&zwj;-&zwj;triggered or programmatic. Use it for
230
- observability or side effects (analytics, reacting to collapse, etc.), not for
231
- updating the controlled `index`&nbsp;state.
256
+ whether that snap was user&zwj;-&zwj;triggered or programmatic. It is the
257
+ signal for the _end_ of any movement. Use it for observability or side effects
258
+ (analytics, reacting to collapse, etc.), not for updating the controlled
259
+ `index`&nbsp;state.
232
260
 
233
261
  ```tsx
234
262
  const [index, setIndex] = useState(0);
@@ -238,10 +266,10 @@ const [index, setIndex] = useState(0);
238
266
  <BottomSheet // Or `ModalBottomSheet`.
239
267
  detents={[0, 300, 'content']} // Collapsed, 300 px, content height.
240
268
  index={index}
241
- onIndexChange={setIndex} // Keep controlled state in sync.
269
+ onIndexChange={setIndex} // Fires when a drag commits; keep state in sync.
242
270
  surface={/* ... */}
243
271
  onSettle={(nextIndex) => {
244
- if (nextIndex === 0) console.log('Sheet collapsed.');
272
+ if (nextIndex === 0) console.log('Sheet finished collapsing.');
245
273
  }}
246
274
  >
247
275
  {/* ... */}
@@ -83,6 +83,9 @@ class BottomSheetView(context: Context) : ReactViewGroup(context) {
83
83
  private var scrimPressed = false
84
84
  private var scrimTouchActive = false
85
85
  private var scrimColor = Color.TRANSPARENT
86
+ // The JS layer always supplies a per-detent array; the fully-opaque fallback
87
+ // only guards against empty input (indexing requires a non-empty array).
88
+ private var scrimOpacities = listOf(1f)
86
89
  private var scrimProgress = 0f
87
90
  private var suppressScrimForClosingTarget = false
88
91
  private var scrimPinnedFull = false
@@ -255,6 +258,11 @@ class BottomSheetView(context: Context) : ReactViewGroup(context) {
255
258
  invalidate()
256
259
  }
257
260
 
261
+ fun setScrimOpacities(values: List<Float>) {
262
+ scrimOpacities = if (values.isEmpty()) listOf(1f) else values
263
+ updateScrim()
264
+ }
265
+
258
266
  fun setMaxDetentHeight(maxDetentHeight: Double) {
259
267
  this.maxDetentHeight = (maxDetentHeight * density).toFloat()
260
268
  refreshDetentsFromLayout()
@@ -566,13 +574,16 @@ class BottomSheetView(context: Context) : ReactViewGroup(context) {
566
574
  }
567
575
  emitPosition()
568
576
  updateInteractionState()
569
- if (emitIndexChange) listener?.onIndexChange(index)
570
577
  if (emitSettle) listener?.onSettle(index)
571
578
  }
572
579
  }
573
580
 
574
581
  activeAnimation = spring
575
582
  startChoreographer()
583
+ // Report the index change as soon as the snap is committed, not when it
584
+ // finishes: targetIndex is already set, and a programmatic snap's start is
585
+ // known to the caller. onSettle remains the signal for movement end.
586
+ if (emitIndexChange) listener?.onIndexChange(index)
576
587
  spring.start()
577
588
  }
578
589
 
@@ -878,18 +889,55 @@ class BottomSheetView(context: Context) : ReactViewGroup(context) {
878
889
 
879
890
  // While the sheet is fully open and only its content/detent geometry is
880
891
  // resizing, the position momentarily lags the grown detent height. Keep the
881
- // scrim at full opacity instead of dipping it until the re-anchor settles.
892
+ // scrim pinned to the fully-open opacity instead of dipping it until the
893
+ // re-anchor settles.
882
894
  if (scrimPinnedFull) {
883
- scrimProgress = 1f
895
+ scrimProgress = fullyOpenScrimOpacity()
884
896
  invalidate()
885
897
  return
886
898
  }
887
899
 
888
- val threshold = firstNonZeroDetentHeight
889
- scrimProgress = if (threshold <= 0f) 0f else (position / threshold).coerceIn(0f, 1f)
900
+ scrimProgress = scrimOpacityAt(position)
890
901
  invalidate()
891
902
  }
892
903
 
904
+ /** The opacity at the tallest detent, held while the sheet re-anchors. */
905
+ private fun fullyOpenScrimOpacity(): Float {
906
+ val maxHeight = detentSpecs.maxOfOrNull { it.height } ?: return 1f
907
+ return scrimOpacityAt(maxHeight)
908
+ }
909
+
910
+ /**
911
+ * Interpolates the scrim opacity for a sheet height by bracketing it between adjacent detent
912
+ * heights and lerping each detent index's configured value.
913
+ */
914
+ private fun scrimOpacityAt(position: Float): Float {
915
+ if (detentSpecs.isEmpty()) return 0f
916
+ val pairs =
917
+ detentSpecs.indices
918
+ .map { index ->
919
+ detentSpecs[index].height to
920
+ scrimOpacities[index.coerceAtMost(scrimOpacities.size - 1)].coerceIn(0f, 1f)
921
+ }
922
+ .sortedBy { it.first }
923
+
924
+ val first = pairs.first()
925
+ val last = pairs.last()
926
+ if (position <= first.first) return first.second
927
+ if (position >= last.first) return last.second
928
+
929
+ for (i in 1 until pairs.size) {
930
+ val upper = pairs[i]
931
+ if (position <= upper.first) {
932
+ val lower = pairs[i - 1]
933
+ val span = upper.first - lower.first
934
+ val t = if (span <= 0f) 1f else (position - lower.first) / span
935
+ return lower.second + (upper.second - lower.second) * t
936
+ }
937
+ }
938
+ return last.second
939
+ }
940
+
893
941
  private fun hideScrim() {
894
942
  scrimProgress = 0f
895
943
  invalidate()
@@ -138,6 +138,17 @@ class BottomSheetViewManager :
138
138
  view.setScrimColor(scrimColor)
139
139
  }
140
140
 
141
+ @ReactProp(name = "scrimOpacities")
142
+ override fun setScrimOpacities(view: BottomSheetView, value: ReadableArray?) {
143
+ val opacities = mutableListOf<Float>()
144
+ if (value != null) {
145
+ for (i in 0 until value.size()) {
146
+ opacities.add(value.getDouble(i).toFloat())
147
+ }
148
+ }
149
+ view.setScrimOpacities(opacities)
150
+ }
151
+
141
152
  override fun onDropViewInstance(view: BottomSheetView) {
142
153
  super.onDropViewInstance(view)
143
154
  view.destroy()
@@ -86,6 +86,14 @@ using namespace facebook::react;
86
86
  [_sheetView setScrimColor:RCTUIColorFromSharedColor(newViewProps.scrimColor)];
87
87
  }
88
88
 
89
+ if (newViewProps.scrimOpacities != oldViewProps.scrimOpacities) {
90
+ NSMutableArray<NSNumber *> *opacities = [NSMutableArray new];
91
+ for (const auto &opacity : newViewProps.scrimOpacities) {
92
+ [opacities addObject:@(opacity)];
93
+ }
94
+ [_sheetView setScrimOpacities:opacities];
95
+ }
96
+
89
97
  [super updateProps:props oldProps:oldProps];
90
98
  }
91
99
 
@@ -23,6 +23,7 @@ NS_ASSUME_NONNULL_BEGIN
23
23
  - (void)setMaxDetentHeight:(CGFloat)maxDetentHeight;
24
24
  - (void)setDetentIndex:(NSInteger)newIndex;
25
25
  - (void)setScrimColor:(UIColor *_Nullable)color;
26
+ - (void)setScrimOpacities:(NSArray<NSNumber *> *)opacities;
26
27
  - (CGFloat)currentContentOffsetY;
27
28
  - (void)mountChildComponentView:(UIView *)childView atIndex:(NSInteger)index;
28
29
  - (void)unmountChildComponentView:(UIView *)childView;
@@ -80,6 +80,11 @@
80
80
  _impl.scrimColor = color;
81
81
  }
82
82
 
83
+ - (void)setScrimOpacities:(NSArray<NSNumber *> *)opacities
84
+ {
85
+ [_impl setScrimOpacities:opacities];
86
+ }
87
+
83
88
  - (CGFloat)currentContentOffsetY
84
89
  {
85
90
  return _impl.currentContentOffsetY;
@@ -34,6 +34,19 @@ public final class BottomSheetHostingView: UIView {
34
34
  didSet { scrimView.backgroundColor = scrimColor }
35
35
  }
36
36
 
37
+ /// Scrim opacity per detent index. Linearly interpolated between detents and
38
+ /// clamped to the last value for detents beyond the array. The JS layer always
39
+ /// supplies a per-detent array; the fully-opaque fallback only guards against
40
+ /// empty input (indexing requires a non-empty array).
41
+ private var scrimOpacities: [CGFloat] = [1] {
42
+ didSet { updateScrim() }
43
+ }
44
+
45
+ public func setScrimOpacities(_ values: [NSNumber]) {
46
+ let mapped = values.map { CGFloat(truncating: $0) }
47
+ scrimOpacities = mapped.isEmpty ? [1] : mapped
48
+ }
49
+
37
50
  public var maxDetentHeight: CGFloat = .nan {
38
51
  didSet { refreshDetentsFromLayout() }
39
52
  }
@@ -425,13 +438,16 @@ public final class BottomSheetHostingView: UIView {
425
438
  self.scrimPinnedFull = false
426
439
  self.setContentInteractionEnabled(true)
427
440
  self.updateInteractionState()
428
- if emitIndexChange {
429
- self.eventDelegate?.bottomSheetHostingView(self, didChangeIndex: index)
430
- }
431
441
  if emitSettle {
432
442
  self.eventDelegate?.bottomSheetHostingView(self, didSettle: index)
433
443
  }
434
444
  }
445
+ // Report the index change as soon as the snap is committed, not when it
446
+ // finishes: `targetIndex` is already set, and a programmatic snap's start is
447
+ // known to the caller. `onSettle` remains the signal for movement end.
448
+ if emitIndexChange {
449
+ eventDelegate?.bottomSheetHostingView(self, didChangeIndex: index)
450
+ }
435
451
  animator.startAnimation()
436
452
  activeAnimator = animator
437
453
  startDisplayLink()
@@ -860,24 +876,55 @@ private extension BottomSheetHostingView {
860
876
 
861
877
  // While the sheet is fully open and only its content/detent geometry is
862
878
  // resizing, the position momentarily lags the grown detent height. Keep the
863
- // scrim at full opacity instead of dipping it until the re-anchor settles.
879
+ // scrim pinned to the fully-open opacity instead of dipping it until the
880
+ // re-anchor settles.
864
881
  if scrimPinnedFull {
865
- scrimView.alpha = 1
866
- scrimView.isHidden = false
882
+ let opacity = fullyOpenScrimOpacity
883
+ scrimView.alpha = opacity
884
+ scrimView.isHidden = opacity <= 0.001
867
885
  return
868
886
  }
869
887
 
870
- let threshold = firstNonZeroDetentHeight
871
- let progress: CGFloat
872
- if threshold <= 0 {
873
- progress = 0
874
- } else {
875
- progress = min(1, max(0, position / threshold))
876
- }
888
+ let progress = scrimOpacity(forPosition: position)
877
889
  scrimView.alpha = progress
878
890
  scrimView.isHidden = progress <= 0.001
879
891
  }
880
892
 
893
+ /// The opacity at the tallest detent, held while the sheet re-anchors.
894
+ private var fullyOpenScrimOpacity: CGFloat {
895
+ guard let maxHeight = detentSpecs.map({ $0.height }).max() else { return 1 }
896
+ return scrimOpacity(forPosition: maxHeight)
897
+ }
898
+
899
+ /// Interpolates the scrim opacity for a sheet height by bracketing it between
900
+ /// adjacent detent heights and lerping each detent index's configured value.
901
+ private func scrimOpacity(forPosition position: CGFloat) -> CGFloat {
902
+ guard !detentSpecs.isEmpty else { return 0 }
903
+ let pairs = detentSpecs.indices
904
+ .map { (
905
+ height: detentSpecs[$0].height,
906
+ opacity: clampOpacity(scrimOpacities[min($0, scrimOpacities.count - 1)])
907
+ ) }
908
+ .sorted { $0.height < $1.height }
909
+
910
+ guard let first = pairs.first, let last = pairs.last else { return 0 }
911
+ if position <= first.height { return first.opacity }
912
+ if position >= last.height { return last.opacity }
913
+
914
+ for i in 1 ..< pairs.count where position <= pairs[i].height {
915
+ let lower = pairs[i - 1]
916
+ let upper = pairs[i]
917
+ let span = upper.height - lower.height
918
+ let t = span <= 0 ? 1 : (position - lower.height) / span
919
+ return lower.opacity + (upper.opacity - lower.opacity) * t
920
+ }
921
+ return last.opacity
922
+ }
923
+
924
+ private func clampOpacity(_ value: CGFloat) -> CGFloat {
925
+ min(1, max(0, value))
926
+ }
927
+
881
928
  func updateInteractionState() {
882
929
  scrimView.isUserInteractionEnabled = modal && (closedIndex != nil) && !scrimView.isHidden
883
930
  }
@@ -25,7 +25,8 @@ export const BottomSheet = ({
25
25
  onPositionChange,
26
26
  modal = false,
27
27
  disableScrollableNegotiation = false,
28
- scrimColor
28
+ scrimColor,
29
+ scrimOpacities
29
30
  }) => {
30
31
  const {
31
32
  height: windowHeight
@@ -51,6 +52,10 @@ export const BottomSheet = ({
51
52
  const clampedIndex = Math.max(0, Math.min(index, nativeDetents.length - 1));
52
53
  const selectedDetentValue = detents[clampedIndex] ? resolveDetentValue(detents[clampedIndex]) : 0;
53
54
  const isCollapsed = selectedDetentValue === 0;
55
+ // Default the scrim opacity per detent: transparent at any closed detent,
56
+ // fully opaque at every open one. Mapping each detent independently keeps
57
+ // this correct regardless of the order detents are passed in.
58
+ const resolvedScrimOpacity = scrimOpacities ?? detents.map(detent => resolveDetentValue(detent) === 0 ? 0 : 1);
54
59
  const handleIndexChange = event => {
55
60
  onIndexChange?.(event.nativeEvent.index);
56
61
  };
@@ -86,6 +91,7 @@ export const BottomSheet = ({
86
91
  modal: modal,
87
92
  disableScrollableNegotiation: disableScrollableNegotiation,
88
93
  scrimColor: scrimColor,
94
+ scrimOpacities: resolvedScrimOpacity,
89
95
  onIndexChange: handleIndexChange,
90
96
  onSettle: handleSettle,
91
97
  onPositionChange: handlePositionChange,
@@ -1 +1 @@
1
- {"version":3,"names":["StyleSheet","View","useWindowDimensions","useSafeAreaInsets","BottomSheetNativeComponent","BottomSheetSurfaceNativeComponent","Portal","jsx","_jsx","jsxs","_jsxs","programmatic","BottomSheet","children","surface","style","detents","index","animateIn","onIndexChange","onSettle","onPositionChange","modal","disableScrollableNegotiation","scrimColor","height","windowHeight","insets","maxHeight","top","nativeDetents","map","detent","isDetentProgrammatic","value","resolveDetentValue","kind","Math","max","min","clampedIndex","length","selectedDetentValue","isCollapsed","handleIndexChange","event","nativeEvent","handleSettle","handlePositionChange","position","sheet","absoluteFill","pointerEvents","left","right","bottom","maxDetentHeight","collapsable","flex"],"sourceRoot":"../../src","sources":["BottomSheet.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["StyleSheet","View","useWindowDimensions","useSafeAreaInsets","BottomSheetNativeComponent","BottomSheetSurfaceNativeComponent","Portal","jsx","_jsx","jsxs","_jsxs","programmatic","BottomSheet","children","surface","style","detents","index","animateIn","onIndexChange","onSettle","onPositionChange","modal","disableScrollableNegotiation","scrimColor","scrimOpacities","height","windowHeight","insets","maxHeight","top","nativeDetents","map","detent","isDetentProgrammatic","value","resolveDetentValue","kind","Math","max","min","clampedIndex","length","selectedDetentValue","isCollapsed","resolvedScrimOpacity","handleIndexChange","event","nativeEvent","handleSettle","handlePositionChange","position","sheet","absoluteFill","pointerEvents","left","right","bottom","maxDetentHeight","collapsable","flex"],"sourceRoot":"../../src","sources":["BottomSheet.tsx"],"mappings":"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","ignoreList":[]}
@@ -19,6 +19,7 @@ export interface NativeProps extends ViewProps {
19
19
  modal: boolean;
20
20
  disableScrollableNegotiation?: boolean;
21
21
  scrimColor?: ColorValue;
22
+ scrimOpacities?: ReadonlyArray<CodegenTypes.Double>;
22
23
  onIndexChange?: CodegenTypes.DirectEventHandler<
23
24
  Readonly<{ index: CodegenTypes.Int32 }>
24
25
  >;
@@ -29,7 +29,12 @@ export interface BottomSheetProps {
29
29
  index: number;
30
30
  /** Whether the sheet should animate in on first layout. */
31
31
  animateIn?: boolean;
32
- /** Called after a user-driven snap changes the active index. */
32
+ /**
33
+ * Called when a user-driven snap is initiated: the moment a drag commits to a
34
+ * detent, before the animation settles. Does not fire for programmatic `index`
35
+ * changes; you already know when you make those. Use it to keep your controlled
36
+ * `index` state in sync. For the end of any movement, use `onSettle`.
37
+ */
33
38
  onIndexChange?: (index: number) => void;
34
39
  /** Called when a snap animation settles, including programmatic changes. */
35
40
  onSettle?: (index: number) => void;
@@ -45,7 +50,21 @@ export interface BottomSheetProps {
45
50
  disableScrollableNegotiation?: boolean;
46
51
  /** Scrim color used by `ModalBottomSheet`. */
47
52
  scrimColor?: string;
53
+ /**
54
+ * Scrim opacities per detent, indexed to match `detents`. Each value in 0–1
55
+ * scales the scrim color’s alpha at the detent of the same index, and the
56
+ * opacity is linearly interpolated as the sheet is dragged between detents.
57
+ * A shorter array than `detents` reuses its last value for any remaining
58
+ * detents.
59
+ *
60
+ * The default maps each detent to 0 when it is closed and 1 otherwise,
61
+ * so the scrim is transparent at any closed detent and fully opaque at every
62
+ * open one; e.g., `[0, 'content']` defaults to `[0, 1]`, and all-open detents
63
+ * default to a constant opaque scrim. Pass one value per detent—e.g.,
64
+ * `[0, 0.5, 1]`—to keep the scrim deepening across every detent.
65
+ */
66
+ scrimOpacities?: number[];
48
67
  }
49
68
  /** Native bottom sheet that renders inline within the current screen layout. */
50
- export declare const BottomSheet: ({ children, surface, style, detents, index, animateIn, onIndexChange, onSettle, onPositionChange, modal, disableScrollableNegotiation, scrimColor, }: BottomSheetProps) => import("react/jsx-runtime").JSX.Element;
69
+ export declare const BottomSheet: ({ children, surface, style, detents, index, animateIn, onIndexChange, onSettle, onPositionChange, modal, disableScrollableNegotiation, scrimColor, scrimOpacities, }: BottomSheetProps) => import("react/jsx-runtime").JSX.Element;
51
70
  //# sourceMappingURL=BottomSheet.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"BottomSheet.d.ts","sourceRoot":"","sources":["../../../src/BottomSheet.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAOzD,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACjD,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,wDAAwD;IACxD,QAAQ,EAAE,SAAS,CAAC;IACpB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gEAAgE;IAChE,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,gDAAgD;IAChD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;OAIG;IACH,4BAA4B,CAAC,EAAE,OAAO,CAAC;IACvC,8CAA8C;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,gFAAgF;AAChF,eAAO,MAAM,WAAW,GAAI,sJAazB,gBAAgB,4CAiGlB,CAAC"}
1
+ {"version":3,"file":"BottomSheet.d.ts","sourceRoot":"","sources":["../../../src/BottomSheet.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAOzD,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACjD,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,wDAAwD;IACxD,QAAQ,EAAE,SAAS,CAAC;IACpB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,gDAAgD;IAChD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;OAIG;IACH,4BAA4B,CAAC,EAAE,OAAO,CAAC;IACvC,8CAA8C;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;;;;;;OAYG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,gFAAgF;AAChF,eAAO,MAAM,WAAW,GAAI,sKAczB,gBAAgB,4CAwGlB,CAAC"}
@@ -12,6 +12,7 @@ export interface NativeProps extends ViewProps {
12
12
  modal: boolean;
13
13
  disableScrollableNegotiation?: boolean;
14
14
  scrimColor?: ColorValue;
15
+ scrimOpacities?: ReadonlyArray<CodegenTypes.Double>;
15
16
  onIndexChange?: CodegenTypes.DirectEventHandler<Readonly<{
16
17
  index: CodegenTypes.Int32;
17
18
  }>>;
@@ -1 +1 @@
1
- {"version":3,"file":"BottomSheetNativeComponent.d.ts","sourceRoot":"","sources":["../../../src/BottomSheetNativeComponent.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAEtB,KAAK,YAAY,GAAG,QAAQ,CAAC;IAC3B,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,OAAO,CAAC;CACvB,CAAC,CAAC;AAEH,MAAM,WAAW,WAAY,SAAQ,SAAS;IAC5C,OAAO,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IACrC,eAAe,EAAE,YAAY,CAAC,MAAM,CAAC;IACrC,KAAK,EAAE,YAAY,CAAC,KAAK,CAAC;IAC1B,SAAS,CAAC,EAAE,YAAY,CAAC,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACpD,KAAK,EAAE,OAAO,CAAC;IACf,4BAA4B,CAAC,EAAE,OAAO,CAAC;IACvC,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,aAAa,CAAC,EAAE,YAAY,CAAC,kBAAkB,CAC7C,QAAQ,CAAC;QAAE,KAAK,EAAE,YAAY,CAAC,KAAK,CAAA;KAAE,CAAC,CACxC,CAAC;IACF,QAAQ,CAAC,EAAE,YAAY,CAAC,kBAAkB,CACxC,QAAQ,CAAC;QAAE,KAAK,EAAE,YAAY,CAAC,KAAK,CAAA;KAAE,CAAC,CACxC,CAAC;IACF,gBAAgB,CAAC,EAAE,YAAY,CAAC,kBAAkB,CAChD,QAAQ,CAAC;QAAE,QAAQ,EAAE,YAAY,CAAC,MAAM,CAAA;KAAE,CAAC,CAC5C,CAAC;CACH;;AAED,wBAAsE"}
1
+ {"version":3,"file":"BottomSheetNativeComponent.d.ts","sourceRoot":"","sources":["../../../src/BottomSheetNativeComponent.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAEtB,KAAK,YAAY,GAAG,QAAQ,CAAC;IAC3B,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,OAAO,CAAC;CACvB,CAAC,CAAC;AAEH,MAAM,WAAW,WAAY,SAAQ,SAAS;IAC5C,OAAO,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IACrC,eAAe,EAAE,YAAY,CAAC,MAAM,CAAC;IACrC,KAAK,EAAE,YAAY,CAAC,KAAK,CAAC;IAC1B,SAAS,CAAC,EAAE,YAAY,CAAC,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACpD,KAAK,EAAE,OAAO,CAAC;IACf,4BAA4B,CAAC,EAAE,OAAO,CAAC;IACvC,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,cAAc,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IACpD,aAAa,CAAC,EAAE,YAAY,CAAC,kBAAkB,CAC7C,QAAQ,CAAC;QAAE,KAAK,EAAE,YAAY,CAAC,KAAK,CAAA;KAAE,CAAC,CACxC,CAAC;IACF,QAAQ,CAAC,EAAE,YAAY,CAAC,kBAAkB,CACxC,QAAQ,CAAC;QAAE,KAAK,EAAE,YAAY,CAAC,KAAK,CAAA;KAAE,CAAC,CACxC,CAAC;IACF,gBAAgB,CAAC,EAAE,YAAY,CAAC,kBAAkB,CAChD,QAAQ,CAAC;QAAE,QAAQ,EAAE,YAAY,CAAC,MAAM,CAAA;KAAE,CAAC,CAC5C,CAAC;CACH;;AAED,wBAAsE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swmansion/react-native-bottom-sheet",
3
- "version": "0.11.0",
3
+ "version": "0.13.0-next.1",
4
4
  "description": "Provides bottom-sheet components for React Native.",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -36,7 +36,12 @@ export interface BottomSheetProps {
36
36
  index: number;
37
37
  /** Whether the sheet should animate in on first layout. */
38
38
  animateIn?: boolean;
39
- /** Called after a user-driven snap changes the active index. */
39
+ /**
40
+ * Called when a user-driven snap is initiated: the moment a drag commits to a
41
+ * detent, before the animation settles. Does not fire for programmatic `index`
42
+ * changes; you already know when you make those. Use it to keep your controlled
43
+ * `index` state in sync. For the end of any movement, use `onSettle`.
44
+ */
40
45
  onIndexChange?: (index: number) => void;
41
46
  /** Called when a snap animation settles, including programmatic changes. */
42
47
  onSettle?: (index: number) => void;
@@ -52,6 +57,20 @@ export interface BottomSheetProps {
52
57
  disableScrollableNegotiation?: boolean;
53
58
  /** Scrim color used by `ModalBottomSheet`. */
54
59
  scrimColor?: string;
60
+ /**
61
+ * Scrim opacities per detent, indexed to match `detents`. Each value in 0–1
62
+ * scales the scrim color’s alpha at the detent of the same index, and the
63
+ * opacity is linearly interpolated as the sheet is dragged between detents.
64
+ * A shorter array than `detents` reuses its last value for any remaining
65
+ * detents.
66
+ *
67
+ * The default maps each detent to 0 when it is closed and 1 otherwise,
68
+ * so the scrim is transparent at any closed detent and fully opaque at every
69
+ * open one; e.g., `[0, 'content']` defaults to `[0, 1]`, and all-open detents
70
+ * default to a constant opaque scrim. Pass one value per detent—e.g.,
71
+ * `[0, 0.5, 1]`—to keep the scrim deepening across every detent.
72
+ */
73
+ scrimOpacities?: number[];
55
74
  }
56
75
 
57
76
  /** Native bottom sheet that renders inline within the current screen layout. */
@@ -68,6 +87,7 @@ export const BottomSheet = ({
68
87
  modal = false,
69
88
  disableScrollableNegotiation = false,
70
89
  scrimColor,
90
+ scrimOpacities,
71
91
  }: BottomSheetProps) => {
72
92
  const { height: windowHeight } = useWindowDimensions();
73
93
  const insets = useSafeAreaInsets();
@@ -96,6 +116,12 @@ export const BottomSheet = ({
96
116
  ? resolveDetentValue(detents[clampedIndex])
97
117
  : 0;
98
118
  const isCollapsed = selectedDetentValue === 0;
119
+ // Default the scrim opacity per detent: transparent at any closed detent,
120
+ // fully opaque at every open one. Mapping each detent independently keeps
121
+ // this correct regardless of the order detents are passed in.
122
+ const resolvedScrimOpacity =
123
+ scrimOpacities ??
124
+ detents.map((detent) => (resolveDetentValue(detent) === 0 ? 0 : 1));
99
125
  const handleIndexChange = (event: { nativeEvent: { index: number } }) => {
100
126
  onIndexChange?.(event.nativeEvent.index);
101
127
  };
@@ -138,6 +164,7 @@ export const BottomSheet = ({
138
164
  modal={modal}
139
165
  disableScrollableNegotiation={disableScrollableNegotiation}
140
166
  scrimColor={scrimColor}
167
+ scrimOpacities={resolvedScrimOpacity}
141
168
  onIndexChange={handleIndexChange}
142
169
  onSettle={handleSettle}
143
170
  onPositionChange={handlePositionChange}
@@ -19,6 +19,7 @@ export interface NativeProps extends ViewProps {
19
19
  modal: boolean;
20
20
  disableScrollableNegotiation?: boolean;
21
21
  scrimColor?: ColorValue;
22
+ scrimOpacities?: ReadonlyArray<CodegenTypes.Double>;
22
23
  onIndexChange?: CodegenTypes.DirectEventHandler<
23
24
  Readonly<{ index: CodegenTypes.Int32 }>
24
25
  >;