I'm creating a custom modal that appears when you click a button, similar to a tooltip look but with selectable fields.
The problem i am having is that the modal is pushing the content below it down(out of its way) and i just want it to be over the top of everything.
I have used fragments to attempt to keep the modal on the same level as all the other elements and position relative to maintain the position of where it was clicked.
Here is without the modal visible:
Here is the modal pushing the content out of the way:
This is the structure of the button and the modal underneath it.
<>
<View style={[styles.f_con]}>
<Text style={[styles.f_title, item.required && styles.f_required]}>{item.title}</Text>
<TouchableOpacity style={styles.d_btn} onPress={e => setVisible(true)}>
<Text>{selection !== null ? item.options[selection] : 'Select from list'}</Text>
</TouchableOpacity>
</View>
<SelectModal visible={visible} close={close} item={item} clear={() => makeSelection(null)}>
{item.options.map((option, index) => (
<TouchableOpacity style={styles.s_option_con} key={index} onPress={() => makeSelection(index)} value={option}>
<Text style={styles.s_option_txt}>{option}</Text>
</TouchableOpacity>
))}
</SelectModal>
</>
This is my modal styling
modal: {
position: 'relative',
backgroundColor: Colors.secondaryBackground,
top: -30,
left: 100,
height: 'auto',
borderRadius: 12,
width: 250,
zIndex: 20
}
I found the solution if anyone has a similar problem:
I restructured the item with the modal to have the modal as a child as follows:
<View style={[styles.f_con, { zIndex: 2 }]}>
<Text style={[styles.f_title, item.required && styles.f_required]}>{item.title}</Text>
<TouchableOpacity style={styles.d_btn} onPress={e => setVisible(true)}>
<Text>{selection !== null ? item.options[selection] : 'Select from list'}</Text>
</TouchableOpacity>
<SelectModal visible={visible} close={close} item={item} clear={() => makeSelection(null)}>
{item.options.map((option, index) => (
<TouchableOpacity style={styles.s_option_con} key={index} onPress={() => makeSelection(index)} value={option}>
<Text style={styles.s_option_txt}>{option}</Text>
</TouchableOpacity>
))}
</SelectModal>
</View>
I changed the position of the modal to position: 'absolute'
and ensured the zIndex of the item was zIndex: 2 and all works.
Fragments unnecessary.
Related
i am trying to use Keyboard.dismiss() inside of a TouchableWithoutFeedback element and for some reason, no matter where i place this TouchableWithoutFeedback element, the keyboard doesn't dismiss when tapping on areas outside the TextInput,
but it does dismiss when tapping the keyboard itself!
why does this happen and how do i fix it?
p.s. it worked fine on a different project
Message for Stack Overflow staff: many of my threads are being locked for "not being clear" even though i am being very thorough in each and every one of them
DO NOT LOCK THIS THREAD
the code:
return (
<TouchableWithoutFeedback onPress={Keyboard.dismiss()}>
<View style={styles.mainView}>
<View style={styles.topSearchView}>
<Image source={require("../../assets/NewSearchIcon.png")} style={styles.searchIcon} />
<TextInput
placeholder="Search for food you like..."
onChangeText={searchedCoursesHandler}
value={searchedCourses}
onFocus={() => { setInputBorderOnFocus(true) }}
style={{
borderColor: inputBorderOnFocus === true ? "#428463" : "grey",
borderWidth: 2,
width: 260,
height: 50,
marginLeft: 10,
fontSize: 18,
padding: 5,
borderRadius: 8,
fontSize: 16
}}
/>
<TouchableOpacity
onPress={() => { setSearchedCourses("") }}
style={styles.deleteInputTouchable}
>
<Image source={require("../../assets/newXicon.png")} style={styles.xButtonIcon} />
</TouchableOpacity>
</View>
<View style={styles.flatListView}>
<FlatList
data={searchedCourses}
renderItem={({ item }) => (
<TouchableOpacity
onPress={() => {
navigation.navigate('item profile', {
item: item,
});
}}
>
<View style={styles.flatListItemView}>
<Image source={{ uri: item.img }} style={styles.flatListImg} />
<Text>{item.name}</Text>
<Text>{item.price}$</Text>
</View>
</TouchableOpacity>
)}
horizontal={true}
/>
</View>
{
searchedCourses.length < 1 &&
<Image source={require("../../assets/NewPastaImage.png")} style={styles.pastaIcon} />
}
</View>
</TouchableWithoutFeedback>
);
enter code here
Change This <TouchableWithoutFeedback onPress={Keyboard.dismiss()}>
into this
<TouchableWithoutFeedback onPress={() => Keyboard.dismiss()}>
I am new to React native and implementing FlatList where I am facing some issues with dynamic data. I have a list of total seats and booked seats.
this.state = {
seats: props.route.params.seats,
reservedSeats: props.route.params.Reserved,
date: new Date()
}
Following is the FlatList i have implemented
<FlatList style={styles.flatListArea1}
contentContainerStyle={{margin:0}}
data={this.state.seats}
numColumns={4}
showsHorizontalScrollIndicator={false}
renderItem={({item}) =>
<View style={styles.containerIcons} key={item}>
<TouchableOpacity style={this.state.selectedItem === item ? styles.menuSelected : styles.menuTop } onPress={ () => this.selectSeat(item)}>
<View style={styles.buttons}>
<Text style={styles.HallsText} key={item.key}>{item.Id}</Text>
</View>
</TouchableOpacity>
</View>}
/>
On Click event, I am able to change the color. I appreciate if someone can help to understand, How we can re-render FlatList based on dynamic data presented in reserved state. For example. for today, out of 5 seats 3 are booked. Those should be gray and others should be red.
I appreciate your help on this.
Regards,
You firstly need a method that tells if a seat is available or not.
isReserved = (item) => {
return this.state.reservedSeats.filter(seat => seat.Id ==item.Id).length > 0;
}
Then you change the appearance of your list like this
<FlatList
style={styles.flatListArea1}
contentContainerStyle={{ margin: 0 }}
data={this.state.seats}
numColumns={4}
showsHorizontalScrollIndicator={false}
renderItem={({ item, index }) => (
<View style={[styles.containerIcons, { backgroundColor: isReserved(item) ? "#FAFAFA" : "white" }]} key={index}>
<TouchableOpacity
style={this.state.selectedItem === item ? styles.menuSelected : styles.menuTop}
onPress={() => this.selectSeat(item)}
disabled={isReserved(item)}
>
<View style={styles.buttons}>
<Text
style={[styles.HallsText, { backgroundColor: isReserved(item) ? "#CCC" : "white" }]}
key={item.key}
>
{item.Id}
</Text>
</View>
</TouchableOpacity>
</View>
)}
/>;
Pay attention to the key attribute
As title says, I need to create a modal popup for every list item for the selected sizes to be displayed. I'm using the react-native-popup-dialog but without results. Could you help me? Here's my code:
step1 = () => {
return (
<View style={{ flex: 8}}>
<Text h3 style={{ ...styles.title, marginVertical: 10.0 }}>{this.state.user=="User"?"Size":"CargoSize"}</Text>
<ScrollView>
<View style={{ marginHorizontal: 16.0 }}>{
this.state.size.map((l, i) => (
<ListItem key={i} onPress={() => this.setState({sizeSelected: i, sizeName: l.title, sizeId: l.id})} underlayColor='transparent'
containerStyle={{backgroundColor: this.state.sizeSelected==i?'#F76858':'white', borderWidth: 1.0,
borderColor: '#707070', marginBottom: 10.0, paddingVertical: 5.0, paddingHorizontal: 40.0}}>
<ListItem.Content>
<View style={{
flexDirection: 'row', alignItems: 'center',
justifyContent: 'center'
}}>
<Text style={styles.textSize}>{l.title}</Text>
<Text style={{ fontSize: 16 }}>{l.example}</Text>
</View>
</ListItem.Content>
</ListItem>
))
}</View>
</ScrollView>
</View>
);
}
I believe you can declare a single modal in your list component and show or hide the modal when the user presses the listItem. Also, make the pressed item active and pass the item to your modal so that the modal can show the details of the active item.
App.js
export default function App() {
const [modalVisible, setModalVisible] = React.useState(false);
const [activeItem, setActiveItem] = React.useState(null);
const onPress = (item) => {
setActiveItem(item)
setModalVisible(true)
}
const renderItem = ({ item }) => (
<TouchableOpacity onPress={()=>onPress(item)}>
<Item title={item.title} />
</TouchableOpacity>
);
return (
<View style={styles.container}>
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={(item) => item.id}
/>
<Popup modalVisible={modalVisible} setModalVisible={setModalVisible} activeItem={activeItem} />
</View>
);
}
Modal.js
export default function Popup({modalVisible, setModalVisible, activeItem}) {
return (
<View style={styles.centeredView}>
<Modal
animationType="slide"
transparent={true}
visible={modalVisible}
onRequestClose={() => {
Alert.alert('Modal has been closed.');
}}>
<View style={styles.centeredView}>
<View style={styles.modalView}>
<Text style={styles.modalText}>{activeItem?.title}</Text>
<TouchableHighlight
style={{ ...styles.openButton, backgroundColor: '#2196F3' }}
onPress={() => {
setModalVisible(!modalVisible);
}}>
<Text style={styles.textStyle}>Hide Modal</Text>
</TouchableHighlight>
</View>
</View>
</Modal>
</View>
);
}
Here is a working demo for you.
I made something similar in my react-native project and do this:
Made with {useState} the variables i need in my modal, example, the name of my component
Wrap the ListItem component in a Pressable component
Put the onPress={() => {//Put your code here}} function, inside you will put the SetVariable of the variable you want in the modal
The Pressable will update the variable with the wich you want in the modal
I have a scrollview that has as a child an array of video components. It displays on the UI a carousel of videos. I would like the user to be able to click a play button that is above the video and play that video, but the way my logic is set, all the videos play together because the condition to play the video lives in a react useState.
see the code:
const VideosView = (props) => {
const videoClips = props.route.params.data;
const [state, setState] = React.useState(0);
const [play, setPlay] = React.useState(false);
return (
<>
<SafeAreaView pointerEvents='box-none' style={styles.root}>
<Header
goBack={() => props.navigation.goBack()}
title={state === 0 ? 'RYGGRöRLIGHET' : 'STYRKA & BALANS'}
/>
<View style={styles.paragraphsContainer}>
<Text style={styles.title}>
{state === 0 ? 'Ryggrörlighet' : 'Styrka & Balans'}
</Text>
<Text style={styles.paragraph}>
'Utför övningen i ca 5 min för bästa resultat.'
</Text>
<View style={styles.circlesContainer}>
{renderImages.map((_, index) => {
return (
<TouchableOpacity key={index} onPress={() => setState(0)}>
<View
style={[
styles.circles,
{ opacity: index === state ? 1 : 0.5 }
]}
/>
</TouchableOpacity>
);
})}
</View>
</View>
</SafeAreaView>
<View style={styles.scrollViewContainer}>
<ScrollView
bounces={false}
showsHorizontalScrollIndicator={false}
scrollEventThrottle={30}
onScroll={({ nativeEvent }) => {
const slide = Math.ceil(
nativeEvent.contentOffset.x / nativeEvent.layoutMeasurement.width
);
if (slide !== state) {
setState(slide);
}
}}
horizontal>
{videoClips.map((video, index) => (
<View style={{ position: 'relative' }}>
{!play && (
<TouchableOpacity
style={{
position: 'absolute',
backgroundColor: 'rgba(255,255,255,0.5)',
alignSelf: 'center',
top: 130,
width: 160,
height: 160,
borderRadius: 500,
zIndex: 4000
}}
onPress={() => setPlay(true)}></TouchableOpacity>
)}
<Video
shouldPlay={play}
key={index}
source={{
uri: video
}}
resizeMode='cover'
style={{ width: wp('100%'), height: hp('100%') }}
/>
</View>
))}
</ScrollView>
</View>
</>
);
};
I would like the user to click a button and play one video, use the carousel to go to next video, click play and play the next video.
Please, help.
If you change the play state from Boolean to Number you will probably solve your problem. Here is how:
const [videoBeingDisplayedIndex, setVideoBeingDisplayedIndex] = React.useState(5);
const [play, setPlay] = React.useState(5); // If it is equals to the video id the video should play, else it should not
...
<Button onClick={() => setPlay(videoBeingDisplayedIndex)}>
Play Video
</Button>
<VideoCarousel>
{
myVideoList.map((video, index) => {
return (
<Video
content={video}
index={index}
play={play === index} // Check if the current play state is equals to the video unique id
/>
);
})
}
</VideoCarousel>
...
PS.: please, have in mind that this snippet is abstracting the react-native elements and focusing on the problem.
Based on the code below, I would expect to see 'onDismiss!' when I swipe down on the modal view.
When I swipe down on the modal view it does not invoke the provided function. I can't find any other React Native users experiencing this problem. I am using React Native version 0.60.6.
Am I using Modal the wrong way or is this a bug?
<Modal
animationType="slide"
presentationStyle="pageSheet"
visible={showSetup}
hardwareAccelerated
onDismiss={() => alert('onDismiss!')}
onRequestClose={() => alert('onRequestClose!')}
>
<SetupView closeView={() => this.willClose()} />
</Modal>
This issue on the React Native issue tracker accurately describes this issue: https://github.com/facebook/react-native/issues/26892
I hope it's fixed soon!
Hey there you should try something like that
This is the code for the component rendered in my modal:
<TouchableWithoutFeedback
onPressOut={() => {
this.refs.view_ref.measure((fx, fy, width, height, px, py) => {
if (py === Dimensions.get('window').height) {
this.props.hide(false);
}
});
}}
>
<View style={styles.view} ref={'view_ref'}>
<View style={styles.nav}>
<Text style={styles.nav_title}>New Currency</Text>
<TouchableOpacity
onPress={() => {
this.props.hide(false);
}}
>
<Icon
style={styles.nav_close}
name={'close'}
size={30}
color={mode === 'dark' ? 'white' : 'black'}
/>
</TouchableOpacity>
</View>
</View>
</TouchableWithoutFeedback>
When wrapping the view you want to render in a TouchableWithoutFeedback, you get the "onPressOut" functionality. Then with this portion of code:
onPressOut={() => {
this.refs.view_ref.measure((fx, fy, width, height, px, py) => {
if (py === Dimensions.get('window').height) {
this.props.hide(false);
}
});
}}
you basically tell the view to listen on touch events happening at the top of the modal, thus it will be also executed when you swipe to dismiss
I was inspired by this answer:
https://github.com/facebook/react-native/issues/26892#issuecomment-605516625
Enjoy 😊
This is a bug with React Native, but I have found a workaround that works for me. Basically I have a TouchableWithoutFeedback fill the entire Modal. Then I use the onPressOut prop, which receives an event from which you can measure the locationY. What I have found is that if locationY < 0, the swipe was successful in dismissing the Modal, and in that case you can call setModalVisible(false). Then, next time you want to re-show the Modal, it will work. Hope it helps!
<Modal animationType="slide" presentationStyle="pageSheet" visible={ modal }>
<TouchableWithoutFeedback onPressOut={(e) => {
if (e.nativeEvent.locationY < 0) {
handleModal(false)
}}
>
<View style={ styles.modalOuter }>
<View style={ styles.modalInner }>
<Text>Hi from Modal</Text>
</View>
</View>
</TouchableWithoutFeedback>
</Modal>
// ...
const styles = StyleSheet.create({
modalInner: {
backgroundColor: "white",
position: "absolute",
bottom: 0,
width: "100%",
height: 400
},
modalOuter: {
backgroundColor: "white",
height: "100%"
}
});