react-native-bottom-sheet-stack 1.7.1 → 1.7.3

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 (44) hide show
  1. package/lib/commonjs/BottomSheetDefaultIndex.context.js +14 -0
  2. package/lib/commonjs/BottomSheetDefaultIndex.context.js.map +1 -0
  3. package/lib/commonjs/BottomSheetManaged.js +59 -65
  4. package/lib/commonjs/BottomSheetManaged.js.map +1 -1
  5. package/lib/commonjs/BottomSheetPersistent.js +30 -17
  6. package/lib/commonjs/BottomSheetPersistent.js.map +1 -1
  7. package/lib/commonjs/BottomSheetScaleView.js +1 -1
  8. package/lib/commonjs/BottomSheetScaleView.js.map +1 -1
  9. package/lib/commonjs/QueueItem.js +67 -77
  10. package/lib/commonjs/QueueItem.js.map +1 -1
  11. package/lib/commonjs/bottomSheetCoordinator.js +1 -3
  12. package/lib/commonjs/bottomSheetCoordinator.js.map +1 -1
  13. package/lib/commonjs/store/hooks.js +32 -2
  14. package/lib/commonjs/store/hooks.js.map +1 -1
  15. package/lib/commonjs/store/store.js +9 -6
  16. package/lib/commonjs/store/store.js.map +1 -1
  17. package/lib/commonjs/useScaleAnimation.js +29 -29
  18. package/lib/commonjs/useScaleAnimation.js.map +1 -1
  19. package/lib/commonjs/useSheetRenderData.js +17 -2
  20. package/lib/commonjs/useSheetRenderData.js.map +1 -1
  21. package/lib/typescript/src/BottomSheetDefaultIndex.context.d.ts +7 -0
  22. package/lib/typescript/src/BottomSheetDefaultIndex.context.d.ts.map +1 -0
  23. package/lib/typescript/src/BottomSheetManaged.d.ts.map +1 -1
  24. package/lib/typescript/src/BottomSheetPersistent.d.ts.map +1 -1
  25. package/lib/typescript/src/QueueItem.d.ts +1 -1
  26. package/lib/typescript/src/QueueItem.d.ts.map +1 -1
  27. package/lib/typescript/src/bottomSheetCoordinator.d.ts.map +1 -1
  28. package/lib/typescript/src/store/hooks.d.ts +1 -0
  29. package/lib/typescript/src/store/hooks.d.ts.map +1 -1
  30. package/lib/typescript/src/store/store.d.ts.map +1 -1
  31. package/lib/typescript/src/useScaleAnimation.d.ts +22 -3
  32. package/lib/typescript/src/useScaleAnimation.d.ts.map +1 -1
  33. package/lib/typescript/src/useSheetRenderData.d.ts.map +1 -1
  34. package/package.json +1 -1
  35. package/src/BottomSheetDefaultIndex.context.ts +14 -0
  36. package/src/BottomSheetManaged.tsx +3 -6
  37. package/src/BottomSheetPersistent.tsx +7 -4
  38. package/src/BottomSheetScaleView.tsx +2 -2
  39. package/src/QueueItem.tsx +9 -5
  40. package/src/bottomSheetCoordinator.ts +1 -3
  41. package/src/store/hooks.ts +25 -0
  42. package/src/store/store.ts +13 -5
  43. package/src/useScaleAnimation.ts +18 -12
  44. package/src/useSheetRenderData.ts +26 -2
@@ -1,7 +1,7 @@
1
1
  import { type PropsWithChildren } from 'react';
2
2
  import { StyleSheet } from 'react-native';
3
3
  import Animated from 'react-native-reanimated';
4
- import { useScaleAnimatedStyle } from './useScaleAnimation';
4
+ import { useBackgroundScaleAnimatedStyle } from './useScaleAnimation';
5
5
 
6
6
  /**
7
7
  * Wraps your app content with iOS-style scale animation when a bottom sheet
@@ -19,7 +19,7 @@ import { useScaleAnimatedStyle } from './useScaleAnimation';
19
19
  * ```
20
20
  */
21
21
  export function BottomSheetScaleView({ children }: PropsWithChildren) {
22
- const animatedStyle = useScaleAnimatedStyle();
22
+ const animatedStyle = useBackgroundScaleAnimatedStyle();
23
23
 
24
24
  return (
25
25
  <Animated.View style={[styles.container, animatedStyle]}>
package/src/QueueItem.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import { useEffect } from 'react';
1
+ import { memo, useEffect } from 'react';
2
2
  import { StyleSheet, View } from 'react-native';
3
3
  import Animated from 'react-native-reanimated';
4
4
  import { useSafeAreaFrame } from 'react-native-safe-area-context';
@@ -15,7 +15,7 @@ import {
15
15
  } from './bottomSheet.store';
16
16
  import { BottomSheetBackdrop } from './BottomSheetBackdrop';
17
17
  import { cleanupSheetRef } from './refsMap';
18
- import { useScaleAnimatedStyle } from './useScaleAnimation';
18
+ import { useSheetScaleAnimatedStyle } from './useScaleAnimation';
19
19
 
20
20
  interface QueueItemProps {
21
21
  id: string;
@@ -23,7 +23,11 @@ interface QueueItemProps {
23
23
  isActive: boolean;
24
24
  }
25
25
 
26
- export function QueueItem({ id, stackIndex, isActive }: QueueItemProps) {
26
+ export const QueueItem = memo(function QueueItem({
27
+ id,
28
+ stackIndex,
29
+ isActive,
30
+ }: QueueItemProps) {
27
31
  const content = useSheetContent(id);
28
32
  const usePortal = useSheetUsePortal(id);
29
33
  const keepMounted = useSheetKeepMounted(id);
@@ -31,7 +35,7 @@ export function QueueItem({ id, stackIndex, isActive }: QueueItemProps) {
31
35
  const startClosing = useStartClosing();
32
36
 
33
37
  const { width, height } = useSafeAreaFrame();
34
- const scaleStyle = useScaleAnimatedStyle({ id });
38
+ const scaleStyle = useSheetScaleAnimatedStyle(id);
35
39
 
36
40
  useEffect(() => {
37
41
  return () => {
@@ -76,7 +80,7 @@ export function QueueItem({ id, stackIndex, isActive }: QueueItemProps) {
76
80
  </Animated.View>
77
81
  </>
78
82
  );
79
- }
83
+ });
80
84
 
81
85
  const styles = StyleSheet.create({
82
86
  container: {},
@@ -19,9 +19,7 @@ export function initBottomSheetCoordinator(groupId: string) {
19
19
 
20
20
  switch (status) {
21
21
  case 'opening':
22
- requestAnimationFrame(() => {
23
- getSheetRef(id)?.current?.expand();
24
- });
22
+ getSheetRef(id)?.current?.expand();
25
23
  break;
26
24
  case 'hidden':
27
25
  if (ref) ref.close();
@@ -32,6 +32,31 @@ export const useIsSheetOpen = (id: string) =>
32
32
  return status === 'open' || status === 'opening';
33
33
  }, shallow);
34
34
 
35
+ export const useHasScaleBackgroundAbove = (id: string) =>
36
+ useBottomSheetStore((state) => {
37
+ const { stackOrder, sheetsById } = state;
38
+ const sheetIndex = stackOrder.indexOf(id);
39
+
40
+ if (sheetIndex === -1) {
41
+ return false;
42
+ }
43
+
44
+ // Check if any sheet above this one has scaleBackground
45
+ for (let i = sheetIndex + 1; i < stackOrder.length; i++) {
46
+ const aboveId = stackOrder[i]!;
47
+ const aboveSheet = sheetsById[aboveId];
48
+ if (
49
+ aboveSheet &&
50
+ aboveSheet.scaleBackground &&
51
+ aboveSheet.status !== 'closing' &&
52
+ aboveSheet.status !== 'hidden'
53
+ ) {
54
+ return true;
55
+ }
56
+ }
57
+ return false;
58
+ }, shallow);
59
+
35
60
  // Action hooks
36
61
 
37
62
  export const useOpen = () => useBottomSheetStore((state) => state.open);
@@ -33,9 +33,9 @@ export const useBottomSheetStore = create(
33
33
  mode
34
34
  );
35
35
 
36
- // Get next portalSession from registry for portal-based sheets
37
- // Registry persists across sheet deletions to ensure unique Portal/PortalHost names
38
- const nextPortalSession = sheet.usePortal
36
+ const shouldGetNewPortalSession =
37
+ sheet.usePortal && (!existingSheet || !existingSheet.keepMounted);
38
+ const nextPortalSession = shouldGetNewPortalSession
39
39
  ? getNextPortalSession(sheet.id)
40
40
  : undefined;
41
41
 
@@ -46,7 +46,9 @@ export const useBottomSheetStore = create(
46
46
  scaleBackground:
47
47
  sheet.scaleBackground ?? existingSheet.scaleBackground,
48
48
  params: sheet.params ?? existingSheet.params,
49
- portalSession: nextPortalSession ?? existingSheet.portalSession,
49
+ portalSession: existingSheet.keepMounted
50
+ ? existingSheet.portalSession
51
+ : (nextPortalSession ?? existingSheet.portalSession),
50
52
  }
51
53
  : { ...sheet, status: 'opening', portalSession: nextPortalSession };
52
54
 
@@ -144,10 +146,16 @@ export const useBottomSheetStore = create(
144
146
  set((state) => {
145
147
  if (state.sheetsById[sheet.id]) return state;
146
148
 
149
+ // For portal-based persistent sheets, set initial portalSession
150
+ // This session will be reused across open/close cycles
151
+ const portalSession = sheet.usePortal
152
+ ? getNextPortalSession(sheet.id)
153
+ : undefined;
154
+
147
155
  return {
148
156
  sheetsById: {
149
157
  ...state.sheetsById,
150
- [sheet.id]: { ...sheet, status: 'hidden' },
158
+ [sheet.id]: { ...sheet, status: 'hidden', portalSession },
151
159
  },
152
160
  };
153
161
  }),
@@ -38,7 +38,7 @@ const DEFAULT_CONFIG = {
38
38
  } satisfies Required<ScaleConfig>;
39
39
 
40
40
  function useBackgroundScaleDepth(groupId: string): number {
41
- return useBottomSheetStore((state) => {
41
+ const depth = useBottomSheetStore((state) => {
42
42
  const { stackOrder, sheetsById } = state;
43
43
 
44
44
  for (let i = 0; i < stackOrder.length; i++) {
@@ -55,6 +55,7 @@ function useBackgroundScaleDepth(groupId: string): number {
55
55
  }
56
56
  return 0;
57
57
  });
58
+ return depth;
58
59
  }
59
60
 
60
61
  function useSheetScaleDepth(
@@ -63,7 +64,7 @@ function useSheetScaleDepth(
63
64
  ): number {
64
65
  const prevDepthRef = useRef(0);
65
66
 
66
- return useBottomSheetStore((state) => {
67
+ const result = useBottomSheetStore((state) => {
67
68
  if (!sheetId) {
68
69
  return 0;
69
70
  }
@@ -93,10 +94,11 @@ function useSheetScaleDepth(
93
94
  prevDepthRef.current = depth;
94
95
  return depth;
95
96
  });
97
+ return result;
96
98
  }
97
99
 
98
- export function useScaleAnimatedStyle({ id }: { id?: string } = {}) {
99
- const { groupId, scaleConfig } = useBottomSheetManagerContext();
100
+ function useScaleAnimatedStyleInternal(scaleDepth: number) {
101
+ const { scaleConfig } = useBottomSheetManagerContext();
100
102
 
101
103
  const {
102
104
  scale = DEFAULT_CONFIG.scale,
@@ -105,14 +107,6 @@ export function useScaleAnimatedStyle({ id }: { id?: string } = {}) {
105
107
  animation = DEFAULT_CONFIG.animation,
106
108
  } = scaleConfig ?? {};
107
109
 
108
- const isBackground = id === undefined;
109
-
110
- const backgroundDepth = useBackgroundScaleDepth(groupId);
111
- const sheetDepth = useSheetScaleDepth(groupId, id);
112
-
113
- const scaleDepth = isBackground ? backgroundDepth : sheetDepth;
114
-
115
- // Animate the depth change
116
110
  const progress = useDerivedValue(() => {
117
111
  if (animation.type === 'spring') {
118
112
  return withSpring(scaleDepth, animation.config);
@@ -142,3 +136,15 @@ export function useScaleAnimatedStyle({ id }: { id?: string } = {}) {
142
136
  };
143
137
  });
144
138
  }
139
+
140
+ export function useBackgroundScaleAnimatedStyle() {
141
+ const { groupId } = useBottomSheetManagerContext();
142
+ const scaleDepth = useBackgroundScaleDepth(groupId);
143
+ return useScaleAnimatedStyleInternal(scaleDepth);
144
+ }
145
+
146
+ export function useSheetScaleAnimatedStyle(sheetId: string) {
147
+ const { groupId } = useBottomSheetManagerContext();
148
+ const scaleDepth = useSheetScaleDepth(groupId, sheetId);
149
+ return useScaleAnimatedStyleInternal(scaleDepth);
150
+ }
@@ -1,4 +1,3 @@
1
- import { shallow } from 'zustand/shallow';
2
1
  import {
3
2
  useBottomSheetStore,
4
3
  type BottomSheetState,
@@ -11,6 +10,31 @@ export interface SheetRenderItem {
11
10
  isActive: boolean;
12
11
  }
13
12
 
13
+ /**
14
+ * Deep comparison for SheetRenderItem arrays.
15
+ * Returns true if arrays have same items with same values.
16
+ */
17
+ function sheetRenderDataEqual(
18
+ a: SheetRenderItem[],
19
+ b: SheetRenderItem[]
20
+ ): boolean {
21
+ if (a.length !== b.length) return false;
22
+
23
+ for (let i = 0; i < a.length; i++) {
24
+ const itemA = a[i]!;
25
+ const itemB = b[i]!;
26
+ if (
27
+ itemA.id !== itemB.id ||
28
+ itemA.stackIndex !== itemB.stackIndex ||
29
+ itemA.isActive !== itemB.isActive
30
+ ) {
31
+ return false;
32
+ }
33
+ }
34
+
35
+ return true;
36
+ }
37
+
14
38
  /**
15
39
  * Returns a flat list of sheets to render.
16
40
  *
@@ -29,7 +53,7 @@ export function useSheetRenderData(): SheetRenderItem[] {
29
53
  const active = getActiveSheets(state, groupId);
30
54
 
31
55
  return [...hiddenPersistent, ...active];
32
- }, shallow);
56
+ }, sheetRenderDataEqual);
33
57
  }
34
58
 
35
59
  function getHiddenPersistentSheets(