Skip to content

Commit

Permalink
fix: dynamic snap points on modals (#113)
Browse files Browse the repository at this point in the history
* chore: update bottom sheet container height handling

* chore: added dynamic snap point example for bottom sheet modal

* chore: remove console log
  • Loading branch information
gorhom authored Dec 14, 2020
1 parent 7944f7a commit 2bb8bf4
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 3 deletions.
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/DynamicSnapPointExample"
options={{
title: 'Dynamic Snap Point',
}}
getComponent={() =>
require('./screens/modal/DynamicSnapPointExample').default
}
/>
{/* advanced examples */}
<Stack.Screen
name="Advanced/NavigatorExample"
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: 'Dynamic Snap Point',
slug: 'Modal/DynamicSnapPointExample',
},
],
},
{
Expand Down
144 changes: 144 additions & 0 deletions example/src/screens/modal/DynamicSnapPointExample.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { View, StyleSheet, Text } from 'react-native';
import { BottomSheetModal, BottomSheetView } from '@gorhom/bottom-sheet';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import Button from '../../components/button';
import withModalProvider from '../withModalProvider';

const DynamicSnapPointExample = () => {
// state
const [count, setCount] = useState(0);
const [contentHeight, setContentHeight] = useState(0);

// hooks
const bottomSheetRef = useRef<BottomSheetModal>(null);
const { bottom: safeBottomArea } = useSafeAreaInsets();

// variables
const snapPoints = useMemo(() => [contentHeight], [contentHeight]);

// callbacks
const handleIncreaseContentPress = useCallback(() => {
setCount(state => state + 1);
}, []);
const handleDecreaseContentPress = useCallback(() => {
setCount(state => Math.max(state - 1, 0));
}, []);

const handlePresentPress = useCallback(() => {
bottomSheetRef.current?.present();
}, []);
const handleDismissPress = useCallback(() => {
bottomSheetRef.current?.dismiss();
}, []);
const handleOnLayout = useCallback(
({
nativeEvent: {
layout: { height },
},
}) => {
setContentHeight(height);
},
[]
);

// styles
const contentContainerStyle = useMemo(
() => ({
...styles.contentContainerStyle,
paddingBottom: safeBottomArea,
}),
[safeBottomArea]
);
const emojiContainerStyle = useMemo(
() => ({
...styles.emojiContainer,
height: 50 * count,
}),
[count]
);

// renders
const renderBackground = useCallback(
() => <View style={styles.background} />,
[]
);

return (
<View style={styles.container}>
<Button
label="Present"
style={styles.buttonContainer}
onPress={handlePresentPress}
/>
<Button
label="Dismiss"
style={styles.buttonContainer}
onPress={handleDismissPress}
/>
<BottomSheetModal
ref={bottomSheetRef}
index={0}
snapPoints={snapPoints}
backgroundComponent={renderBackground}
>
<BottomSheetView
style={contentContainerStyle}
onLayout={handleOnLayout}
>
<Text style={styles.message}>
Could this sheet modal resize to its content height ?
</Text>
<View style={emojiContainerStyle}>
<Text style={styles.emoji}>😍</Text>
</View>
<Button
label="Yes"
style={styles.buttonContainer}
onPress={handleIncreaseContentPress}
/>
<Button
label="Maybe"
style={styles.buttonContainer}
onPress={handleDecreaseContentPress}
/>
</BottomSheetView>
</BottomSheetModal>
</View>
);
};

const styles = StyleSheet.create({
container: {
flex: 1,
padding: 24,
},
background: {
...StyleSheet.absoluteFillObject,
backgroundColor: 'white',
},
buttonContainer: {
marginBottom: 6,
},
contentContainerStyle: {
paddingTop: 12,
paddingHorizontal: 24,
backgroundColor: 'white',
},
message: {
fontSize: 24,
fontWeight: '600',
marginBottom: 12,
},
emoji: {
fontSize: 156,
textAlign: 'center',
alignSelf: 'center',
},
emojiContainer: {
overflow: 'hidden',
justifyContent: 'center',
},
});

export default withModalProvider(DynamicSnapPointExample);
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/DynamicSnapPointExample']: undefined;
// Advanced
['Advanced/NavigatorExample']: undefined;
['Advanced/CustomHandleExample']: undefined;
Expand Down
6 changes: 3 additions & 3 deletions src/components/bottomSheet/BottomSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,8 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
[handleHeight]
);
const safeContainerHeight = useMemo(
() => containerHeight || WINDOW_HEIGHT,
[containerHeight]
() => _providedContainerHeight || containerHeight || WINDOW_HEIGHT,
[_providedContainerHeight, containerHeight]
);

// conditions
Expand Down Expand Up @@ -508,7 +508,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
//#endregion

//#region render
// console.log('BottomSheet', 'render', shouldMeasureContainerHeight);
// console.log('BottomSheet', 'render', snapPoints, safeHandleHeight);
return (
<BottomSheetProvider value={externalContextVariables}>
<BottomSheetBackdropContainer
Expand Down
7 changes: 7 additions & 0 deletions src/hooks/useNormalizedSnapPoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ export const useNormalizedSnapPoints = (
topInset
);
return normalizedSnapPoints.map(normalizedSnapPoint => {
/**
* we subset handleHeight from the `normalizedSnapPoint` to make
* sure that sheets and its handle will be out of the screen.
*/
if (normalizedSnapPoint === 0 && handleHeight !== 0) {
normalizedSnapPoint = normalizedSnapPoint - handleHeight;
}
return Math.ceil(
Math.max(containerHeight - normalizedSnapPoint - handleHeight, topInset)
);
Expand Down

0 comments on commit 2bb8bf4

Please sign in to comment.