@niibase/bottom-sheet-manager 1.2.0 → 1.4.0

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 (64) hide show
  1. package/README.md +414 -69
  2. package/lib/commonjs/events.js +100 -15
  3. package/lib/commonjs/events.js.map +1 -1
  4. package/lib/commonjs/index.js +14 -0
  5. package/lib/commonjs/index.js.map +1 -1
  6. package/lib/commonjs/manager.js +153 -35
  7. package/lib/commonjs/manager.js.map +1 -1
  8. package/lib/commonjs/provider.js +92 -54
  9. package/lib/commonjs/provider.js.map +1 -1
  10. package/lib/commonjs/router/index.js +80 -21
  11. package/lib/commonjs/router/index.js.map +1 -1
  12. package/lib/commonjs/router/router.js +137 -12
  13. package/lib/commonjs/router/router.js.map +1 -1
  14. package/lib/commonjs/router/view.js +93 -126
  15. package/lib/commonjs/router/view.js.map +1 -1
  16. package/lib/commonjs/sheet.js +122 -98
  17. package/lib/commonjs/sheet.js.map +1 -1
  18. package/lib/module/events.js +100 -15
  19. package/lib/module/events.js.map +1 -1
  20. package/lib/module/index.js +2 -2
  21. package/lib/module/index.js.map +1 -1
  22. package/lib/module/manager.js +154 -35
  23. package/lib/module/manager.js.map +1 -1
  24. package/lib/module/provider.js +87 -50
  25. package/lib/module/provider.js.map +1 -1
  26. package/lib/module/router/index.js +66 -19
  27. package/lib/module/router/index.js.map +1 -1
  28. package/lib/module/router/router.js +135 -11
  29. package/lib/module/router/router.js.map +1 -1
  30. package/lib/module/router/view.js +92 -126
  31. package/lib/module/router/view.js.map +1 -1
  32. package/lib/module/sheet.js +124 -100
  33. package/lib/module/sheet.js.map +1 -1
  34. package/lib/typescript/events.d.ts +46 -12
  35. package/lib/typescript/events.d.ts.map +1 -1
  36. package/lib/typescript/index.d.ts +2 -2
  37. package/lib/typescript/index.d.ts.map +1 -1
  38. package/lib/typescript/manager.d.ts +73 -7
  39. package/lib/typescript/manager.d.ts.map +1 -1
  40. package/lib/typescript/provider.d.ts +22 -16
  41. package/lib/typescript/provider.d.ts.map +1 -1
  42. package/lib/typescript/router/index.d.ts +47 -17
  43. package/lib/typescript/router/index.d.ts.map +1 -1
  44. package/lib/typescript/router/router.d.ts +44 -5
  45. package/lib/typescript/router/router.d.ts.map +1 -1
  46. package/lib/typescript/router/types.d.ts +142 -32
  47. package/lib/typescript/router/types.d.ts.map +1 -1
  48. package/lib/typescript/router/view.d.ts +3 -3
  49. package/lib/typescript/router/view.d.ts.map +1 -1
  50. package/lib/typescript/sheet.d.ts +1 -1
  51. package/lib/typescript/sheet.d.ts.map +1 -1
  52. package/lib/typescript/types.d.ts +52 -21
  53. package/lib/typescript/types.d.ts.map +1 -1
  54. package/package.json +14 -15
  55. package/src/events.ts +118 -27
  56. package/src/index.ts +2 -1
  57. package/src/manager.ts +209 -42
  58. package/src/provider.tsx +144 -71
  59. package/src/router/index.tsx +77 -33
  60. package/src/router/router.ts +188 -15
  61. package/src/router/types.ts +172 -57
  62. package/src/router/view.tsx +111 -213
  63. package/src/sheet.tsx +192 -124
  64. package/src/types.ts +51 -24
package/src/sheet.tsx CHANGED
@@ -19,29 +19,28 @@ import {
19
19
  type NativeEventSubscription,
20
20
  } from "react-native";
21
21
  import {
22
- Easing,
23
22
  interpolate,
24
23
  useAnimatedReaction,
25
24
  useSharedValue,
26
25
  } from "react-native-reanimated";
27
26
  import { useSafeAreaInsets } from "react-native-safe-area-context";
28
- import { useTheme } from "@react-navigation/native";
29
27
  import React from "react";
30
28
 
31
29
  import {
32
30
  useProviderContext,
33
- useSheetAnimationContext,
34
31
  useSheetIDContext,
35
32
  useSheetRef,
33
+ useSheetSharedContext,
34
+ useStackBehaviorContext,
36
35
  } from "./provider";
37
- import { BottomSheetInstance, BottomSheetProps, SheetIds } from "./types";
36
+ import { BottomSheetInstance, BottomSheetProps, SheetIds, StackBehavior } from "./types";
38
37
  import { PrivateManager } from "./manager";
39
38
  import { eventManager } from "./events";
40
39
 
41
40
  interface BottomSheetFC
42
41
  extends React.MemoExoticComponent<React.ForwardRefExoticComponent<BottomSheetProps>> {
43
42
  <Id extends SheetIds>(
44
- props: BottomSheetProps & React.RefAttributes<BottomSheetInstance<Id>>,
43
+ props: BottomSheetProps<Id> & React.RefAttributes<BottomSheetInstance<Id>>,
45
44
  ): React.JSX.Element;
46
45
 
47
46
  // Components
@@ -57,6 +56,9 @@ interface BottomSheetFC
57
56
  TextInput: typeof BottomSheetTextInput;
58
57
  }
59
58
 
59
+ const FULL_SCREEN_POINTS: (string | number)[] =
60
+ Platform.OS === "ios" ? ["%90", "90%"] : ["%93", "93%"];
61
+
60
62
  const useSheetManager = ({
61
63
  id,
62
64
  onHide,
@@ -64,79 +66,105 @@ const useSheetManager = ({
64
66
  onContextUpdate,
65
67
  }: {
66
68
  id?: string;
67
- onHide: (data?: any, dismiss?: boolean) => void;
68
- onBeforeShow?: (data?: any) => void;
69
+ onHide: (data?: unknown, dismiss?: boolean, behavior?: StackBehavior) => void;
70
+ onBeforeShow?: (data?: unknown, behavior?: StackBehavior) => void;
69
71
  onContextUpdate: () => void;
70
72
  }) => {
71
- const [visible, setVisible] = React.useState(false);
72
73
  const currentContext = useProviderContext();
74
+ const hasShownRef = React.useRef(false);
73
75
 
74
76
  React.useEffect(() => {
75
77
  if (!id) return undefined;
76
78
 
77
79
  const subscriptions = [
78
- eventManager.subscribe(`show_${id}`, (data: any, context?: string) => {
79
- if (currentContext !== context || visible) return;
80
- onContextUpdate?.();
81
- onBeforeShow?.(data);
82
- setVisible(true);
83
- }),
84
- eventManager.subscribe(`hide_${id}`, (data: any, context, dismiss?: boolean) => {
85
- if (currentContext !== context) return;
86
- onHide?.(data, dismiss);
87
- }),
80
+ eventManager.subscribe(
81
+ `show_${id}`,
82
+ (data: unknown, context?: string, behavior?: StackBehavior) => {
83
+ if (currentContext !== context) return;
84
+ if (!hasShownRef.current) {
85
+ hasShownRef.current = true;
86
+ onContextUpdate?.();
87
+ onBeforeShow?.(data, behavior);
88
+ }
89
+ },
90
+ ),
91
+ eventManager.subscribe(
92
+ `hide_${id}`,
93
+ (data: unknown, context: string, dismiss?: boolean, behavior?: StackBehavior) => {
94
+ if (currentContext !== context) return;
95
+ hasShownRef.current = false;
96
+ onHide?.(data, dismiss, behavior);
97
+ },
98
+ ),
88
99
  ];
100
+
89
101
  return () => {
102
+ hasShownRef.current = false;
90
103
  subscriptions.forEach((s) => s?.unsubscribe?.());
91
104
  };
92
105
  }, [id, onHide, onBeforeShow, onContextUpdate, currentContext]);
93
-
94
- return { visible, setVisible };
95
106
  };
96
107
 
97
108
  const BottomSheetComponent = React.forwardRef<BottomSheetInstance, BottomSheetProps>(
98
109
  (
99
110
  {
100
111
  children,
101
- snapPoints,
102
112
  onClose,
103
113
  onBeforeShow,
104
114
  stackBehavior = "switch",
105
115
  hardwareBackPressToClose = true,
106
116
  enableDynamicSizing = false,
107
117
  handleIndicatorStyle,
108
- iosModalSheetTypeOfAnimation: pageAnimation,
118
+ iosModalSheetTypeOfAnimation,
119
+ snapPoints: defaultSnapPoints,
120
+ animatedIndex: defaultAnimatedIndex,
109
121
  backgroundStyle,
122
+ onAnimate,
110
123
  handleStyle,
111
- clickThrough,
112
- fullScreen,
124
+ style,
125
+ passThrough,
113
126
  opacity,
114
-
115
127
  ...props
116
128
  },
117
129
  ref,
118
130
  ) => {
119
131
  const currentSheetRef = useSheetRef();
120
132
  const currentCtx = useProviderContext();
133
+ const stackContext = useStackBehaviorContext();
121
134
 
122
- const { isFullScreen, iosModalSheetTypeOfAnimation } = useSheetAnimationContext();
123
135
  const animatedIndex = useSharedValue(0);
124
136
 
125
- const { colors } = useTheme();
126
- const { top } = useSafeAreaInsets();
127
- const themeBackgroundStyle = React.useMemo(
128
- () => ({ backgroundColor: colors.card }),
129
- [colors.card],
130
- );
131
- const themeHandleIndicatorStyle = React.useMemo(
132
- () => ({
133
- backgroundColor: colors.border,
134
- height: 5,
135
- width: 50,
136
- }),
137
- [colors.border],
137
+ const [currentStackBehavior, setCurrentStackBehavior] =
138
+ React.useState<StackBehavior>(stackBehavior);
139
+ const isPushed = currentStackBehavior === "push";
140
+
141
+ const { bottom, left, right } = useSafeAreaInsets();
142
+
143
+ const defaultStyle = React.useMemo(
144
+ () => ({ paddingBottom: bottom, paddingLeft: left, paddingRight: right }),
145
+ [bottom, left, right],
138
146
  );
139
147
 
148
+ const { isFullScreen } = useSheetSharedContext();
149
+ const [snapPoints, fullScreenIndex] = React.useMemo(() => {
150
+ let resolved = defaultSnapPoints;
151
+
152
+ if (
153
+ Platform.OS === "android" &&
154
+ iosModalSheetTypeOfAnimation &&
155
+ Array.isArray(resolved)
156
+ ) {
157
+ resolved = resolved.map((p) => (p === "90%" || p === "%90" ? "93%" : p));
158
+ }
159
+
160
+ const fullScreenIndex =
161
+ resolved instanceof Array
162
+ ? resolved.findIndex((p) => FULL_SCREEN_POINTS.includes(p))
163
+ : resolved?.value?.findIndex((p) => FULL_SCREEN_POINTS.includes(p)) || -1;
164
+
165
+ return [resolved, fullScreenIndex] as const;
166
+ }, [defaultSnapPoints, iosModalSheetTypeOfAnimation]);
167
+
140
168
  const valueRef = React.useRef<unknown>(null);
141
169
  const bottomSheetRef = React.useRef<BottomSheetModal>(null);
142
170
  const hardwareBackPressEvent = React.useRef<NativeEventSubscription>(
@@ -145,11 +173,28 @@ const BottomSheetComponent = React.forwardRef<BottomSheetInstance, BottomSheetPr
145
173
 
146
174
  const id = useSheetIDContext();
147
175
  const sheetId = props.id || id;
176
+
177
+ const hideSheetRef = React.useRef<
178
+ (
179
+ data?: unknown,
180
+ fromManager?: boolean,
181
+ dismiss?: boolean,
182
+ incomingBehavior?: StackBehavior,
183
+ ) => void
184
+ >(null!);
185
+
148
186
  useSheetManager({
149
187
  id: sheetId,
150
- onHide: (data, dismiss) => hideSheet(data, true, dismiss),
151
- onBeforeShow: (data) => {
152
- onBeforeShow?.(data);
188
+ onHide: (data, dismiss, behavior) => {
189
+ // Update state for future renders, but also pass behavior directly
190
+ // so hideSheet doesn't read a stale closure value (React state update
191
+ // is async — hideSheet runs before the re-render).
192
+ if (behavior) setCurrentStackBehavior(behavior);
193
+ hideSheetRef.current(data, true, dismiss, behavior);
194
+ },
195
+ onBeforeShow: (data, behavior) => {
196
+ if (behavior) setCurrentStackBehavior(behavior);
197
+ onBeforeShow?.(data as never);
153
198
  valueRef.current = undefined;
154
199
  currentSheetRef.current = getInstance();
155
200
  },
@@ -164,70 +209,107 @@ const BottomSheetComponent = React.forwardRef<BottomSheetInstance, BottomSheetPr
164
209
  });
165
210
 
166
211
  useAnimatedReaction(
167
- () => {
168
- if (!!iosModalSheetTypeOfAnimation || !!pageAnimation) {
169
- isFullScreen.value = 0;
170
- }
171
- return animatedIndex.value;
172
- },
212
+ () => animatedIndex.value,
173
213
  (index) => {
174
214
  "worklet";
175
- const points: (string | number)[] = ["%100", "100%"];
176
- const checkFullScreen = fullScreen
177
- ? -1
178
- : snapPoints instanceof Array
179
- ? snapPoints.findIndex((p) => points.includes(p))
180
- : snapPoints?.value?.findIndex((p) => points.includes(p)) || -1;
181
-
182
- if (
183
- -1 !== checkFullScreen &&
184
- (!!iosModalSheetTypeOfAnimation || !!pageAnimation)
185
- ) {
186
- isFullScreen.value = interpolate(
187
- index,
188
- [checkFullScreen - 1, checkFullScreen],
189
- [0, 1],
215
+ if (defaultAnimatedIndex) {
216
+ defaultAnimatedIndex.set(index);
217
+ }
218
+
219
+ if (iosModalSheetTypeOfAnimation) {
220
+ isFullScreen.set(
221
+ interpolate(
222
+ index,
223
+ [fullScreenIndex - 1, fullScreenIndex, fullScreenIndex + 1],
224
+ [0, 1, 0],
225
+ ),
190
226
  );
191
227
  }
192
228
  },
193
- [snapPoints],
229
+ [iosModalSheetTypeOfAnimation],
194
230
  );
195
231
 
196
232
  const hideSheet = React.useCallback(
197
- (data?: any, isSheetManagerOrRef?: boolean, dismiss?: boolean) => {
233
+ (
234
+ data?: unknown,
235
+ fromManager?: boolean,
236
+ dismiss?: boolean,
237
+ incomingBehavior?: StackBehavior,
238
+ ) => {
239
+ // Use the freshly-delivered behavior from the event when available.
240
+ // currentStackBehavior comes from React state which may not have flushed
241
+ // yet when this callback fires synchronously from the manager.
242
+ const activeBehavior = incomingBehavior ?? currentStackBehavior;
243
+
198
244
  let value = data ?? valueRef.current;
199
245
 
200
- if (!dismiss || stackBehavior !== "push") {
201
- hardwareBackPressEvent.current?.remove();
246
+ hardwareBackPressEvent.current?.remove();
247
+
248
+ const closeValue = onClose?.(value as never);
249
+ if (closeValue !== undefined) value = closeValue;
250
+
251
+ if (dismiss && activeBehavior === "push") {
252
+ // For push behavior, a "dismiss" event means another sheet wants to
253
+ // appear on top — do not close this sheet.
254
+ if (fromManager) valueRef.current = data;
255
+ return;
256
+ }
257
+
258
+ if (activeBehavior !== "replace" || !dismiss) {
202
259
  bottomSheetRef.current?.close();
203
- const closeValue = onClose?.(value);
204
- if (undefined !== closeValue) value = closeValue;
205
260
  }
206
261
 
207
262
  if (sheetId) {
208
- PrivateManager.remove(sheetId, currentCtx);
209
- if (dismiss && stackBehavior === "push") return;
263
+ const hasHistory = PrivateManager.history.length > 0;
264
+ const shouldRestorePrevious = activeBehavior !== "replace";
210
265
 
211
- const history = PrivateManager.history.length >= 1;
212
266
  eventManager.publish(
213
267
  `onclose_${sheetId}`,
214
268
  value,
215
269
  currentCtx,
216
- history || dismiss,
270
+ hasHistory || !!dismiss,
271
+ activeBehavior,
217
272
  );
218
273
 
219
- if (stackBehavior === "replace") return;
220
- if (dismiss) {
221
- PrivateManager.history.push({ id: sheetId, context: currentCtx });
222
- } else if (history) {
223
- const { id, context } = PrivateManager.history.pop()!;
224
- eventManager.publish(`show_wrap_${id}`, undefined, context, true);
274
+ if (shouldRestorePrevious) {
275
+ if (dismiss) {
276
+ // it will surface naturally when the push sheet is closed.
277
+ if (activeBehavior !== "push") {
278
+ PrivateManager.history.push({
279
+ id: sheetId,
280
+ context: currentCtx,
281
+ behavior: activeBehavior,
282
+ });
283
+ }
284
+ } else if (hasHistory) {
285
+ const otherSheetsStillOpen = PrivateManager.stack().some(
286
+ (s) => !(s.id === sheetId && s.context === currentCtx),
287
+ );
288
+ if (!otherSheetsStillOpen) {
289
+ const prev = PrivateManager.history.pop()!;
290
+ eventManager.publish(
291
+ `show_wrap_${prev.id}`,
292
+ undefined,
293
+ prev.context,
294
+ true,
295
+ prev.behavior,
296
+ );
297
+ }
298
+ }
225
299
  }
300
+
301
+ PrivateManager.remove(sheetId, currentCtx);
226
302
  }
227
- if (isSheetManagerOrRef) valueRef.current = data;
303
+
304
+ if (fromManager) valueRef.current = data;
228
305
  },
229
- [sheetId, currentCtx, onClose],
306
+ [sheetId, currentCtx, onClose, currentStackBehavior],
230
307
  );
308
+
309
+ React.useEffect(() => {
310
+ hideSheetRef.current = hideSheet;
311
+ }, [hideSheet]);
312
+
231
313
  const getInstance = React.useCallback(
232
314
  (): BottomSheetInstance => ({
233
315
  close(options = {}): void {
@@ -252,9 +334,7 @@ const BottomSheetComponent = React.forwardRef<BottomSheetInstance, BottomSheetPr
252
334
 
253
335
  React.useEffect(() => {
254
336
  if (sheetId) {
255
- PrivateManager.registerRef(sheetId, currentCtx, {
256
- current: getInstance(),
257
- } as React.RefObject<BottomSheetInstance>);
337
+ PrivateManager.registerRef(sheetId, currentCtx, { current: getInstance() });
258
338
  }
259
339
  currentSheetRef.current = getInstance();
260
340
  }, [currentCtx, getInstance, sheetId, currentSheetRef]);
@@ -264,7 +344,9 @@ const BottomSheetComponent = React.forwardRef<BottomSheetInstance, BottomSheetPr
264
344
  hardwareBackPressEvent.current = BackHandler.addEventListener(
265
345
  "hardwareBackPress",
266
346
  () => {
267
- bottomSheetRef.current?.close();
347
+ // Go through hideSheet so internal state (PrivateManager, events,
348
+ // history) is updated correctly — not just the visual sheet.
349
+ hideSheetRef.current(undefined, true, false);
268
350
  return true;
269
351
  },
270
352
  );
@@ -275,44 +357,43 @@ const BottomSheetComponent = React.forwardRef<BottomSheetInstance, BottomSheetPr
275
357
 
276
358
  React.useImperativeHandle(ref, getInstance, [getInstance]);
277
359
 
360
+ const zIndex = React.useMemo(
361
+ () => (isPushed && sheetId ? PrivateManager.zIndex(sheetId, currentCtx) : 0),
362
+ [sheetId, isPushed, currentCtx],
363
+ );
364
+
365
+ const backdropOpacity = React.useMemo(() => {
366
+ if (isPushed && stackContext.previousSheetId) {
367
+ return (opacity || 0.45) * 0.6;
368
+ }
369
+ return opacity || 0.45;
370
+ }, [isPushed, stackContext.previousSheetId, opacity]);
371
+
278
372
  return (
279
- <View
280
- pointerEvents="box-none"
281
- style={[
282
- StyleSheet.absoluteFill,
283
- {
284
- zIndex:
285
- sheetId && stackBehavior === "push"
286
- ? PrivateManager.zIndex(sheetId, currentCtx)
287
- : 0,
288
- },
289
- ]}
290
- >
373
+ <View pointerEvents="box-none" style={[StyleSheet.absoluteFill, { zIndex }]}>
291
374
  <RNBottomSheet
292
375
  enableDynamicSizing={enableDynamicSizing}
293
- animationConfigs={{ duration: 400, easing: Easing.bezier(0.25, 0.1, 0.25, 1) }}
294
- backdropComponent={(props) => (
376
+ backdropComponent={(backdropProps) => (
295
377
  <BottomSheetBackdrop
296
- enableTouchThrough={!!clickThrough}
297
- opacity={opacity || 0.45}
378
+ enableTouchThrough={!!passThrough}
379
+ opacity={backdropOpacity}
298
380
  disappearsOnIndex={-1}
299
381
  appearsOnIndex={0}
300
- {...props}
382
+ {...backdropProps}
301
383
  />
302
384
  )}
385
+ topInset={0}
386
+ bottomInset={0}
303
387
  {...props}
304
388
  ref={bottomSheetRef}
305
389
  onClose={hideSheet}
390
+ onAnimate={onAnimate}
306
391
  animatedIndex={animatedIndex}
307
- topInset={
308
- fullScreen
309
- ? 0
310
- : top + (iosModalSheetTypeOfAnimation || pageAnimation ? 20 : 5)
311
- }
312
- snapPoints={enableDynamicSizing ? undefined : (snapPoints ?? ["66%"])}
313
- handleIndicatorStyle={[themeHandleIndicatorStyle, handleIndicatorStyle]}
314
- backgroundStyle={[themeBackgroundStyle, backgroundStyle]}
315
- handleStyle={[themeBackgroundStyle, handleStyle]}
392
+ style={[defaultStyle, style]}
393
+ snapPoints={enableDynamicSizing ? undefined : snapPoints}
394
+ handleIndicatorStyle={handleIndicatorStyle}
395
+ backgroundStyle={backgroundStyle}
396
+ handleStyle={handleStyle}
316
397
  >
317
398
  {children}
318
399
  </RNBottomSheet>
@@ -335,17 +416,4 @@ BottomSheet.FooterContainer = BottomSheetFooterContainer;
335
416
  BottomSheet.Backdrop = BottomSheetBackdrop;
336
417
  BottomSheet.TextInput = BottomSheetTextInput;
337
418
 
338
- try {
339
- const { cssInterop } = require("nativewind");
340
- cssInterop(BottomSheet, {
341
- handleIndicatorClassName: "handleIndicatorStyle",
342
- backgroundClassName: "backgroundStyle",
343
- containerClassName: "containerStyle",
344
- handleClassName: "handleStyle",
345
- className: "style",
346
- });
347
- } catch (e) {
348
- // Ignore error
349
- }
350
-
351
419
  export default BottomSheet;
package/src/types.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { BottomSheetProps as RNBottomSheetProps } from "@gorhom/bottom-sheet";
2
2
  import type { WithSpringConfig, WithTimingConfig } from "react-native-reanimated";
3
+ import { StatusBarStyle } from "react-native";
3
4
  import React from "react";
4
5
 
5
6
  export interface Sheets {}
@@ -21,6 +22,37 @@ export interface SheetDefinition<Payload = never, ReturnValue = never> {
21
22
  returnValue: ReturnValue;
22
23
  }
23
24
 
25
+ /**
26
+ * Defines how sheets behave when a new sheet is opened.
27
+ *
28
+ * - `switch`: Closes current sheet and shows new one. Previous sheet is
29
+ * restored when new one closes.
30
+ *
31
+ * - `replace`: Closes current sheet and opens new one. Previous sheet is
32
+ * removed from stack (not restored on close).
33
+ *
34
+ * - `push`: Stacks new sheet on top. Previous sheet remains visible underneath.
35
+ */
36
+ export type StackBehavior = "switch" | "replace" | "push";
37
+
38
+ export type SheetProviderProps = React.PropsWithChildren<{
39
+ context?: string;
40
+ /** @default default */
41
+ statusBar?: StatusBarStyle;
42
+ scaleConfig?: {
43
+ /** Scale factor when sheet is open (default: 0.92) */
44
+ scale?: number;
45
+ /** Vertical translation when sheet is open (default: 5) */
46
+ translateY?: number;
47
+ /** Border radius when sheet is open (default: 24) */
48
+ borderRadius?: number;
49
+ /** Animation config - timing or spring (default: timing with 300ms duration) */
50
+ animation?:
51
+ | { type: "timing"; config?: WithTimingConfig }
52
+ | { type: "spring"; config?: WithSpringConfig };
53
+ };
54
+ }>;
55
+
24
56
  export interface BottomSheetInstance<Id extends SheetIds = SheetIds> {
25
57
  /**
26
58
  * Close the bottom sheet.
@@ -81,14 +113,14 @@ export interface BottomSheetInstance<Id extends SheetIds = SheetIds> {
81
113
  ) => void;
82
114
  }
83
115
 
84
- export type BottomSheetProps = Omit<
116
+ export type BottomSheetProps<Id extends SheetIds = SheetIds> = Omit<
85
117
  RNBottomSheetProps,
86
- "children" | "onClose" | "animatedIndex" | "topInset"
118
+ "children" | "onClose"
87
119
  > & {
88
120
  /**
89
121
  * ID of the `BottomSheet`.
90
122
  */
91
- id?: SheetID<SheetIds>;
123
+ id?: SheetID<Id>;
92
124
 
93
125
  /**
94
126
  * Content of the `BottomSheet`.
@@ -102,25 +134,25 @@ export type BottomSheetProps = Omit<
102
134
  hardwareBackPressToClose?: boolean;
103
135
 
104
136
  /**
105
- * Callback when the sheet close.
137
+ * Callback when the sheet closes. Return a value to override the close data
138
+ * forwarded to `SheetManager.show()` callers.
106
139
  *
107
- * @type () => any;
108
- * @returns The data returned by the sheet to be returned when closed.
140
+ * @returns Optionally return a new value to pass back to the caller.
109
141
  */
110
- onClose?: (data?: any) => any;
142
+ onClose?: (data?: SheetReturnValue<Id>) => SheetReturnValue<Id> | void;
111
143
 
112
144
  /**
113
145
  * Event called before sheets is visible.
114
146
  * @param data Payload of the sheet if any.
115
147
  * @type () => void;
116
148
  */
117
- onBeforeShow?: (data?: any) => void;
149
+ onBeforeShow?: (data?: SheetPayload<Id>) => void;
118
150
 
119
151
  /**
120
152
  * Can click through the sheet to the underlying view.
121
153
  * @default false
122
154
  */
123
- clickThrough?: boolean;
155
+ passThrough?: boolean;
124
156
 
125
157
  /**
126
158
  * Opacity of the sheet's overlay.
@@ -129,26 +161,21 @@ export type BottomSheetProps = Omit<
129
161
  opacity?: number;
130
162
 
131
163
  /**
132
- * Defines the stack behavior when modal mounts. (experimental)
164
+ * Defines the stack behavior when sheets are opened.
165
+ *
166
+ * - `switch`: (default) Dismisses the current sheet before showing the new one.
167
+ * - `replace`: Swaps the current sheet's content with smooth crossfade animation.
168
+ * - `push`: Pushes new sheet on top, creating a navigable stack.
169
+ *
133
170
  * @default "switch"
134
171
  */
135
- stackBehavior?: "push" | "replace" | "switch";
136
-
137
- /**
138
- * Whether the bottom sheet edge to edge.
139
- * @default false
140
- */
141
- fullScreen?: boolean;
172
+ stackBehavior?: StackBehavior;
142
173
 
143
174
  /**
144
- * Whether the bottom sheet is an iOS modal sheet type of animation.
175
+ * Whether the bottom sheet is an iOS 18 modal sheet type of animation.
176
+ * When enabled at snap point 90%, the content behind the sheet scales down and gets a
177
+ * border radius, similar to iOS 18 system sheets.
145
178
  * @default false
146
179
  */
147
180
  iosModalSheetTypeOfAnimation?: boolean;
148
-
149
- className?: string;
150
- handleIndicatorClassName?: string;
151
- backgroundClassName?: string;
152
- containerClassName?: string;
153
- handleClassName?: string;
154
181
  };