Skip to content

Commit 8017fb6

Browse files
authored
feat: added default dynamic sizing (#1513)(with @Eli-Nathan & @ororsatti) (#1683)
1 parent f7af53d commit 8017fb6

16 files changed

+242
-190
lines changed

example/src/screens/advanced/DynamicSnapPointExample.tsx

+15-17
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,20 @@ import React, { useCallback, useMemo, useRef, useState } from 'react';
22
import { View, StyleSheet, Text } from 'react-native';
33
import BottomSheet, {
44
BottomSheetView,
5-
useBottomSheetDynamicSnapPoints,
5+
SNAP_POINT_TYPE,
66
} from '@gorhom/bottom-sheet';
77
import { useSafeAreaInsets } from 'react-native-safe-area-context';
88
import { Button } from '../../components/button';
99

1010
const DynamicSnapPointExample = () => {
1111
// state
1212
const [count, setCount] = useState(0);
13-
const initialSnapPoints = useMemo(() => ['CONTENT_HEIGHT'], []);
1413

1514
// hooks
1615
const { bottom: safeBottomArea } = useSafeAreaInsets();
1716
const bottomSheetRef = useRef<BottomSheet>(null);
18-
const {
19-
animatedHandleHeight,
20-
animatedSnapPoints,
21-
animatedContentHeight,
22-
handleContentLayout,
23-
} = useBottomSheetDynamicSnapPoints(initialSnapPoints);
2417

25-
// callbacks
18+
//#region callbacks
2619
const handleIncreaseContentPress = useCallback(() => {
2720
setCount(state => state + 1);
2821
}, []);
@@ -35,8 +28,16 @@ const DynamicSnapPointExample = () => {
3528
const handleClosePress = useCallback(() => {
3629
bottomSheetRef.current?.close();
3730
}, []);
31+
const handleSheetChange = useCallback(
32+
(index: number, position: number, type: SNAP_POINT_TYPE) => {
33+
// eslint-disable-next-line no-console
34+
console.log('handleSheetChange', { index, position, type });
35+
},
36+
[]
37+
);
38+
//#endregion
3839

39-
// styles
40+
//#region styles
4041
const contentContainerStyle = useMemo(
4142
() => [
4243
styles.contentContainerStyle,
@@ -51,6 +52,7 @@ const DynamicSnapPointExample = () => {
5152
}),
5253
[count]
5354
);
55+
//#endregion
5456

5557
// renders
5658
return (
@@ -59,16 +61,12 @@ const DynamicSnapPointExample = () => {
5961
<Button label="Close" onPress={handleClosePress} />
6062
<BottomSheet
6163
ref={bottomSheetRef}
62-
snapPoints={animatedSnapPoints}
63-
handleHeight={animatedHandleHeight}
64-
contentHeight={animatedContentHeight}
64+
enableDynamicSizing={true}
6565
enablePanDownToClose={true}
6666
animateOnMount={true}
67+
onChange={handleSheetChange}
6768
>
68-
<BottomSheetView
69-
style={contentContainerStyle}
70-
onLayout={handleContentLayout}
71-
>
69+
<BottomSheetView style={contentContainerStyle}>
7270
<Text style={styles.message}>
7371
Could this sheet resize to its content height ?
7472
</Text>

example/src/screens/modal/DynamicSnapPointExample.tsx

+15-17
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { View, StyleSheet, Text } from 'react-native';
33
import {
44
BottomSheetModal,
55
BottomSheetView,
6-
useBottomSheetDynamicSnapPoints,
6+
SNAP_POINT_TYPE,
77
} from '@gorhom/bottom-sheet';
88
import { useSafeAreaInsets } from 'react-native-safe-area-context';
99
import { Button } from '../../components/button';
@@ -12,19 +12,12 @@ import { withModalProvider } from './withModalProvider';
1212
const DynamicSnapPointExample = () => {
1313
// state
1414
const [count, setCount] = useState(0);
15-
const initialSnapPoints = useMemo(() => ['CONTENT_HEIGHT'], []);
1615

1716
// hooks
1817
const { bottom: safeBottomArea } = useSafeAreaInsets();
1918
const bottomSheetRef = useRef<BottomSheetModal>(null);
20-
const {
21-
animatedHandleHeight,
22-
animatedSnapPoints,
23-
animatedContentHeight,
24-
handleContentLayout,
25-
} = useBottomSheetDynamicSnapPoints(initialSnapPoints);
2619

27-
// callbacks
20+
//#region callbacks
2821
const handleIncreaseContentPress = useCallback(() => {
2922
setCount(state => state + 1);
3023
}, []);
@@ -38,8 +31,16 @@ const DynamicSnapPointExample = () => {
3831
const handleDismissPress = useCallback(() => {
3932
bottomSheetRef.current?.dismiss();
4033
}, []);
34+
const handleSheetChange = useCallback(
35+
(index: number, position: number, type: SNAP_POINT_TYPE) => {
36+
// eslint-disable-next-line no-console
37+
console.log('handleSheetChange', { index, position, type });
38+
},
39+
[]
40+
);
41+
//#endregion
4142

42-
// styles
43+
//#region styles
4344
const contentContainerStyle = useMemo(
4445
() => ({
4546
...styles.contentContainerStyle,
@@ -54,6 +55,7 @@ const DynamicSnapPointExample = () => {
5455
}),
5556
[count]
5657
);
58+
//#endregion
5759

5860
// renders
5961
return (
@@ -62,15 +64,11 @@ const DynamicSnapPointExample = () => {
6264
<Button label="Dismiss" onPress={handleDismissPress} />
6365
<BottomSheetModal
6466
ref={bottomSheetRef}
65-
snapPoints={animatedSnapPoints}
66-
handleHeight={animatedHandleHeight}
67-
contentHeight={animatedContentHeight}
67+
enableDynamicSizing={true}
6868
enablePanDownToClose={true}
69+
onChange={handleSheetChange}
6970
>
70-
<BottomSheetView
71-
style={contentContainerStyle}
72-
onLayout={handleContentLayout}
73-
>
71+
<BottomSheetView style={contentContainerStyle}>
7472
<Text style={styles.message}>
7573
Could this sheet modal resize to its content height ?
7674
</Text>

src/components/bottomSheet/BottomSheet.tsx

+38-32
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import {
5151
KEYBOARD_BLUR_BEHAVIOR,
5252
KEYBOARD_INPUT_MODE,
5353
ANIMATION_SOURCE,
54+
SNAP_POINT_TYPE,
5455
} from '../../constants';
5556
import {
5657
animate,
@@ -74,6 +75,7 @@ import {
7475
DEFAULT_ENABLE_PAN_DOWN_TO_CLOSE,
7576
INITIAL_CONTAINER_OFFSET,
7677
INITIAL_VALUE,
78+
DEFAULT_DYNAMIC_SIZING,
7779
} from './constants';
7880
import type { BottomSheetMethods, Insets } from '../../types';
7981
import type { BottomSheetProps, AnimateToPositionType } from './types';
@@ -104,6 +106,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
104106
enableHandlePanningGesture = DEFAULT_ENABLE_HANDLE_PANNING_GESTURE,
105107
enableOverDrag = DEFAULT_ENABLE_OVER_DRAG,
106108
enablePanDownToClose = DEFAULT_ENABLE_PAN_DOWN_TO_CLOSE,
109+
enableDynamicSizing = DEFAULT_DYNAMIC_SIZING,
107110
overDragResistanceFactor = DEFAULT_OVER_DRAG_RESISTANCE_FACTOR,
108111

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

124127
// layout
125-
handleHeight: _providedHandleHeight,
126128
containerHeight: _providedContainerHeight,
127-
contentHeight: _providedContentHeight,
128129
containerOffset: _providedContainerOffset,
129130
topInset = 0,
130131
bottomInset = 0,
132+
maxDynamicContentSize,
131133

132134
// animated callback shared values
133135
animatedPosition: _providedAnimatedPosition,
@@ -181,17 +183,20 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
181183
const animatedContainerOffset = useReactiveSharedValue(
182184
_providedContainerOffset ?? INITIAL_CONTAINER_OFFSET
183185
) as Animated.SharedValue<Insets>;
184-
const animatedHandleHeight = useReactiveSharedValue(
185-
_providedHandleHeight ?? INITIAL_HANDLE_HEIGHT
186+
const animatedHandleHeight = useReactiveSharedValue<number>(
187+
INITIAL_HANDLE_HEIGHT
186188
);
187189
const animatedFooterHeight = useSharedValue(0);
188-
const animatedSnapPoints = useNormalizedSnapPoints(
189-
_providedSnapPoints,
190-
animatedContainerHeight,
191-
topInset,
192-
bottomInset,
193-
$modal
194-
);
190+
const animatedContentHeight = useSharedValue(INITIAL_CONTAINER_HEIGHT);
191+
const [animatedSnapPoints, animatedDynamicSnapPointIndex] =
192+
useNormalizedSnapPoints(
193+
_providedSnapPoints,
194+
animatedContainerHeight,
195+
animatedContentHeight,
196+
animatedHandleHeight,
197+
enableDynamicSizing,
198+
maxDynamicContentSize
199+
);
195200
const animatedHighestSnapPoint = useDerivedValue(
196201
() => animatedSnapPoints.value[animatedSnapPoints.value.length - 1]
197202
);
@@ -232,14 +237,6 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
232237
}
233238

234239
let isHandleHeightCalculated = false;
235-
// handle height is provided.
236-
if (
237-
_providedHandleHeight !== null &&
238-
_providedHandleHeight !== undefined &&
239-
typeof _providedHandleHeight === 'number'
240-
) {
241-
isHandleHeightCalculated = true;
242-
}
243240
// handle component is null.
244241
if (handleComponent === null) {
245242
animatedHandleHeight.value = 0;
@@ -388,7 +385,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
388385
return SCROLLABLE_STATE.LOCKED;
389386
});
390387
// dynamic
391-
const animatedContentHeight = useDerivedValue(() => {
388+
const animatedContentHeightMax = useDerivedValue(() => {
392389
const keyboardHeightInContainer = animatedKeyboardHeightInContainer.value;
393390
const handleHeight = Math.max(0, animatedHandleHeight.value);
394391
let contentHeight = animatedSheetHeight.value - handleHeight;
@@ -590,7 +587,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
590587
]
591588
);
592589
const handleOnChange = useCallback(
593-
function handleOnChange(index: number) {
590+
function handleOnChange(index: number, position: number) {
594591
print({
595592
component: BottomSheet.name,
596593
method: handleOnChange.name,
@@ -601,10 +598,16 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
601598
});
602599

603600
if (_providedOnChange) {
604-
_providedOnChange(index);
601+
_providedOnChange(
602+
index,
603+
position,
604+
index === animatedDynamicSnapPointIndex.value
605+
? SNAP_POINT_TYPE.DYNAMIC
606+
: SNAP_POINT_TYPE.PROVIDED
607+
);
605608
}
606609
},
607-
[_providedOnChange, animatedCurrentIndex]
610+
[_providedOnChange, animatedCurrentIndex, animatedDynamicSnapPointIndex]
608611
);
609612
const handleOnAnimate = useCallback(
610613
function handleOnAnimate(toPoint: number) {
@@ -854,9 +857,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
854857
*/
855858
const nextPosition = normalizeSnapPoint(
856859
position,
857-
animatedContainerHeight.value,
858-
topInset,
859-
bottomInset
860+
animatedContainerHeight.value
860861
);
861862

862863
/**
@@ -1101,6 +1102,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
11011102
const internalContextVariables = useMemo(
11021103
() => ({
11031104
enableContentPanningGesture,
1105+
enableDynamicSizing,
11041106
overDragResistanceFactor,
11051107
enableOverDrag,
11061108
enablePanDownToClose,
@@ -1168,6 +1170,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
11681170
overDragResistanceFactor,
11691171
enableOverDrag,
11701172
enablePanDownToClose,
1173+
enableDynamicSizing,
11711174
_providedSimultaneousHandlers,
11721175
_providedWaitFor,
11731176
_providedActiveOffsetX,
@@ -1223,20 +1226,23 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
12231226
);
12241227
const contentContainerAnimatedStyle = useAnimatedStyle(() => {
12251228
/**
1226-
* if content height was provided, then we skip setting
1227-
* calculated height.
1229+
* if dynamic sizing is enabled, and content height
1230+
* is still not set, then we exit method.
12281231
*/
1229-
if (_providedContentHeight) {
1232+
if (
1233+
enableDynamicSizing &&
1234+
animatedContentHeight.value === INITIAL_CONTAINER_HEIGHT
1235+
) {
12301236
return {};
12311237
}
12321238

12331239
return {
12341240
height: animate({
1235-
point: animatedContentHeight.value,
1241+
point: animatedContentHeightMax.value,
12361242
configs: _providedAnimationConfigs,
12371243
}),
12381244
};
1239-
}, [animatedContentHeight, _providedContentHeight]);
1245+
}, [animatedContentHeightMax, enableDynamicSizing, animatedContentHeight]);
12401246
const contentContainerStyle = useMemo(
12411247
() => [styles.contentContainer, contentContainerAnimatedStyle],
12421248
[contentContainerAnimatedStyle]
@@ -1620,7 +1626,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
16201626
});
16211627

16221628
animatedCurrentIndex.value = _animatedIndex;
1623-
runOnJS(handleOnChange)(_animatedIndex);
1629+
runOnJS(handleOnChange)(_animatedIndex, _animatedPosition);
16241630
}
16251631

16261632
/**

src/components/bottomSheet/constants.ts

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const DEFAULT_ENABLE_HANDLE_PANNING_GESTURE = true;
1313
const DEFAULT_ENABLE_OVER_DRAG = true;
1414
const DEFAULT_ENABLE_PAN_DOWN_TO_CLOSE = false;
1515
const DEFAULT_ANIMATE_ON_MOUNT = true;
16+
const DEFAULT_DYNAMIC_SIZING = true;
1617

1718
// keyboard
1819
const DEFAULT_KEYBOARD_BEHAVIOR = KEYBOARD_BEHAVIOR.interactive;
@@ -39,6 +40,7 @@ export {
3940
DEFAULT_ENABLE_HANDLE_PANNING_GESTURE,
4041
DEFAULT_ENABLE_OVER_DRAG,
4142
DEFAULT_ENABLE_PAN_DOWN_TO_CLOSE,
43+
DEFAULT_DYNAMIC_SIZING,
4244
DEFAULT_ANIMATE_ON_MOUNT,
4345
// keyboard
4446
DEFAULT_KEYBOARD_BEHAVIOR,

0 commit comments

Comments
 (0)