React Native - how to catch touch events behind an absolute position component

1.7k Views Asked by At

I'm trying to have a popup dialog (created as a component with position: 'absolute') and to close it when user clicks outside of the popup.

I'm new to React Native so other things I did might be wrong, forgive me for that :)

This is a screenshot of the popup when it is open enter image description here

This is some of the code:

From the Popup component:

render() {
    if (!this.props.open) {
      return <View />
    }
    return (
      <View style={styles.container}>
        <View style={styles.popupStyle}>
            <View style={{flex:1, justifyContent: 'center', marginRight: 10}}>
                <View style={styles.amountEnterStyle}>
                  <TextInput
                    keyboardType={'numeric'}
                    underlineColorAndroid='transparent'
                    autoCorrect={false}
                    style={styles.textInputStyle}
                    value={this.state.amount}
                    onChangeText={this._onAmountEnter.bind(this)} />
                  <Text style={styles.currencyTextStyle}>NIS</Text>
                </View>
                <View style={styles.separatorStyle} />
                    <View style={styles.categorySectionStyle}>
                      {this._renderCategoryDropdown()}
                    </View>
              </View>
            <View style={{justifyContent: 'center'}}>
              <TouchableWithoutFeedback
                onPress={this._onAddPaymentClicked.bind(this)} >
                <View style={{width: 110, height:100, backgroundColor: "#ff0000"}} />
              </TouchableWithoutFeedback>
            </View>
        </View>
      </View>
    );

const styles = StyleSheet.create({
    container: {
      position: 'absolute',
      top: 0,
      bottom: 0,
      left: 0,
      right: 0,
      width: undefined,
      height: undefined,
      justifyContent: 'center',
      alignItems: 'center',
    },
    amountEnterStyle: {
      justifyContent: 'center',
      alignItems: 'center',
      flexDirection: 'row'
    },
    textInputStyle: {
      width: 100,
      fontSize: 50,
      fontWeight: 'bold',
      height: 65,
      flex: 1,
      marginTop: -20,
      paddingBottom: 0,
      marginBottom: 0,
      marginLeft:10,
      textAlignVertical: 'center'
    },
    currencyTextStyle: {
      fontSize: 50,
      color: '#c0c0c0',
      marginTop: -20,
      textAlignVertical: 'center'
    },
    separatorStyle: {
      height: 2,
      marginTop: 0,
      backgroundColor: '#c0c0c0',
    },
    categorySectionStyle: {
        justifyContent: 'flex-start',
        flexDirection: 'row',
        alignItems: 'flex-start',
        marginTop: 8
    },
    categoryColorDotStyle: {
      width: 20,
      height: 20,
      borderRadius: 100/2,
      marginLeft: 10,
      marginTop: 5,
    },
    categoryTextStyle: {
      fontSize: 24,
      marginHorizontal: 4,
      color: '#c0c0c0',
      marginLeft: 10,
      paddingRight: 10,
      marginTop: -3
    },
    popupStyle: {
      width: Dimensions.get('window').width - 60,
      flexDirection: 'row',
      justifyContent: 'space-between',
      height: 150,
      paddingRight:10,
      paddingLeft:10,
      borderRadius: 2,
      margin: 20,
      padding: 10,
      opacity: 1,
      borderRadius: 10,
      backgroundColor: '#ffffff'
   },
   dropdownSeparator: {
    height: 1,
    backgroundColor: 'cornflowerblue'
  },

And this is the hosting component:

render() {
    var totalIncome = this.props.balance.totalIncome;
    var totalExpanses = this.props.balance.totalExpanses;
    return (
      <View style={{flex:1, justifyContent: 'space-between'}}>
        <Image pointerEvents='none' source={require('../../res/background.png')} style={styles.container} />
        <MainScreenHeader onSettingPress={this._onBalanceSettingsPress.bind(this)}/>
        <MainBalanceCounter totalCount={totalIncome} currentCount={totalIncome-totalExpanses} />
        <View style={{justifyContent: 'center', alignItems: 'center', marginBottom: 150}}>
          <AddPaymentButton onPress={this._onAddPaymentPress.bind(this)} />
          <PaymentPopup
              open={this.state.paymentPopupOpen}
              onAddPaymentClicked={this._onPaymentPopupAddClicked.bind(this)} />
        </View>

      </View>
    );
  }


const styles = StyleSheet.create({
  container: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
    width: undefined,
    height: undefined,
    backgroundColor:'transparent',
    justifyContent: 'center',
    alignItems: 'center',
  },
  innerContainer: {
    justifyContent: 'space-around',
    alignItems: 'center',
    flex: 1,
    backgroundColor: 'transparent'
  },
});

I was trying man things to have a view that will catch clicks that happens outside of the popup, but no success.

Any help will be much appreciated,

Thanks, Giora.

1

There are 1 best solutions below

0
On BEST ANSWER

have you tried out react-native-modalbox? I use it in my applications and it works pretty great. If you have touch events behind the modal, it will close. Also, you can define how large you'd like the modal to be. It doesn't have to take up the whole screen.

Otherwise, you were on the right track. You'd need a TouchableWithoutFeedback component that covers the full width and height of the screen. Your popup would be alongside of this component with a higher zIndex. Let me know if you'd like to go this route and I can provide more advice. Cheers!