Skip to content

Commit

Permalink
feat: add panGestureComponentEnabled, closeSnapPointStraightEnabled p…
Browse files Browse the repository at this point in the history
…rops and fix swipes (#188)
  • Loading branch information
jeremybarbet authored May 4, 2020
1 parent 149af45 commit b831d3d
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 18 deletions.
20 changes: 20 additions & 0 deletions docs/PROPS.md
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,16 @@ Using this prop will enable/disable pan gesture.
| ---- | -------- | ------- |
| bool | No | `true` |

### `panGestureComponentEnabled`

Define if HeaderComponent/FooterComponent/FloatingComponent should have pan gesture enable (Android specific).

!> Because of a limitation from `react-native-gesture-handler` for Android, when enable it might break touchable inside the view, that's why it's false by default. e.g. you might need to use `TouchableOpacity` from RNGH for Android and `TouchableOpacity` from `react-native` for iOS if set to `true`.

| Type | Required | Default | Platform |
| ---- | -------- | ------- | -------- |
| bool | No | `false` | Android |

### `tapGestureEnabled`

Define if the `TapGestureHandler` wrapping Modalize's core should be enable or not.
Expand All @@ -308,6 +318,16 @@ Using this prop will enable/disable the overlay tap gesture.
| ---- | -------- | ------- |
| bool | No | `true` |

### `closeSnapPointStraightEnabled`

Define if `snapPoint` props should close straight when swiping down or come back to initial position.

?> However, if the velocity value is reached it will close Modalize straight, even when `false`.

| Type | Required | Default |
| ---- | -------- | ------- |
| bool | No | `true` |

## Animations

### `openAnimationConfig`
Expand Down
84 changes: 66 additions & 18 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ const { height: screenHeight } = Dimensions.get('window');
const AnimatedKeyboardAvoidingView = Animated.createAnimatedComponent(KeyboardAvoidingView);
const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);
const AnimatedSectionList = Animated.createAnimatedComponent(SectionList);
/**
* When scrolling, it happens than beginScrollYValue is not always equal to 0 (top of the ScrollView).
* Since we use this to trigger the swipe down gesture animation, we allow a small threshold to
* not dismiss Modalize when we are using the ScrollView and we don't want to dismiss.
*/
const SCROLL_THRESHOLD = -4;
const USE_NATIVE_DRIVER = true;
const ACTIVATED = 20;
const PAN_DURATION = 150;
Expand Down Expand Up @@ -89,8 +95,10 @@ const ModalizeBase = (
keyboardAvoidingBehavior = 'padding',
keyboardAvoidingOffset,
panGestureEnabled = true,
panGestureComponentEnabled = false,
tapGestureEnabled = true,
closeOnOverlayTap = true,
closeSnapPointStraightEnabled = true,

// Animations
openAnimationConfig = {
Expand All @@ -100,7 +108,7 @@ const ModalizeBase = (
closeAnimationConfig = {
timing: { duration: 240, easing: Easing.ease },
},
dragToss = 0.15,
dragToss = 0.18,
threshold = 120,
velocity = 2800,
panGestureAnimatedValue,
Expand Down Expand Up @@ -148,6 +156,7 @@ const ModalizeBase = (
);
const [beginScrollYValue, setBeginScrollYValue] = React.useState(0);
const [modalPosition, setModalPosition] = React.useState<'top' | 'initial'>('initial');
const [cancelClose, setCancelClose] = React.useState(false);

const cancelTranslateY = React.useRef(new Animated.Value(1)).current; // 1 by default to have the translateY animation running
const componentTranslateY = React.useRef(new Animated.Value(0)).current;
Expand Down Expand Up @@ -402,13 +411,15 @@ const ModalizeBase = (
): void => {
const { timing } = closeAnimationConfig;
const { velocityY, translationY } = nativeEvent;
const enableBouncesValue = isAndroid ? false : beginScrollYValue > 0 || translationY < 0;
const negativeReverseScroll =
modalPosition === 'top' &&
beginScrollYValue >= (snapPoint ? 0 : SCROLL_THRESHOLD) &&
translationY < 0;
const thresholdProps = translationY > threshold && beginScrollYValue === 0;
const closeThreshold = velocity
? (beginScrollYValue <= 20 && velocityY >= velocity) || thresholdProps
: thresholdProps;

setEnableBounces(enableBouncesValue);
let enableBouncesValue = true;

// We make sure to reset the value if we are dragging from the children
if (type !== 'component' && (cancelTranslateY as any)._value === 0) {
Expand All @@ -420,12 +431,21 @@ const ModalizeBase = (
* We cancel the translation animation if the ScrollView is not scrolled to the top
*/
if (nativeEvent.oldState === State.BEGAN) {
if (beginScrollYValue > 0) {
setCancelClose(false);

if (
!closeSnapPointStraightEnabled && snapPoint
? beginScrollYValue > 0
: beginScrollYValue > 0 || negativeReverseScroll
) {
setCancelClose(true);
translateY.setValue(0);
dragY.setValue(0);
cancelTranslateY.setValue(0);
enableBouncesValue = true;
} else {
cancelTranslateY.setValue(1);
enableBouncesValue = false;

if (!tapGestureEnabled) {
setDisableScroll(
Expand All @@ -435,31 +455,59 @@ const ModalizeBase = (
}
}

setEnableBounces(
isAndroid
? false
: alwaysOpen
? beginScrollYValue > 0 || translationY < 0
: enableBouncesValue,
);

if (nativeEvent.oldState === State.ACTIVE) {
const toValue = translationY - beginScrollYValue;
let destSnapPoint = 0;

if (snapPoint || alwaysOpen) {
const endOffsetY = lastSnap + toValue + dragToss * velocityY;

/**
* snapPoint and alwaysOpen use both an array of points to define the first open state and the final state.
*/
snaps.forEach((snap: number) => {
const distFromSnap = Math.abs(snap - endOffsetY);

if (distFromSnap < Math.abs(destSnapPoint - endOffsetY)) {
destSnapPoint = snap;
willCloseModalize = false;

if (alwaysOpen) {
destSnapPoint = (modalHeightValue || 0) - alwaysOpen;
const diffPoint = Math.abs(destSnapPoint - endOffsetY);

// For snapPoint
if (distFromSnap < diffPoint && !alwaysOpen) {
if (closeSnapPointStraightEnabled) {
if (modalPosition === 'initial' && negativeReverseScroll) {
destSnapPoint = snap;
willCloseModalize = false;
}

if (snap === endHeight) {
destSnapPoint = snap;
willCloseModalize = true;
handleClose();
}
} else {
destSnapPoint = snap;
willCloseModalize = false;

if (snap === endHeight) {
willCloseModalize = true;
handleClose();
}
}
}

if (snap === endHeight && !alwaysOpen) {
willCloseModalize = true;
handleClose();
}
// For alwaysOpen props
if (distFromSnap < diffPoint && alwaysOpen && beginScrollYValue <= 0) {
destSnapPoint = (modalHeightValue || 0) - alwaysOpen;
willCloseModalize = false;
}
});
} else if (closeThreshold && !alwaysOpen) {
} else if (closeThreshold && !alwaysOpen && !cancelClose) {
willCloseModalize = true;
handleClose();
}
Expand Down Expand Up @@ -604,7 +652,7 @@ const ModalizeBase = (
* Until a better solution lands in RNGH, I will disable the PanGestureHandler for Android only,
* so inner touchable/gestures are working from the custom components you can pass in.
*/
if (isAndroid) {
if (isAndroid && !panGestureComponentEnabled) {
return tag;
}

Expand Down
12 changes: 12 additions & 0 deletions src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,12 @@ export interface IProps<ListItem = any> {
*/
panGestureEnabled?: boolean;

/**
* Define if HeaderComponent/FooterComponent/FloatingComponent should have pan gesture enable (Android specific). When enable it might break touchable inside the view.
* @default false
*/
panGestureComponentEnabled?: boolean;

/**
* Define if the `TapGestureHandler` wrapping Modalize's core should be enable or not.
* @default true
Expand All @@ -173,6 +179,12 @@ export interface IProps<ListItem = any> {
*/
closeOnOverlayTap?: boolean;

/**
* Define if `snapPoint` props should close straight when swiping down or come back to initial position.
* @default true
*/
closeSnapPointStraightEnabled?: boolean;

/**
* Object to change the open animations.
* @default
Expand Down

0 comments on commit b831d3d

Please sign in to comment.