Zuet
Zuet

Reputation: 593

Absolute position in React-Native

Fix: My problem was that I didn't know what I should use. And this is which I want.

If you want to make it yourself, see ksav's answer


I have a component which acts as a Picker in React Native.

  <Modal
    transparent
    visible={editVisible}
    animationType="fade"
    onRequestClose={modalChange}>
    <Pressable style={styles.modalView} onPressOut={modalChange}>
      <View style={styles.modalContainer}>
        <TouchableOpacity style={styles.button} onPressOut={modalChange}>
          <Text>Edit button</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.button} onPressOut={modalChange}>
          <Text>Delete button</Text>
        </TouchableOpacity>
      </View>
    </Pressable>
  </Modal>
  <TouchableOpacity onPress={modalChange}>
    <Ionicons name="ios-ellipsis-horizontal-sharp" size={20} />
  </TouchableOpacity>

I want to set the modalContainer to always be at the bottom of TouchableOpacity (Ionicons). But I set TouchableOpacity's css: position: 'relative' and

modalContainer: {
  position: absolute,
  bottom: 0,
  right: 0,
}

It's not working as expected. Because:

position in React Native is similar to regular CSS, but everything is set to relative by default, so absolute positioning is always relative to the parent.

To simplify the problem, I want some View is always under the button which will show the view when I onPress

My demo

Upvotes: 1

Views: 3179

Answers (1)

ksav
ksav

Reputation: 20840

Ensure the parent View is flexing vertically and that your style is position: 'absolute',

    import * as React from 'react';
    import { Text, View, StyleSheet, TouchableOpacity, Modal, Pressable } from 'react-native';

    export default function App() {
      const [modalVisible, setModalVisible] = React.useState(false);

      return (
        <View style={styles.centeredView}>
          <Modal
            animationType="slide"
            transparent={true}
            visible={modalVisible}
            onRequestClose={() => {
              setModalVisible(!modalVisible);
            }}
          >
            <View style={styles.modalContainer}>
              <View style={styles.modalView}>
                <Text style={styles.modalText}>Hello World!</Text>
                <Pressable
                  style={[styles.button, styles.buttonClose]}
                  onPress={() => setModalVisible(!modalVisible)}
                >
                  <Text style={styles.textStyle}>Hide Modal</Text>
                </Pressable>
              </View>
            </View>
          </Modal>
          <Pressable
            style={[styles.button, styles.buttonOpen]}
            onPress={() => setModalVisible(true)}
          >
            <Text style={styles.textStyle}>Show Modal</Text>
          </Pressable>
        </View>
      );
    }

    const styles = StyleSheet.create({
      modalContainer: {
        position: 'absolute',
        bottom: 0,
        right: 0,
      },
      centeredView: {
        flex: 1,
        justifyContent: "center",
        alignItems: "center",
        marginTop: 22
      },
      modalView: {
        margin: 20,
        backgroundColor: "white",
        borderRadius: 20,
        padding: 35,
        alignItems: "center",
        shadowColor: "#000",
        shadowOffset: {
          width: 0,
          height: 2
        },
        shadowOpacity: 0.25,
        shadowRadius: 4,
        elevation: 5
      },
    });



After discussion, I think what you want would better be described as a tooltip or a popover rather than modal. E.g it is positioned at the bottom of the button that opens it, but doesnt take up any space in the flow.

import * as React from 'react';
import {
  Text,
  View,
  StyleSheet,
  TouchableOpacity,
  Modal,
  Pressable,
  SafeAreaView,
} from 'react-native';

export default function App() {
  const [tooltipVisible, settooltipVisible] = React.useState(false);
  const [buttonHeight, setButtonHeight] = React.useState(0);

  const handleLayout = (event) => {
    const { x, y, height, width } = event.nativeEvent.layout;
    setButtonHeight(height)
  };

  const handlePress = () => {
    settooltipVisible(!tooltipVisible);
  };  

  return (
    <SafeAreaView style={styles.centeredView}>
      <Pressable
        onLayout={handleLayout}
        style={[styles.button, styles.buttonOpen]}
        onPress={() => settooltipVisible(true)}>
        <Text style={styles.textStyle}>Show Thing</Text>
      </Pressable>

      {tooltipVisible && (
        <View style={[styles.tooltipView, {top: buttonHeight}]}>
          <Text style={styles.modalText}>Hello World!</Text>
          <Pressable
            style={[styles.button, styles.buttonClose]}
            onPress={handlePress}>
            <Text style={styles.textStyle}>Hide Thing</Text>
          </Pressable>
        </View>
      )}

      <Text>Some other content</Text>
      <Text>Some other content</Text>
      <Text>Some other content</Text>
      <Text>Some other content</Text>
      <Text>Some other content</Text>
      <Text>Some other content</Text>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  button: {
    padding: 20,
    backgroundColor: 'blue',
    color: 'white',
  },

  centeredView: {
    justifyContent: 'center',
    alignItems: 'center',
    marginTop: 60,
  },
  tooltipView: {
    zIndex:1,
    padding: 20,
    position: 'absolute',
    backgroundColor: 'white',
    borderRadius: 20,
    shadowColor: '#000',
    shadowOffset: {
      width: 0,
      height: 2,
    },
    shadowOpacity: 0.25,
    shadowRadius: 4,
    elevation: 5,
  },
});

Demo

Upvotes: 1

Related Questions