Skip to content

Commit

Permalink
fix: sheet positioning on modals
Browse files Browse the repository at this point in the history
  • Loading branch information
gorhom committed May 12, 2021
1 parent 1f103b0 commit ee573e9
Show file tree
Hide file tree
Showing 10 changed files with 91 additions and 52 deletions.
4 changes: 2 additions & 2 deletions example/src/screens/integrations/MapExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const MapExample = () => {
() => [
bottomSafeArea === 0 ? SEARCH_HANDLE_HEIGHT / 2 : bottomSafeArea,
LOCATION_DETAILS_HEIGHT + bottomSafeArea,
SCREEN_HEIGHT,
'100%',
],
[bottomSafeArea]
);
Expand Down Expand Up @@ -180,7 +180,7 @@ const MapExample = () => {
snapPoints={poiListSnapPoints}
handleHeight={SEARCH_HANDLE_HEIGHT}
topInset={headerHeight}
dismissOnPanDown={false}
enableDismissOnClose={false}
keyboardBehavior="extend"
animatedPosition={animatedPOIListPosition}
animatedIndex={animatedPOIListIndex}
Expand Down
31 changes: 26 additions & 5 deletions src/components/bottomSheet/BottomSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
onChange: _providedOnChange,
onAnimate: _providedOnAnimate,

// private
$modal = false,

// components
handleComponent,
backdropComponent,
Expand All @@ -136,16 +139,33 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
//#endregion

//#region layout variables
const animatedContainerHeight = useReactiveSharedValue(
/**
* This variable is consider an internal variable,
* that will be used conditionally in `animatedContainerHeight`
*/
const _animatedContainerHeight = useReactiveSharedValue(
_providedContainerHeight ?? INITIAL_CONTAINER_HEIGHT
);
/**
* This is a conditional variable, where if the `BottomSheet` is used
* in a modal, then it will subset vertical insets (top+bottom) from
* provided container height.
*/
const animatedContainerHeight = useDerivedValue(() => {
const verticalInset = topInset + bottomInset;
return $modal
? _animatedContainerHeight.value - verticalInset
: _animatedContainerHeight.value;
});
const animatedHandleHeight = useReactiveSharedValue(
_providedHandleHeight ?? INITIAL_HANDLE_HEIGHT
);
const animatedSnapPoints = useNormalizedSnapPoints(
_providedSnapPoints,
animatedContainerHeight,
topInset
topInset,
bottomInset,
$modal
);
const animatedLastSnapPoint = useDerivedValue(
() => animatedSnapPoints.value[animatedSnapPoints.value.length - 1]
Expand Down Expand Up @@ -721,7 +741,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
const containerAnimatedStyle = useAnimatedStyle(() => ({
transform: [
{
translateY: Math.max(animatedPosition.value, topInset),
translateY: animatedPosition.value,
},
],
}));
Expand Down Expand Up @@ -911,7 +931,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
() => animatedPosition.value,
_animatedPosition => {
if (_providedAnimatedPosition) {
_providedAnimatedPosition.value = _animatedPosition;
_providedAnimatedPosition.value = _animatedPosition + topInset;
}
}
);
Expand Down Expand Up @@ -990,7 +1010,8 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
/>
<BottomSheetContainer
key="BottomSheetContainer"
containerHeight={animatedContainerHeight}
shouldCalculateHeight={!$modal}
containerHeight={_animatedContainerHeight}
topInset={topInset}
bottomInset={bottomInset}
>
Expand Down
8 changes: 8 additions & 0 deletions src/components/bottomSheet/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,14 @@ export interface BottomSheetProps
*/
children: (() => React.ReactNode) | React.ReactNode[] | React.ReactNode;
//#endregion

//#region private
/**
* An indicator whether if the sheet running in a modal.
* @type boolean
*/
$modal?: boolean;
//#endregion
}

export interface BottomSheetAnimationConfigs {
Expand Down
46 changes: 21 additions & 25 deletions src/components/bottomSheetContainer/BottomSheetContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import React, { memo, useMemo } from 'react';
import React, { memo, useCallback, useMemo } from 'react';
import { LayoutChangeEvent, View } from 'react-native';
import { print } from '../../utilities';
import { INITIAL_CONTAINER_HEIGHT } from '../bottomSheet/constants';
import { styles } from './styles';
import type { BottomSheetContainerProps } from './types';

function BottomSheetContainerComponent({
containerHeight,
topInset = 0,
bottomInset = 0,
shouldCalculateHeight = true,
children,
}: BottomSheetContainerProps) {
//#region styles
Expand All @@ -25,28 +25,24 @@ function BottomSheetContainerComponent({
//#endregion

//#region callbacks
const getHandleContainerLayout = useMemo(
() =>
containerHeight.value === INITIAL_CONTAINER_HEIGHT
? function handleContainerLayout({
nativeEvent: {
layout: { height },
},
}: LayoutChangeEvent) {
if (height === containerHeight.value) {
return;
}
containerHeight.value = height;
print({
component: BottomSheetContainer.displayName,
method: 'handleContainerLayout',
params: {
height,
},
});
}
: undefined,

const handleContainerLayout = useCallback(
function handleContainerLayout({
nativeEvent: {
layout: { height },
},
}: LayoutChangeEvent) {
if (height === containerHeight.value) {
return;
}
containerHeight.value = height;
print({
component: BottomSheetContainer.displayName,
method: 'handleContainerLayout',
params: {
height,
},
});
},
[containerHeight]
);
//#endregion
Expand All @@ -55,7 +51,7 @@ function BottomSheetContainerComponent({
return (
<View
pointerEvents="box-none"
onLayout={getHandleContainerLayout}
onLayout={shouldCalculateHeight ? handleContainerLayout : undefined}
style={containerStyle}
children={children}
/>
Expand Down
1 change: 1 addition & 0 deletions src/components/bottomSheetContainer/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ export interface BottomSheetContainerProps {
containerHeight: Animated.SharedValue<number>;
topInset?: number;
bottomInset?: number;
shouldCalculateHeight?: boolean;
children: ReactNode;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import ReText from './ReText';
import { styles } from './styles';

interface BottomSheetDebugViewProps {
values: Record<string, Animated.SharedValue<number | boolean>>;
values: Record<string, Animated.SharedValue<number | boolean> | number>;
}

const BottomSheetDebugView = ({ values }: BottomSheetDebugViewProps) => {
Expand Down
28 changes: 15 additions & 13 deletions src/components/bottomSheetDebugView/ReText.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,37 @@
import React from 'react';
import { TextProps as RNTextProps, TextInput } from 'react-native';
import Animated, { useAnimatedProps } from 'react-native-reanimated';
import Animated, {
useAnimatedProps,
useDerivedValue,
} from 'react-native-reanimated';

interface TextProps {
text: string;
value: Animated.SharedValue<number | boolean>;
value: Animated.SharedValue<number | boolean> | number;
style?: Animated.AnimateProps<RNTextProps>['style'];
}

const AnimatedTextInput = Animated.createAnimatedComponent(TextInput);

const ReText = (props: TextProps) => {
const { text, value: _providedValue, style } = { style: {}, ...props };
const providedValue = useDerivedValue(() =>
typeof _providedValue === 'number'
? _providedValue
: typeof _providedValue.value === 'number'
? _providedValue.value.toFixed(2)
: _providedValue.value
);
const animatedProps = useAnimatedProps(() => {
return {
text: `${text}: ${
typeof _providedValue.value === 'number'
? _providedValue.value.toFixed(2)
: _providedValue.value
}`,
text: `${text}: ${providedValue.value}`,
};
}, [_providedValue.value]);
}, [providedValue]);
return (
<AnimatedTextInput
underlineColorAndroid="transparent"
editable={false}
value={`${text}: ${
typeof _providedValue.value === 'number'
? _providedValue.value.toFixed(2)
: _providedValue.value
}`}
value={`${text}: ${providedValue.value}`}
style={style}
// @ts-ignore
animatedProps={animatedProps}
Expand Down
1 change: 1 addition & 0 deletions src/components/bottomSheetModal/BottomSheetModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ const BottomSheetModalComponent = forwardRef<
containerHeight={containerHeight}
onChange={handleBottomSheetOnChange}
children={children}
$modal={true}
/>
</Portal>
) : null;
Expand Down
14 changes: 12 additions & 2 deletions src/hooks/useNormalizedSnapPoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,30 @@ import {
* @param providedSnapPoints provided snap points.
* @param containerHeight BottomSheetContainer height.
* @param topInset top inset.
* @param bottomInset bottom inset.
* @param $modal is sheet in a modal.
* @returns {Animated.SharedValue<number[]>}
*/
export const useNormalizedSnapPoints = (
providedSnapPoints: BottomSheetProps['snapPoints'],
containerHeight: Animated.SharedValue<number>,
topInset: number
topInset: number,
bottomInset: number,
$modal: boolean
) => {
const normalizedSnapPoints = useDerivedValue(() =>
providedSnapPoints.map(snapPoint => {
if (containerHeight.value === INITIAL_CONTAINER_HEIGHT) {
return INITIAL_SNAP_POINT;
}

return normalizeSnapPoint(snapPoint, containerHeight.value, topInset);
return normalizeSnapPoint(
snapPoint,
containerHeight.value,
topInset,
bottomInset,
$modal
);
})
);

Expand Down
8 changes: 4 additions & 4 deletions src/utilities/normalizeSnapPoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@
export const normalizeSnapPoint = (
snapPoint: number | string,
containerHeight: number,
topInset: number
topInset: number,
_bottomInset: number,
$modal: boolean
) => {
'worklet';

let normalizedSnapPoint = snapPoint;

// percentage snap point
if (typeof normalizedSnapPoint === 'string') {
normalizedSnapPoint =
(Number(normalizedSnapPoint.split('%')[0]) * containerHeight) / 100;
}

return Math.max(topInset, containerHeight - normalizedSnapPoint);
return Math.max($modal ? 0 : topInset, containerHeight - normalizedSnapPoint);
};

0 comments on commit ee573e9

Please sign in to comment.