Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: improve content/scrollable panning handling #23

Merged
merged 2 commits into from
Aug 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 21 additions & 11 deletions example/src/screens/BasicExample.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import React, { useCallback, useMemo, useRef } from 'react';
import { View, StyleSheet, Text } from 'react-native';
import { View, StyleSheet } from 'react-native';
import { useHeaderHeight } from '@react-navigation/stack';
import Animated, {
interpolate,
concat,
Extrapolate,
} from 'react-native-reanimated';
import { useValue } from 'react-native-redash';
import BottomSheet from '@gorhom/bottom-sheet';
import BottomSheet, { BottomSheetFlatList } from '@gorhom/bottom-sheet';
import Handle from '../components/handle';
import Button from '../components/button';
import ContactList from '../components/contactList';
import { ReText } from 'react-native-redash';

const BasicExample = () => {
Expand Down Expand Up @@ -49,13 +48,13 @@ const BasicExample = () => {
}, []);

// renders
const renderHeader = useCallback(() => {
return (
<View style={styles.headerContainer}>
<Text style={styles.title}>Basic Screen</Text>
</View>
);
}, []);
// const renderHeader = useCallback(() => {
// return (
// <View style={styles.headerContainer}>
// <Text style={styles.title}>Basic Screen</Text>
// </View>
// );
// }, []);

return (
<View style={styles.container}>
Expand Down Expand Up @@ -153,7 +152,18 @@ const BasicExample = () => {
style={styles.buttonContainer}
onPress={() => handleSnapPress(1)}
/> */}
<ContactList type="ScrollView" header={renderHeader} />
{/* <ContactList type="ScrollView" header={renderHeader} /> */}
<View style={{ height: 100, backgroundColor: 'blue' }} />
<BottomSheetFlatList
contentContainerStyle={{ flexGrow: 1, backgroundColor: '#fff' }}
data={[0, 1, 2, 3, 4, 5, 6, 7, 8]}
renderItem={() => (
<View
style={{ backgroundColor: 'red', height: 100, marginBottom: 20 }}
/>
)}
/>
<View style={{ height: 100, backgroundColor: 'green' }} />
</BottomSheet>
</View>
);
Expand Down
22 changes: 20 additions & 2 deletions src/components/bottomSheet/BottomSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import {
import {
DEFAULT_ANIMATION_EASING,
DEFAULT_ANIMATION_DURATION,
GESTURE,
} from '../../constants';
import type { ScrollableRef, BottomSheetMethods } from '../../types';
import type { BottomSheetProps } from './types';
Expand Down Expand Up @@ -190,7 +191,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
//#endregion

//#region animation
const { position, currentPosition } = useTransition({
const { position, currentPosition, currentGesture } = useTransition({
animationDuration,
animationEasing,
contentPanGestureState,
Expand Down Expand Up @@ -299,6 +300,9 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
const internalContextVariables = useMemo(
() => ({
rootTapGestureRef,
handlePanGestureState,
handlePanGestureTranslationY,
handlePanGestureVelocityY,
contentPanGestureState,
contentPanGestureTranslationY,
contentPanGestureVelocityY,
Expand Down Expand Up @@ -366,7 +370,13 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
useCode(
() =>
cond(
and(eq(tapGestureState, State.FAILED), neq(position, 0)),
and(
eq(tapGestureState, State.FAILED),
eq(currentGesture, GESTURE.CONTENT),
eq(contentPanGestureState, State.END),
eq(handlePanGestureState, State.END),
neq(position, 0)
),
call([], () => {
scrollToTop();
})
Expand Down Expand Up @@ -423,6 +433,10 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
/>
)}
{/* <Animated.View pointerEvents="none" style={styles.debug}>
<ReText
style={styles.debugText}
text={concat('currentGesture: ', currentGesture)}
/>
<ReText
style={styles.debugText}
text={concat('tapState: ', tapGestureState)}
Expand All @@ -431,6 +445,10 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
style={styles.debugText}
text={concat('contentState: ', contentPanGestureState)}
/>
<ReText
style={styles.debugText}
text={concat('handleState: ', handlePanGestureState)}
/>
<ReText
style={styles.debugText}
text={concat(
Expand Down
89 changes: 51 additions & 38 deletions src/components/bottomSheet/useTransition.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useMemo } from 'react';
import Animated, {
eq,
set,
Expand All @@ -21,6 +22,7 @@ import Animated, {
import { State } from 'react-native-gesture-handler';
import { useClock, useValue, snapPoint } from 'react-native-redash';
import type { BottomSheetAnimationConfigs } from './types';
import { GESTURE } from '../../constants';

interface TransitionProps extends Required<BottomSheetAnimationConfigs> {
contentPanGestureState: Animated.Value<State>;
Expand All @@ -37,12 +39,6 @@ interface TransitionProps extends Required<BottomSheetAnimationConfigs> {
initialPosition: number;
}

enum GESTURE {
UNDETERMINED = 0,
CONTENT,
HANDLE,
}

export const useTransition = ({
animationDuration,
animationEasing,
Expand All @@ -66,26 +62,36 @@ export const useTransition = ({
const shouldAnimate = useValue(0);

const clock = useClock();
const config = {
toValue: useValue(0),
duration: animationDuration,
easing: animationEasing,
};
const config = useMemo(
() => ({
toValue: new Animated.Value(0),
duration: animationDuration,
easing: animationEasing,
}),
[animationEasing, animationDuration]
);

const animationState = {
finished: useValue(0),
position: useValue(initialPosition),
frameTime: useValue(0),
time: useValue(0),
};
const animationState = useMemo(
() => ({
finished: new Animated.Value(0),
position: new Animated.Value(initialPosition),
frameTime: new Animated.Value(0),
time: new Animated.Value(0),
}),
[initialPosition]
);

const finishTiming = [
set(shouldAnimate, 0),
set(currentPosition, config.toValue),
set(animationState.frameTime, 0),
set(animationState.time, 0),
stopClock(clock),
];
const finishTiming = useMemo(
() => [
set(shouldAnimate, 0),
set(currentPosition, config.toValue),
set(animationState.frameTime, 0),
set(animationState.time, 0),
stopClock(clock),
],
// eslint-disable-next-line react-hooks/exhaustive-deps
[]
);

const translateY = cond(
eq(currentGesture, GESTURE.CONTENT),
Expand Down Expand Up @@ -148,22 +154,28 @@ export const useTransition = ({
/**
* Gesture ended node.
*/
cond(
or(
eq(contentPanGestureState, State.END),
eq(handlePanGestureState, State.END)
),
[
set(contentPanGestureState, State.UNDETERMINED),
set(handlePanGestureState, State.UNDETERMINED),
set(
config.toValue,
snapPoint(add(currentPosition, translateY), velocityY, snapPoints)
onChange(
add(contentPanGestureState, handlePanGestureState),
cond(
or(
and(
eq(currentGesture, GESTURE.CONTENT),
eq(contentPanGestureState, State.END)
),
and(
neq(currentGesture, GESTURE.CONTENT),
eq(handlePanGestureState, State.END)
)
),
set(shouldAnimate, 1),
]
[
set(
config.toValue,
snapPoint(add(currentPosition, translateY), velocityY, snapPoints)
),
set(shouldAnimate, 1),
]
)
),

/**
* Manual snapping node.
*/
Expand Down Expand Up @@ -198,5 +210,6 @@ export const useTransition = ({
return {
position,
currentPosition,
currentGesture,
};
};
38 changes: 26 additions & 12 deletions src/components/draggableView/DraggableView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import type { BottomSheetDraggableViewProps } from './types';
import { styles } from './styles';

const BottomSheetDraggableViewComponent = ({
children,
style,
nativeGestureRef,
gestureType = 'HANDLE',
style,
children,
...rest
}: BottomSheetDraggableViewProps) => {
// refs
Expand All @@ -18,6 +19,9 @@ const BottomSheetDraggableViewComponent = ({
// hooks
const {
rootTapGestureRef,
handlePanGestureState,
handlePanGestureTranslationY,
handlePanGestureVelocityY,
contentPanGestureState,
contentPanGestureTranslationY,
contentPanGestureVelocityY,
Expand All @@ -41,17 +45,27 @@ const BottomSheetDraggableViewComponent = ({
// callbacks
const handleGestureEvent = useMemo(
() =>
event([
{
nativeEvent: {
state: contentPanGestureState,
translationY: contentPanGestureTranslationY,
velocityY: contentPanGestureVelocityY,
},
},
]),
gestureType === 'CONTENT'
? event([
{
nativeEvent: {
state: contentPanGestureState,
translationY: contentPanGestureTranslationY,
velocityY: contentPanGestureVelocityY,
},
},
])
: event([
{
nativeEvent: {
state: handlePanGestureState,
translationY: handlePanGestureTranslationY,
velocityY: handlePanGestureVelocityY,
},
},
]),
// eslint-disable-next-line react-hooks/exhaustive-deps
[]
[gestureType]
);

// effects
Expand Down
2 changes: 2 additions & 0 deletions src/components/draggableView/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { ViewProps as RNViewProps } from 'react-native';
import type { NativeViewGestureHandler } from 'react-native-gesture-handler';
import type { GESTURE } from '../../constants';

export type BottomSheetDraggableViewProps = RNViewProps & {
children: React.ReactNode[] | React.ReactNode;
gestureType?: keyof typeof GESTURE;
nativeGestureRef?: Ref<NativeViewGestureHandler> | null;
};
3 changes: 2 additions & 1 deletion src/components/flatList/FlatList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,9 @@ const BottomSheetFlatListComponent = forwardRef(
// render
return (
<DraggableView
style={styles.container}
nativeGestureRef={nativeGestureRef}
gestureType="CONTENT"
style={styles.container}
>
<NativeViewGestureHandler
ref={nativeGestureRef}
Expand Down
3 changes: 2 additions & 1 deletion src/components/scrollView/ScrollView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@ const BottomSheetScrollViewComponent = forwardRef(

return (
<DraggableView
style={styles.container}
nativeGestureRef={nativeGestureRef}
gestureType="CONTENT"
style={styles.container}
>
<NativeViewGestureHandler
ref={nativeGestureRef}
Expand Down
3 changes: 2 additions & 1 deletion src/components/sectionList/SectionList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ const BottomSheetSectionListComponent = forwardRef(
// render
return (
<DraggableView
style={styles.container}
nativeGestureRef={nativeGestureRef}
gestureType="CONTENT"
style={styles.container}
>
<NativeViewGestureHandler
ref={nativeGestureRef}
Expand Down
6 changes: 6 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,9 @@ export const DEFAULT_ANIMATION_EASING: Animated.EasingFunction = Easing.out(
Easing.back(0.75)
);
export const DEFAULT_ANIMATION_DURATION = 500;

export enum GESTURE {
UNDETERMINED = 0,
CONTENT,
HANDLE,
}
3 changes: 3 additions & 0 deletions src/contexts/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ export type BottomSheetInternalContextType = {
contentPanGestureState: Animated.Value<State>;
contentPanGestureTranslationY: Animated.Value<number>;
contentPanGestureVelocityY: Animated.Value<number>;
handlePanGestureState: Animated.Value<State>;
handlePanGestureTranslationY: Animated.Value<number>;
handlePanGestureVelocityY: Animated.Value<number>;
scrollableContentOffsetY: Animated.Value<number>;
decelerationRate: Animated.Node<number>;
setScrollableRef: (ref: ScrollableRef) => void;
Expand Down