react-native-bottom-sheet-stack 1.0.1 → 1.0.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.
- package/lib/commonjs/BottomSheet.context.js.map +1 -1
- package/lib/commonjs/BottomSheetBackdrop.js +76 -0
- package/lib/commonjs/BottomSheetBackdrop.js.map +1 -0
- package/lib/commonjs/BottomSheetHost.js +215 -51
- package/lib/commonjs/BottomSheetHost.js.map +1 -1
- package/lib/commonjs/BottomSheetManaged.js +128 -38
- package/lib/commonjs/BottomSheetManaged.js.map +1 -1
- package/lib/commonjs/BottomSheetManager.context.js.map +1 -1
- package/lib/commonjs/BottomSheetManager.provider.js +38 -13
- package/lib/commonjs/BottomSheetManager.provider.js.map +1 -1
- package/lib/commonjs/BottomSheetScaleView.js +67 -0
- package/lib/commonjs/BottomSheetScaleView.js.map +1 -0
- package/lib/commonjs/animatedRegistry.js +25 -0
- package/lib/commonjs/animatedRegistry.js.map +1 -0
- package/lib/commonjs/bottomSheet.store.js +86 -73
- package/lib/commonjs/bottomSheet.store.js.map +1 -1
- package/lib/commonjs/bottomSheetCoordinator.js +12 -14
- package/lib/commonjs/bottomSheetCoordinator.js.map +1 -1
- package/lib/commonjs/index.js +3 -3
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/useBottomSheetManager.js +88 -44
- package/lib/commonjs/useBottomSheetManager.js.map +1 -1
- package/lib/commonjs/useBottomSheetState.js +40 -17
- package/lib/commonjs/useBottomSheetState.js.map +1 -1
- package/lib/commonjs/useScaleAnimation.js +108 -0
- package/lib/commonjs/useScaleAnimation.js.map +1 -0
- package/lib/typescript/example/src/App.d.ts +0 -2
- package/lib/typescript/example/src/App.d.ts.map +1 -1
- package/lib/typescript/src/BottomSheetBackdrop.d.ts +12 -0
- package/lib/typescript/src/BottomSheetBackdrop.d.ts.map +1 -0
- package/lib/typescript/src/BottomSheetHost.d.ts +2 -6
- package/lib/typescript/src/BottomSheetHost.d.ts.map +1 -1
- package/lib/typescript/src/BottomSheetManaged.d.ts.map +1 -1
- package/lib/typescript/src/BottomSheetManager.context.d.ts +2 -0
- package/lib/typescript/src/BottomSheetManager.context.d.ts.map +1 -1
- package/lib/typescript/src/BottomSheetManager.provider.d.ts +4 -3
- package/lib/typescript/src/BottomSheetManager.provider.d.ts.map +1 -1
- package/lib/typescript/src/BottomSheetScaleView.d.ts +18 -0
- package/lib/typescript/src/BottomSheetScaleView.d.ts.map +1 -0
- package/lib/typescript/src/animatedRegistry.d.ts +4 -0
- package/lib/typescript/src/animatedRegistry.d.ts.map +1 -0
- package/lib/typescript/src/bottomSheet.store.d.ts +6 -4
- package/lib/typescript/src/bottomSheet.store.d.ts.map +1 -1
- package/lib/typescript/src/bottomSheetCoordinator.d.ts +1 -1
- package/lib/typescript/src/bottomSheetCoordinator.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +2 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/useBottomSheetManager.d.ts.map +1 -1
- package/lib/typescript/src/useBottomSheetState.d.ts.map +1 -1
- package/lib/typescript/src/useScaleAnimation.d.ts +43 -0
- package/lib/typescript/src/useScaleAnimation.d.ts.map +1 -0
- package/package.json +9 -2
- package/src/BottomSheetBackdrop.tsx +61 -0
- package/src/BottomSheetHost.tsx +89 -51
- package/src/BottomSheetManaged.tsx +26 -33
- package/src/BottomSheetManager.context.tsx +2 -0
- package/src/BottomSheetManager.provider.tsx +10 -6
- package/src/BottomSheetScaleView.tsx +41 -0
- package/src/animatedRegistry.ts +22 -0
- package/src/bottomSheet.store.ts +132 -97
- package/src/bottomSheetCoordinator.ts +15 -15
- package/src/index.tsx +2 -4
- package/src/useBottomSheetManager.tsx +37 -48
- package/src/useBottomSheetState.ts +5 -17
- package/src/useScaleAnimation.ts +114 -0
- package/lib/commonjs/ScaleBackgroundWrapper.js +0 -69
- package/lib/commonjs/ScaleBackgroundWrapper.js.map +0 -1
- package/lib/typescript/src/ScaleBackgroundWrapper.d.ts +0 -32
- package/lib/typescript/src/ScaleBackgroundWrapper.d.ts.map +0 -1
- package/src/ScaleBackgroundWrapper.tsx +0 -98
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export interface ScaleConfig {
|
|
2
|
+
/** Scale factor when sheet is open (default: 0.92) */
|
|
3
|
+
scale?: number;
|
|
4
|
+
/** Vertical translation when sheet is open (default: 10) */
|
|
5
|
+
translateY?: number;
|
|
6
|
+
/** Border radius when sheet is open (default: 12) */
|
|
7
|
+
borderRadius?: number;
|
|
8
|
+
/** Animation duration in ms (default: 300) */
|
|
9
|
+
duration?: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Returns the number of sheets with scaleBackground above a given element.
|
|
13
|
+
* For background wrapper, pass undefined as sheetId - returns 0 or 1 (binary).
|
|
14
|
+
* For sheets, returns the count of scaleBackground sheets above it.
|
|
15
|
+
* Uses shallow comparison internally for optimal re-renders.
|
|
16
|
+
*/
|
|
17
|
+
export declare function useScaleDepth(groupId: string, sheetId?: string): number;
|
|
18
|
+
/**
|
|
19
|
+
* Returns animated style for scale effect based on depth.
|
|
20
|
+
* Uses power scaling: scale^depth for cascading effect.
|
|
21
|
+
*/
|
|
22
|
+
export declare function useScaleAnimatedStyle(depth: number, config?: ScaleConfig): {
|
|
23
|
+
transform: ({
|
|
24
|
+
scale: number;
|
|
25
|
+
translateY?: undefined;
|
|
26
|
+
} | {
|
|
27
|
+
translateY: number;
|
|
28
|
+
scale?: undefined;
|
|
29
|
+
})[];
|
|
30
|
+
borderRadius: number;
|
|
31
|
+
overflow: "visible";
|
|
32
|
+
} | {
|
|
33
|
+
transform: ({
|
|
34
|
+
scale: number;
|
|
35
|
+
translateY?: undefined;
|
|
36
|
+
} | {
|
|
37
|
+
translateY: number;
|
|
38
|
+
scale?: undefined;
|
|
39
|
+
})[];
|
|
40
|
+
borderRadius: number;
|
|
41
|
+
overflow: "hidden";
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=useScaleAnimation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useScaleAnimation.d.ts","sourceRoot":"","sources":["../../../src/useScaleAnimation.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,WAAW;IAC1B,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AASD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAuCvE;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW;;;;;;;;;;;;;;;;;;;;EAiCxE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-bottom-sheet-stack",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Bottom Sheet Stack Manager",
|
|
5
5
|
"source": "./src/index.tsx",
|
|
6
6
|
"main": "lib/commonjs/index.js",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"registry": "https://registry.npmjs.org/"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
+
"@babel/cli": "^7.28.6",
|
|
41
42
|
"@commitlint/config-conventional": "^19.6.0",
|
|
42
43
|
"@eslint/compat": "^1.2.7",
|
|
43
44
|
"@eslint/eslintrc": "^3.3.0",
|
|
@@ -48,6 +49,7 @@
|
|
|
48
49
|
"@release-it/conventional-changelog": "^9.0.2",
|
|
49
50
|
"@types/jest": "^29.5.5",
|
|
50
51
|
"@types/react": "^19.0.12",
|
|
52
|
+
"babel-plugin-react-compiler": "^1.0.0",
|
|
51
53
|
"commitlint": "^19.6.1",
|
|
52
54
|
"del-cli": "^5.1.0",
|
|
53
55
|
"eslint": "^9.22.0",
|
|
@@ -121,7 +123,12 @@
|
|
|
121
123
|
"source": "src",
|
|
122
124
|
"output": "lib",
|
|
123
125
|
"targets": [
|
|
124
|
-
|
|
126
|
+
[
|
|
127
|
+
"commonjs",
|
|
128
|
+
{
|
|
129
|
+
"configFile": true
|
|
130
|
+
}
|
|
131
|
+
],
|
|
125
132
|
"typescript"
|
|
126
133
|
]
|
|
127
134
|
},
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Pressable, StyleSheet } from 'react-native';
|
|
2
|
+
import Animated, {
|
|
3
|
+
Extrapolation,
|
|
4
|
+
interpolate,
|
|
5
|
+
useAnimatedStyle,
|
|
6
|
+
} from 'react-native-reanimated';
|
|
7
|
+
import { getAnimatedIndex } from './animatedRegistry';
|
|
8
|
+
import { useBottomSheetStore } from './bottomSheet.store';
|
|
9
|
+
|
|
10
|
+
interface BottomSheetBackdropProps {
|
|
11
|
+
sheetId: string;
|
|
12
|
+
onPress?: () => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const AnimatedPressable = Animated.createAnimatedComponent(Pressable);
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Custom backdrop component rendered separately from the scaled sheet content.
|
|
19
|
+
* This ensures the backdrop doesn't scale with the sheet.
|
|
20
|
+
* Opacity is interpolated from the bottom sheet's animatedIndex for smooth animation.
|
|
21
|
+
*/
|
|
22
|
+
export function BottomSheetBackdrop({
|
|
23
|
+
sheetId,
|
|
24
|
+
onPress,
|
|
25
|
+
}: BottomSheetBackdropProps) {
|
|
26
|
+
const status = useBottomSheetStore(
|
|
27
|
+
(state) => state.sheetsById[sheetId]?.status
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const animatedIndex = getAnimatedIndex(sheetId);
|
|
31
|
+
|
|
32
|
+
const isVisible = status === 'opening' || status === 'open';
|
|
33
|
+
|
|
34
|
+
const animatedStyle = useAnimatedStyle(() => {
|
|
35
|
+
// Interpolate opacity based on animatedIndex
|
|
36
|
+
// -1 = closed, 0+ = open at snap point
|
|
37
|
+
const opacity = interpolate(
|
|
38
|
+
animatedIndex.value,
|
|
39
|
+
[-1, 0],
|
|
40
|
+
[0, 1],
|
|
41
|
+
Extrapolation.CLAMP
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
return { opacity };
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<AnimatedPressable
|
|
49
|
+
style={[styles.backdrop, animatedStyle]}
|
|
50
|
+
onPress={onPress}
|
|
51
|
+
pointerEvents={isVisible ? 'auto' : 'none'}
|
|
52
|
+
/>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const styles = StyleSheet.create({
|
|
57
|
+
backdrop: {
|
|
58
|
+
...StyleSheet.absoluteFillObject,
|
|
59
|
+
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
60
|
+
},
|
|
61
|
+
});
|
package/src/BottomSheetHost.tsx
CHANGED
|
@@ -1,39 +1,33 @@
|
|
|
1
|
-
import
|
|
2
|
-
Fragment,
|
|
3
|
-
useEffect,
|
|
4
|
-
useMemo,
|
|
5
|
-
type PropsWithChildren,
|
|
6
|
-
} from 'react';
|
|
1
|
+
import { useEffect } from 'react';
|
|
7
2
|
import { StyleSheet, View } from 'react-native';
|
|
3
|
+
import Animated from 'react-native-reanimated';
|
|
8
4
|
import { useSafeAreaFrame } from 'react-native-safe-area-context';
|
|
9
5
|
|
|
6
|
+
import { shallow } from 'zustand/shallow';
|
|
7
|
+
import { cleanupAnimatedIndex } from './animatedRegistry';
|
|
8
|
+
import { BottomSheetBackdrop } from './BottomSheetBackdrop';
|
|
10
9
|
import { BottomSheetContext } from './BottomSheet.context';
|
|
11
10
|
import { useBottomSheetStore } from './bottomSheet.store';
|
|
12
|
-
import { initBottomSheetCoordinator } from './bottomSheetCoordinator';
|
|
13
11
|
import { useBottomSheetManagerContext } from './BottomSheetManager.provider';
|
|
12
|
+
import { initBottomSheetCoordinator } from './bottomSheetCoordinator';
|
|
13
|
+
import {
|
|
14
|
+
useScaleAnimatedStyle,
|
|
15
|
+
useScaleDepth,
|
|
16
|
+
type ScaleConfig,
|
|
17
|
+
} from './useScaleAnimation';
|
|
14
18
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
Container?: React.ComponentType<any>;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function BottomSheetHostComp({ Container = Fragment }: BottomSheetHostProps) {
|
|
22
|
-
const { bottomSheetsStack, clearAll } = useBottomSheetStore((store) => ({
|
|
23
|
-
bottomSheetsStack: store.stack,
|
|
24
|
-
clearAll: store.clearAll,
|
|
25
|
-
}));
|
|
19
|
+
function BottomSheetHostComp() {
|
|
20
|
+
const queueIds = useQueueIds();
|
|
21
|
+
const clearAll = useBottomSheetStore((store) => store.clearAll);
|
|
26
22
|
|
|
27
|
-
const {
|
|
28
|
-
const { groupId } = useBottomSheetManagerContext();
|
|
23
|
+
const { groupId, scaleConfig } = useBottomSheetManagerContext();
|
|
29
24
|
|
|
30
|
-
|
|
31
|
-
()
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
);
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
const unsubscribe = initBottomSheetCoordinator(groupId);
|
|
27
|
+
return () => {
|
|
28
|
+
unsubscribe();
|
|
29
|
+
};
|
|
30
|
+
}, [groupId]);
|
|
37
31
|
|
|
38
32
|
useEffect(() => {
|
|
39
33
|
return () => {
|
|
@@ -43,38 +37,82 @@ function BottomSheetHostComp({ Container = Fragment }: BottomSheetHostProps) {
|
|
|
43
37
|
|
|
44
38
|
return (
|
|
45
39
|
<>
|
|
46
|
-
{
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
{
|
|
54
|
-
width,
|
|
55
|
-
height,
|
|
56
|
-
},
|
|
57
|
-
]}
|
|
58
|
-
>
|
|
59
|
-
<MemoizedContent id={id}>{content}</MemoizedContent>
|
|
60
|
-
</View>
|
|
61
|
-
</Container>
|
|
62
|
-
</BottomSheetContext.Provider>
|
|
40
|
+
{queueIds.map((id) => (
|
|
41
|
+
<QueueItem
|
|
42
|
+
key={id}
|
|
43
|
+
id={id}
|
|
44
|
+
groupId={groupId}
|
|
45
|
+
scaleConfig={scaleConfig}
|
|
46
|
+
/>
|
|
63
47
|
))}
|
|
64
48
|
</>
|
|
65
49
|
);
|
|
66
50
|
}
|
|
67
51
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
52
|
+
function QueueItem({
|
|
53
|
+
id,
|
|
54
|
+
groupId,
|
|
55
|
+
scaleConfig,
|
|
56
|
+
}: {
|
|
57
|
+
id: string;
|
|
58
|
+
groupId: string;
|
|
59
|
+
scaleConfig?: ScaleConfig;
|
|
60
|
+
}) {
|
|
61
|
+
const content = useBottomSheetStore((state) => state.sheetsById[id]?.content);
|
|
62
|
+
|
|
63
|
+
const { width, height } = useSafeAreaFrame();
|
|
64
|
+
const value = { id };
|
|
65
|
+
|
|
66
|
+
const scaleDepth = useScaleDepth(groupId, id);
|
|
67
|
+
const scaleStyle = useScaleAnimatedStyle(scaleDepth, scaleConfig);
|
|
68
|
+
|
|
69
|
+
// Cleanup animated index when sheet is unmounted
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
return () => {
|
|
72
|
+
cleanupAnimatedIndex(id);
|
|
73
|
+
};
|
|
74
|
+
}, [id]);
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<BottomSheetContext.Provider value={value}>
|
|
78
|
+
{/* Backdrop - rendered without scaling */}
|
|
79
|
+
<View style={[StyleSheet.absoluteFillObject, styles.backdropContainer]}>
|
|
80
|
+
<BottomSheetBackdrop sheetId={id} />
|
|
81
|
+
</View>
|
|
82
|
+
|
|
83
|
+
{/* Sheet content - rendered with scaling */}
|
|
84
|
+
<Animated.View
|
|
85
|
+
style={[
|
|
86
|
+
StyleSheet.absoluteFillObject,
|
|
87
|
+
styles.container,
|
|
88
|
+
{ width, height },
|
|
89
|
+
scaleStyle,
|
|
90
|
+
]}
|
|
91
|
+
>
|
|
92
|
+
{content}
|
|
93
|
+
</Animated.View>
|
|
94
|
+
</BottomSheetContext.Provider>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const useQueueIds = () => {
|
|
99
|
+
const { groupId } = useBottomSheetManagerContext();
|
|
74
100
|
|
|
75
|
-
|
|
101
|
+
return useBottomSheetStore(
|
|
102
|
+
(state) =>
|
|
103
|
+
state.stackOrder.filter(
|
|
104
|
+
(sheetId) => state.sheetsById[sheetId]?.groupId === groupId
|
|
105
|
+
),
|
|
106
|
+
shallow
|
|
107
|
+
);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export const BottomSheetHost = BottomSheetHostComp;
|
|
76
111
|
|
|
77
112
|
const styles = StyleSheet.create({
|
|
113
|
+
backdropContainer: {
|
|
114
|
+
zIndex: 99_999_999,
|
|
115
|
+
},
|
|
78
116
|
container: {
|
|
79
117
|
zIndex: 100_000_000,
|
|
80
118
|
pointerEvents: 'box-none',
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import BottomSheetOriginal, {
|
|
2
|
-
BottomSheetBackdrop,
|
|
3
2
|
useBottomSheetSpringConfigs,
|
|
4
|
-
type BottomSheetBackdropProps,
|
|
5
3
|
type BottomSheetProps,
|
|
6
4
|
} from '@gorhom/bottom-sheet';
|
|
7
5
|
import { type BottomSheetMethods } from '@gorhom/bottom-sheet/lib/typescript/types';
|
|
8
|
-
import React
|
|
6
|
+
import React from 'react';
|
|
9
7
|
|
|
8
|
+
import { getAnimatedIndex } from './animatedRegistry';
|
|
10
9
|
import { createSheetEventHandlers } from './bottomSheetCoordinator';
|
|
11
10
|
import { useBottomSheetState } from './useBottomSheetState';
|
|
12
11
|
|
|
@@ -14,6 +13,9 @@ export interface BottomSheetRef extends BottomSheetMethods {}
|
|
|
14
13
|
|
|
15
14
|
interface BottomSheetManagedProps extends BottomSheetProps {}
|
|
16
15
|
|
|
16
|
+
// Null backdrop - we render our own backdrop separately in BottomSheetHost
|
|
17
|
+
const nullBackdrop = () => null;
|
|
18
|
+
|
|
17
19
|
export const BottomSheetManaged = React.forwardRef<
|
|
18
20
|
BottomSheetRef,
|
|
19
21
|
BottomSheetManagedProps
|
|
@@ -24,35 +26,36 @@ export const BottomSheetManaged = React.forwardRef<
|
|
|
24
26
|
onAnimate,
|
|
25
27
|
onClose,
|
|
26
28
|
enablePanDownToClose = true,
|
|
27
|
-
backdropComponent,
|
|
29
|
+
backdropComponent = nullBackdrop,
|
|
30
|
+
animatedIndex: externalAnimatedIndex,
|
|
28
31
|
...props
|
|
29
32
|
},
|
|
30
33
|
ref
|
|
31
34
|
) => {
|
|
32
35
|
const { bottomSheetState } = useBottomSheetState();
|
|
33
36
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
);
|
|
37
|
+
// Get or create shared animated index for this sheet
|
|
38
|
+
const animatedIndex =
|
|
39
|
+
externalAnimatedIndex ?? getAnimatedIndex(bottomSheetState.id);
|
|
38
40
|
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
fromIndex: number,
|
|
42
|
-
toIndex: number,
|
|
43
|
-
fromPosition: number,
|
|
44
|
-
toPosition: number
|
|
45
|
-
) => {
|
|
46
|
-
handleAnimate(fromIndex, toIndex);
|
|
47
|
-
onAnimate?.(fromIndex, toIndex, fromPosition, toPosition);
|
|
48
|
-
},
|
|
49
|
-
[handleAnimate, onAnimate]
|
|
41
|
+
const { handleAnimate, handleClose } = createSheetEventHandlers(
|
|
42
|
+
bottomSheetState.id
|
|
50
43
|
);
|
|
51
44
|
|
|
52
|
-
const
|
|
45
|
+
const wrappedOnAnimate: BottomSheetProps['onAnimate'] = (
|
|
46
|
+
fromIndex: number,
|
|
47
|
+
toIndex: number,
|
|
48
|
+
fromPosition: number,
|
|
49
|
+
toPosition: number
|
|
50
|
+
) => {
|
|
51
|
+
handleAnimate(fromIndex, toIndex);
|
|
52
|
+
onAnimate?.(fromIndex, toIndex, fromPosition, toPosition);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const wrappedOnClose = () => {
|
|
53
56
|
onClose?.();
|
|
54
57
|
handleClose();
|
|
55
|
-
}
|
|
58
|
+
};
|
|
56
59
|
|
|
57
60
|
const config = useBottomSheetSpringConfigs({
|
|
58
61
|
stiffness: 400,
|
|
@@ -60,25 +63,15 @@ export const BottomSheetManaged = React.forwardRef<
|
|
|
60
63
|
mass: 0.7,
|
|
61
64
|
});
|
|
62
65
|
|
|
63
|
-
const renderBackdropComponent = useCallback(
|
|
64
|
-
(backdropProps: BottomSheetBackdropProps) => (
|
|
65
|
-
<BottomSheetBackdrop
|
|
66
|
-
{...backdropProps}
|
|
67
|
-
disappearsOnIndex={-1}
|
|
68
|
-
appearsOnIndex={0}
|
|
69
|
-
/>
|
|
70
|
-
),
|
|
71
|
-
[]
|
|
72
|
-
);
|
|
73
|
-
|
|
74
66
|
return (
|
|
75
67
|
<BottomSheetOriginal
|
|
76
68
|
animationConfigs={config}
|
|
77
69
|
ref={ref}
|
|
78
70
|
{...props}
|
|
71
|
+
animatedIndex={animatedIndex}
|
|
79
72
|
onClose={wrappedOnClose}
|
|
80
73
|
onAnimate={wrappedOnAnimate}
|
|
81
|
-
backdropComponent={backdropComponent
|
|
74
|
+
backdropComponent={backdropComponent}
|
|
82
75
|
enablePanDownToClose={enablePanDownToClose}
|
|
83
76
|
>
|
|
84
77
|
{children}
|
|
@@ -4,23 +4,27 @@ import {
|
|
|
4
4
|
BottomSheetManagerContext,
|
|
5
5
|
type BottomSheetManagerContextValue,
|
|
6
6
|
} from './BottomSheetManager.context';
|
|
7
|
+
import type { ScaleConfig } from './useScaleAnimation';
|
|
7
8
|
|
|
8
9
|
interface ProviderProps extends PropsWithChildren {
|
|
9
10
|
id: string;
|
|
11
|
+
scaleConfig?: ScaleConfig;
|
|
10
12
|
}
|
|
11
13
|
|
|
12
|
-
function
|
|
14
|
+
export function BottomSheetManagerProvider({
|
|
15
|
+
id,
|
|
16
|
+
scaleConfig,
|
|
17
|
+
children,
|
|
18
|
+
}: ProviderProps) {
|
|
19
|
+
const value = { groupId: id, scaleConfig };
|
|
20
|
+
|
|
13
21
|
return (
|
|
14
|
-
<BottomSheetManagerContext.Provider key={id} value={
|
|
22
|
+
<BottomSheetManagerContext.Provider key={id} value={value}>
|
|
15
23
|
{children}
|
|
16
24
|
</BottomSheetManagerContext.Provider>
|
|
17
25
|
);
|
|
18
26
|
}
|
|
19
27
|
|
|
20
|
-
export const BottomSheetManagerProvider = React.memo(
|
|
21
|
-
BottomSheetManagerProviderComp
|
|
22
|
-
);
|
|
23
|
-
|
|
24
28
|
export const useBottomSheetManagerContext =
|
|
25
29
|
(): BottomSheetManagerContextValue => {
|
|
26
30
|
const context = React.useContext(BottomSheetManagerContext);
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { useContext, type PropsWithChildren } from 'react';
|
|
2
|
+
import { StyleSheet } from 'react-native';
|
|
3
|
+
import Animated from 'react-native-reanimated';
|
|
4
|
+
import { BottomSheetManagerContext } from './BottomSheetManager.context';
|
|
5
|
+
import { useScaleAnimatedStyle, useScaleDepth } from './useScaleAnimation';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Wraps your app content with iOS-style scale animation when a bottom sheet
|
|
9
|
+
* with scaleBackground: true is open. Place your main content inside this
|
|
10
|
+
* component, but keep BottomSheetHost outside of it.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* <BottomSheetManagerProvider id="default" scaleConfig={{ scale: 0.92 }}>
|
|
15
|
+
* <BottomSheetScaleView>
|
|
16
|
+
* <MainContent />
|
|
17
|
+
* </BottomSheetScaleView>
|
|
18
|
+
* <BottomSheetHost />
|
|
19
|
+
* </BottomSheetManagerProvider>
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function BottomSheetScaleView({ children }: PropsWithChildren) {
|
|
23
|
+
const context = useContext(BottomSheetManagerContext);
|
|
24
|
+
const groupId = context?.groupId ?? 'default';
|
|
25
|
+
const scaleConfig = context?.scaleConfig;
|
|
26
|
+
|
|
27
|
+
const scaleDepth = useScaleDepth(groupId);
|
|
28
|
+
const animatedStyle = useScaleAnimatedStyle(scaleDepth, scaleConfig);
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<Animated.View style={[styles.container, animatedStyle]}>
|
|
32
|
+
{children}
|
|
33
|
+
</Animated.View>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const styles = StyleSheet.create({
|
|
38
|
+
container: {
|
|
39
|
+
flex: 1,
|
|
40
|
+
},
|
|
41
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { makeMutable, type SharedValue } from 'react-native-reanimated';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Registry for shared animated values per sheet.
|
|
5
|
+
* This allows backdrop to access the animatedIndex from the bottom sheet.
|
|
6
|
+
*/
|
|
7
|
+
const animatedIndexRegistry = new Map<string, SharedValue<number>>();
|
|
8
|
+
|
|
9
|
+
export function getAnimatedIndex(sheetId: string): SharedValue<number> {
|
|
10
|
+
let animatedIndex = animatedIndexRegistry.get(sheetId);
|
|
11
|
+
|
|
12
|
+
if (!animatedIndex) {
|
|
13
|
+
animatedIndex = makeMutable(-1);
|
|
14
|
+
animatedIndexRegistry.set(sheetId, animatedIndex);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return animatedIndex;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function cleanupAnimatedIndex(sheetId: string): void {
|
|
21
|
+
animatedIndexRegistry.delete(sheetId);
|
|
22
|
+
}
|