react-native-bottom-sheet-stack 1.9.1 → 1.10.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 (85) hide show
  1. package/README.md +22 -8
  2. package/lib/commonjs/BottomSheetBackdrop.js +15 -17
  3. package/lib/commonjs/BottomSheetBackdrop.js.map +1 -1
  4. package/lib/commonjs/QueueItem.js +2 -0
  5. package/lib/commonjs/QueueItem.js.map +1 -1
  6. package/lib/commonjs/adapters/actions-sheet/ActionsSheetAdapter.js +18 -18
  7. package/lib/commonjs/adapters/actions-sheet/ActionsSheetAdapter.js.map +1 -1
  8. package/lib/commonjs/adapters/gorhom-sheet/GorhomSheetAdapter.js +14 -22
  9. package/lib/commonjs/adapters/gorhom-sheet/GorhomSheetAdapter.js.map +1 -1
  10. package/lib/commonjs/adapters/gorhom-sheet/index.js +6 -0
  11. package/lib/commonjs/adapters/gorhom-sheet/index.js.map +1 -1
  12. package/lib/commonjs/adapters/react-native-modal/ReactNativeModalAdapter.js +15 -17
  13. package/lib/commonjs/adapters/react-native-modal/ReactNativeModalAdapter.js.map +1 -1
  14. package/lib/commonjs/bottomSheetCoordinator.js +103 -0
  15. package/lib/commonjs/bottomSheetCoordinator.js.map +1 -1
  16. package/lib/commonjs/index.js +38 -34
  17. package/lib/commonjs/index.js.map +1 -1
  18. package/lib/commonjs/onBeforeCloseRegistry.js +42 -0
  19. package/lib/commonjs/onBeforeCloseRegistry.js.map +1 -0
  20. package/lib/commonjs/store/hooks.js +24 -4
  21. package/lib/commonjs/store/hooks.js.map +1 -1
  22. package/lib/commonjs/store/store.js +8 -0
  23. package/lib/commonjs/store/store.js.map +1 -1
  24. package/lib/commonjs/useBottomSheetContext.js +31 -17
  25. package/lib/commonjs/useBottomSheetContext.js.map +1 -1
  26. package/lib/commonjs/useBottomSheetControl.js +46 -33
  27. package/lib/commonjs/useBottomSheetControl.js.map +1 -1
  28. package/lib/commonjs/useBottomSheetManager.js +16 -10
  29. package/lib/commonjs/useBottomSheetManager.js.map +1 -1
  30. package/lib/commonjs/useOnBeforeClose.js +107 -0
  31. package/lib/commonjs/useOnBeforeClose.js.map +1 -0
  32. package/lib/typescript/example/src/screens/HomeScreen.d.ts.map +1 -1
  33. package/lib/typescript/example/src/sheets/CloseInterceptionSheets.d.ts +14 -0
  34. package/lib/typescript/example/src/sheets/CloseInterceptionSheets.d.ts.map +1 -0
  35. package/lib/typescript/example/src/sheets/index.d.ts +1 -0
  36. package/lib/typescript/example/src/sheets/index.d.ts.map +1 -1
  37. package/lib/typescript/src/BottomSheetBackdrop.d.ts.map +1 -1
  38. package/lib/typescript/src/QueueItem.d.ts.map +1 -1
  39. package/lib/typescript/src/adapters/actions-sheet/ActionsSheetAdapter.d.ts.map +1 -1
  40. package/lib/typescript/src/adapters/gorhom-sheet/GorhomSheetAdapter.d.ts +1 -1
  41. package/lib/typescript/src/adapters/gorhom-sheet/GorhomSheetAdapter.d.ts.map +1 -1
  42. package/lib/typescript/src/adapters/gorhom-sheet/index.d.ts +4 -0
  43. package/lib/typescript/src/adapters/gorhom-sheet/index.d.ts.map +1 -1
  44. package/lib/typescript/src/adapters/react-native-modal/ReactNativeModalAdapter.d.ts.map +1 -1
  45. package/lib/typescript/src/bottomSheetCoordinator.d.ts +25 -0
  46. package/lib/typescript/src/bottomSheetCoordinator.d.ts.map +1 -1
  47. package/lib/typescript/src/index.d.ts +7 -9
  48. package/lib/typescript/src/index.d.ts.map +1 -1
  49. package/lib/typescript/src/onBeforeCloseRegistry.d.ts +26 -0
  50. package/lib/typescript/src/onBeforeCloseRegistry.d.ts.map +1 -0
  51. package/lib/typescript/src/store/hooks.d.ts +2 -0
  52. package/lib/typescript/src/store/hooks.d.ts.map +1 -1
  53. package/lib/typescript/src/store/store.d.ts.map +1 -1
  54. package/lib/typescript/src/store/types.d.ts +8 -0
  55. package/lib/typescript/src/store/types.d.ts.map +1 -1
  56. package/lib/typescript/src/useBottomSheetContext.d.ts +5 -0
  57. package/lib/typescript/src/useBottomSheetContext.d.ts.map +1 -1
  58. package/lib/typescript/src/useBottomSheetControl.d.ts +2 -0
  59. package/lib/typescript/src/useBottomSheetControl.d.ts.map +1 -1
  60. package/lib/typescript/src/useBottomSheetManager.d.ts +5 -0
  61. package/lib/typescript/src/useBottomSheetManager.d.ts.map +1 -1
  62. package/lib/typescript/src/useOnBeforeClose.d.ts +65 -0
  63. package/lib/typescript/src/useOnBeforeClose.d.ts.map +1 -0
  64. package/package.json +19 -1
  65. package/src/BottomSheetBackdrop.tsx +2 -3
  66. package/src/QueueItem.tsx +2 -0
  67. package/src/adapters/actions-sheet/ActionsSheetAdapter.tsx +6 -15
  68. package/src/adapters/gorhom-sheet/GorhomSheetAdapter.tsx +7 -18
  69. package/src/adapters/gorhom-sheet/index.ts +5 -0
  70. package/src/adapters/react-native-modal/ReactNativeModalAdapter.tsx +5 -14
  71. package/src/bottomSheetCoordinator.ts +128 -0
  72. package/src/index.tsx +17 -18
  73. package/src/onBeforeCloseRegistry.ts +44 -0
  74. package/src/store/hooks.ts +8 -0
  75. package/src/store/store.ts +10 -0
  76. package/src/store/types.ts +8 -0
  77. package/src/useBottomSheetContext.ts +11 -1
  78. package/src/useBottomSheetControl.ts +11 -8
  79. package/src/useBottomSheetManager.tsx +14 -8
  80. package/src/useOnBeforeClose.ts +92 -0
  81. package/lib/commonjs/BottomSheetManaged.js +0 -13
  82. package/lib/commonjs/BottomSheetManaged.js.map +0 -1
  83. package/lib/typescript/src/BottomSheetManaged.d.ts +0 -11
  84. package/lib/typescript/src/BottomSheetManaged.d.ts.map +0 -1
  85. package/src/BottomSheetManaged.tsx +0 -14
@@ -22,6 +22,11 @@ export const useSheetKeepMounted = (id: string) =>
22
22
  useBottomSheetStore((state) => state.sheetsById[id]?.keepMounted, shallow);
23
23
  export const useSheetPortalSession = (id: string) =>
24
24
  useBottomSheetStore((state) => state.sheetsById[id]?.portalSession, shallow);
25
+ export const useSheetPreventDismiss = (id: string) =>
26
+ useBottomSheetStore(
27
+ (state) => state.sheetsById[id]?.preventDismiss ?? false,
28
+ shallow
29
+ );
25
30
 
26
31
  export const useSheetExists = (id: string) =>
27
32
  useBottomSheetStore((state) => !!state.sheetsById[id], shallow);
@@ -70,6 +75,9 @@ export const useUpdateParams = () =>
70
75
  export const useClearGroup = () =>
71
76
  useBottomSheetStore((state) => state.clearGroup);
72
77
 
78
+ export const useSetPreventDismiss = () =>
79
+ useBottomSheetStore((state) => state.setPreventDismiss);
80
+
73
81
  export const useMount = () => useBottomSheetStore((state) => state.mount);
74
82
 
75
83
  export const useUnmount = () => useBottomSheetStore((state) => state.unmount);
@@ -130,6 +130,16 @@ export const useBottomSheetStore = create(
130
130
  return { sheetsById: updateSheet(state.sheetsById, id, { params }) };
131
131
  }),
132
132
 
133
+ setPreventDismiss: (id, prevent) =>
134
+ set((state) => {
135
+ if (!state.sheetsById[id]) return state;
136
+ return {
137
+ sheetsById: updateSheet(state.sheetsById, id, {
138
+ preventDismiss: prevent,
139
+ }),
140
+ };
141
+ }),
142
+
133
143
  clearGroup: (groupId) =>
134
144
  set((state) => {
135
145
  const idsToRemove = new Set(
@@ -18,6 +18,13 @@ export interface BottomSheetState {
18
18
  * react-native-teleport connection issues after replace flows.
19
19
  */
20
20
  portalSession?: number;
21
+ /**
22
+ * When true, the adapter should block user-initiated dismiss gestures
23
+ * (swipe down, backdrop tap). Set by `useOnBeforeClose` to ensure the
24
+ * interceptor runs before closing. Programmatic close via `forceClose()`
25
+ * bypasses this.
26
+ */
27
+ preventDismiss?: boolean;
21
28
  }
22
29
 
23
30
  export type TriggerState = Omit<BottomSheetState, 'status'>;
@@ -33,6 +40,7 @@ export interface BottomSheetStoreActions {
33
40
  startClosing(id: string): void;
34
41
  finishClosing(id: string): void;
35
42
  updateParams(id: string, params: Record<string, unknown> | undefined): void;
43
+ setPreventDismiss(id: string, prevent: boolean): void;
36
44
  clearGroup(groupId: string): void;
37
45
  clearAll(): void;
38
46
  mount(sheet: Omit<BottomSheetState, 'status'>): void;
@@ -1,5 +1,6 @@
1
1
  import { useMaybeBottomSheetContext } from './BottomSheet.context';
2
2
  import { useSheetParams, useStartClosing } from './bottomSheet.store';
3
+ import { requestClose } from './bottomSheetCoordinator';
3
4
  import type {
4
5
  BottomSheetPortalId,
5
6
  BottomSheetPortalParams,
@@ -9,6 +10,11 @@ export interface UseBottomSheetContextReturn<TParams> {
9
10
  id: string;
10
11
  params: TParams;
11
12
  close: () => void;
13
+ /**
14
+ * Close the sheet, bypassing any onBeforeClose interceptor.
15
+ * Useful for force-closing from within onBeforeClose confirmation flows.
16
+ */
17
+ forceClose: () => void;
12
18
  /** @deprecated Use `close` instead */
13
19
  closeBottomSheet: () => void;
14
20
  }
@@ -32,12 +38,16 @@ export function useBottomSheetContext<
32
38
  );
33
39
  }
34
40
 
35
- const close = () => startClosing(context.id);
41
+ const close = () => {
42
+ requestClose(context.id);
43
+ };
44
+ const forceClose = () => startClosing(context.id);
36
45
 
37
46
  return {
38
47
  id: context.id,
39
48
  params: params as BottomSheetPortalParams<T>,
40
49
  close,
50
+ forceClose,
41
51
  closeBottomSheet: close,
42
52
  };
43
53
  }
@@ -1,19 +1,16 @@
1
1
  import React from 'react';
2
2
  import type { SheetAdapterRef } from './adapter.types';
3
3
 
4
- import {
5
- useOpen,
6
- useStartClosing,
7
- useUpdateParams,
8
- type OpenMode,
9
- } from './bottomSheet.store';
4
+ import { useOpen, useUpdateParams, type OpenMode } from './bottomSheet.store';
10
5
  import { useMaybeBottomSheetManagerContext } from './BottomSheetManager.provider';
6
+ import { closeAllAnimated, requestClose } from './bottomSheetCoordinator';
11
7
  import type {
12
8
  BottomSheetPortalId,
13
9
  BottomSheetPortalParams,
14
10
  HasParams,
15
11
  } from './portal.types';
16
12
  import { getSheetRef, setSheetRef } from './refsMap';
13
+ import type { CloseAllOptions } from './useBottomSheetManager';
17
14
 
18
15
  interface BaseOpenOptions<TParams> {
19
16
  mode?: OpenMode;
@@ -37,6 +34,7 @@ type OpenFunction<T extends BottomSheetPortalId> =
37
34
  export interface UseBottomSheetControlReturn<T extends BottomSheetPortalId> {
38
35
  open: OpenFunction<T>;
39
36
  close: () => void;
37
+ closeAll: (options?: CloseAllOptions) => Promise<void>;
40
38
  updateParams: (params: BottomSheetPortalParams<T>) => void;
41
39
  resetParams: () => void;
42
40
  }
@@ -47,7 +45,6 @@ export function useBottomSheetControl<T extends BottomSheetPortalId>(
47
45
  const bottomSheetManagerContext = useMaybeBottomSheetManagerContext();
48
46
 
49
47
  const storeOpen = useOpen();
50
- const startClosing = useStartClosing();
51
48
  const storeUpdateParams = useUpdateParams();
52
49
 
53
50
  const open = (options?: OpenOptions<T>) => {
@@ -74,7 +71,12 @@ export function useBottomSheetControl<T extends BottomSheetPortalId>(
74
71
  };
75
72
 
76
73
  const close = () => {
77
- startClosing(id);
74
+ requestClose(id);
75
+ };
76
+
77
+ const closeAll = (options?: CloseAllOptions) => {
78
+ const groupId = bottomSheetManagerContext?.groupId || 'default';
79
+ return closeAllAnimated(groupId, options);
78
80
  };
79
81
 
80
82
  const updateParams = (params: BottomSheetPortalParams<T>) => {
@@ -88,6 +90,7 @@ export function useBottomSheetControl<T extends BottomSheetPortalId>(
88
90
  return {
89
91
  open: open as OpenFunction<T>,
90
92
  close,
93
+ closeAll,
91
94
  updateParams,
92
95
  resetParams,
93
96
  };
@@ -1,20 +1,20 @@
1
1
  import React from 'react';
2
2
 
3
- import {
4
- useOpen,
5
- useStartClosing,
6
- useClearGroup,
7
- type OpenMode,
8
- } from './bottomSheet.store';
3
+ import { useOpen, useClearGroup, type OpenMode } from './bottomSheet.store';
9
4
  import { useMaybeBottomSheetManagerContext } from './BottomSheetManager.provider';
10
5
  import type { SheetAdapterRef } from './adapter.types';
6
+ import { closeAllAnimated, requestClose } from './bottomSheetCoordinator';
11
7
  import { setSheetRef } from './refsMap';
12
8
 
9
+ export interface CloseAllOptions {
10
+ /** Delay in ms between each cascading close animation. Default: 100 */
11
+ stagger?: number;
12
+ }
13
+
13
14
  export const useBottomSheetManager = () => {
14
15
  const bottomSheetManagerContext = useMaybeBottomSheetManagerContext();
15
16
 
16
17
  const storeOpen = useOpen();
17
- const startClosing = useStartClosing();
18
18
  const storeClearGroup = useClearGroup();
19
19
 
20
20
  const openBottomSheet = (
@@ -52,7 +52,12 @@ export const useBottomSheetManager = () => {
52
52
  };
53
53
 
54
54
  const close = (id: string) => {
55
- startClosing(id);
55
+ requestClose(id);
56
+ };
57
+
58
+ const closeAll = (options?: CloseAllOptions) => {
59
+ const groupId = bottomSheetManagerContext?.groupId || 'default';
60
+ return closeAllAnimated(groupId, options);
56
61
  };
57
62
 
58
63
  const clear = () => {
@@ -63,6 +68,7 @@ export const useBottomSheetManager = () => {
63
68
  return {
64
69
  open: openBottomSheet,
65
70
  close,
71
+ closeAll,
66
72
  clear,
67
73
  /** @deprecated Use `open` instead */
68
74
  openBottomSheet,
@@ -0,0 +1,92 @@
1
+ import { useEffect } from 'react';
2
+
3
+ import { useMaybeBottomSheetContext } from './BottomSheet.context';
4
+ import { useSetPreventDismiss } from './bottomSheet.store';
5
+ import type { OnBeforeCloseCallback } from './onBeforeCloseRegistry';
6
+ import { removeOnBeforeClose, setOnBeforeClose } from './onBeforeCloseRegistry';
7
+ import { useEvent } from './useEvent';
8
+
9
+ /**
10
+ * Registers an interceptor that is called before the sheet closes.
11
+ *
12
+ * When active, this hook:
13
+ * 1. Sets `preventDismiss` on the sheet so adapters block user-initiated
14
+ * gestures (swipe down, pan-to-close) at the native level.
15
+ * 2. Intercepts all programmatic close paths (backdrop tap, back button,
16
+ * `close()`, `closeAll()`) and calls the callback first.
17
+ *
18
+ * The interceptor receives `onConfirm` and `onCancel` callbacks. Call these
19
+ * when the user makes a decision. This works seamlessly with `Alert.alert`:
20
+ *
21
+ * ```tsx
22
+ * useOnBeforeClose(({ onConfirm, onCancel }) => {
23
+ * if (dirty) {
24
+ * Alert.alert('Discard changes?', '', [
25
+ * { text: 'Cancel', onPress: onCancel },
26
+ * { text: 'Discard', onPress: onConfirm },
27
+ * ]);
28
+ * } else {
29
+ * onConfirm(); // Allow close immediately
30
+ * }
31
+ * });
32
+ * ```
33
+ *
34
+ * For backward compatibility, you can still return `boolean` or `Promise<boolean>`:
35
+ * - Return `false` (or resolve to `false`) to prevent closing
36
+ * - Return `true` (or resolve to `true`) to allow closing
37
+ *
38
+ * Use `forceClose()` from `useBottomSheetContext` to bypass the interceptor entirely.
39
+ *
40
+ * Must be used inside a sheet component (within BottomSheetContext).
41
+ *
42
+ * @example Callback pattern (recommended)
43
+ * ```tsx
44
+ * function MySheet() {
45
+ * const [dirty, setDirty] = useState(false);
46
+ *
47
+ * useOnBeforeClose(({ onConfirm, onCancel }) => {
48
+ * if (dirty) {
49
+ * Alert.alert('Discard changes?', '', [
50
+ * { text: 'Cancel', style: 'cancel', onPress: onCancel },
51
+ * { text: 'Discard', onPress: onConfirm },
52
+ * ]);
53
+ * } else {
54
+ * onConfirm();
55
+ * }
56
+ * });
57
+ * }
58
+ * ```
59
+ *
60
+ * @example Boolean return (backward compatible)
61
+ * ```tsx
62
+ * function MySheet() {
63
+ * const [dirty, setDirty] = useState(false);
64
+ *
65
+ * useOnBeforeClose(() => {
66
+ * return !dirty; // false blocks, true allows
67
+ * });
68
+ * }
69
+ * ```
70
+ */
71
+ export function useOnBeforeClose(callback: OnBeforeCloseCallback): void {
72
+ const context = useMaybeBottomSheetContext();
73
+ const setPreventDismiss = useSetPreventDismiss();
74
+
75
+ if (!context?.id) {
76
+ throw new Error(
77
+ 'useOnBeforeClose must be used within a BottomSheet component'
78
+ );
79
+ }
80
+
81
+ const id = context.id;
82
+ const stableCallback = useEvent(callback);
83
+
84
+ useEffect(() => {
85
+ setOnBeforeClose(id, stableCallback);
86
+ setPreventDismiss(id, true);
87
+ return () => {
88
+ removeOnBeforeClose(id);
89
+ setPreventDismiss(id, false);
90
+ };
91
+ }, [id, stableCallback, setPreventDismiss]);
92
+ }
@@ -1,13 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- Object.defineProperty(exports, "BottomSheetManaged", {
7
- enumerable: true,
8
- get: function () {
9
- return _gorhomSheet.GorhomSheetAdapter;
10
- }
11
- });
12
- var _gorhomSheet = require("./adapters/gorhom-sheet");
13
- //# sourceMappingURL=BottomSheetManaged.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["_gorhomSheet","require"],"sourceRoot":"../../src","sources":["BottomSheetManaged.tsx"],"mappings":";;;;;;;;;;;AAQA,IAAAA,YAAA,GAAAC,OAAA","ignoreList":[]}
@@ -1,11 +0,0 @@
1
- /**
2
- * Backward-compatible re-export of GorhomSheetAdapter.
3
- *
4
- * Existing users can continue importing `BottomSheetManaged` and `BottomSheetRef`
5
- * without any changes. For new code, prefer importing `GorhomSheetAdapter` directly
6
- * from 'react-native-bottom-sheet-stack/adapters/gorhom' or use the `ModalAdapter`
7
- * for modal-based sheets.
8
- */
9
- export { GorhomSheetAdapter as BottomSheetManaged, type GorhomSheetAdapterProps as BottomSheetManagedProps, } from './adapters/gorhom-sheet';
10
- export type { SheetAdapterRef as BottomSheetRef } from './adapter.types';
11
- //# sourceMappingURL=BottomSheetManaged.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"BottomSheetManaged.d.ts","sourceRoot":"","sources":["../../../src/BottomSheetManaged.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EACL,kBAAkB,IAAI,kBAAkB,EACxC,KAAK,uBAAuB,IAAI,uBAAuB,GACxD,MAAM,yBAAyB,CAAC;AAEjC,YAAY,EAAE,eAAe,IAAI,cAAc,EAAE,MAAM,iBAAiB,CAAC"}
@@ -1,14 +0,0 @@
1
- /**
2
- * Backward-compatible re-export of GorhomSheetAdapter.
3
- *
4
- * Existing users can continue importing `BottomSheetManaged` and `BottomSheetRef`
5
- * without any changes. For new code, prefer importing `GorhomSheetAdapter` directly
6
- * from 'react-native-bottom-sheet-stack/adapters/gorhom' or use the `ModalAdapter`
7
- * for modal-based sheets.
8
- */
9
- export {
10
- GorhomSheetAdapter as BottomSheetManaged,
11
- type GorhomSheetAdapterProps as BottomSheetManagedProps,
12
- } from './adapters/gorhom-sheet';
13
-
14
- export type { SheetAdapterRef as BottomSheetRef } from './adapter.types';