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

Pass RNGH props to FlatList's underlying ScrollView #1557

Merged
merged 1 commit into from
Aug 13, 2021

Conversation

jakub-gonet
Copy link
Member

Description

Pass native gesture handler props to Flatlist's ScrollView.
Fixes #1494.

Test plan

Code used to test below, based on panAndScroll example. Touch pink area and move horizontally. Without fix, scrollview should intercept those events. After, PanGH should activate.
import React, { Component } from 'react';
import { Animated, Dimensions, StyleSheet, Text } from 'react-native';
import {
  PanGestureHandler,
  TapGestureHandler,
  State,
  PanGestureHandlerGestureEvent,
  TapGestureHandlerStateChangeEvent,
  FlatList,
} from 'react-native-gesture-handler';

const windowWidth = Dimensions.get('window').width;
const circleRadius = 30;

type Props = {
  tapRef: React.RefObject<TapGestureHandler>;
  panRef: React.RefObject<PanGestureHandler>;
};

export class TapOrPan extends Component<Props> {
  private touchX: Animated.Value;
  private translateX: Animated.AnimatedAddition;
  private onPanGestureEvent: (event: PanGestureHandlerGestureEvent) => void;
  constructor(props: Props) {
    super(props);
    this.touchX = new Animated.Value(windowWidth / 2 - circleRadius);
    this.translateX = Animated.add(
      this.touchX,
      new Animated.Value(-circleRadius)
    );
    this.onPanGestureEvent = Animated.event(
      [
        {
          nativeEvent: {
            x: this.touchX,
          },
        },
      ],
      { useNativeDriver: true }
    );
  }

  private onTapHandlerStateChange = ({
    nativeEvent,
  }: TapGestureHandlerStateChangeEvent) => {
    if (nativeEvent.oldState === State.ACTIVE) {
      // Once tap happened we set the position of the circle under the tapped spot
      this.touchX.setValue(nativeEvent.x);
    }
  };

  render() {
    const { tapRef, panRef } = this.props;
    return (
      <TapGestureHandler
        ref={tapRef}
        waitFor={panRef}
        onHandlerStateChange={this.onTapHandlerStateChange}
        shouldCancelWhenOutside>
        <Animated.View style={styles.wrapper}>
          <PanGestureHandler
            ref={panRef}
            activeOffsetX={[-20, 20]}
            onGestureEvent={this.onPanGestureEvent}
            shouldCancelWhenOutside>
            <Animated.View style={styles.horizontalPan}>
              <Animated.View
                style={[
                  styles.circle,
                  {
                    transform: [
                      {
                        translateX: this.translateX,
                      },
                    ],
                  },
                ]}
              />
            </Animated.View>
          </PanGestureHandler>
        </Animated.View>
      </TapGestureHandler>
    );
  }
}

export default class Example extends Component {
  render() {
    const tapRef = React.createRef<TapGestureHandler>();
    const panRef = React.createRef<PanGestureHandler>();
    return (
      <FlatList
        data={[{ key: 1 }]}
        waitFor={[tapRef, panRef]}
        renderItem={() => (
          <>
            <Text>
              Curabitur accumsan sit amet massa quis cursus. Fusce sollicitudin
              nunc nisl, quis efficitur quam tristique eget. Ut non erat
              molestie, ullamcorper turpis nec, euismod neque. Praesent aliquam
              risus ultricies, cursus mi consectetur, bibendum lorem. Nunc
              eleifend consectetur metus quis pulvinar. In vitae lacus eu nibh
              tincidunt sagittis ut id lorem. Pellentesque habitant morbi
              tristique senectus et netus et malesuada fames ac turpis egestas.
              Quisque sagittis mauris rhoncus, maximus justo in, consequat
              dolor. Pellentesque ornare laoreet est vulputate vestibulum.
              Aliquam sit amet metus lorem. Morbi tempus elit lorem, ut pulvinar
              nunc sagittis pharetra.
            </Text>
            <TapOrPan tapRef={tapRef} panRef={panRef} />
            <Text>
              Curabitur accumsan sit amet massa quis cursus. Fusce sollicitudin
              nunc nisl, quis efficitur quam tristique eget. Ut non erat
              molestie, ullamcorper turpis nec, euismod neque. Praesent aliquam
              risus ultricies, cursus mi consectetur, bibendum lorem. Nunc
              eleifend consectetur metus quis pulvinar. In vitae lacus eu nibh
              tincidunt sagittis ut id lorem. Pellentesque habitant morbi
              tristique senectus et netus et malesuada fames ac turpis egestas.
              Quisque sagittis mauris rhoncus, maximus justo in, consequat
              dolor. Pellentesque ornare laoreet est vulputate vestibulum.
              Aliquam sit amet metus lorem. Morbi tempus elit lorem, ut pulvinar
              nunc sagittis pharetra.
            </Text>
          </>
        )}
      />
    );
  }
}

const styles = StyleSheet.create({
  horizontalPan: {
    backgroundColor: '#f48fb1',
    height: 150,
    justifyContent: 'center',
    marginVertical: 10,
  },
  circle: {
    backgroundColor: '#42a5f5',
    borderRadius: circleRadius,
    height: circleRadius * 2,
    width: circleRadius * 2,
  },
  wrapper: {
    flex: 1,
  },
});

Code used to test below, based on panAndScroll example. Touch pink area and move horizontally.
Without fix, scrollview should intercept those events. After, PanGH should activate.

import React, { Component } from 'react';
import { Animated, Dimensions, StyleSheet, Text } from 'react-native';
import {
  PanGestureHandler,
  TapGestureHandler,
  State,
  PanGestureHandlerGestureEvent,
  TapGestureHandlerStateChangeEvent,
  FlatList,
} from 'react-native-gesture-handler';

const windowWidth = Dimensions.get('window').width;
const circleRadius = 30;

type Props = {
  tapRef: React.RefObject<TapGestureHandler>;
  panRef: React.RefObject<PanGestureHandler>;
};

export class TapOrPan extends Component<Props> {
  private touchX: Animated.Value;
  private translateX: Animated.AnimatedAddition;
  private onPanGestureEvent: (event: PanGestureHandlerGestureEvent) => void;
  constructor(props: Props) {
    super(props);
    this.touchX = new Animated.Value(windowWidth / 2 - circleRadius);
    this.translateX = Animated.add(
      this.touchX,
      new Animated.Value(-circleRadius)
    );
    this.onPanGestureEvent = Animated.event(
      [
        {
          nativeEvent: {
            x: this.touchX,
          },
        },
      ],
      { useNativeDriver: true }
    );
  }

  private onTapHandlerStateChange = ({
    nativeEvent,
  }: TapGestureHandlerStateChangeEvent) => {
    if (nativeEvent.oldState === State.ACTIVE) {
      // Once tap happened we set the position of the circle under the tapped spot
      this.touchX.setValue(nativeEvent.x);
    }
  };

  render() {
    const { tapRef, panRef } = this.props;
    return (
      <TapGestureHandler
        ref={tapRef}
        waitFor={panRef}
        onHandlerStateChange={this.onTapHandlerStateChange}
        shouldCancelWhenOutside>
        <Animated.View style={styles.wrapper}>
          <PanGestureHandler
            ref={panRef}
            activeOffsetX={[-20, 20]}
            onGestureEvent={this.onPanGestureEvent}
            shouldCancelWhenOutside>
            <Animated.View style={styles.horizontalPan}>
              <Animated.View
                style={[
                  styles.circle,
                  {
                    transform: [
                      {
                        translateX: this.translateX,
                      },
                    ],
                  },
                ]}
              />
            </Animated.View>
          </PanGestureHandler>
        </Animated.View>
      </TapGestureHandler>
    );
  }
}

export default class Example extends Component {
  render() {
    const tapRef = React.createRef<TapGestureHandler>();
    const panRef = React.createRef<PanGestureHandler>();
    return (
      <FlatList
        data={[{ key: 1 }]}
        waitFor={[tapRef, panRef]}
        renderItem={() => (
          <>
            <Text>
              Curabitur accumsan sit amet massa quis cursus. Fusce sollicitudin
              nunc nisl, quis efficitur quam tristique eget. Ut non erat
              molestie, ullamcorper turpis nec, euismod neque. Praesent aliquam
              risus ultricies, cursus mi consectetur, bibendum lorem. Nunc
              eleifend consectetur metus quis pulvinar. In vitae lacus eu nibh
              tincidunt sagittis ut id lorem. Pellentesque habitant morbi
              tristique senectus et netus et malesuada fames ac turpis egestas.
              Quisque sagittis mauris rhoncus, maximus justo in, consequat
              dolor. Pellentesque ornare laoreet est vulputate vestibulum.
              Aliquam sit amet metus lorem. Morbi tempus elit lorem, ut pulvinar
              nunc sagittis pharetra.
            </Text>
            <TapOrPan tapRef={tapRef} panRef={panRef} />
            <Text>
              Curabitur accumsan sit amet massa quis cursus. Fusce sollicitudin
              nunc nisl, quis efficitur quam tristique eget. Ut non erat
              molestie, ullamcorper turpis nec, euismod neque. Praesent aliquam
              risus ultricies, cursus mi consectetur, bibendum lorem. Nunc
              eleifend consectetur metus quis pulvinar. In vitae lacus eu nibh
              tincidunt sagittis ut id lorem. Pellentesque habitant morbi
              tristique senectus et netus et malesuada fames ac turpis egestas.
              Quisque sagittis mauris rhoncus, maximus justo in, consequat
              dolor. Pellentesque ornare laoreet est vulputate vestibulum.
              Aliquam sit amet metus lorem. Morbi tempus elit lorem, ut pulvinar
              nunc sagittis pharetra.
            </Text>
          </>
        )}
      />
    );
  }
}

const styles = StyleSheet.create({
  horizontalPan: {
    backgroundColor: '#f48fb1',
    height: 150,
    justifyContent: 'center',
    marginVertical: 10,
  },
  circle: {
    backgroundColor: '#42a5f5',
    borderRadius: circleRadius,
    height: circleRadius * 2,
    width: circleRadius * 2,
  },
  wrapper: {
    flex: 1,
  },
});
@jakub-gonet jakub-gonet merged commit 534bd1d into master Aug 13, 2021
@jakub-gonet jakub-gonet deleted the jgonet/fix-flatlist-props-passing branch August 13, 2021 09:16
fluiddot pushed a commit to wordpress-mobile/react-native-gesture-handler that referenced this pull request Feb 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Flat list missing simultaneousHandlers
2 participants