Skip to content

Commit

Permalink
feat: added default dynamic sizing (gorhom#1513)(with @Eli-Nathan & @…
Browse files Browse the repository at this point in the history
  • Loading branch information
gorhom committed Jan 3, 2024
1 parent f7af53d commit 8017fb6
Show file tree
Hide file tree
Showing 16 changed files with 242 additions and 190 deletions.
32 changes: 15 additions & 17 deletions example/src/screens/advanced/DynamicSnapPointExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,20 @@ import React, { useCallback, useMemo, useRef, useState } from 'react';
import { View, StyleSheet, Text } from 'react-native';
import BottomSheet, {
BottomSheetView,
useBottomSheetDynamicSnapPoints,
SNAP_POINT_TYPE,
} 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 initialSnapPoints = useMemo(() => ['CONTENT_HEIGHT'], []);

// hooks
const { bottom: safeBottomArea } = useSafeAreaInsets();
const bottomSheetRef = useRef<BottomSheet>(null);
const {
animatedHandleHeight,
animatedSnapPoints,
animatedContentHeight,
handleContentLayout,
} = useBottomSheetDynamicSnapPoints(initialSnapPoints);

// callbacks
//#region callbacks
const handleIncreaseContentPress = useCallback(() => {
setCount(state => state + 1);
}, []);
Expand All @@ -35,8 +28,16 @@ const DynamicSnapPointExample = () => {
const handleClosePress = useCallback(() => {
bottomSheetRef.current?.close();
}, []);
const handleSheetChange = useCallback(
(index: number, position: number, type: SNAP_POINT_TYPE) => {
// eslint-disable-next-line no-console
console.log('handleSheetChange', { index, position, type });
},
[]
);
//#endregion

// styles
//#region styles
const contentContainerStyle = useMemo(
() => [
styles.contentContainerStyle,
Expand All @@ -51,6 +52,7 @@ const DynamicSnapPointExample = () => {
}),
[count]
);
//#endregion

// renders
return (
Expand All @@ -59,16 +61,12 @@ const DynamicSnapPointExample = () => {
<Button label="Close" onPress={handleClosePress} />
<BottomSheet
ref={bottomSheetRef}
snapPoints={animatedSnapPoints}
handleHeight={animatedHandleHeight}
contentHeight={animatedContentHeight}
enableDynamicSizing={true}
enablePanDownToClose={true}
animateOnMount={true}
onChange={handleSheetChange}
>
<BottomSheetView
style={contentContainerStyle}
onLayout={handleContentLayout}
>
<BottomSheetView style={contentContainerStyle}>
<Text style={styles.message}>
Could this sheet resize to its content height ?
</Text>
Expand Down
32 changes: 15 additions & 17 deletions example/src/screens/modal/DynamicSnapPointExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { View, StyleSheet, Text } from 'react-native';
import {
BottomSheetModal,
BottomSheetView,
useBottomSheetDynamicSnapPoints,
SNAP_POINT_TYPE,
} from '@gorhom/bottom-sheet';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { Button } from '../../components/button';
Expand All @@ -12,19 +12,12 @@ import { withModalProvider } from './withModalProvider';
const DynamicSnapPointExample = () => {
// state
const [count, setCount] = useState(0);
const initialSnapPoints = useMemo(() => ['CONTENT_HEIGHT'], []);

// hooks
const { bottom: safeBottomArea } = useSafeAreaInsets();
const bottomSheetRef = useRef<BottomSheetModal>(null);
const {
animatedHandleHeight,
animatedSnapPoints,
animatedContentHeight,
handleContentLayout,
} = useBottomSheetDynamicSnapPoints(initialSnapPoints);

// callbacks
//#region callbacks
const handleIncreaseContentPress = useCallback(() => {
setCount(state => state + 1);
}, []);
Expand All @@ -38,8 +31,16 @@ const DynamicSnapPointExample = () => {
const handleDismissPress = useCallback(() => {
bottomSheetRef.current?.dismiss();
}, []);
const handleSheetChange = useCallback(
(index: number, position: number, type: SNAP_POINT_TYPE) => {
// eslint-disable-next-line no-console
console.log('handleSheetChange', { index, position, type });
},
[]
);
//#endregion

// styles
//#region styles
const contentContainerStyle = useMemo(
() => ({
...styles.contentContainerStyle,
Expand All @@ -54,6 +55,7 @@ const DynamicSnapPointExample = () => {
}),
[count]
);
//#endregion

// renders
return (
Expand All @@ -62,15 +64,11 @@ const DynamicSnapPointExample = () => {
<Button label="Dismiss" onPress={handleDismissPress} />
<BottomSheetModal
ref={bottomSheetRef}
snapPoints={animatedSnapPoints}
handleHeight={animatedHandleHeight}
contentHeight={animatedContentHeight}
enableDynamicSizing={true}
enablePanDownToClose={true}
onChange={handleSheetChange}
>
<BottomSheetView
style={contentContainerStyle}
onLayout={handleContentLayout}
>
<BottomSheetView style={contentContainerStyle}>
<Text style={styles.message}>
Could this sheet modal resize to its content height ?
</Text>
Expand Down
70 changes: 38 additions & 32 deletions src/components/bottomSheet/BottomSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import {
KEYBOARD_BLUR_BEHAVIOR,
KEYBOARD_INPUT_MODE,
ANIMATION_SOURCE,
SNAP_POINT_TYPE,
} from '../../constants';
import {
animate,
Expand All @@ -74,6 +75,7 @@ import {
DEFAULT_ENABLE_PAN_DOWN_TO_CLOSE,
INITIAL_CONTAINER_OFFSET,
INITIAL_VALUE,
DEFAULT_DYNAMIC_SIZING,
} from './constants';
import type { BottomSheetMethods, Insets } from '../../types';
import type { BottomSheetProps, AnimateToPositionType } from './types';
Expand Down Expand Up @@ -104,6 +106,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
enableHandlePanningGesture = DEFAULT_ENABLE_HANDLE_PANNING_GESTURE,
enableOverDrag = DEFAULT_ENABLE_OVER_DRAG,
enablePanDownToClose = DEFAULT_ENABLE_PAN_DOWN_TO_CLOSE,
enableDynamicSizing = DEFAULT_DYNAMIC_SIZING,
overDragResistanceFactor = DEFAULT_OVER_DRAG_RESISTANCE_FACTOR,

// styles
Expand All @@ -122,12 +125,11 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
android_keyboardInputMode = DEFAULT_KEYBOARD_INPUT_MODE,

// layout
handleHeight: _providedHandleHeight,
containerHeight: _providedContainerHeight,
contentHeight: _providedContentHeight,
containerOffset: _providedContainerOffset,
topInset = 0,
bottomInset = 0,
maxDynamicContentSize,

// animated callback shared values
animatedPosition: _providedAnimatedPosition,
Expand Down Expand Up @@ -181,17 +183,20 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
const animatedContainerOffset = useReactiveSharedValue(
_providedContainerOffset ?? INITIAL_CONTAINER_OFFSET
) as Animated.SharedValue<Insets>;
const animatedHandleHeight = useReactiveSharedValue(
_providedHandleHeight ?? INITIAL_HANDLE_HEIGHT
const animatedHandleHeight = useReactiveSharedValue<number>(
INITIAL_HANDLE_HEIGHT
);
const animatedFooterHeight = useSharedValue(0);
const animatedSnapPoints = useNormalizedSnapPoints(
_providedSnapPoints,
animatedContainerHeight,
topInset,
bottomInset,
$modal
);
const animatedContentHeight = useSharedValue(INITIAL_CONTAINER_HEIGHT);
const [animatedSnapPoints, animatedDynamicSnapPointIndex] =
useNormalizedSnapPoints(
_providedSnapPoints,
animatedContainerHeight,
animatedContentHeight,
animatedHandleHeight,
enableDynamicSizing,
maxDynamicContentSize
);
const animatedHighestSnapPoint = useDerivedValue(
() => animatedSnapPoints.value[animatedSnapPoints.value.length - 1]
);
Expand Down Expand Up @@ -232,14 +237,6 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
}

let isHandleHeightCalculated = false;
// handle height is provided.
if (
_providedHandleHeight !== null &&
_providedHandleHeight !== undefined &&
typeof _providedHandleHeight === 'number'
) {
isHandleHeightCalculated = true;
}
// handle component is null.
if (handleComponent === null) {
animatedHandleHeight.value = 0;
Expand Down Expand Up @@ -388,7 +385,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
return SCROLLABLE_STATE.LOCKED;
});
// dynamic
const animatedContentHeight = useDerivedValue(() => {
const animatedContentHeightMax = useDerivedValue(() => {
const keyboardHeightInContainer = animatedKeyboardHeightInContainer.value;
const handleHeight = Math.max(0, animatedHandleHeight.value);
let contentHeight = animatedSheetHeight.value - handleHeight;
Expand Down Expand Up @@ -590,7 +587,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
]
);
const handleOnChange = useCallback(
function handleOnChange(index: number) {
function handleOnChange(index: number, position: number) {
print({
component: BottomSheet.name,
method: handleOnChange.name,
Expand All @@ -601,10 +598,16 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
});

if (_providedOnChange) {
_providedOnChange(index);
_providedOnChange(
index,
position,
index === animatedDynamicSnapPointIndex.value
? SNAP_POINT_TYPE.DYNAMIC
: SNAP_POINT_TYPE.PROVIDED
);
}
},
[_providedOnChange, animatedCurrentIndex]
[_providedOnChange, animatedCurrentIndex, animatedDynamicSnapPointIndex]
);
const handleOnAnimate = useCallback(
function handleOnAnimate(toPoint: number) {
Expand Down Expand Up @@ -854,9 +857,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
*/
const nextPosition = normalizeSnapPoint(
position,
animatedContainerHeight.value,
topInset,
bottomInset
animatedContainerHeight.value
);

/**
Expand Down Expand Up @@ -1101,6 +1102,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
const internalContextVariables = useMemo(
() => ({
enableContentPanningGesture,
enableDynamicSizing,
overDragResistanceFactor,
enableOverDrag,
enablePanDownToClose,
Expand Down Expand Up @@ -1168,6 +1170,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
overDragResistanceFactor,
enableOverDrag,
enablePanDownToClose,
enableDynamicSizing,
_providedSimultaneousHandlers,
_providedWaitFor,
_providedActiveOffsetX,
Expand Down Expand Up @@ -1223,20 +1226,23 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
);
const contentContainerAnimatedStyle = useAnimatedStyle(() => {
/**
* if content height was provided, then we skip setting
* calculated height.
* if dynamic sizing is enabled, and content height
* is still not set, then we exit method.
*/
if (_providedContentHeight) {
if (
enableDynamicSizing &&
animatedContentHeight.value === INITIAL_CONTAINER_HEIGHT
) {
return {};
}

return {
height: animate({
point: animatedContentHeight.value,
point: animatedContentHeightMax.value,
configs: _providedAnimationConfigs,
}),
};
}, [animatedContentHeight, _providedContentHeight]);
}, [animatedContentHeightMax, enableDynamicSizing, animatedContentHeight]);
const contentContainerStyle = useMemo(
() => [styles.contentContainer, contentContainerAnimatedStyle],
[contentContainerAnimatedStyle]
Expand Down Expand Up @@ -1620,7 +1626,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
});

animatedCurrentIndex.value = _animatedIndex;
runOnJS(handleOnChange)(_animatedIndex);
runOnJS(handleOnChange)(_animatedIndex, _animatedPosition);
}

/**
Expand Down
2 changes: 2 additions & 0 deletions src/components/bottomSheet/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const DEFAULT_ENABLE_HANDLE_PANNING_GESTURE = true;
const DEFAULT_ENABLE_OVER_DRAG = true;
const DEFAULT_ENABLE_PAN_DOWN_TO_CLOSE = false;
const DEFAULT_ANIMATE_ON_MOUNT = true;
const DEFAULT_DYNAMIC_SIZING = true;

// keyboard
const DEFAULT_KEYBOARD_BEHAVIOR = KEYBOARD_BEHAVIOR.interactive;
Expand All @@ -39,6 +40,7 @@ export {
DEFAULT_ENABLE_HANDLE_PANNING_GESTURE,
DEFAULT_ENABLE_OVER_DRAG,
DEFAULT_ENABLE_PAN_DOWN_TO_CLOSE,
DEFAULT_DYNAMIC_SIZING,
DEFAULT_ANIMATE_ON_MOUNT,
// keyboard
DEFAULT_KEYBOARD_BEHAVIOR,
Expand Down
Loading

0 comments on commit 8017fb6

Please sign in to comment.