react-native-bottom-sheet-stack 1.6.0 → 1.7.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 (135) hide show
  1. package/README.md +9 -7
  2. package/lib/commonjs/BottomSheet.context.js +1 -2
  3. package/lib/commonjs/BottomSheet.context.js.map +1 -1
  4. package/lib/commonjs/BottomSheetBackdrop.js +23 -32
  5. package/lib/commonjs/BottomSheetBackdrop.js.map +1 -1
  6. package/lib/commonjs/BottomSheetHost.js +17 -254
  7. package/lib/commonjs/BottomSheetHost.js.map +1 -1
  8. package/lib/commonjs/BottomSheetManaged.js +87 -54
  9. package/lib/commonjs/BottomSheetManaged.js.map +1 -1
  10. package/lib/commonjs/BottomSheetPersistent.js +113 -0
  11. package/lib/commonjs/BottomSheetPersistent.js.map +1 -0
  12. package/lib/commonjs/BottomSheetPortal.js +4 -3
  13. package/lib/commonjs/BottomSheetPortal.js.map +1 -1
  14. package/lib/commonjs/BottomSheetRef.context.js +17 -0
  15. package/lib/commonjs/BottomSheetRef.context.js.map +1 -0
  16. package/lib/commonjs/QueueItem.js +167 -0
  17. package/lib/commonjs/QueueItem.js.map +1 -0
  18. package/lib/commonjs/animatedRegistry.js +9 -0
  19. package/lib/commonjs/animatedRegistry.js.map +1 -1
  20. package/lib/commonjs/bottomSheet.store.js +11 -133
  21. package/lib/commonjs/bottomSheet.store.js.map +1 -1
  22. package/lib/commonjs/bottomSheetCoordinator.js +9 -10
  23. package/lib/commonjs/bottomSheetCoordinator.js.map +1 -1
  24. package/lib/commonjs/index.js +28 -0
  25. package/lib/commonjs/index.js.map +1 -1
  26. package/lib/commonjs/portalSessionRegistry.js +32 -0
  27. package/lib/commonjs/portalSessionRegistry.js.map +1 -0
  28. package/lib/commonjs/refsMap.js +9 -0
  29. package/lib/commonjs/refsMap.js.map +1 -1
  30. package/lib/commonjs/store/helpers.js +59 -0
  31. package/lib/commonjs/store/helpers.js.map +1 -0
  32. package/lib/commonjs/store/hooks.js +176 -0
  33. package/lib/commonjs/store/hooks.js.map +1 -0
  34. package/lib/commonjs/store/index.js +52 -0
  35. package/lib/commonjs/store/index.js.map +1 -0
  36. package/lib/commonjs/store/store.js +140 -0
  37. package/lib/commonjs/store/store.js.map +1 -0
  38. package/lib/commonjs/store/types.js +6 -0
  39. package/lib/commonjs/store/types.js.map +1 -0
  40. package/lib/commonjs/useBottomSheetContext.js +24 -42
  41. package/lib/commonjs/useBottomSheetContext.js.map +1 -1
  42. package/lib/commonjs/useBottomSheetControl.js +8 -14
  43. package/lib/commonjs/useBottomSheetControl.js.map +1 -1
  44. package/lib/commonjs/useBottomSheetManager.js +3 -13
  45. package/lib/commonjs/useBottomSheetManager.js.map +1 -1
  46. package/lib/commonjs/useBottomSheetStatus.js +9 -17
  47. package/lib/commonjs/useBottomSheetStatus.js.map +1 -1
  48. package/lib/commonjs/useEvent.js +39 -0
  49. package/lib/commonjs/useEvent.js.map +1 -0
  50. package/lib/commonjs/useScaleAnimation.js +53 -30
  51. package/lib/commonjs/useScaleAnimation.js.map +1 -1
  52. package/lib/commonjs/useSheetRenderData.js +62 -0
  53. package/lib/commonjs/useSheetRenderData.js.map +1 -0
  54. package/lib/typescript/example/src/App.d.ts.map +1 -1
  55. package/lib/typescript/example/src/screens/HomeScreen.d.ts.map +1 -1
  56. package/lib/typescript/example/src/sheets/NavigationSheets.d.ts.map +1 -1
  57. package/lib/typescript/example/src/sheets/ScannerNestedSheets.d.ts +4 -0
  58. package/lib/typescript/example/src/sheets/ScannerNestedSheets.d.ts.map +1 -0
  59. package/lib/typescript/example/src/sheets/ScannerSheet.d.ts +3 -0
  60. package/lib/typescript/example/src/sheets/ScannerSheet.d.ts.map +1 -0
  61. package/lib/typescript/example/src/sheets/index.d.ts +1 -0
  62. package/lib/typescript/example/src/sheets/index.d.ts.map +1 -1
  63. package/lib/typescript/src/BottomSheet.context.d.ts.map +1 -1
  64. package/lib/typescript/src/BottomSheetBackdrop.d.ts +0 -5
  65. package/lib/typescript/src/BottomSheetBackdrop.d.ts.map +1 -1
  66. package/lib/typescript/src/BottomSheetHost.d.ts +1 -3
  67. package/lib/typescript/src/BottomSheetHost.d.ts.map +1 -1
  68. package/lib/typescript/src/BottomSheetManaged.d.ts.map +1 -1
  69. package/lib/typescript/src/BottomSheetPersistent.d.ts +9 -0
  70. package/lib/typescript/src/BottomSheetPersistent.d.ts.map +1 -0
  71. package/lib/typescript/src/BottomSheetPortal.d.ts.map +1 -1
  72. package/lib/typescript/src/BottomSheetRef.context.d.ts +11 -0
  73. package/lib/typescript/src/BottomSheetRef.context.d.ts.map +1 -0
  74. package/lib/typescript/src/QueueItem.d.ts +8 -0
  75. package/lib/typescript/src/QueueItem.d.ts.map +1 -0
  76. package/lib/typescript/src/animatedRegistry.d.ts +5 -0
  77. package/lib/typescript/src/animatedRegistry.d.ts.map +1 -1
  78. package/lib/typescript/src/bottomSheet.store.d.ts +1 -37
  79. package/lib/typescript/src/bottomSheet.store.d.ts.map +1 -1
  80. package/lib/typescript/src/bottomSheetCoordinator.d.ts +2 -1
  81. package/lib/typescript/src/bottomSheetCoordinator.d.ts.map +1 -1
  82. package/lib/typescript/src/index.d.ts +5 -1
  83. package/lib/typescript/src/index.d.ts.map +1 -1
  84. package/lib/typescript/src/portalSessionRegistry.d.ts +8 -0
  85. package/lib/typescript/src/portalSessionRegistry.d.ts.map +1 -0
  86. package/lib/typescript/src/refsMap.d.ts +5 -0
  87. package/lib/typescript/src/refsMap.d.ts.map +1 -1
  88. package/lib/typescript/src/store/helpers.d.ts +11 -0
  89. package/lib/typescript/src/store/helpers.d.ts.map +1 -0
  90. package/lib/typescript/src/store/hooks.d.ts +16 -0
  91. package/lib/typescript/src/store/hooks.d.ts.map +1 -0
  92. package/lib/typescript/src/store/index.d.ts +5 -0
  93. package/lib/typescript/src/store/index.d.ts.map +1 -0
  94. package/lib/typescript/src/store/store.d.ts +11 -0
  95. package/lib/typescript/src/store/store.d.ts.map +1 -0
  96. package/lib/typescript/src/store/types.d.ts +37 -0
  97. package/lib/typescript/src/store/types.d.ts.map +1 -0
  98. package/lib/typescript/src/useBottomSheetContext.d.ts.map +1 -1
  99. package/lib/typescript/src/useBottomSheetControl.d.ts.map +1 -1
  100. package/lib/typescript/src/useBottomSheetManager.d.ts.map +1 -1
  101. package/lib/typescript/src/useBottomSheetStatus.d.ts +1 -2
  102. package/lib/typescript/src/useBottomSheetStatus.d.ts.map +1 -1
  103. package/lib/typescript/src/useEvent.d.ts +4 -0
  104. package/lib/typescript/src/useEvent.d.ts.map +1 -0
  105. package/lib/typescript/src/useScaleAnimation.d.ts +10 -13
  106. package/lib/typescript/src/useScaleAnimation.d.ts.map +1 -1
  107. package/lib/typescript/src/useSheetRenderData.d.ts +17 -0
  108. package/lib/typescript/src/useSheetRenderData.d.ts.map +1 -0
  109. package/package.json +1 -1
  110. package/src/BottomSheet.context.ts +1 -3
  111. package/src/BottomSheetBackdrop.tsx +10 -19
  112. package/src/BottomSheetHost.tsx +13 -99
  113. package/src/BottomSheetManaged.tsx +24 -2
  114. package/src/BottomSheetPersistent.tsx +57 -0
  115. package/src/BottomSheetPortal.tsx +5 -7
  116. package/src/BottomSheetRef.context.ts +14 -0
  117. package/src/QueueItem.tsx +83 -0
  118. package/src/animatedRegistry.ts +8 -0
  119. package/src/bottomSheet.store.ts +1 -173
  120. package/src/bottomSheetCoordinator.ts +10 -8
  121. package/src/index.tsx +7 -1
  122. package/src/portalSessionRegistry.ts +25 -0
  123. package/src/refsMap.ts +8 -0
  124. package/src/store/helpers.ts +65 -0
  125. package/src/store/hooks.ts +50 -0
  126. package/src/store/index.ts +4 -0
  127. package/src/store/store.ts +168 -0
  128. package/src/store/types.ts +42 -0
  129. package/src/useBottomSheetContext.ts +6 -15
  130. package/src/useBottomSheetControl.ts +16 -7
  131. package/src/useBottomSheetManager.tsx +9 -10
  132. package/src/useBottomSheetStatus.ts +4 -14
  133. package/src/useEvent.ts +17 -0
  134. package/src/useScaleAnimation.ts +67 -35
  135. package/src/useSheetRenderData.ts +74 -0
@@ -2,14 +2,18 @@ import { useRef } from 'react';
2
2
  import {
3
3
  useAnimatedStyle,
4
4
  useDerivedValue,
5
+ withSpring,
5
6
  withTiming,
7
+ type WithSpringConfig,
8
+ type WithTimingConfig,
6
9
  } from 'react-native-reanimated';
7
- import {
8
- useBottomSheetStore,
9
- type BottomSheetStore,
10
- } from './bottomSheet.store';
10
+ import { useBottomSheetStore } from './bottomSheet.store';
11
11
  import { useBottomSheetManagerContext } from './BottomSheetManager.provider';
12
12
 
13
+ export type ScaleAnimationConfig =
14
+ | { type: 'timing'; config?: WithTimingConfig }
15
+ | { type: 'spring'; config?: WithSpringConfig };
16
+
13
17
  export interface ScaleConfig {
14
18
  /** Scale factor when sheet is open (default: 0.92) */
15
19
  scale?: number;
@@ -17,76 +21,104 @@ export interface ScaleConfig {
17
21
  translateY?: number;
18
22
  /** Border radius when sheet is open (default: 12) */
19
23
  borderRadius?: number;
20
- /** Animation duration in ms (default: 300) */
21
- duration?: number;
24
+ /** Animation config - timing or spring (default: timing with 300ms duration) */
25
+ animation?: ScaleAnimationConfig;
22
26
  }
23
27
 
24
- const DEFAULT_CONFIG: Required<ScaleConfig> = {
28
+ const DEFAULT_ANIMATION: ScaleAnimationConfig = {
29
+ type: 'timing',
30
+ config: { duration: 300 },
31
+ };
32
+
33
+ const DEFAULT_CONFIG = {
25
34
  scale: 0.92,
26
35
  translateY: 10,
27
36
  borderRadius: 12,
28
- duration: 300,
29
- };
37
+ animation: DEFAULT_ANIMATION,
38
+ } satisfies Required<ScaleConfig>;
39
+
40
+ function useBackgroundScaleDepth(groupId: string): number {
41
+ return useBottomSheetStore((state) => {
42
+ const { stackOrder, sheetsById } = state;
43
+
44
+ for (let i = 0; i < stackOrder.length; i++) {
45
+ const id = stackOrder[i]!;
46
+ const sheet = sheetsById[id];
47
+ if (
48
+ sheet &&
49
+ sheet.groupId === groupId &&
50
+ sheet.scaleBackground &&
51
+ sheet.status !== 'closing' &&
52
+ sheet.status !== 'hidden'
53
+ ) {
54
+ return 1;
55
+ }
56
+ }
57
+ return 0;
58
+ });
59
+ }
30
60
 
31
- /**
32
- * Returns the number of sheets with scaleBackground above a given element.
33
- * For background wrapper, pass undefined as sheetId - returns 0 or 1 (binary).
34
- * For sheets, returns the count of scaleBackground sheets above it.
35
- * Uses shallow comparison internally for optimal re-renders.
36
- */
37
- export function useScaleDepth(groupId: string, sheetId?: string): number {
61
+ function useSheetScaleDepth(
62
+ groupId: string,
63
+ sheetId: string | undefined
64
+ ): number {
38
65
  const prevDepthRef = useRef(0);
39
66
 
40
- const scaleDepthSelector = (state: BottomSheetStore) => {
41
- const { stackOrder, sheetsById } = state;
67
+ return useBottomSheetStore((state) => {
68
+ if (!sheetId) {
69
+ return 0;
70
+ }
42
71
 
43
- const startIndex = sheetId ? stackOrder.indexOf(sheetId) + 1 : 0;
72
+ const { stackOrder, sheetsById } = state;
73
+ const sheetIndex = stackOrder.indexOf(sheetId);
44
74
 
45
- if (sheetId && startIndex === 0) {
75
+ if (sheetIndex === -1) {
46
76
  return prevDepthRef.current;
47
77
  }
48
78
 
49
79
  let depth = 0;
50
- for (let i = startIndex; i < stackOrder.length; i++) {
80
+ for (let i = sheetIndex + 1; i < stackOrder.length; i++) {
51
81
  const id = stackOrder[i]!;
52
82
  const sheet = sheetsById[id];
53
83
  if (
54
84
  sheet &&
55
85
  sheet.groupId === groupId &&
56
86
  sheet.scaleBackground &&
57
- sheet.status !== 'closing'
87
+ sheet.status !== 'closing' &&
88
+ sheet.status !== 'hidden'
58
89
  ) {
59
90
  depth++;
60
- if (!sheetId) {
61
- break;
62
- }
63
91
  }
64
92
  }
65
93
 
66
94
  prevDepthRef.current = depth;
67
95
  return depth;
68
- };
69
-
70
- return useBottomSheetStore(scaleDepthSelector);
96
+ });
71
97
  }
72
98
 
73
- /**
74
- * Returns animated style for scale effect based on depth.
75
- * Uses power scaling: scale^depth for cascading effect.
76
- */
77
99
  export function useScaleAnimatedStyle({ id }: { id?: string } = {}) {
78
100
  const { groupId, scaleConfig } = useBottomSheetManagerContext();
79
- const scaleDepth = useScaleDepth(groupId, id);
80
101
 
81
102
  const {
82
103
  scale = DEFAULT_CONFIG.scale,
83
104
  translateY = DEFAULT_CONFIG.translateY,
84
105
  borderRadius = DEFAULT_CONFIG.borderRadius,
85
- duration = DEFAULT_CONFIG.duration,
106
+ animation = DEFAULT_CONFIG.animation,
86
107
  } = scaleConfig ?? {};
87
108
 
109
+ const isBackground = id === undefined;
110
+
111
+ const backgroundDepth = useBackgroundScaleDepth(groupId);
112
+ const sheetDepth = useSheetScaleDepth(groupId, id);
113
+
114
+ const scaleDepth = isBackground ? backgroundDepth : sheetDepth;
115
+
116
+ // Animate the depth change
88
117
  const progress = useDerivedValue(() => {
89
- return withTiming(scaleDepth, { duration });
118
+ if (animation.type === 'spring') {
119
+ return withSpring(scaleDepth, animation.config);
120
+ }
121
+ return withTiming(scaleDepth, animation.config);
90
122
  });
91
123
 
92
124
  return useAnimatedStyle(() => {
@@ -0,0 +1,74 @@
1
+ import { shallow } from 'zustand/shallow';
2
+ import {
3
+ useBottomSheetStore,
4
+ type BottomSheetState,
5
+ } from './bottomSheet.store';
6
+ import { useBottomSheetManagerContext } from './BottomSheetManager.provider';
7
+
8
+ export interface SheetRenderItem {
9
+ id: string;
10
+ stackIndex: number;
11
+ isActive: boolean;
12
+ }
13
+
14
+ /**
15
+ * Returns a flat list of sheets to render.
16
+ *
17
+ * Each sheet appears exactly once - this prevents React from
18
+ * unmounting/remounting when a sheet transitions between states.
19
+ *
20
+ * Render order:
21
+ * 1. Hidden persistent sheets (keepMounted=true, not in stack)
22
+ * 2. Active sheets (in stackOrder)
23
+ */
24
+ export function useSheetRenderData(): SheetRenderItem[] {
25
+ const { groupId } = useBottomSheetManagerContext();
26
+
27
+ return useBottomSheetStore((state) => {
28
+ const hiddenPersistent = getHiddenPersistentSheets(state, groupId);
29
+ const active = getActiveSheets(state, groupId);
30
+
31
+ return [...hiddenPersistent, ...active];
32
+ }, shallow);
33
+ }
34
+
35
+ function getHiddenPersistentSheets(
36
+ state: { sheetsById: Record<string, BottomSheetState>; stackOrder: string[] },
37
+ groupId: string
38
+ ): SheetRenderItem[] {
39
+ const inStack = new Set(state.stackOrder);
40
+
41
+ return Object.values(state.sheetsById)
42
+ .filter((sheet) => isHiddenPersistent(sheet, groupId, inStack))
43
+ .map((sheet) => ({
44
+ id: sheet.id,
45
+ stackIndex: -1,
46
+ isActive: false,
47
+ }));
48
+ }
49
+
50
+ function isHiddenPersistent(
51
+ sheet: BottomSheetState,
52
+ groupId: string,
53
+ inStack: Set<string>
54
+ ): boolean {
55
+ const belongsToGroup = sheet.groupId === groupId;
56
+ const isPersistent = sheet.keepMounted === true;
57
+ const isHidden = sheet.status === 'hidden';
58
+ const isNotInStack = !inStack.has(sheet.id);
59
+
60
+ return belongsToGroup && isPersistent && isHidden && isNotInStack;
61
+ }
62
+
63
+ function getActiveSheets(
64
+ state: { sheetsById: Record<string, BottomSheetState>; stackOrder: string[] },
65
+ groupId: string
66
+ ): SheetRenderItem[] {
67
+ return state.stackOrder
68
+ .filter((id) => state.sheetsById[id]?.groupId === groupId)
69
+ .map((id, index) => ({
70
+ id,
71
+ stackIndex: index,
72
+ isActive: true,
73
+ }));
74
+ }