Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,15 @@ const App = () => {
}}
getComponent={() => require('./screens/modal/StackExample').default}
/>
<Stack.Screen
name="Modal/StackWithBottomInsetExample"
options={{
title: 'Stack Modals With Footer Example',
}}
getComponent={() =>
require('./screens/modal/StackWithBottomInsetExample').default
}
/>
<Stack.Screen
name="Modal/DynamicSnapPointExample"
options={{
Expand Down
4 changes: 4 additions & 0 deletions example/src/screens/Root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ const data = [
name: 'Stack Modals',
slug: 'Modal/StackExample',
},
{
name: 'Stack Modals With Footer',
slug: 'Modal/StackWithBottomInsetExample',
},
{
name: 'Dynamic Snap Point',
slug: 'Modal/DynamicSnapPointExample',
Expand Down
178 changes: 178 additions & 0 deletions example/src/screens/modal/StackWithBottomInsetExample.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import React, { useCallback, useMemo, useRef } from 'react';
import { View, StyleSheet } from 'react-native';
import { BottomSheetModal, useBottomSheetModal } from '@gorhom/bottom-sheet';
import Button from '../../components/button';
import ContactListContainer from '../../components/contactListContainer';
import withModalProvider from '../withModalProvider';

const footerHeight = 100;

const StackExample = () => {
// hooks
const { dismiss, dismissAll } = useBottomSheetModal();

// refs
const bottomSheetModalARef = useRef<BottomSheetModal>(null);
const bottomSheetModalBRef = useRef<BottomSheetModal>(null);
const bottomSheetModalCRef = useRef<BottomSheetModal>(null);

// variables
const snapPoints = useMemo(() => ['25%', '50%'], []);

// callbacks
const handlePresentAPress = useCallback(() => {
if (bottomSheetModalARef.current) {
bottomSheetModalARef.current.present();
}
}, []);
const handleDismissAPress = useCallback(() => {
if (bottomSheetModalARef.current) {
bottomSheetModalARef.current.dismiss();
}
}, []);
const handlePresentBPress = useCallback(() => {
if (bottomSheetModalBRef.current) {
bottomSheetModalBRef.current.present();
}
}, []);
const handleDismissBPress = useCallback(() => {
if (bottomSheetModalBRef.current) {
bottomSheetModalBRef.current.dismiss();
}
}, []);
const handlePresentCPress = useCallback(() => {
if (bottomSheetModalCRef.current) {
bottomSheetModalCRef.current.present();
}
}, []);
const handleDismissCPress = useCallback(() => {
if (bottomSheetModalCRef.current) {
bottomSheetModalCRef.current.dismiss();
}
}, []);
const handleDismissAllPress = useCallback(() => {
dismissAll();
}, [dismissAll]);

const handleDismissByHookPress = useCallback(() => {
dismiss('A');
}, [dismiss]);

// renders

const renderBottomSheetContent = useCallback(
(title, onPress) => (
<ContactListContainer
title={title}
type="FlatList"
onItemPress={onPress}
/>
),
[]
);
return (
<>
<View style={styles.container}>
<Button
label="Present Modal A"
style={styles.buttonContainer}
onPress={handlePresentAPress}
/>
<Button
label="Dismiss Modal A"
style={styles.buttonContainer}
onPress={handleDismissAPress}
/>
<Button
label="Present Modal B"
style={styles.buttonContainer}
onPress={handlePresentBPress}
/>
<Button
label="Dismiss Modal B"
style={styles.buttonContainer}
onPress={handleDismissBPress}
/>
<Button
label="Present Modal C"
style={styles.buttonContainer}
onPress={handlePresentCPress}
/>
<Button
label="Dismiss Modal C"
style={styles.buttonContainer}
onPress={handleDismissCPress}
/>

<Button
label="Dismiss A By Hook"
style={styles.buttonContainer}
onPress={handleDismissByHookPress}
/>

<BottomSheetModal
name="A"
ref={bottomSheetModalARef}
snapPoints={snapPoints}
bottomInset={footerHeight}
children={renderBottomSheetContent('Modal A', handlePresentBPress)}
/>

<BottomSheetModal
name="B"
ref={bottomSheetModalBRef}
snapPoints={snapPoints}
bottomInset={footerHeight}
children={renderBottomSheetContent('Modal B', handlePresentCPress)}
/>

<BottomSheetModal
name="C"
ref={bottomSheetModalCRef}
index={1}
snapPoints={snapPoints}
dismissOnPanDown={false}
bottomInset={footerHeight}
children={renderBottomSheetContent('Modal C', handleDismissCPress)}
/>
</View>
<View style={styles.footer}>
<Button
label="Dismiss All Modal"
style={styles.buttonContainer}
onPress={handleDismissAllPress}
/>
</View>
</>
);
};

const styles = StyleSheet.create({
container: {
flex: 1,
padding: 24,
},
buttonContainer: {
marginBottom: 6,
},
footer: {
height: footerHeight,
position: 'absolute',
bottom: 0,
width: '100%',
zIndex: 10,
backgroundColor: 'white',
padding: 24,

shadowColor: 'black',
shadowOffset: {
width: 0,
height: -20,
},
shadowOpacity: 0.1,
shadowRadius: 10,
elevation: 16,
},
});

export default withModalProvider(StackExample);
1 change: 1 addition & 0 deletions example/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export type AppStackParamsList = {
['Modal/SimpleExample']: undefined;
['Modal/BackdropExample']: undefined;
['Modal/StackExample']: undefined;
['Modal/StackWithBottomInsetExample']: undefined;
['Modal/DynamicSnapPointExample']: undefined;
// Advanced
['Advanced/NavigatorExample']: undefined;
Expand Down
27 changes: 18 additions & 9 deletions src/components/bottomSheet/BottomSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
handleHeight: _providedHandleHeight,
containerHeight: _providedContainerHeight,
topInset = 0,
bottomInset = 0,
enableContentPanningGesture = DEFAULT_ENABLE_CONTENT_PANNING_GESTURE,
enableHandlePanningGesture = DEFAULT_ENABLE_HANDLE_PANNING_GESTURE,
animateOnMount = DEFAULT_ANIMATE_ON_MOUNT,
Expand Down Expand Up @@ -183,7 +184,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(

const snapPoints = useNormalizedSnapPoints(
_providedSnapPoints,
topInset,
topInset + bottomInset,
safeContainerHeight,
safeHandleHeight
);
Expand Down Expand Up @@ -451,14 +452,20 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
{
translateY: cond(
animatedIsLayoutReady,
position,
sub(position, bottomInset),
safeContainerHeight
),
},
],
},
],
[safeContainerHeight, _providedStyle, position, animatedIsLayoutReady]
[
safeContainerHeight,
_providedStyle,
position,
animatedIsLayoutReady,
bottomInset,
]
);
const contentContainerStyle = useMemo(
() => ({
Expand Down Expand Up @@ -595,13 +602,13 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
//#endregion

//#region render
// console.log(
// 'BottomSheet',
// 'render',
// console.log('BottomSheet', 'render', {
// snapPoints,
// sheetHeight,
// safeHandleHeight
// );
// shouldMeasureContainerHeight,
// safeContainerHeight,
// topInset,
// bottomInset,
// });
return (
<BottomSheetProvider value={externalContextVariables}>
<BottomSheetBackdropContainer
Expand All @@ -614,6 +621,8 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
key="BottomSheetContainer"
shouldMeasureHeight={shouldMeasureContainerHeight}
onMeasureHeight={handleOnContainerMeasureHeight}
topInset={topInset}
bottomInset={bottomInset}
>
<BottomSheetContentWrapper
key="BottomSheetContentWrapper"
Expand Down
11 changes: 9 additions & 2 deletions src/components/bottomSheet/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,19 @@ export type BottomSheetProps = {
*/
containerHeight?: number;
/**
* Top inset value helps to calculate percentage snap points values,
* usually comes from `@react-navigation/stack` hook `useHeaderHeight` or from `react-native-safe-area-context` hook `useSafeArea`.
* Top inset to be added to the bottom sheet container,
* usually comes from `@react-navigation/stack` hook `useHeaderHeight`
* or from `react-native-safe-area-context` hook `useSafeArea`.
* @type number
* @default 0
*/
topInset?: number;
/**
* Bottom inset to be added to the bottom sheet container.
* @type number
* @default 0
*/
bottomInset?: number;
/**
* Enable content panning gesture interaction.
* @type boolean
Expand Down
19 changes: 16 additions & 3 deletions src/components/bottomSheetContainer/BottomSheetContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { memo, useCallback } from 'react';
import { View } from 'react-native';
import React, { memo, useCallback, useMemo } from 'react';
import { View, ViewStyle } from 'react-native';
import isEqual from 'lodash.isequal';
import type { BottomSheetContainerProps } from './types';
import { styles } from './styles';
Expand All @@ -8,6 +8,8 @@ const BottomSheetContainerComponent = ({
shouldMeasureHeight,
onMeasureHeight,
children,
topInset = 0,
bottomInset = 0,
}: BottomSheetContainerProps) => {
//#region callbacks
const handleOnLayout = useCallback(
Expand All @@ -22,12 +24,23 @@ const BottomSheetContainerComponent = ({
);
//#endregion

const containerStyle: ViewStyle[] = useMemo(
() => [
styles.container,
{
top: topInset,
bottom: bottomInset,
},
],
[bottomInset, topInset]
);

//#region render
// console.log('BottomSheetContainer', 'render', shouldMeasureHeight);
return (
<View
pointerEvents="box-none"
style={styles.container}
style={containerStyle}
onLayout={shouldMeasureHeight ? handleOnLayout : undefined}
children={children}
/>
Expand Down
1 change: 1 addition & 0 deletions src/components/bottomSheetContainer/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ import { StyleSheet } from 'react-native';
export const styles = StyleSheet.create({
container: {
...StyleSheet.absoluteFillObject,
overflow: 'hidden',
},
});
2 changes: 2 additions & 0 deletions src/components/bottomSheetContainer/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ export interface BottomSheetContainerProps {
shouldMeasureHeight: boolean;
onMeasureHeight: (height: number) => void;
children: ReactNode;
topInset?: number;
bottomInset?: nummber;
}
Loading