Skip to content

Commit

Permalink
feat: introduced more stable handling for dynamic snap points
Browse files Browse the repository at this point in the history
  • Loading branch information
gorhom committed Jun 6, 2021
1 parent 016a01f commit 3edb2d1
Show file tree
Hide file tree
Showing 18 changed files with 166 additions and 102 deletions.
34 changes: 12 additions & 22 deletions example/src/screens/advanced/DynamicSnapPointExample.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { View, StyleSheet, Text } from 'react-native';
import BottomSheet, { BottomSheetView } from '@gorhom/bottom-sheet';
import BottomSheet, {
BottomSheetView,
useBottomSheetDynamicSnapPoints,
} from '@gorhom/bottom-sheet';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import Button from '../../components/button';

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

// hooks
const bottomSheetRef = useRef<BottomSheet>(null);
const { animatedHandleHeight, animatedSnapPoints, contentProps } =
useBottomSheetDynamicSnapPoints(['CONTENT_HEIGHT']);
const { bottom: safeBottomArea } = useSafeAreaInsets();

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

// callbacks
const handleIncreaseContentPress = useCallback(() => {
setCount(state => state + 1);
Expand All @@ -29,22 +30,12 @@ const DynamicSnapPointExample = () => {
const handleClosePress = useCallback(() => {
bottomSheetRef.current?.close();
}, []);
const handleOnLayout = useCallback(
({
nativeEvent: {
layout: { height },
},
}) => {
setContentHeight(height);
},
[]
);

// styles
const contentContainerStyle = useMemo(
() => ({
...styles.contentContainerStyle,
paddingBottom: safeBottomArea,
paddingBottom: safeBottomArea || 6,
}),
[safeBottomArea]
);
Expand All @@ -63,14 +54,12 @@ const DynamicSnapPointExample = () => {
<Button label="Close" onPress={handleClosePress} />
<BottomSheet
ref={bottomSheetRef}
index={1}
snapPoints={snapPoints}
snapPoints={animatedSnapPoints}
handleHeight={animatedHandleHeight}
enablePanDownToClose={true}
animateOnMount={true}
>
<BottomSheetView
style={contentContainerStyle}
onLayout={handleOnLayout}
>
<BottomSheetView style={contentContainerStyle} {...contentProps}>
<Text style={styles.message}>
Could this sheet resize to its content height ?
</Text>
Expand All @@ -92,6 +81,7 @@ const styles = StyleSheet.create({
},
contentContainerStyle: {
paddingTop: 12,
paddingBottom: 6,
paddingHorizontal: 24,
backgroundColor: 'white',
},
Expand Down
36 changes: 15 additions & 21 deletions example/src/screens/modal/DynamicSnapPointExample.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { View, StyleSheet, Text } from 'react-native';
import { BottomSheetModal, BottomSheetView } from '@gorhom/bottom-sheet';
import {
BottomSheetModal,
BottomSheetView,
useBottomSheetDynamicSnapPoints,
} 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 { animatedHandleHeight, animatedSnapPoints, contentProps } =
useBottomSheetDynamicSnapPoints(['CONTENT_HEIGHT']);
const { bottom: safeBottomArea } = useSafeAreaInsets();

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

// callbacks
const handleIncreaseContentPress = useCallback(() => {
setCount(state => state + 1);
Expand All @@ -31,22 +33,12 @@ const DynamicSnapPointExample = () => {
const handleDismissPress = useCallback(() => {
bottomSheetRef.current?.dismiss();
}, []);
const handleOnLayout = useCallback(
({
nativeEvent: {
layout: { height },
},
}) => {
setContentHeight(height);
},
[]
);

// styles
const contentContainerStyle = useMemo(
() => ({
...styles.contentContainerStyle,
paddingBottom: safeBottomArea,
paddingBottom: safeBottomArea || 6,
}),
[safeBottomArea]
);
Expand All @@ -63,11 +55,13 @@ const DynamicSnapPointExample = () => {
<View style={styles.container}>
<Button label="Present" onPress={handlePresentPress} />
<Button label="Dismiss" onPress={handleDismissPress} />
<BottomSheetModal ref={bottomSheetRef} index={0} snapPoints={snapPoints}>
<BottomSheetView
style={contentContainerStyle}
onLayout={handleOnLayout}
>
<BottomSheetModal
ref={bottomSheetRef}
snapPoints={animatedSnapPoints}
handleHeight={animatedHandleHeight}
enablePanDownToClose={true}
>
<BottomSheetView style={contentContainerStyle} {...contentProps}>
<Text style={styles.message}>
Could this sheet modal resize to its content height ?
</Text>
Expand Down
44 changes: 28 additions & 16 deletions src/components/bottomSheet/BottomSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
//#endregion

//#region conditional variables
const isContentHeightFixed = useSharedValue(false);
const isLayoutCalculated = useDerivedValue(() => {
let isContainerHeightCalculated = false;
//container height was provided.
Expand All @@ -209,8 +210,9 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
let isHandleHeightCalculated = false;
// handle height is provided.
if (
_providedHandleHeight !== null ||
_providedHandleHeight !== undefined
_providedHandleHeight !== null &&
_providedHandleHeight !== undefined &&
typeof _providedHandleHeight === 'number'
) {
isHandleHeightCalculated = true;
}
Expand Down Expand Up @@ -422,7 +424,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
const adjustedSnapPoints = animatedSnapPoints.value.slice().reverse();
const adjustedSnapPointsIndexes = animatedSnapPoints.value
.slice()
.map((_, index) => index)
.map((_: any, index: number) => index)
.reverse();

/**
Expand Down Expand Up @@ -858,6 +860,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
animatedContainerHeight,
scrollableContentOffsetY,
isInTemporaryPosition,
isContentHeightFixed,
isScrollableRefreshable,
shouldHandleKeyboardEvents,
simultaneousHandlers: _providedSimultaneousHandlers,
Expand Down Expand Up @@ -890,6 +893,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
animatedScrollableState,
scrollableContentOffsetY,
isScrollableRefreshable,
isContentHeightFixed,
isInTemporaryPosition,
enableContentPanningGesture,
_providedSimultaneousHandlers,
Expand Down Expand Up @@ -934,9 +938,16 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
() => [_providedStyle, styles.container, containerAnimatedStyle],
[_providedStyle, containerAnimatedStyle]
);
const contentContainerAnimatedStyle = useAnimatedStyle(() => ({
height: animate(animatedContentHeight.value, _providedAnimationConfigs),
}));
const contentContainerAnimatedStyle = useAnimatedStyle(() =>
isContentHeightFixed.value
? {}
: {
height: animate(
animatedContentHeight.value,
_providedAnimationConfigs
),
}
);
const contentContainerStyle = useMemo(
() => [styles.contentContainer, contentContainerAnimatedStyle],
[contentContainerAnimatedStyle]
Expand Down Expand Up @@ -992,6 +1003,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
nextPosition === INITIAL_POSITION ||
nextPosition === animatedContainerHeight.value
) {
isAnimatedOnMount.value = true;
return;
}

Expand Down Expand Up @@ -1274,20 +1286,20 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
// topInset,
// bottomInset,
animatedSheetState,
animatedScrollableState,
isScrollableRefreshable,
scrollableContentOffsetY,
// animatedScrollableState,
// isScrollableRefreshable,
// scrollableContentOffsetY,
// keyboardState,
// animatedIndex,
// animatedCurrentIndex,
// animatedPosition,
// animatedContainerHeight,
// animatedSheetHeight,
// animatedHandleHeight,
// animatedContentHeight,
animatedPosition,
animatedContainerHeight,
animatedSheetHeight,
animatedHandleHeight,
animatedContentHeight,
// keyboardHeight,
// isLayoutCalculated,
isInTemporaryPosition,
isLayoutCalculated,
// isInTemporaryPosition,
}}
/> */}
</BottomSheetContainer>
Expand Down
6 changes: 4 additions & 2 deletions src/components/bottomSheet/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ export interface BottomSheetProps
* snapPoints={[-1, '%100']}
* @type Array<string | number>
*/
snapPoints: Array<string | number>;
snapPoints:
| Array<string | number>
| Animated.SharedValue<Array<string | number>>;
/**
* Defines how violently sheet has to stopped while over dragging.
* @type number
Expand Down Expand Up @@ -92,7 +94,7 @@ export interface BottomSheetProps
* unless `handleHeight` is provided.
* @type number
*/
handleHeight?: number;
handleHeight?: number | Animated.SharedValue<number>;
/**
* Container height helps to calculate the internal sheet layouts,
* if `containerHeight` not provided, the library internally will calculate it,
Expand Down
2 changes: 1 addition & 1 deletion src/components/bottomSheetDebugView/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export const styles = StyleSheet.create({
container: {
position: 'absolute',
right: 4,
top: 0,
top: 50,
padding: 2,
backgroundColor: 'rgba(0, 0,0,0.75)',
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import BottomSheetHandle from '../bottomSheetHandle';
import { useBottomSheetInternal } from '../../hooks';
import { print } from '../../utilities';
import type { BottomSheetHandleContainerProps } from './types';
import { INITIAL_HANDLE_HEIGHT } from '../bottomSheet/constants';

function BottomSheetHandleContainerComponent({
animatedIndex,
Expand Down Expand Up @@ -47,28 +46,25 @@ function BottomSheetHandleContainerComponent({
//#endregion

//#region callbacks
const getHandleContainerLayout = useMemo(
() =>
handleHeight.value === INITIAL_HANDLE_HEIGHT
? function handleContainerLayout({
nativeEvent: {
layout: { height },
},
}: LayoutChangeEvent) {
if (height === handleHeight.value) {
return;
}
handleHeight.value = height;
const handleContainerLayout = useCallback(
function handleContainerLayout({
nativeEvent: {
layout: { height },
},
}: LayoutChangeEvent) {
if (height === handleHeight.value) {
return;
}
handleHeight.value = height;

print({
component: BottomSheetHandleContainer.displayName,
method: 'handleContainerLayout',
params: {
height,
},
});
}
: undefined,
print({
component: BottomSheetHandleContainer.displayName,
method: 'handleContainerLayout',
params: {
height,
},
});
},
[handleHeight]
);
//#endregion
Expand Down Expand Up @@ -109,7 +105,7 @@ function BottomSheetHandleContainerComponent({
accessibilityRole="adjustable"
accessibilityLabel="Bottom Sheet handle"
accessibilityHint="Drag up or down to extend or minimize the Bottom Sheet"
onLayout={getHandleContainerLayout}
onLayout={handleContainerLayout}
>
{renderHandle()}
</Animated.View>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,36 +1,39 @@
import React, { memo, useMemo, useEffect, useCallback } from 'react';
import { View as RNView } from 'react-native';
import { View } from 'react-native';
import { useBottomSheetInternal } from '../../hooks';
import type { BottomSheetViewProps } from './types';
import { styles } from './styles';
import type { BottomSheetViewProps } from './types';

const BottomSheetViewComponent = ({
function BottomSheetViewComponent({
shouldMeasureLayout = false,
style,
focusHook: useFocusHook = useEffect,
children,
...rest
}: BottomSheetViewProps) => {
}: BottomSheetViewProps) {
// hooks
const { scrollableContentOffsetY } = useBottomSheetInternal();
const { scrollableContentOffsetY, isContentHeightFixed } =
useBottomSheetInternal();

// styles
const containerStyle = useMemo(() => [styles.container, style], [style]);

// callback
const handleSettingScrollable = useCallback(() => {
isContentHeightFixed.value = shouldMeasureLayout;
scrollableContentOffsetY.value = 0;
}, [scrollableContentOffsetY]);
}, [isContentHeightFixed, scrollableContentOffsetY, shouldMeasureLayout]);

// effects
useFocusHook(handleSettingScrollable);

//render
return (
<RNView style={containerStyle} {...rest}>
<View style={containerStyle} {...rest}>
{children}
</RNView>
</View>
);
};
}

const BottomSheetView = memo(BottomSheetViewComponent);
BottomSheetView.displayName = 'BottomSheetView';
Expand Down
1 change: 1 addition & 0 deletions src/components/bottomSheetView/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './BottomSheetView';
File renamed without changes.

0 comments on commit 3edb2d1

Please sign in to comment.