I am trying to make the drawer component on material-ui a responsive drawer, by making it to be toggled open and close on smaller screens and permanent on desktop
I followed the documentation on mui, which says to add the temporary variant to the drawer, however it doesn't show up at all on desktop mode
Below is my drawer component
import React from "react";
import ReactDOM from "react-dom";
import { useHistory } from "react-router-dom";
import { makeStyles, Typography, Drawer, Toolbar, Divider, List, ListItem, ListItemText, ListItemIcon, Container, Box } from "#material-ui/core";
import HomeOutlinedIcon from '#material-ui/icons/HomeOutlined';
import AlternateEmailOutlinedIcon from '#material-ui/icons/AlternateEmailOutlined';
import NotificationsNoneOutlinedIcon from '#material-ui/icons/NotificationsNoneOutlined';
import DashboardOutlinedIcon from '#material-ui/icons/DashboardOutlined';
import ArchiveOutlinedIcon from '#material-ui/icons/ArchiveOutlined';
const drawerWidth = 280
const useStyles = makeStyles((theme) => ({
drawer : {
width: drawerWidth,
},
drawerPaper : {
width: drawerWidth,
},
list : {
marginLeft: '24px',
marginRight: '24px'
},
listItem : {
padding: '8px',
borderRadius: '5px',
},
text : {
color: theme.palette.primary.light,
fontWeight: '500',
lineHeight: '2000'
},
title : {
color: theme.palette.primary.light,
textAlign: 'center',
display: 'block',
fontWeight: '500',
fontSize: '20px',
marginTop: '10px',
marginBottom: '10px'
}
}));
export default function LeftBar(){
const classes = useStyles();
const [mobileOpen, setMobileOpen] = React.useState(false);
const handleDrawerToggle = () => {
setMobileOpen(!mobileOpen);
};
const list = [
{
text : 'Home',
icon : <HomeOutlinedIcon />,
path : '/'
},
{
text : 'Personal',
icon : <AlternateEmailOutlinedIcon />,
path : '/'
},
{
text : 'Notificatons',
icon : <NotificationsNoneOutlinedIcon />,
path : '/'
},
{
text : 'Dashboard',
icon : <DashboardOutlinedIcon />,
path : '/'
},
{
text : 'Archives',
icon : <ArchiveOutlinedIcon />,
path : '/'
}
];
return (
<Box>
<Drawer
className={classes.drawer}
anchor="left" classes={{
paper: classes.drawerPaper
}}
open={mobileOpen}
onClose={handleDrawerToggle}
ModalProps={{
keepMounted: true, // Better open performance on mobile.
}}
sx={{
display: { xs: 'block', sm: 'none' },
flexShrink: 0,
'& .MuiDrawer-paper': { boxSizing: 'border-box', width: drawerWidth },
}}
>
<Toolbar />
<Container>
<Typography variant="h5" component="h2" className={classes.title} gutterBottom>
My Account
</Typography>
</Container>
<Divider />
<List className={classes.list}>
{ list.map((item) => (
<ListItem key={item.text} className={classes.listItem} button>
<ListItemIcon className={classes.icon}>
{item.icon}
</ListItemIcon>
<ListItemText sx={{color: 'primary'}} primary={item.text} className={classes.text}/>
</ListItem>
))}
</List>
</Drawer>
</Box>
);
};
You have only included the drawer for xs - with display: { xs: 'block', sm: 'none' } that drawer will not display for any size but xs. You will need another drawer for sm and up. The MUI docs show a second drawer like this:
<Drawer
variant="permanent"
sx={{
display: { xs: 'none', sm: 'block' },
'& .MuiDrawer-paper': { boxSizing: 'border-box', width: drawerWidth },
}}
open
>
{you would put your list stuff here}
</Drawer>
You will probably need to move the Toolbar component outside of the Drawer component.
Related
I am trying to sort images by order, i have the button new and old. the [data-cy="list-file"] is the wrap for all images and [data-cy=fileCard-list] represent each one image.
I want after clicking the button old for example to be able the check with cypress if the sort is working perfectly.
import { sortFilteredComments } from './../../../../lib/vmr';
import loginData from '../../../fixtures/login.json';
/// <reference types='cypress' />
describe('upload multiple photos', () => {
before(() => {
cy.clearLocalStorageSnapshot();
});
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
it('login', () => {
cy.get('[data-cy=projects-card-table]').contains('E2E-TEST').click();
cy.get('[data-cy="header-search-bar"]').click();
cy.wait(3000).get('[data-cy=file-order-old]').click();
cy.get('[data-cy="list-file"]').should('be.visible');
cy.get('[data-cy=each-item-file]').should('be.visible');
});
});
Here is the file with sort method
`import Card from '#material-ui/core/Card';
import CardMedia from '#material-ui/core/CardMedia';
import Grid from '#material-ui/core/Grid';
import { makeStyles } from '#material-ui/core/styles';
import Typography from '#material-ui/core/Typography';
import { IFetchedFiles } from 'api';
import clsx from 'clsx';
import { useRouter } from 'next/router';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { formatFullDate } from '~/lib/date';
import MimeIcon from '../../MimeIcon';
import { COLORS } from '~/lib/theme';
const useStyles = makeStyles(() => ({
card: {
backgroundColor: '#F1F3F3',
height: '400px',
width: '288px',
margin: '8px'
},
cardMedia: {
height: '200px',
margin: '10px',
cursor: 'pointer',
position: 'relative'
},
cardMediaImgContain: {
backgroundSize: 'contain'
},
excelTitle: {
backgroundColor: COLORS.white,
bottom: 0,
color: 'black',
fontSize: 14,
fontWeight: 'bold',
position: 'absolute',
padding: 4,
right: 0
},
group: {
marginRight: 20
},
groupWrap: {
display: 'flex',
padding: '0px 10px'
},
projectLabel: {
fontSize: 14,
fontWeight: 'bold',
marginBottom: '11px',
padding: '0px 10px'
},
title: {
fontSize: 14
},
titleWithMarginTop: {
fontSize: 14,
marginTop: 12
},
fileData: {
fontSize: 14,
fontWeight: 'bold'
},
fileDataMarginTop: {
fontSize: 14,
fontWeight: 'bold',
marginTop: 12
},
iconsContainer: {
display: 'flex',
gap: '4px',
padding: '4px'
}
}));
const iconSize = 24;
interface IProps {
file: IFetchedFiles['files'][number];
organizationName: string;
getImageUrl: () => any;
imageMime: boolean;
excelMime: boolean;
pdfMime: boolean;
tiffMime: boolean;
}
const FileCard: React.VFC<IProps> = ({
file,
organizationName,
getImageUrl,
imageMime,
excelMime
}) => {
const classes = useStyles();
const { t, i18n } = useTranslation();
const router = useRouter();
return (
<Grid **data-cy='each-item-file'** key={file.file.id}>
<Card className={classes.card}>
<CardMedia
className={clsx(classes.cardMedia, {
[classes.cardMediaImgContain]: excelMime
})}
image={getImageUrl()}
onClick={() => {
const photoUrl = `/orgs/${organizationName}/projects/${file.project.id}/photos/${file.file.id}`;
router.push(photoUrl);
}}
>
<div className={classes.iconsContainer}>
{file.file.mime && (
<MimeIcon mime={file.file.mime} iconSizeMime={iconSize} />
)}
</div>
{excelMime && (
<div className={classes.excelTitle}>{file.file.name}</div>
)}
</CardMedia>
<Typography variant='h6' className={classes.projectLabel}>
{file.project.label}
</Typography>
<div className={classes.groupWrap}>
<div className={classes.group}>
{file.material_group.label && (
<Typography variant='h6' className={classes.title}>
{t('searchFile.fileList.materialGroup')}
</Typography>
)}
<Typography variant='h6' className={classes.title}>
{t('searchFile.fileList.material')}
</Typography>
<Typography variant='h6' className={classes.title}>
{t('searchFile.fileList.process')}
</Typography>
<Typography variant='h6' className={classes.titleWithMarginTop}>
{imageMime
? t('searchFile.fileList.date')
: t('file.lastModified')}
</Typography>
<Typography variant='h6' className={classes.title}>
{imageMime
? t('searchFile.fileList.photographer')
: t('searchFile.fileList.registrant')}
</Typography>
</div>
<div>
{file.material_group.label && (
<Typography
variant='h6'
color='primary'
className={classes.fileData}
>
{file.material_group.label}
</Typography>
)}
<Typography
variant='h6'
color='primary'
className={classes.fileData}
>
{file.material.label ?? t('searchFile.fileList.uncategorized')}
</Typography>
<Typography
variant='h6'
color='primary'
className={classes.fileData}
>
{file.process.label ?? t('searchFile.fileList.uncategorized')}
</Typography>
<Typography
variant='h6'
className={classes.fileDataMarginTop}
color='primary'
>
{`${formatFullDate(new Date(file.file.date), i18n.language)}`}
</Typography>
<Typography
variant='h6'
color='primary'
className={classes.fileData}
>
{file.user.display_name ?? ''}
</Typography>
</div>
</div>
</Card>
</Grid>
);
};
export default FileCard;
`
It's a little bit confusing because it's not a table and i am working on react.js and typescript. Here everything is working perfectly but just the last assertion is where i have no clue.
Notes: you can see that [data-cy="list-file"] represent the whole container and [data-cy=fileCard-list] represent each file inside of the container.
I think we need more details about the sorting method you are using. In a table we could check the aria-sort property. Do you have any similar way to check it?
Another way could be iterating over all the [data-cy=fileCard-list] and checking if the parameter you are using to order them is correctly sorted. I don't understand what you mean with that last assertion cy.get('[data-cy="list-file"]').should('be.visible');
I need your help related to mui/x-data-grid. I am trying to render a cell in mui/x-data-grid with text/icon in it based on the text value, but unable to do so.
The following is the codesandbox link:
codesandbox link
And I want something similar to this.
You can use the Chip component in MUI instead of rolling out your own, to make it look like in the screenshot, set its variant to outlined. Below is an example:
function getChipProps(params: GridRenderCellParams): ChipProps {
if (params.value === "RED") {
return {
icon: <WarningIcon style={{ fill: red[500] }} />,
label: params.value,
style: {
borderColor: red[500]
}
};
} else {
return {
icon: <CheckCircleIcon style={{ fill: blue[500] }} />,
label: params.value,
style: {
borderColor: blue[500]
}
};
}
}
const columns: GridColDef[] = [
{...},
{
field: "status",
headerName: "Status",
renderCell: (params) => {
return <Chip variant="outlined" {...getChipProps(params)} />;
}
}
];
Live Demo
MUI column data grid example :
{
field: "",
width:150,
renderCell: (cellValues) => {
return (
<>
<IconButton
color="secondary"
aria-label="add an alarm"
onClick={(event) =>
this.handleModalDelete(event, cellValues)
}
>
<DeleteIcon style={{ color: blue[500] }} />
</IconButton>
</>
);
},
}
I tried to use Expo - Webview to display an HTML5 content(.html) using <object> but it does not work.
The same html5 content is working perfectly in a regular webpage.
Can somebody guide with a simple example to achieve the above mentioned scenario ?
Here is the code,
I tried both react-native-render-html and webview, nothing works.
import React, { Component } from 'react';
import { withNavigation } from '#react-navigation/compat';
import { StyleSheet, Dimensions, Image, TouchableWithoutFeedback,ScrollView } from 'react-native';
import { Block, Text, theme } from 'galio-framework';
import HTML from 'react-native-render-html';
import Constants from 'expo-constants';
import materialTheme from '../constants/Theme';
import { heightPercentageToDP } from 'react-native-responsive-screen';
import {widthPercentageToDP as wp, heightPercentageToDP as hp} from 'react-native-responsive-screen';
import {WebView} from 'react-native-webview';
import { Video } from 'expo-av';
class CourseDetail extends React.Component {
render() {
const imageStyles = [styles.image, styles.normalImage];
const {navigation,route}= this.props;
const Course = route.params.course;
const imageUrl = route.params.imageUrl;
const videoUrl = route.params.videoUrl;
return (
<ScrollView
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.catalogItems}>
<Block flex style={[styles.article]}>
<Text size={30} style={styles.CourseTitle}>{Course.Title}</Text>
<HTML containerStyle={styles.courseWrap } html={ '<object type="text/html" data="https://domainname.com/story_html5.html" ></object>'} />
<WebView source={{ uri: 'https://domainname.com/story_html5.html' }} />
</Block>
</ScrollView>
);
}
}
export default withNavigation(CourseDetail);
const styles = StyleSheet.create({
article:{
backgroundColor: theme.COLORS.WHITE,
padding:hp('3%')
},
content: {
flex:1,
},
CourseTitle:{
flex: 1,
flexWrap: 'wrap',
paddingBottom: 10,
paddingTop:5,
fontFamily:'manifa-regular',
color:'#333333',
},
imageContainer: {
elevation: 1,
},
shadow: {
shadowColor: 'rgba(0,51,160,0.15)',
shadowOffset: { width: 0, height: 5 },
shadowRadius:15,
shadowOpacity: 1,
elevation:15,
},
image: {
borderRadius: 3,
marginHorizontal: wp('1%'),
marginTop: hp('5%'),
backgroundColor:'#84BD00',
},
normalImage: {
height:hp('25%'),
width:'auto',
// / marginTop:hp('3.3%'),
marginHorizontal: 0,
marginRight: wp('0%'),
marginLeft:wp('0%'),
marginTop:hp('3 %'),
marginBottom:hp('1%'),
flex: 1,
flexDirection: 'row',
padding:0
},
courseWrap:
{
marginVertical:10,
width:'100%',
height:hp('50%'),
backgroundColor:'red'
},
description:
{
marginTop:10
}
});
I am using react-native-google-places-autocomplete to select a location. I want to extract the location selected and use it in other component.
How can I save the address
This is my code
import React, {Component} from 'react';
import { View, Image, Text, StyleSheet } from 'react-native';
import { GooglePlacesAutocomplete } from 'react-native-google-places-autocomplete';
const homePlace = { description: 'Home', geometry: { location: { lat: 48.8152937, lng: 2.4597668 } }};
const workPlace = { description: 'Work', geometry: { location: { lat: 48.8496818, lng: 2.2940881 } }};
export default class google extends Component {
render(){
return (
<View style={styles.container}>
<View style={styles.top}>
<GooglePlacesAutocomplete
placeholder='Search'
minLength={2} // minimum length of text to search
autoFocus={false}
returnKeyType={'search'} // Can be left out for default return key https://facebook.github.io/react-native/docs/textinput.html#returnkeytype
listViewDisplayed='auto' // true/false/undefined
fetchDetails={true}
renderDescription={row => row.description} // custom description render
onPress={(data, details = null) => { // 'details' is provided when fetchDetails = true
console.log(data, details);
this.setState(
{
address: data.description, // selected address
coordinates: `${details.geometry.location.lat},${details.geometry.location.lng}` // selected coordinates
}
);
}}
getDefaultValue={() => ''}
query={{
// available options: https://developers.google.com/places/web-service/autocomplete
key: 'AIzaS#################',
language: 'es', // language of the results
}}
styles={{
textInputContainer: {
width: '100%'
},
description: {
fontWeight: 'bold'
},
predefinedPlacesDescription: {
color: '#1faadb'
}
}}
currentLocation={true} // Will add a 'Current location' button at the top of the predefined places list
currentLocationLabel="Current location"
nearbyPlacesAPI='GooglePlacesSearch' // Which API to use: GoogleReverseGeocoding or GooglePlacesSearch
GoogleReverseGeocodingQuery={{
// available options for GoogleReverseGeocoding API : https://developers.google.com/maps/documentation/geocoding/intro
}}
GooglePlacesSearchQuery={{
// available options for GooglePlacesSearch API : https://developers.google.com/places/web-service/search
rankby: 'distance',
types: 'food'
}}
filterReverseGeocodingByTypes={['locality', 'administrative_area_level_3']} // filter the reverse geocoding results by types - ['locality', 'administrative_area_level_3'] if you want to display only cities
predefinedPlaces={[homePlace, workPlace]}
debounce={200} // debounce the requests in ms. Set to 0 to remove debounce. By default 0ms.
renderRightButton={() => <Text>Custom text after the input</Text>}
/>
</View>
<View style={styles.container2}>
<Text>
hola {this.setState.address}
</Text>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
width: '100%',
height: '100%',
},
welcome: {
fontSize: 40,
textAlign: 'center',
margin: 10,
color:'black',
},
instructions: {
textAlign: 'center',
color: 'black',
marginBottom: 5,
},
top: {
height: '50%',
justifyContent: 'center',
alignItems: 'center',
},
container2: {
height:'75%',
justifyContent: 'center',
},
buttonContainer: {
alignItems: 'center',
backgroundColor: 'rgba(255, 255,255, 0.5)',
padding: 1,
margin: 50,
borderRadius: 25,
borderWidth: 2,
borderColor: '#0B0B3B'
},
textoboton: {
textAlign: 'center',
color: 'black',
marginBottom: 5,
fontSize: 12
},
})
I've been also using another library with some differences
app.js
import React,{Component} from 'react';
import { Constants } from 'expo';
import Icon from 'react-native-vector-icons/FontAwesome';
import { View, Image, Text, StyleSheet, AsyncStorage, Button,ScrollView, TextInput, ActivityIndicator } from 'react-native';
import {
NavigationActions
} from 'react-navigation';
import { GoogleAutoComplete } from 'react-native-google-autocomplete';
import {Card, Input} from "react-native-elements";
import LocationItem from './locationItem';
export default class App extends React.Component {
state={
datos:[],
}
componentDidMount(){
this._loadedinitialstate().done();
}
_loadedinitialstate =async() => {
AsyncStorage.getItem('datos');
}
render() {
return (
<View style={styles.container}>
<GoogleAutoComplete apiKey={'AIzaSyB2HyNTBm1sQJYJkwOOUA1LXRHAKh4gmjU'} debounce={20} minLength={2} getDefaultValue={() => ''} onPress={(data, details = null) => { // 'details' is provided when fetchDetails = true
console.log(data, details);}} returnKeyType={'default'} fetchDetails={true}
>
{({
locationResults,
isSearching,
clearSearchs,
datos,
handleTextChange
}) => (
<React.Fragment>
<View style={styles.inputWrapper}>
<Input
style={styles.textInput}
placeholder="Ingresa tu direccion"
onChangeText={(datos) => this.setState({datos})}
value={datos}
/>
</View>
{isSearching && <ActivityIndicator size="large" color="red" />}
<ScrollView>
{locationResults.map(el => (
<LocationItem
{...el}
key={el.id}
/>
))}
</ScrollView>
</React.Fragment>
)}
</GoogleAutoComplete>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
textInput: {
height: 40,
width: 300,
borderWidth: 1,
paddingHorizontal: 16,
},
inputWrapper: {
marginTop: 80,
flexDirection: 'row'
},
});
locationitem.js
import React, { PureComponent } from 'react';
import { View, Alert, Text, StyleSheet, TouchableOpacity, AsyncStorage} from 'react-native';
class LocationItem extends PureComponent {
constructor(props) {
super(props);
this.state = {datos:''};
}
_handlePress = () => {
AsyncStorage.setItem('datos',datos)
}
render() {
return (
<TouchableOpacity style={styles.root} onPress={this._handlePress} >
<Text value={this.state.datos}> {this.props.description} </Text>
</TouchableOpacity>
);
}
}
const styles = StyleSheet.create({
root: {
height: 40,
borderBottomWidth: StyleSheet.hairlineWidth,
justifyContent: 'center'
}
})
export default LocationItem;
The source of both codes is here react-native-google-places-autocomplete enter link description here
Which code will be easy to use, and How can I solve my Issue (get the address) ??
Any Answer will be Helpful
first install
npm i react-native-google-places-autocomplete
then
import React from 'react';
import { View, Image } from 'react-native';
import { GooglePlacesAutocomplete } from 'react-native-google-places-autocomplete';
const homePlace = { description: 'Home', geometry: { location: { lat: 48.8152937, lng: 2.4597668 } }};
const workPlace = { description: 'Work', geometry: { location: { lat: 48.8496818, lng: 2.2940881 } }};
const GooglePlacesInput = () => {
return (
<GooglePlacesAutocomplete
placeholder='Search'
minLength={2} // minimum length of text to search
autoFocus={false}
returnKeyType={'search'} // Can be left out for default return key https://facebook.github.io/react-native/docs/textinput.html#returnkeytype
listViewDisplayed='auto' // true/false/undefined
fetchDetails={true}
renderDescription={row => row.description} // custom description render
onPress={(data, details = null) => { // 'details' is provided when fetchDetails = true
console.log(data, details);
}}
getDefaultValue={() => ''}
query={{
// available options: https://developers.google.com/places/web-service/autocomplete
key: 'YOUR API KEY',
language: 'en', // language of the results
types: '(cities)' // default: 'geocode'
}}
styles={{
textInputContainer: {
width: '100%'
},
description: {
fontWeight: 'bold'
},
predefinedPlacesDescription: {
color: '#1faadb'
}
}}
currentLocation={true} // Will add a 'Current location' button at the top of the predefined places list
currentLocationLabel="Current location"
nearbyPlacesAPI='GooglePlacesSearch' // Which API to use: GoogleReverseGeocoding or GooglePlacesSearch
GoogleReverseGeocodingQuery={{
// available options for GoogleReverseGeocoding API : https://developers.google.com/maps/documentation/geocoding/intro
}}
GooglePlacesSearchQuery={{
// available options for GooglePlacesSearch API : https://developers.google.com/places/web-service/search
rankby: 'distance',
types: 'food'
}}
filterReverseGeocodingByTypes={['locality', 'administrative_area_level_3']} // filter the reverse geocoding results by types - ['locality', 'administrative_area_level_3'] if you want to display only cities
predefinedPlaces={[homePlace, workPlace]}
debounce={200} // debounce the requests in ms. Set to 0 to remove debounce. By default 0ms.
renderLeftButton={() => <Image source={require('path/custom/left-icon')} />}
renderRightButton={() => <Text>Custom text after the input</Text>}
/>
);
}
After a long journey, these steps helped me solve the problem.
1) Install npm install react-native-google-places-autocomplete --save.
2) Then use this code below, as an element in your component.
<GooglePlacesAutocomplete
query={{
key: "GOOGLE_PLACES_API_KEY",
language: "en", // language of the results
}}
onPress={(data, details = null) => {
console.log(details);
console.log(data);
console.log("data.description",data.description.split(','));
}}
onFail={(error) => console.error(error)}
listViewDisplayed="false"
requestUrl={{
url:
"https://cors-
anywhere.herokuapp.com/https://maps.googleapis.com/maps/api",
useOnPlatform: "web",
}} // this in only required for use on the web. See https://git.io/JflFv
more for details.
styles={{
textInputContainer: {
backgroundColor: "rgba(0,0,0,0)",
borderTopWidth: 0,
borderBottomWidth: 0,
},
textInput: {
marginLeft: 0,
marginRight: 0,
height: 38,
color: "#5d5d5d",
fontSize: 16,
},
predefinedPlacesDescription: {
color: "#1faadb",
},
}}
/>
3) You may have the same problem that i had, which the list disappears when i try to select result. However, this is the action that solved this problem for me.
Commenting out onBlur on node_modules. path: "..\node_modules\react-native-google-places-autocomplete\GooglePlacesAutocomplete.js".
...
onFocus={onFocus ? () => {this._onFocus(); onFocus()} : this._onFocus}
// onBlur={this._onBlur}
underlineColorAndroid={this.props.underlineColorAndroid}
...
4) For saving the address you can check the console.log in the code, and then use setState or something like this.
5) For more information and options of these element check out this repository:
https://github.com/FaridSafi/react-native-google-places-autocomplete.
hope this will help for you :)
First of all, I used listViewDisplayed={false} because otherwise the list view get stuck with the results, and even on location press the list doesn't closes.
Second, to answer your question: The results are in the onPress function of GooglePlacesAutocomplete, you can update the state with the chosen location and then use it where ever you want in your component:
onPress={(data, details = null) => {
this.setState({
latitude: details.geometry.location.lat,
longitude: details.geometry.location.lng,
moveToUserLocation: true
});
this._gotoLocation();
}
}
As i wrote it, onPress also trigger the function that moves the map to display the new location.
import React, {Component} from 'react';
import { View, Image, Text, StyleSheet } from 'react-native';
import { GooglePlacesAutocomplete } from 'react-native-google-places-autocomplete';
export default class google extends Component {
constructor(props) {
super(props);
this.state = {
address:null,
lat:null,
lng:null,
}
}
getAdd(data){
console.log("add",data);
this.setState(
{
address: data.formatted_address, // selected address
lat: data.geometry.location.lat,// selected coordinates latitude
lng:data.geometry.location.lng, // selected coordinates longitute
}
);
console.log("this.state.address",this.state.address); ///to console address
console.log("this.state.coordinates",this.state.lat,this.state.lng); /// to console coordinates
}
render(){
return (
<View style={styles.container}>
<View style={styles.top}>
<GooglePlacesAutocomplete
placeholder='Search'
minLength={2} // minimum length of text to search
autoFocus={false}
fetchDetails={true}
returnKeyType={'default'}
onPress={(data, details = null) => { // 'details' is provided when fetchDetails = true
var data = details;
this.getAdd(data);
}}
query={{
// available options: https://developers.google.com/places/web-service/autocomplete
key: 'AIzaS#################',
language: 'en',
types: 'geocode', // default: 'geocode'
}}
listViewDisplayed={this.state.showPlacesList}
textInputProps={{
onFocus: () => this.setState({ showPlacesList: true }),
onBlur: () => this.setState({ showPlacesList: false }),
}}
styles={{
textInputContainer: {
width: '100%'
},
description: {
fontWeight: 'bold'
},
predefinedPlacesDescription: {
color: '#1faadb'
}
}}
currentLocation={true} // Will add a 'Current location' button at the top of the predefined places list
currentLocationLabel="Current location"
nearbyPlacesAPI='GooglePlacesSearch' // Which API to use: GoogleReverseGeocoding or GooglePlacesSearch
filterReverseGeocodingByTypes={['locality', 'administrative_area_level_3']} // filter the reverse geocoding results by types - ['locality', 'administrative_area_level_3'] if you want to display only cities
// predefinedPlaces={[]}
predefinedPlacesAlwaysVisible={true}
/>
</View>
{ this.state.address !=null ?(
<View style={styles.container2}>
<Text>
Address: {this.state.address}
</Text>
</View>
):null }
</View>
);
}
}
const styles = StyleSheet.create({
container: {
width: '100%',
height: '100%',
},
welcome: {
fontSize: 40,
textAlign: 'center',
margin: 10,
color:'black',
},
instructions: {
textAlign: 'center',
color: 'black',
marginBottom: 5,
},
top: {
height: '50%',
justifyContent: 'center',
alignItems: 'center',
},
container2: {
height:'75%',
justifyContent: 'center',
},
buttonContainer: {
alignItems: 'center',
backgroundColor: 'rgba(255, 255,255, 0.5)',
padding: 1,
margin: 50,
borderRadius: 25,
borderWidth: 2,
borderColor: '#0B0B3B'
},
textoboton: {
textAlign: 'center',
color: 'black',
marginBottom: 5,
fontSize: 12
},
})
I create a menu and want to highlight the item which i choose,and i did it. But when i press back/forward button,the menu item don't highlight. What should i do?
I have tried to use addEventListener but failed.
Have someone could give some advice?
class Sidebar extends React.Component {
constructor(props) {
super(props);
this.state={
test: "home"
}
this.menuClickHandle = this.menuClickHandle.bind(this);
}
componentWillMount(){
hashHistory.listen((event)=>{
test1 = event.pathname.split("/");
});
this.setState({
test:test1[1]
});
}
menuClickHandle(item) {
this.props.clickItem(item.key);
}
onCollapseChange() {
this.props.toggle();
}
render() {
var {collapse} = this.props;
return (
<aside className="ant-layout-sider">
<Menu mode="inline" theme="dark" defaultSelectedKeys={[this.state.test || "home"]} onClick={this.menuClickHandle.bind(this)}>
<Menu.Item key="home">
<Link to="/home">
<Icon type="user"/><span className="nav-text">用户管理</span>
</Link>
</Menu.Item>
<Menu.Item key="banner">
<Link to="/banner">
<Icon type="setting"/><span className="nav-text">Banner管理</span>
</Link>
</Menu.Item>
</Menu>
<div className="ant-aside-action" onClick={this.onCollapseChange.bind(this)}>
{collapse ? <Icon type="right"/> : <Icon type="left"/>}
</div>
</aside>
)
}
}
I could come up with a solution using WithRouter
import React,{ Component } from 'react';
import { NavLink, withRouter } from 'react-router-dom';
import { Layout, Menu, Icon } from 'antd';
import PropTypes from 'prop-types';
const { Sider } = Layout;
class SideMenu extends Component{
static propTypes = {
location: PropTypes.object.isRequired
}
render() {
const { location } = this.props;
return (
<Sider
trigger={null}
collapsible
collapsed={this.props.collapsed}>
<div className="logo" />
<Menu
theme="dark"
mode="inline"
defaultSelectedKeys={['/']}
selectedKeys={[location.pathname]}>
<Menu.Item key="/">
<NavLink to="/">
<Icon type="home" />
<span>Home</span>
</NavLink>
</Menu.Item>
<Menu.Item key="/other">
<NavLink to="/other">
<Icon type="mobile"/>
<span>Applications</span>
</NavLink>
</Menu.Item>
<Menu.Item key="/notifications">
<NavLink to="/notifications">
<Icon type="notification" />
<span>Notifications</span>
</NavLink>
</Menu.Item>
</Menu>
</Sider>
)
}
}
export default withRouter(SideMenu);
Intercepts the current URL and then set selectedKeys(Note that it is not defaultSelectedKeys).
componentWillMount(){
hashHistory.listen((event)=>{
pathname = event.pathname.split("/");
if(pathname != null){
this.setState({
test:pathname[1]
});
}
});
}
you can set the paths of the link as keys on each Menu.Item . then selectedKeys={this.props.location.pathname}
<Menu
theme="light"
mode='inline'
selectedKeys={[this.props.location.pathname,]}
>
<Menu.Item key={item.path} style={{float:'right'}}>
Link to={item.path}>{item.name}</Link>
</Menu.Item>
{menulist}
</Menu>
Item would be set active according to the current path.
i added [] and trailing comma because selectedKeys accepts array while this.props.location.pathname is a String. i just code as hobby so idont know if its acceptable.
The following answer assumes you are using hooks. I know you are not in your question, but it might be useful for other people. In addition, this solution will work if you have nested paths such as /banner/this/is/nested, and it works not only when pressing back and forward buttons but also when refreshing the current page:
import React, { useState, useEffect } from 'react'
import { useHistory, useLocation } from 'react-router-dom'
import { Layout, Menu } from 'antd'
const { Sider } = Layout
const items = [
{ key: '1', label: 'Invoices', path: '/admin/invoices' },
{ key: '2', label: 'Service Details', path: '/admin/service-details' },
{ key: '3', label: 'Service Contract Details', path: '/admin/service-contract-details' },
{ key: '4', label: 'Cost Centers', path: '/admin/cost-centers' },
{ key: '5', label: 'Clients', path: '/admin/clients' },
{ key: '6', label: 'Vendors', path: '/admin/vendors' }
]
const Sidebar = () => {
const location = useLocation()
const history = useHistory()
const [selectedKey, setSelectedKey] = useState(items.find(_item => location.pathname.startsWith(_item.path)).key)
const onClickMenu = (item) => {
const clicked = items.find(_item => _item.key === item.key)
history.push(clicked.path)
}
useEffect(() => {
setSelectedKey(items.find(_item => location.pathname.startsWith(_item.path)).key)
}, [location])
return (
<Sider style={{ backgroundColor: 'white' }}>
<h3 style={{ paddingLeft: '1rem', paddingTop: '1rem', fontSize: '1.25rem', fontWeight: 'bold', minHeight: 64, margin: 0 }}>
Costek
</h3>
<Menu selectedKeys={[selectedKey]} mode='inline' onClick={onClickMenu}>
{items.map((item) => (
<Menu.Item key={item.key}>{item.label}</Menu.Item>
))}
</Menu>
</Sider>
)
}
export default Sidebar
This is how the sidebar will look like:
#Nadun's solution works for paths that don't contains arguments. If you're however using arguments in your routes, like me, here's a solution that should work for any route path, including /users/:id or crazy stuff like /users/:id/whatever/:otherId. It uses react-router's matchPath API, which uses the exact same logic as the Router component.
// file with routes
export const ROUTE_KEYS = {
ROOT: "/",
USER_DETAIL: "/users/:id",
};
export const ROUTES = {
ROOT: {
component: Home,
exact: true,
key: ROUTE_KEYS.ROOT,
path: ROUTE_KEYS.ROOT,
},
USER_DETAIL: {
component: Users,
key: ROUTE_KEYS.USER_DETAIL,
path: ROUTE_KEYS.USER_DETAIL,
},
};
.
// place within the App component
<Router>
<Layout>
<MyMenu />
<Layout>
<Layout.Content>
{Object.values(ROUTES).map((route) => (
<Route {...route} />
))}
</Layout.Content>
</Layout>
</Layout>
</Router>
.
// MyMenu component
const getMatchedKey = (location) =>
(
Object.values(ROUTES).find((route) =>
matchPath(location.pathname, route)
) || {}
).path;
const MyMenu = ({ location }) => {
return (
<Layout.Sider>
<AntMenu mode="inline" selectedKeys={[getMatchedKey(location)]}>
<AntMenu.SubMenu
title={
<React.Fragment>
<Icon type="appstore" />
Home
</React.Fragment>
}
>
<AntMenu.Item key={ROUTE_KEYS.ROOT}>
<Icon type="appstore" />
<span className="nav-text">
Some subitem
</span>
</AntMenu.Item>
</AntMenu.SubMenu>
<AntMenu.SubMenu
title={
<React.Fragment>
<Icon type="user" />
Users
</React.Fragment>
}
>
<AntMenu.Item key={ROUTE_KEYS.USER_DETAIL}>
<Icon type="user" />
<span className="nav-text">
User detail
</span>
</AntMenu.Item>
</AntMenu.SubMenu>
</AntMenu>
</Layout.Sider>
);
};
export default withRouter(MyMenu);
I do something like this but it doesn't seem to be reactive. Like if I navigate to a new page through a button (not from the menu items), it will not update the active link until the page refreshes.
import React from 'react';
import { StyleSheet, css } from 'aphrodite'
import { browserHistory, Link } from 'react-router';
import 'antd/lib/menu/style/css';
import 'antd/lib/icon/style/css';
import 'antd/lib/row/style/css';
import 'antd/lib/col/style/css';
import 'antd/lib/message/style/css';
import { appConfig } from '../../modules/config';
import { Menu, Icon, Row, Col, message } from 'antd';
const SubMenu = Menu.SubMenu;
const MenuItemGroup = Menu.ItemGroup;
const { appName } = appConfig;
const AppNavigation = React.createClass({
getInitialState() {
return {
current: this.props.pathname
};
},
handleClick(e) {
browserHistory.push(e.key);
this.setState({ current: e.key });
return;
},
render() {
return (
<Row className='landing-menu' type="flex" justify="space-around" align="middle" style={{height: 55, zIndex: 1000, paddingLeft: 95, color: '#fff', backgroundColor: '#da5347', borderBottom: '1px solid #e9e9e9'}}>
<Col span='19'>
<Link to='/'>
<h2 style={{fontSize: 21, color: '#fff'}}>
{appName}
<Icon type="rocket" color="#fff" style={{fontWeight: 200, fontSize: 26, marginLeft: 5 }}/>
</h2>
</Link>
</Col>
<Col span='5'>
<Menu onClick={this.handleClick} selectedKeys={[this.state.current]} mode="horizontal" style={{height: 54, backgroundColor: '#da5347', borderBottom: '0px solid transparent'}}>
<Menu.Item style={{height: 54, }} key="/">Home</Menu.Item>
<Menu.Item style={{height: 54, }} key="/signup">Signup</Menu.Item>
<Menu.Item style={{height: 54, }} key="/login">Login</Menu.Item>
</Menu>
</Col>
</Row>
);
},
});
export const App = React.createClass({
propTypes: {
children: React.PropTypes.element.isRequired,
},
componentWillMount(){
if (Meteor.userId()) {
browserHistory.push('/student/home')
}
},
render() {
return (
<div style={{position: 'relative'}}>
<AppNavigation pathname={this.props.location.pathname} />
<div style={{minHeight: '100vh'}}>
{ this.props.children }
</div>
</div>
);
}
});
EDIT:
the below works pretty well. pass down the pathname from react-router and pop that as a prop into selectedKeys
import React from 'react';
import { StyleSheet, css } from 'aphrodite'
import { browserHistory, Link } from 'react-router';
import 'antd/lib/menu/style/css';
import 'antd/lib/icon/style/css';
import 'antd/lib/row/style/css';
import 'antd/lib/col/style/css';
import 'antd/lib/message/style/css';
import { appConfig } from '../../modules/config';
import { Menu, Icon, Row, Col, message } from 'antd';
const SubMenu = Menu.SubMenu;
const MenuItemGroup = Menu.ItemGroup;
const { appName } = appConfig;
const AppNavigation = React.createClass({
getInitialState() {
return {
current: this.props.pathname
};
},
handleClick(e) {
browserHistory.push(e.key);
this.setState({ current: e.key });
return;
},
render() {
return (
<Row className='landing-menu' type="flex" justify="space-around" align="middle" style={{height: 55, zIndex: 1000, paddingLeft: 95, color: '#fff', backgroundColor: '#da5347', borderBottom: '1px solid #e9e9e9'}}>
<Col span='19'>
<Link to='/'>
<h2 style={{fontSize: 21, color: '#fff'}}>
{appName}
<Icon type="rocket" color="#fff" style={{fontWeight: 200, fontSize: 26, marginLeft: 5 }}/>
</h2>
</Link>
</Col>
<Col span='5'>
<Menu onClick={this.handleClick} selectedKeys={[this.props.pathname]} mode="horizontal" style={{height: 54, backgroundColor: '#da5347', borderBottom: '0px solid transparent'}}>
<Menu.Item style={{height: 54, }} key="/">Home</Menu.Item>
<Menu.Item style={{height: 54, }} key="/signup">Signup</Menu.Item>
<Menu.Item style={{height: 54, }} key="/login">Login</Menu.Item>
</Menu>
</Col>
</Row>
);
},
});
export const App = React.createClass({
propTypes: {
children: React.PropTypes.element.isRequired,
},
componentWillMount(){
if (Meteor.userId()) {
browserHistory.push('/student/home')
}
},
render() {
return (
<div style={{position: 'relative'}}>
<AppNavigation pathname={this.props.location.pathname} />
<div style={{minHeight: '100vh'}}>
{ this.props.children }
</div>
</div>
);
}
});
If you are using an array and mapping over it (as in my case) to set menu Items, They must be in the same order as they appear in the Side menu otherwise, an active bar or background will not be shown.
Environment: React Router V5, Ant Design V4.17.0
I solved this issues by override the onClick props of Menu.Item of antd
<Menu theme="light" mode="inline">
{menuItems.map((item) => {
return (
<NavLink
to={item.navigation}
component={({ navigate, ...rest }) => <Menu.Item {...rest} onClick={navigate} />}
key={item.key}
activeClassName="ant-menu-item-selected"
>
{item.icons}
<span>{item.name}</span>
</NavLink>
)
}
)}
</Menu>
The NavLink component will pass navigate prop to Menu.Item, we need to map it to onClick prop and click behaviour will work correctly.