react-native-bottom-sheet-stack 1.0.2 → 1.1.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.
- package/README.md +262 -50
- 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 +280 -38
- 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 +41 -13
- package/lib/commonjs/BottomSheetManager.provider.js.map +1 -1
- package/lib/commonjs/BottomSheetPortal.js +46 -0
- package/lib/commonjs/BottomSheetPortal.js.map +1 -0
- 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 +19 -0
- package/lib/commonjs/bottomSheet.store.js.map +1 -1
- package/lib/commonjs/bottomSheetCoordinator.js +5 -6
- package/lib/commonjs/bottomSheetCoordinator.js.map +1 -1
- package/lib/commonjs/index.js +17 -3
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/portal.types.js +2 -0
- package/lib/commonjs/portal.types.js.map +1 -0
- package/lib/commonjs/useBottomSheetControl.js +81 -0
- package/lib/commonjs/useBottomSheetControl.js.map +1 -0
- package/lib/commonjs/useBottomSheetManager.js +88 -44
- package/lib/commonjs/useBottomSheetManager.js.map +1 -1
- package/lib/commonjs/useBottomSheetState.js +40 -10
- 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 +1 -2
- 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/BottomSheetPortal.d.ts +9 -0
- package/lib/typescript/src/BottomSheetPortal.d.ts.map +1 -0
- 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 +9 -3
- package/lib/typescript/src/bottomSheet.store.d.ts.map +1 -1
- package/lib/typescript/src/bottomSheetCoordinator.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +5 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/portal.types.d.ts +24 -0
- package/lib/typescript/src/portal.types.d.ts.map +1 -0
- package/lib/typescript/src/useBottomSheetControl.d.ts +10 -0
- package/lib/typescript/src/useBottomSheetControl.d.ts.map +1 -0
- package/lib/typescript/src/useBottomSheetManager.d.ts +2 -0
- 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 +11 -2
- package/src/BottomSheetBackdrop.tsx +61 -0
- package/src/BottomSheetHost.tsx +74 -15
- package/src/BottomSheetManaged.tsx +26 -33
- package/src/BottomSheetManager.context.tsx +2 -0
- package/src/BottomSheetManager.provider.tsx +15 -8
- package/src/BottomSheetPortal.tsx +39 -0
- package/src/BottomSheetScaleView.tsx +41 -0
- package/src/animatedRegistry.ts +22 -0
- package/src/bottomSheet.store.ts +150 -123
- package/src/bottomSheetCoordinator.ts +5 -6
- package/src/index.tsx +8 -4
- package/src/portal.types.ts +25 -0
- package/src/useBottomSheetControl.ts +52 -0
- package/src/useBottomSheetManager.tsx +37 -48
- package/src/useBottomSheetState.ts +2 -6
- package/src/useScaleAnimation.ts +114 -0
- package/lib/commonjs/ScaleBackgroundWrapper.js +0 -71
- 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 -97
|
@@ -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,18 +1,43 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
2
|
import { StyleSheet, View } from 'react-native';
|
|
3
|
+
import Animated from 'react-native-reanimated';
|
|
3
4
|
import { useSafeAreaFrame } from 'react-native-safe-area-context';
|
|
5
|
+
import { PortalHost } from 'react-native-teleport';
|
|
4
6
|
|
|
5
7
|
import { shallow } from 'zustand/shallow';
|
|
8
|
+
import { cleanupAnimatedIndex } from './animatedRegistry';
|
|
9
|
+
import { BottomSheetBackdrop } from './BottomSheetBackdrop';
|
|
6
10
|
import { BottomSheetContext } from './BottomSheet.context';
|
|
7
11
|
import { useBottomSheetStore } from './bottomSheet.store';
|
|
8
12
|
import { useBottomSheetManagerContext } from './BottomSheetManager.provider';
|
|
9
13
|
import { initBottomSheetCoordinator } from './bottomSheetCoordinator';
|
|
14
|
+
import {
|
|
15
|
+
useScaleAnimatedStyle,
|
|
16
|
+
useScaleDepth,
|
|
17
|
+
type ScaleConfig,
|
|
18
|
+
} from './useScaleAnimation';
|
|
19
|
+
|
|
20
|
+
function PortalHostWrapper({
|
|
21
|
+
id,
|
|
22
|
+
width,
|
|
23
|
+
height,
|
|
24
|
+
}: {
|
|
25
|
+
id: string;
|
|
26
|
+
width: number;
|
|
27
|
+
height: number;
|
|
28
|
+
}) {
|
|
29
|
+
return (
|
|
30
|
+
<View style={{ flex: 1, width, height }}>
|
|
31
|
+
<PortalHost name={`bottomsheet-${id}`} style={{ width, height }} />
|
|
32
|
+
</View>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
10
35
|
|
|
11
36
|
function BottomSheetHostComp() {
|
|
12
37
|
const queueIds = useQueueIds();
|
|
13
38
|
const clearAll = useBottomSheetStore((store) => store.clearAll);
|
|
14
39
|
|
|
15
|
-
const { groupId } = useBottomSheetManagerContext();
|
|
40
|
+
const { groupId, scaleConfig } = useBottomSheetManagerContext();
|
|
16
41
|
|
|
17
42
|
useEffect(() => {
|
|
18
43
|
const unsubscribe = initBottomSheetCoordinator(groupId);
|
|
@@ -30,35 +55,66 @@ function BottomSheetHostComp() {
|
|
|
30
55
|
return (
|
|
31
56
|
<>
|
|
32
57
|
{queueIds.map((id) => (
|
|
33
|
-
<QueueItem
|
|
58
|
+
<QueueItem
|
|
59
|
+
key={id}
|
|
60
|
+
id={id}
|
|
61
|
+
groupId={groupId}
|
|
62
|
+
scaleConfig={scaleConfig}
|
|
63
|
+
/>
|
|
34
64
|
))}
|
|
35
65
|
</>
|
|
36
66
|
);
|
|
37
67
|
}
|
|
38
68
|
|
|
39
|
-
|
|
40
|
-
|
|
69
|
+
function QueueItem({
|
|
70
|
+
id,
|
|
71
|
+
groupId,
|
|
72
|
+
scaleConfig,
|
|
73
|
+
}: {
|
|
74
|
+
id: string;
|
|
75
|
+
groupId: string;
|
|
76
|
+
scaleConfig?: ScaleConfig;
|
|
77
|
+
}) {
|
|
78
|
+
const sheet = useBottomSheetStore((state) => state.sheetsById[id]);
|
|
41
79
|
|
|
42
80
|
const { width, height } = useSafeAreaFrame();
|
|
43
|
-
const value =
|
|
81
|
+
const value = { id };
|
|
82
|
+
|
|
83
|
+
const scaleDepth = useScaleDepth(groupId, id);
|
|
84
|
+
const scaleStyle = useScaleAnimatedStyle(scaleDepth, scaleConfig);
|
|
85
|
+
|
|
86
|
+
// Cleanup animated index when sheet is unmounted
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
return () => {
|
|
89
|
+
cleanupAnimatedIndex(id);
|
|
90
|
+
};
|
|
91
|
+
}, [id]);
|
|
44
92
|
|
|
45
93
|
return (
|
|
46
94
|
<BottomSheetContext.Provider value={value}>
|
|
47
|
-
|
|
95
|
+
{/* Backdrop - rendered without scaling */}
|
|
96
|
+
<View style={[StyleSheet.absoluteFillObject, styles.backdropContainer]}>
|
|
97
|
+
<BottomSheetBackdrop sheetId={id} />
|
|
98
|
+
</View>
|
|
99
|
+
|
|
100
|
+
{/* Sheet content - rendered with scaling */}
|
|
101
|
+
<Animated.View
|
|
48
102
|
style={[
|
|
49
103
|
StyleSheet.absoluteFillObject,
|
|
50
104
|
styles.container,
|
|
51
|
-
{
|
|
52
|
-
|
|
53
|
-
height,
|
|
54
|
-
},
|
|
105
|
+
{ width, height },
|
|
106
|
+
scaleStyle,
|
|
55
107
|
]}
|
|
56
108
|
>
|
|
57
|
-
{
|
|
58
|
-
|
|
109
|
+
{sheet?.usePortal ? (
|
|
110
|
+
<PortalHostWrapper id={id} width={width} height={height} />
|
|
111
|
+
) : (
|
|
112
|
+
sheet?.content
|
|
113
|
+
)}
|
|
114
|
+
</Animated.View>
|
|
59
115
|
</BottomSheetContext.Provider>
|
|
60
116
|
);
|
|
61
|
-
}
|
|
117
|
+
}
|
|
62
118
|
|
|
63
119
|
const useQueueIds = () => {
|
|
64
120
|
const { groupId } = useBottomSheetManagerContext();
|
|
@@ -72,9 +128,12 @@ const useQueueIds = () => {
|
|
|
72
128
|
);
|
|
73
129
|
};
|
|
74
130
|
|
|
75
|
-
export const BottomSheetHost =
|
|
131
|
+
export const BottomSheetHost = BottomSheetHostComp;
|
|
76
132
|
|
|
77
133
|
const styles = StyleSheet.create({
|
|
134
|
+
backdropContainer: {
|
|
135
|
+
zIndex: 99_999_999,
|
|
136
|
+
},
|
|
78
137
|
container: {
|
|
79
138
|
zIndex: 100_000_000,
|
|
80
139
|
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}
|
|
@@ -1,26 +1,33 @@
|
|
|
1
1
|
import React, { type PropsWithChildren } from 'react';
|
|
2
|
+
import { PortalProvider } from 'react-native-teleport';
|
|
2
3
|
|
|
3
4
|
import {
|
|
4
5
|
BottomSheetManagerContext,
|
|
5
6
|
type BottomSheetManagerContextValue,
|
|
6
7
|
} from './BottomSheetManager.context';
|
|
8
|
+
import type { ScaleConfig } from './useScaleAnimation';
|
|
7
9
|
|
|
8
10
|
interface ProviderProps extends PropsWithChildren {
|
|
9
11
|
id: string;
|
|
12
|
+
scaleConfig?: ScaleConfig;
|
|
10
13
|
}
|
|
11
14
|
|
|
12
|
-
function
|
|
15
|
+
export function BottomSheetManagerProvider({
|
|
16
|
+
id,
|
|
17
|
+
scaleConfig,
|
|
18
|
+
children,
|
|
19
|
+
}: ProviderProps) {
|
|
20
|
+
const value = { groupId: id, scaleConfig };
|
|
21
|
+
|
|
13
22
|
return (
|
|
14
|
-
<
|
|
15
|
-
{
|
|
16
|
-
|
|
23
|
+
<PortalProvider>
|
|
24
|
+
<BottomSheetManagerContext.Provider key={id} value={value}>
|
|
25
|
+
{children}
|
|
26
|
+
</BottomSheetManagerContext.Provider>
|
|
27
|
+
</PortalProvider>
|
|
17
28
|
);
|
|
18
29
|
}
|
|
19
30
|
|
|
20
|
-
export const BottomSheetManagerProvider = React.memo(
|
|
21
|
-
BottomSheetManagerProviderComp
|
|
22
|
-
);
|
|
23
|
-
|
|
24
31
|
export const useBottomSheetManagerContext =
|
|
25
32
|
(): BottomSheetManagerContextValue => {
|
|
26
33
|
const context = React.useContext(BottomSheetManagerContext);
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use no memo';
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { Portal } from 'react-native-teleport';
|
|
5
|
+
|
|
6
|
+
import { BottomSheetContext } from './BottomSheet.context';
|
|
7
|
+
import { useBottomSheetStore } from './bottomSheet.store';
|
|
8
|
+
import type { BottomSheetPortalId } from './portal.types';
|
|
9
|
+
import { sheetRefs } from './refsMap';
|
|
10
|
+
|
|
11
|
+
interface BottomSheetPortalProps {
|
|
12
|
+
id: BottomSheetPortalId;
|
|
13
|
+
children: React.ReactElement;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function BottomSheetPortal({ id, children }: BottomSheetPortalProps) {
|
|
17
|
+
const sheetState = useBottomSheetStore((state) => state.sheetsById[id]);
|
|
18
|
+
|
|
19
|
+
// Only render when the sheet is active and using portal
|
|
20
|
+
if (!sheetState?.usePortal) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Get the ref that was created in useBottomSheetControl.open()
|
|
25
|
+
const ref = sheetRefs[id];
|
|
26
|
+
|
|
27
|
+
// Clone the child element to add the ref
|
|
28
|
+
// @ts-ignore - same pattern as useBottomSheetManager
|
|
29
|
+
const childWithRef = React.cloneElement(children, { ref });
|
|
30
|
+
|
|
31
|
+
// Wrap with BottomSheetContext so useBottomSheetState() works inside portal content
|
|
32
|
+
return (
|
|
33
|
+
<Portal hostName={`bottomsheet-${id}`}>
|
|
34
|
+
<BottomSheetContext.Provider value={{ id }}>
|
|
35
|
+
{childWithRef}
|
|
36
|
+
</BottomSheetContext.Provider>
|
|
37
|
+
</Portal>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -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
|
+
}
|