My site has a component (NavPanel) that consists of two components (a back button (BackToButton ) and a search field (SearchTextField )). With a standard screen size, they are positioned as they should be, but if the screen sizes are reduced, then these two components overlap each other.
The most optimal for me would be if, with a compressed screen size, the second component (SearchTextField ) will be located under the first (BackToButton ). Tell me how you can solve this problem?
const Style = {
paddingLeft: '8%',
paddingRight: '8%',
minHeight: '70px',
alignItems: 'center',
flexWrap: 'nowrap',
whiteSpace: 'nowrap'
}
export default function NavPanel() {
return (
<Grid container sx={Style}>
<BackToButton />
<SearchTextField />
</Grid>
);
}
Here you go...
You didn't use the grid correctly.
See the forked snippet here.
EDIT 1
A:
B:
EDIT 2
You can change the breakpoint from sm to md. It means that PageNameBackToButton and FilterButtonSearchTextField will be stacked earlier, but A will not happen.
See the forked snippet here.
PageNameBackToButton.jsx
import React from "react";
import { Tooltip } from "#mui/material";
import BackToButton from "./BackToButton";
const PageName = {
color: "#000000",
fontSize: "20px",
fontWeight: "700",
letterSpacing: "0.2px",
paddingRight: "20px"
};
const PageNameBackToButtonContainerStyle = {
width: "50%",
justifyContent: "start",
alignContent: "center"
};
export default function PageNameBackToButton(props) {
return (
<div sx={PageNameBackToButtonContainerStyle}>
<BackToButton />
<div style={{ marginTop: "5px", display: "inline-block" }}>
<span style={PageName}>Some Text</span>
<span>
<Tooltip title={`Here long long text`} placement="right">
<span
style={{ fontSize: "18px", color: "#ef1400", userSelect: "none" }}
>
Live
</span>
</Tooltip>
</span>
</div>
</div>
);
}
FilterButtonSearchTextField.jsx
import { TextField } from "#mui/material";
const FilterButtonSearchTextFieldContainerStyle = {};
const SearchTextField = {};
export default function FilterButtonSearchTextField() {
return (
<div sx={FilterButtonSearchTextFieldContainerStyle}>
<TextField
required
label="Search Search"
size="small"
style={SearchTextField}
/>
</div>
);
}
Filter.jsx
import React from "react";
import { Grid } from "#mui/material";
import FilterButtonSearchTextField from "./FilterButtonSearchTextField";
import PageNameBackToButton from "./PageNameBackToButton";
import { styled } from "#mui/material/styles";
const FilterContainerStyle = {};
const Root = styled("div")(({ theme }) => ({
padding: theme.spacing(1),
[theme.breakpoints.up("md")]: {
display: "flex",
justifyContent: "flex-end"
}
}));
export default function Filter(props) {
const pageName = props.pageName !== undefined ? props.pageName : "";
const showBackToButton =
props.showBackToButton !== undefined ? props.showBackToButton : false;
return (
<Grid container spacing={2} sx={FilterContainerStyle}>
<Grid item md={6} xs={12}>
<PageNameBackToButton
showBackToButton={showBackToButton}
pageName={pageName}
/>
</Grid>
<Grid item md={6} xs={12}>
<Root>
<FilterButtonSearchTextField table={props.table} />
</Root>
</Grid>
</Grid>
);
}
<Grid container> is meaningless without some <Grid item>s inside it.
Just like the Bootstrap,
The grid creates visual consistency between layouts while allowing flexibility across a wide variety of designs. Material Design's responsive UI is based on a 12-column grid layout.
Your code would be like this:
<Grid container spacing={1} sx={Style}>
<Grid item xs={12} sm={6}>
<BackToButton />
</Grid>
<Grid item xs={12} sm={6}>
<SearchTextField />
</Grid>
</Grid>
I strongly recommend reading the MUI Grid Docs.
Related
I have here a CircularLoader using Material UI.
My only problem is that if the variant is indeterminate then show the loader as is with no percentage while if it is determinate show the percentage inside the loader and the text as is.
Codesandbox: CLICK HERE
const CircularLoader = (props) => {
const {
height = "auto",
color = "primary",
text = "",
value = 0,
variant = "indeterminate"
} = props;
return (
<Grid
component="div"
container
justifyContent="center"
alignItems="center"
flexDirection="column"
sx={{ height }}
>
<CircularProgress
// variant={variant}
// value={value}
disableShrink
color={color}
thickness={4}
sx={{ marginBottom: "0.5rem" }}
/>
<Typography variant="body1" component="div" color={color}>
{text}
</Typography>
</Grid>
);
};
export default CircularLoader;
I think it's probably best to split these components up (since they have different props). But if you want them in the same component, you need to provide a dynamic value from 0-100 to the determinate variant. See the docs source code example.
In your case you could have a ternary choose between which variant to render, as a quick demo the following should work (note i'm ignoring value prop and simulating it with progress for demo purposes):
import React from "react";
import Grid from "#mui/material/Grid";
import Stack from "#mui/material/Stack";
import Typography from "#mui/material/Typography";
import CircularProgress from "#mui/material/CircularProgress";
import Box from '#mui/material/Box';
const CircularLoader = ({
height = "auto",
color = "primary",
text = "",
value = 0,
variant = "indeterminate"
}) => {
const [progress, setProgress] = React.useState(0);
React.useEffect(() => {
const timer = setInterval(() => {
setProgress((prevProgress) =>
prevProgress >= 100 ? 0 : prevProgress + 10
);
}, 800);
return () => {
clearInterval(timer);
};
}, []);
return (
<Grid
component="div"
container
justifyContent="center"
alignItems="center"
flexDirection="column"
sx={{ height }}
>
{variant === "indeterminate" ? (
<CircularProgress />
) : (
<Stack spacing={2} direction="row">
<Box sx={{ position: 'relative', display: 'inline-flex' }}>
<CircularProgress variant="determinate" value={progress}/>
<Box
sx={{
top: 0,
left: 0,
bottom: 0,
right: 0,
position: 'absolute',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<Typography variant="caption" component="div" color="text.secondary">
{`${Math.round(progress)}%`}
</Typography>
</Box>
</Box>
</Stack>
)}
<Typography variant="body1" component="div" color={color}>
{text}
</Typography>
</Grid>
);
};
export default CircularLoader;
progress is just simulated in this case for demo purposes, whereas you need to pass down progress as value via props and use that in the CircularProgress component (it should be some dynamic value linked to actual loading progress state).
Link to Sandbox
I am trying to set up a simple layout for a SPA using Material-ui and React. My idea is to have a left hand column as a sidebar and a right-hand main area to render information etc. However, in my current set-up I have two issues:
The <Grid item> and its container <Button> elements extend beyond the left sidebard <Grid container item xs={3} className={classes.sideBarGrid}> into the right hand column. I am not sure what I am doing wrong and any help would be greatly appreciated!
Code Sandbox
Also, I cannot get the right hand grid column <Grid container item xs={9} className={classes.labelGrid}> get to work to be full width, even though I set it to width: "100%".
Code:
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import CssBaseline from "#material-ui/core/CssBaseline";
import Typography from "#material-ui/core/Typography";
import Grid from "#material-ui/core/Grid";
import Button from "#material-ui/core/Button";
import TextField from "#material-ui/core/TextField";
const useStyles = makeStyles(theme => ({
mainContainer: {
width: "100vw",
height: "100vh"
},
labelGrid: {
flexGrow: 1,
flexDirection: "column",
backgroundColor: "#EBEDF0",
alignItems: "center",
justifyContent: "center",
width: "100%"
},
sideBarGrid: {
maxWidth: 300,
flexDirection: "column",
justifyContent: "space-between"
},
avatar: {
margin: theme.spacing(1),
backgroundColor: theme.palette.secondary.main
},
labelarea: {
margin: theme.spacing(1)
},
imagearea: {
minHeight: 200
},
classButton: {
margin: theme.spacing(1)
},
submit: {
margin: theme.spacing(1)
},
commentField: {
margin: theme.spacing(2, 2, 3)
}
}));
export default function Labelscreen(props) {
const classes = useStyles();
// history for react router
// array with potential classes for image
const buttonText = ["one", "two"];
// function to filter list of labels by property and see if object property is null
return (
<Grid container className={classes.mainContainer}>
<CssBaseline />
<Grid container item xs={3} className={classes.sideBarGrid}>
<Grid item>
{buttonText.map((item, key) => (
<Button
className={classes.classButton}
variant="outlined"
color="primary"
fullWidth
>
{item} ({key + 1})
</Button>
))}
<TextField
id="imageComment"
label="Comment"
placeholder="please put comments here"
multiline
fullWidth
variant="outlined"
value="adfljdaf"
/>
</Grid>
<Grid item>
<Button
type="submit"
fullWidth
variant="contained"
color="secondary"
className={classes.submit}
>
Go back
</Button>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
>
Next
</Button>
</Grid>
</Grid>
<Grid container item xs={9} className={classes.labelGrid}>
<Typography component="h1" variant="h5">
Something
</Typography>
</Grid>
</Grid>
);
}
EDIT
The gray area on the right hand does not fill the whole screen when the screen size is large, even though the width is set to 100% in the labelGrid
Your buttons have margin (right & left), so they move beyond your left sidebar.
You can fix this using:
classButton: {
margin: theme.spacing(1, 0)
},
submit: {
margin: theme.spacing(1, 0)
},
To add back the space on the left&right side you can add padding on the container:
sideBarGrid: {
maxWidth: 300,
flexDirection: "column",
justifyContent: "space-between",
padding: theme.spacing(0, 1)
},
I am trying to make my card responsive to mobile applications
const styles = {
edit: {
width: "40%",
marginLeft: 270,
background: "#76ff03"
},
add: {
width: "100%",
background: "#18ffff",
size: "large"
},
root: {
minHeight: 210,
minWidth: 100
}
};
<Container maxWidth="md">
{/*marginTop:15*/}
<Typography component="div" style={{ borderColor:'#00c853' }}>
{/*style={{ height: '30vh' }}*/}
<Card style={styles.root}>
<Typography variant="overline" color="secondary" style={{fontFamily:'Roboto',margin:10}}>
All about your needs
</Typography>
<form onSubmit={this.validateItem} autoComplete='off'>
<TextField id="outlined-full-width" label="Input" style={{ width:'90%',marginTop:30 ,marginLeft:40 }}
placeholder="Add A Todo Item " margin="normal" InputLabelProps={{
shrink: true,
}} error={this.state.errorState} helperText={ this.state.errorState
&& "Item name can't be blank" } size="large" variant="outlined" value={newItem} onChange={handleInput} />
<Grid container justify='center' alignContent='center'>
<Grid item xs={12} md={6}>
{buttonChange()}
</Grid>
</Grid>
</form>
</Card>
</Typography>
</Container>
</div>
The above code is the user interface for the card component , i am trying to make the card component mobile responsive , but the interface i get instead is given below
The card and the text field should be able to be responsive to the screen size, but i am unable to make it work. Is there a way i can make it happen?
Hello and thank you for asking your question,
you can use the breakpoint of [theme.breakpoints.down("(XS, sm,md, lg, xl,)")] : {
maxWidth: 200 // you can change the size of your card component.
}
Here is a clearer example from the material ui Card component
Here is the component from the material UI Card component page, I only added the useTheme and useMediaQuery imports, and added a medium breakpoint inside useStyle under classes.root Here is a useful link on "useMediaQuery" https://material-ui.com/components/use-media-query/#usemediaquery
import { useTheme } from "#material-ui/styles";
import useMediaQuery from "#material-ui/core/useMediaQuery";
const useStyles = makeStyles(theme => ({
root: {
maxWidth: 345,
[theme.breakpoints.down("md")] : {
maxWidth: 200
}
},
media: {
height: 140
}
}));
const Card = () => {
const classes = useStyles();
const theme = useTheme();
const matches = useMediaQuery(theme.breakpoints.up("sm"));
return (
<Card className={classes.root}>
<CardActionArea>
<CardMedia
className={classes.media}
title="Contemplative Reptile"
/>
<CardContent>
<Typography gutterBottom variant="h5" component="h2">
Lizard
</Typography>
<Typography variant="body2" color="textSecondary" component="p">
Lizards are a widespread group of squamate reptiles, with over 6,000
species, ranging across all continents except Antarctica
</Typography>
</CardContent>
</CardActionArea>
<CardActions>
<Button size="small" color="primary">
Share
</Button>
<Button size="small" color="primary">
Learn More
</Button>
</CardActions>
</Card>
);
}
Hope this helps
I am starting to use React/Material-UI, and also new to CSS etc...
I have a simple page layout with an APPBar. Unfortunately this AppBar overlaps the elements which are meant to go below it.
I have found this answer:
AppBar Material UI questions
But this feels completely wrong. What if my AppBar has a variable height, depending on the icons, display modes etc...?
I have tried to create a vertical grid, to wrap the elements in different items, made the top container a flex one and play with flex settings, nothing seems to work, the app bar always sits on top of the text.
The code is very simple:
import React from 'react';
import { AppBar, Typography, Box } from '#material-ui/core';
function App() {
return (
<div>
<AppBar>
<Typography variant='h3'>
AppBar
</Typography>
</AppBar>
<Box>
<Typography variant='h1' style={{ border: '1px solid black' }}>
Hello
</Typography>
</Box>
</div>
)
}
export default App;
The "Hello" text chunk is only half visible:
This is happening because the MaterialUI App Bar defaults to position="fixed". This separates it from the standard DOM's layout to allow content to scroll beneath it, but as a result no space is made for it on the page.
You can get around this by wrapping all content below it in a div and specifying enough margin, or by changing the position property of <AppBar> so it's no longer "fixed". In your example, you could also just apply the styles to <Box> if that's the only content below the <AppBar>.
e.g.
import React from 'react';
import { AppBar, Typography, Box } from '#material-ui/core';
function App() {
return (
<div>
<AppBar>
<Typography variant='h3'>
AppBar
</Typography>
</AppBar>
<div style={{marginTop: 80}}>
<Box>
<Typography variant='h1' style={{ border: '1px solid black' }}>
Hello
</Typography>
</Box>
</div>
</div>
)
}
export default App;
MaterialUI provides a theme mixin for the AppBar that can help. Not sure if you're using the recomended JSS setup, but you can do something like this:
import withStyles from '#material-ui/core/styles/withStyles';
const styles = theme => ({
appBarSpacer: theme.mixins.toolbar
});
const style = withStyles(styles)
function MyScreen ({ classes }) {
<AppBar></AppBar>
<div className={classes.appBarSpacer}></div>
<Box></Box>
}
export default style(MyScreen)
The mixin will give that div the same height as your AppBar, pushing down the other content.
According to Material-ui, there are 3 solutions to this problem.
https://material-ui.com/components/app-bar/#fixed-placement
You can use position="sticky" instead of fixed. ⚠️ sticky is not supported by IE 11.
You can render a second component
You can use theme.mixins.toolbar CSS
I personally enjoy using the 2nd solution like this.
return (
<>
<AppBar position="fixed">
<Toolbar>{/* content */}</Toolbar>
</AppBar>
<Toolbar />
</>
);
<AppBar position='static'>
use this it will do it and content won't hide under Appear
I think having a good app setup is opinianted, but I would recommend the following
import React from "react";
import ReactDOM from "react-dom";
import {
AppBar,
Typography,
Box,
CssBaseline,
makeStyles,
Container,
Grid,
Toolbar
} from "#material-ui/core";
const useStyles = makeStyles(theme => ({
content: {
flexGrow: 1,
height: "100vh",
overflow: "auto"
},
appBarSpacer: theme.mixins.toolbar,
title: {
flexGrow: 1
},
container: {
paddingTop: theme.spacing(4),
paddingBottom: theme.spacing(4)
}
}));
function App() {
const classes = useStyles();
return (
<div className={classes.root}>
<CssBaseline />
<AppBar position="absolute">
<Toolbar className={classes.toolbar}>
<Typography
component="h1"
variant="h6"
color="inherit"
noWrap
className={classes.title}
>
AppBar
</Typography>
</Toolbar>
</AppBar>
<main className={classes.content}>
<div className={classes.appBarSpacer} />
<Container maxWidth="lg" className={classes.container}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Box>
<Typography variant="h1" style={{ border: "1px solid black" }}>
Hello
</Typography>
</Box>
</Grid>
</Grid>
</Container>
</main>
</div>
);
}
try this!
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
[theme.breakpoints.down('sm')]: {
marginBottom: 56,
},
[theme.breakpoints.up('sm')]: {
marginBottom: 64,
},
},
menuButton: {
marginRight: theme.spacing(1),
},
title: {
flexGrow: 1,
}, }))
You can add the above to your code like this
const Navbar = () => {
const classes = useStyles()
return (
<div className={classes.root}>
<AppBar position='fixed' color='primary'>
<Toolbar>
<IconButton
edge='start'
className={classes.menuButton}
color='inherit'
aria-label='menu'>
<MenuIcon />
</IconButton>
<Typography variant='h6' className={classes.title}>
News
</Typography>
<Button color='inherit'>Login</Button>
</Toolbar>
</AppBar>
</div>
)}
For more documentation visit material-ui breakpoint customization
I'm working on a map with an image overlay that has adjustable opacity. Here is the component code:
import React from 'react'
import PropTypes from 'prop-types'
import { MapWithGroundOverlay } from './MapWithGroundOverlay'
import { withStyles } from '#material-ui/core/styles'
import Box from '#material-ui/core/Box'
import FormLabel from '#material-ui/core/FormLabel'
import Slider from '#material-ui/lab/Slider'
import Grid from '#material-ui/core/Grid'
import Paper from '#material-ui/core/Paper'
const styles = theme => ({
root: {
flexGrow: 1,
},
paper: {
padding: theme.spacing(2),
textAlign: 'center',
color: theme.palette.text.secondary,
},
label: {
padding: theme.spacing(3),
}
})
class AdjustableGroundoverlay extends React.PureComponent {
constructor(props, context) {
super(props, context)
this.state = {opacity: 0.5}
this.handleChange = this.handleChange.bind(this);
}
handleChange(event, value) {
this.setState(state => ({
opacity: value
}));
}
render() {
return (
<Grid container className={this.props.classes.root} spacing={2}>
<Grid item xs={12}>
<MapWithGroundOverlay
googleMapURL={`https://maps.googleapis.com/maps/api/js?key=${process.env.REACT_APP_GOOGLE_MAPS_API_KEY}&v=3.exp&libraries=geometry,drawing,places`}
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `600px` }} />}
mapElement={<div style={{ height: `100%` }} />}
opacity={this.state.opacity}
/>
</Grid>
<Grid item xs={6}>
<Paper className={this.props.classes.paper}>
<Box flexDirection="column">
<FormLabel className={this.props.classes.label}>Overlay opacity</FormLabel>
<Slider
value={this.state.opacity}
min={0}
max={1}
onChange={this.handleChange}
/>
</Box>
</Paper>
</Grid>
</Grid>
);
}
}
AdjustableGroundoverlay.propTypes = {
classes: PropTypes.object.isRequired,
}
export default withStyles(styles)(AdjustableGroundoverlay)
The problem is that the FormLabel and Slider are too close together. If I hover over them, I see that the Slider has a negative margin of -24px:
It seems like the content of the FormLabel therefore sits directly on top of it:
I've tried to change the styling of the Slider by adding these classes to the component in accordance with https://material-ui.com/api/slider/#css:
<Slider
classes={{container: {marginTop: -12}}}
value={this.state.opacity}
min={0}
max={1}
onChange={this.handleChange}
/>
but the spacing between the FormLabel and the Slider remains the same. Any idea what is wrong with this implementation?
Update
I've noticed in the console that there is this error:
I'm not sure why the key 'container' is not valid though since it is mentioned in https://material-ui.com/api/slider/#css.
I resolved this by putting the slider in a Box with mt set to 1:
<Paper className={this.props.classes.paper}>
<Box flexDirection="column">
<FormLabel className={this.props.classes.label}>Overlay opacity</FormLabel>
<Box mt={1}>
<Slider
value={this.state.opacity}
min={0}
max={1}
onChange={this.handleChange}
/>
</Box>
</Box>
</Paper>
Now there is more spacing between the label and the slider: