Skip to content

Commit 7330c7c

Browse files
authored
feat: added dynamic sizing (#1513)(with @Eli-Nathan & @ororsatti)
* feat: added dynamic sizing * chore: updated dynamic sizing example * chore: removed commented code * chore: added deprecated tag to useBottomSheetDynamicSnapPoints * chore: added extra description for snap points prop
1 parent 43de6d7 commit 7330c7c

File tree

12 files changed

+178
-93
lines changed

12 files changed

+178
-93
lines changed

example/app/src/screens/advanced/DynamicSnapPointExample.tsx

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,16 @@
11
import React, { useCallback, useMemo, useRef, useState } from 'react';
22
import { View, StyleSheet, Text } from 'react-native';
3-
import BottomSheet, {
4-
BottomSheetView,
5-
useBottomSheetDynamicSnapPoints,
6-
} from '@gorhom/bottom-sheet';
3+
import BottomSheet, { BottomSheetView } from '@gorhom/bottom-sheet';
74
import { useSafeAreaInsets } from 'react-native-safe-area-context';
85
import { Button } from '../../components/button';
96

107
const DynamicSnapPointExample = () => {
118
// state
129
const [count, setCount] = useState(0);
13-
const initialSnapPoints = useMemo(() => ['CONTENT_HEIGHT'], []);
1410

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

2515
// callbacks
2616
const handleIncreaseContentPress = useCallback(() => {
@@ -59,16 +49,11 @@ const DynamicSnapPointExample = () => {
5949
<Button label="Close" onPress={handleClosePress} />
6050
<BottomSheet
6151
ref={bottomSheetRef}
62-
snapPoints={animatedSnapPoints}
63-
handleHeight={animatedHandleHeight}
64-
contentHeight={animatedContentHeight}
52+
enableDynamicSizing={true}
6553
enablePanDownToClose={true}
6654
animateOnMount={true}
6755
>
68-
<BottomSheetView
69-
style={contentContainerStyle}
70-
onLayout={handleContentLayout}
71-
>
56+
<BottomSheetView style={contentContainerStyle}>
7257
<Text style={styles.message}>
7358
Could this sheet resize to its content height ?
7459
</Text>

example/app/src/screens/modal/DynamicSnapPointExample.tsx

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,17 @@
11
import React, { useCallback, useMemo, useRef, useState } from 'react';
22
import { View, StyleSheet, Text } from 'react-native';
3-
import {
4-
BottomSheetModal,
5-
BottomSheetView,
6-
useBottomSheetDynamicSnapPoints,
7-
} from '@gorhom/bottom-sheet';
3+
import { BottomSheetModal, BottomSheetView } from '@gorhom/bottom-sheet';
84
import { useSafeAreaInsets } from 'react-native-safe-area-context';
95
import { Button } from '../../components/button';
106
import { withModalProvider } from './withModalProvider';
117

128
const DynamicSnapPointExample = () => {
139
// state
1410
const [count, setCount] = useState(0);
15-
const initialSnapPoints = useMemo(() => ['CONTENT_HEIGHT'], []);
1611

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

2716
// callbacks
2817
const handleIncreaseContentPress = useCallback(() => {
@@ -62,15 +51,10 @@ const DynamicSnapPointExample = () => {
6251
<Button label="Dismiss" onPress={handleDismissPress} />
6352
<BottomSheetModal
6453
ref={bottomSheetRef}
65-
snapPoints={animatedSnapPoints}
66-
handleHeight={animatedHandleHeight}
67-
contentHeight={animatedContentHeight}
54+
enableDynamicSizing={true}
6855
enablePanDownToClose={true}
6956
>
70-
<BottomSheetView
71-
style={contentContainerStyle}
72-
onLayout={handleContentLayout}
73-
>
57+
<BottomSheetView style={contentContainerStyle}>
7458
<Text style={styles.message}>
7559
Could this sheet modal resize to its content height ?
7660
</Text>

src/components/bottomSheet/BottomSheet.tsx

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ import {
7474
DEFAULT_ENABLE_PAN_DOWN_TO_CLOSE,
7575
INITIAL_CONTAINER_OFFSET,
7676
INITIAL_VALUE,
77+
DEFAULT_DYNAMIC_SIZING,
7778
} from './constants';
7879
import type { BottomSheetMethods, Insets } from '../../types';
7980
import type { BottomSheetProps, AnimateToPositionType } from './types';
@@ -104,6 +105,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
104105
enableHandlePanningGesture = DEFAULT_ENABLE_HANDLE_PANNING_GESTURE,
105106
enableOverDrag = DEFAULT_ENABLE_OVER_DRAG,
106107
enablePanDownToClose = DEFAULT_ENABLE_PAN_DOWN_TO_CLOSE,
108+
enableDynamicSizing = DEFAULT_DYNAMIC_SIZING,
107109
overDragResistanceFactor = DEFAULT_OVER_DRAG_RESISTANCE_FACTOR,
108110

109111
// styles
@@ -128,6 +130,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
128130
containerOffset: _providedContainerOffset,
129131
topInset = 0,
130132
bottomInset = 0,
133+
maxDynamicContentSize,
131134

132135
// animated callback shared values
133136
animatedPosition: _providedAnimatedPosition,
@@ -185,12 +188,14 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
185188
_providedHandleHeight ?? INITIAL_HANDLE_HEIGHT
186189
);
187190
const animatedFooterHeight = useSharedValue(0);
191+
const animatedContentHeight = useSharedValue(INITIAL_CONTAINER_HEIGHT);
188192
const animatedSnapPoints = useNormalizedSnapPoints(
189193
_providedSnapPoints,
190194
animatedContainerHeight,
191-
topInset,
192-
bottomInset,
193-
$modal
195+
animatedContentHeight,
196+
animatedHandleHeight,
197+
enableDynamicSizing,
198+
maxDynamicContentSize
194199
);
195200
const animatedHighestSnapPoint = useDerivedValue(
196201
() => animatedSnapPoints.value[animatedSnapPoints.value.length - 1]
@@ -388,7 +393,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
388393
return SCROLLABLE_STATE.LOCKED;
389394
});
390395
// dynamic
391-
const animatedContentHeight = useDerivedValue(() => {
396+
const animatedContentHeightMax = useDerivedValue(() => {
392397
const keyboardHeightInContainer = animatedKeyboardHeightInContainer.value;
393398
const handleHeight = Math.max(0, animatedHandleHeight.value);
394399
let contentHeight = animatedSheetHeight.value - handleHeight;
@@ -807,9 +812,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
807812
*/
808813
const nextPosition = normalizeSnapPoint(
809814
position,
810-
animatedContainerHeight.value,
811-
topInset,
812-
bottomInset
815+
animatedContainerHeight.value
813816
);
814817

815818
/**
@@ -1054,6 +1057,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
10541057
const internalContextVariables = useMemo(
10551058
() => ({
10561059
enableContentPanningGesture,
1060+
enableDynamicSizing,
10571061
overDragResistanceFactor,
10581062
enableOverDrag,
10591063
enablePanDownToClose,
@@ -1121,6 +1125,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
11211125
overDragResistanceFactor,
11221126
enableOverDrag,
11231127
enablePanDownToClose,
1128+
enableDynamicSizing,
11241129
_providedSimultaneousHandlers,
11251130
_providedWaitFor,
11261131
_providedActiveOffsetX,
@@ -1175,6 +1180,17 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
11751180
[_providedStyle, containerAnimatedStyle]
11761181
);
11771182
const contentContainerAnimatedStyle = useAnimatedStyle(() => {
1183+
/**
1184+
* if dynamic sizing is enabled, and content height
1185+
* is still not set, then we exit method.
1186+
*/
1187+
if (
1188+
enableDynamicSizing &&
1189+
animatedContentHeight.value === INITIAL_CONTAINER_HEIGHT
1190+
) {
1191+
return {};
1192+
}
1193+
11781194
/**
11791195
* if content height was provided, then we skip setting
11801196
* calculated height.
@@ -1185,11 +1201,11 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
11851201

11861202
return {
11871203
height: animate({
1188-
point: animatedContentHeight.value,
1204+
point: animatedContentHeightMax.value,
11891205
configs: _providedAnimationConfigs,
11901206
}),
11911207
};
1192-
}, [animatedContentHeight, _providedContentHeight]);
1208+
}, [animatedContentHeightMax, enableDynamicSizing, animatedContentHeight]);
11931209
const contentContainerStyle = useMemo(
11941210
() => [styles.contentContainer, contentContainerAnimatedStyle],
11951211
[contentContainerAnimatedStyle]
@@ -1664,18 +1680,18 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
16641680
// topInset,
16651681
// bottomInset,
16661682
animatedSheetState,
1667-
animatedScrollableState,
1668-
animatedScrollableOverrideState,
1683+
// animatedScrollableState,
1684+
// animatedScrollableOverrideState,
16691685
// isScrollableRefreshable,
16701686
// animatedScrollableContentOffsetY,
16711687
// keyboardState,
16721688
// animatedIndex,
16731689
// animatedCurrentIndex,
16741690
// animatedPosition,
1675-
// animatedContainerHeight,
1676-
// animatedSheetHeight,
1677-
// animatedHandleHeight,
1678-
// animatedContentHeight,
1691+
animatedContainerHeight,
1692+
animatedSheetHeight,
1693+
animatedHandleHeight,
1694+
animatedContentHeight,
16791695
// // keyboardHeight,
16801696
// isLayoutCalculated,
16811697
// isContentHeightFixed,

src/components/bottomSheet/constants.ts

Lines changed: 2 additions & 0 deletions
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 = false;
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,

src/components/bottomSheet/types.d.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,15 @@ export interface BottomSheetProps
4242
/**
4343
* Points for the bottom sheet to snap to. It accepts array of number, string or mix.
4444
* String values should be a percentage.
45+
*
46+
* ⚠️ This prop is required unless you set `enableDynamicSizing` to `true`.
4547
* @example
4648
* snapPoints={[200, 500]}
4749
* snapPoints={[200, '%50']}
4850
* snapPoints={['%100']}
4951
* @type Array<string | number>
5052
*/
51-
snapPoints: Array<string | number> | SharedValue<Array<string | number>>;
53+
snapPoints?: Array<string | number> | SharedValue<Array<string | number>>;
5254
/**
5355
* Defines how violently sheet has to be stopped while over dragging.
5456
* @type number
@@ -85,6 +87,13 @@ export interface BottomSheetProps
8587
* @default false
8688
*/
8789
enablePanDownToClose?: boolean;
90+
/**
91+
* Enable dynamic sizing for content view and scrollable
92+
* content size.
93+
* @type boolean
94+
* @default false
95+
*/
96+
enableDynamicSizing?: boolean;
8897
/**
8998
* To start the sheet closed and snap to initial index when it's mounted.
9099
* @type boolean
@@ -133,6 +142,13 @@ export interface BottomSheetProps
133142
* @default 0
134143
*/
135144
bottomInset?: number;
145+
/**
146+
* Max dynamic content size height to limit the bottom sheet height
147+
* from exceeding a provided size.
148+
* @type number
149+
* @default container height
150+
*/
151+
maxDynamicContentSize?: number;
136152
//#endregion
137153

138154
//#region keyboard

src/components/bottomSheetScrollable/createBottomSheetScrollableComponent.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
useScrollHandler,
99
useScrollableSetter,
1010
useBottomSheetInternal,
11+
useStableCallback,
1112
} from '../../hooks';
1213
import {
1314
GESTURE_SOURCE,
@@ -41,6 +42,7 @@ export function createBottomSheetScrollableComponent<T, P>(
4142
onScroll,
4243
onScrollBeginDrag,
4344
onScrollEndDrag,
45+
onContentSizeChange,
4446
...rest
4547
}: any = props;
4648

@@ -61,6 +63,7 @@ export function createBottomSheetScrollableComponent<T, P>(
6163
enableContentPanningGesture,
6264
animatedFooterHeight,
6365
animatedScrollableState,
66+
animatedContentHeight,
6467
} = useBottomSheetInternal();
6568
//#endregion
6669

@@ -77,6 +80,18 @@ export function createBottomSheetScrollableComponent<T, P>(
7780
);
7881
//#endregion
7982

83+
//#region callbacks
84+
const handleContentSizeChange = useStableCallback(
85+
(contentWidth: number, contentHeight: number) => {
86+
animatedContentHeight.value = contentHeight;
87+
88+
if (onContentSizeChange) {
89+
onContentSizeChange(contentWidth, contentHeight);
90+
}
91+
}
92+
);
93+
//#endregion
94+
8095
//#region styles
8196
const containerAnimatedStyle = useAnimatedStyle(
8297
() => ({
@@ -124,6 +139,7 @@ export function createBottomSheetScrollableComponent<T, P>(
124139
overScrollMode={overScrollMode}
125140
keyboardDismissMode={keyboardDismissMode}
126141
onScroll={scrollHandler}
142+
onContentSizeChange={handleContentSizeChange}
127143
style={containerStyle}
128144
/>
129145
</NativeViewGestureHandler>
@@ -174,6 +190,7 @@ export function createBottomSheetScrollableComponent<T, P>(
174190
progressViewOffset={progressViewOffset}
175191
refreshControl={refreshControl}
176192
onScroll={scrollHandler}
193+
onContentSizeChange={handleContentSizeChange}
177194
style={containerStyle}
178195
/>
179196
</NativeViewGestureHandler>

0 commit comments

Comments
 (0)